001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019 #include <sos/types.h>
020 #include <sos/assert.h>
021 #include <sos/errno.h>
022 #include <sos/fs.h>
023 #include <sos/ksynch.h>
024 #include <sos/kwaitq.h>
025 #include <sos/uaccess.h>
026 #include <sos/kmalloc.h>
027 #include <sos/list.h>
028 #include <sos/chardev.h>
029 #include <drivers/devices.h>
030
031 #include "tty.h"
032
033 #define TTY_BUFFER_LEN 64
034 #define TTY_NAME_LEN 16
035
036 struct tty_device {
037 sos_ui32_t instance;
038 unsigned int open_count;
039 char buffer[TTY_BUFFER_LEN];
040 unsigned int buffer_read;
041 unsigned int buffer_write;
042 struct sos_ksema sem;
043 struct sos_kwaitq wq;
044 sos_ret_t (*write)(char c);
045 unsigned int param;
046 struct tty_device *next, *prev;
047 };
048
049 static struct tty_device *tty_device_list;
050
051 static struct tty_device *
052 tty_device_find (sos_ui32_t instance)
053 {
054 struct tty_device *t;
055 int n;
056
057 list_foreach (tty_device_list, t, n)
058 {
059 if (t->instance == instance)
060 return t;
061 }
062
063 return NULL;
064 }
065
066 static sos_ret_t tty_read(struct sos_fs_opened_file *this,
067 sos_uaddr_t dest_buf, sos_size_t *len)
068 {
069 sos_size_t count = 0;
070 struct tty_device *t;
071 sos_ret_t ret;
072
073 t = (struct tty_device *) this->custom_data;
074
075 if (*len == 0)
076 return SOS_OK;
077
078 while (1)
079 {
080 char c;
081
082
083 sos_ksema_down (& t->sem, NULL);
084
085
086
087 if (t->buffer_read == t->buffer_write)
088 {
089 sos_ksema_up (& t->sem);
090 sos_kwaitq_wait (& t->wq, NULL);
091
092
093
094 continue;
095 }
096
097 c = t->buffer[t->buffer_read];
098
099
100
101 ret = sos_memcpy_to_user (dest_buf,
102 (sos_vaddr_t) & t->buffer[t->buffer_read],
103 sizeof (char));
104 if (sizeof(char) != ret)
105 {
106 *len = count;
107 sos_ksema_up (& t->sem);
108 return ret;
109 }
110
111 dest_buf++;
112
113
114 t->buffer_read++;
115 if (t->buffer_read == TTY_BUFFER_LEN)
116 t->buffer_read = 0;
117
118 count++;
119
120 if (t->param & SOS_IOCTLPARAM_TTY_ECHO)
121 t->write (c);
122
123 sos_ksema_up (& t->sem);
124
125
126 if (count == *len
127 || (c == '\n' && t->param & SOS_IOCTLPARAM_TTY_CANON))
128 break;
129 }
130
131 *len = count;
132 return SOS_OK;
133 }
134
135
136 static sos_ret_t tty_write(struct sos_fs_opened_file *this,
137 sos_uaddr_t src_buf, sos_size_t *len)
138 {
139 struct tty_device *t;
140 char c;
141 unsigned int i;
142 sos_ret_t ret;
143
144 t = (struct tty_device *) this->custom_data;
145
146 for (i = 0; i < *len; i++)
147 {
148 ret = sos_memcpy_from_user ((sos_vaddr_t) & c, src_buf, sizeof(char));
149 if (sizeof(char) != ret)
150 {
151 *len = i;
152 return ret;
153 }
154
155 sos_ksema_down (& t->sem, NULL);
156 t->write (c);
157 sos_ksema_up (& t->sem);
158
159 src_buf++;
160 }
161
162 return SOS_OK;
163 }
164
165
166 static sos_ret_t tty_ioctl(struct sos_fs_opened_file *this, int req_id,
167 sos_ui32_t req_arg)
168 {
169 struct tty_device *t;
170
171 if (req_arg != SOS_IOCTLPARAM_TTY_ECHO
172 && req_arg != SOS_IOCTLPARAM_TTY_CANON)
173 return -SOS_EINVAL;
174
175 t = (struct tty_device *) this->custom_data;
176
177 sos_ksema_down (& t->sem, NULL);
178
179 switch (req_id)
180 {
181 case SOS_IOCTL_TTY_SETPARAM:
182 t->param |= req_arg;
183 break;
184
185 case SOS_IOCTL_TTY_RESETPARAM:
186 t->param &= ~req_arg;
187 break;
188
189 default:
190 sos_ksema_up (& t->sem);
191 return -SOS_EINVAL;
192 }
193
194 sos_ksema_up (& t->sem);
195
196 return SOS_OK;
197 }
198
199 static sos_ret_t tty_open(struct sos_fs_node * fsnode,
200 struct sos_fs_opened_file * of,
201 void * chardev_class_custom_data)
202 {
203 struct tty_device *t;
204
205 t = tty_device_find (fsnode->dev_id.device_instance);
206 if (t == NULL)
207 return -SOS_ENOENT;
208
209 sos_ksema_down (& t->sem, NULL);
210 of->custom_data = t;
211 t->open_count ++;
212 sos_ksema_up (& t->sem);
213
214 return SOS_OK;
215 }
216
217
218 static sos_ret_t tty_close(struct sos_fs_opened_file *of,
219 void *custom_data)
220 {
221 struct tty_device *t;
222
223 t = (struct tty_device *) of->custom_data;
224
225 sos_ksema_down (& t->sem, NULL);
226 t->open_count --;
227 sos_ksema_up (& t->sem);
228
229 return SOS_OK;
230 }
231
232 void tty_add_chars (struct tty_device *t, const char *s)
233 {
234 sos_ksema_down (& t->sem, NULL);
235 while (*s)
236 {
237
238 t->buffer[t->buffer_write] = *s;
239 t->buffer_write++;
240 if (t->buffer_write == TTY_BUFFER_LEN)
241 t->buffer_write = 0;
242 s++;
243 }
244 sos_ksema_up (& t->sem);
245
246
247 sos_kwaitq_wakeup (& t->wq, SOS_KWQ_WAKEUP_ALL, SOS_OK);
248 }
249
250 struct sos_chardev_ops tty_ops = {
251 .read = tty_read,
252 .write = tty_write,
253 .open = tty_open,
254 .close = tty_close,
255 .ioctl = tty_ioctl
256 };
257
258 sos_ret_t tty_create (sos_ui32_t device_instance,
259 sos_ret_t (*write_func) (char c),
260 struct tty_device **tty_out)
261 {
262 struct tty_device *tty;
263
264 if (tty_device_find (device_instance) != NULL)
265 return -SOS_EBUSY;
266
267 tty = (struct tty_device *) sos_kmalloc (sizeof(struct tty_device), 0);
268 if (tty == NULL)
269 return -SOS_ENOMEM;
270
271 memset (tty->buffer, 0, sizeof(tty->buffer));
272 tty->open_count = 0;
273 tty->instance = device_instance;
274 tty->write = write_func;
275 tty->buffer_read = 0;
276 tty->buffer_write = 0;
277 tty->param = SOS_IOCTLPARAM_TTY_CANON;
278 sos_kwaitq_init(& tty->wq, "tty", SOS_KWQ_ORDER_FIFO);
279 sos_ksema_init(& tty->sem, "tty", 1, SOS_KWQ_ORDER_FIFO);
280
281 list_add_tail (tty_device_list, tty);
282
283 *tty_out = tty;
284
285 return SOS_OK;
286 }
287
288 sos_ret_t tty_remove (struct tty_device *tty)
289 {
290 if (tty == NULL)
291 return -SOS_EINVAL;
292
293 if (SOS_OK != sos_ksema_trydown (& tty->sem))
294 return -SOS_EBUSY;
295
296 if (tty->open_count != 0)
297 return -SOS_EBUSY;
298
299 sos_kwaitq_dispose (& tty->wq);
300 list_delete (tty_device_list, tty);
301
302 sos_kfree ((sos_vaddr_t) tty);
303
304 return SOS_OK;
305 }
306
307 sos_ret_t tty_subsystem_setup (void)
308 {
309 list_init (tty_device_list);
310 return sos_chardev_register_class (SOS_CHARDEV_TTY_MAJOR,
311 & tty_ops, NULL);
312 }
313
314 sos_ret_t tty_subsystem_cleanup (void)
315 {
316 return sos_chardev_unregister_class (SOS_CHARDEV_TTY_MAJOR);
317 }
318