39 #include <linux/tty_driver.h> |
39 #include <linux/tty_driver.h> |
40 #include <linux/tty_flip.h> |
40 #include <linux/tty_flip.h> |
41 #include <linux/termios.h> |
41 #include <linux/termios.h> |
42 #include <linux/timer.h> |
42 #include <linux/timer.h> |
43 #include <linux/version.h> |
43 #include <linux/version.h> |
|
44 #include <linux/serial.h> |
|
45 #include <linux/uaccess.h> |
44 |
46 |
45 #include "../master/globals.h" |
47 #include "../master/globals.h" |
46 #include "../include/ectty.h" |
48 #include "../include/ectty.h" |
47 |
49 |
48 /*****************************************************************************/ |
50 /*****************************************************************************/ |
49 |
51 |
50 #define PFX "ec_tty: " |
52 #define PFX "ec_tty: " |
51 |
53 |
52 #define EC_TTY_MAX_DEVICES 10 |
54 #define EC_TTY_MAX_DEVICES 32 |
53 #define EC_TTY_TX_BUFFER_SIZE 100 |
55 #define EC_TTY_TX_BUFFER_SIZE 100 |
54 #define EC_TTY_RX_BUFFER_SIZE 100 |
56 #define EC_TTY_RX_BUFFER_SIZE 100 |
55 |
57 |
56 #define EC_TTY_DEBUG 0 |
58 #define EC_TTY_DEBUG 0 |
57 |
59 |
76 module_param_named(debug_level, debug_level, uint, S_IRUGO); |
80 module_param_named(debug_level, debug_level, uint, S_IRUGO); |
77 MODULE_PARM_DESC(debug_level, "Debug level"); |
81 MODULE_PARM_DESC(debug_level, "Debug level"); |
78 |
82 |
79 /** \endcond */ |
83 /** \endcond */ |
80 |
84 |
|
85 /** Standard termios for ec_tty devices. |
|
86 * |
|
87 * Simplest possible configuration, as you would expect. |
|
88 */ |
81 static struct ktermios ec_tty_std_termios = { |
89 static struct ktermios ec_tty_std_termios = { |
82 .c_iflag = ICRNL | IXON, |
90 .c_iflag = 0, |
83 .c_oflag = OPOST, |
91 .c_oflag = 0, |
84 .c_cflag = B38400 | CS8 | CREAD | HUPCL, |
92 .c_cflag = B9600 | CS8 | CREAD, |
85 .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN, |
93 .c_lflag = 0, |
86 .c_cc = INIT_C_CC, |
94 .c_cc = INIT_C_CC, |
87 }; |
95 }; |
88 |
96 |
89 struct ec_tty { |
97 struct ec_tty { |
90 int minor; |
98 int minor; |
137 tty_driver->minor_start = 0; |
148 tty_driver->minor_start = 0; |
138 tty_driver->type = TTY_DRIVER_TYPE_SERIAL; |
149 tty_driver->type = TTY_DRIVER_TYPE_SERIAL; |
139 tty_driver->subtype = SERIAL_TYPE_NORMAL; |
150 tty_driver->subtype = SERIAL_TYPE_NORMAL; |
140 tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; |
151 tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; |
141 tty_driver->init_termios = ec_tty_std_termios; |
152 tty_driver->init_termios = ec_tty_std_termios; |
142 tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; |
|
143 tty_set_operations(tty_driver, &ec_tty_ops); |
153 tty_set_operations(tty_driver, &ec_tty_ops); |
144 |
154 |
145 ret = tty_register_driver(tty_driver); |
155 ret = tty_register_driver(tty_driver); |
146 if (ret) { |
156 if (ret) { |
147 printk(KERN_ERR PFX "Failed to register tty driver.\n"); |
157 printk(KERN_ERR PFX "Failed to register tty driver.\n"); |
167 tty_unregister_driver(tty_driver); |
177 tty_unregister_driver(tty_driver); |
168 put_tty_driver(tty_driver); |
178 put_tty_driver(tty_driver); |
169 printk(KERN_INFO PFX "Module unloading.\n"); |
179 printk(KERN_INFO PFX "Module unloading.\n"); |
170 } |
180 } |
171 |
181 |
|
182 /****************************************************************************** |
|
183 * ec_tty_t methods. |
|
184 *****************************************************************************/ |
|
185 |
|
186 int ec_tty_init(ec_tty_t *t, int minor, |
|
187 const ec_tty_operations_t *ops, void *cb_data) |
|
188 { |
|
189 int ret; |
|
190 tcflag_t cflag; |
|
191 struct tty_struct *tty; |
|
192 |
|
193 t->minor = minor; |
|
194 t->tx_read_idx = 0; |
|
195 t->tx_write_idx = 0; |
|
196 t->wakeup = 0; |
|
197 t->rx_read_idx = 0; |
|
198 t->rx_write_idx = 0; |
|
199 init_timer(&t->timer); |
|
200 t->tty = NULL; |
|
201 t->ops = *ops; |
|
202 t->cb_data = cb_data; |
|
203 |
|
204 t->dev = tty_register_device(tty_driver, t->minor, NULL); |
|
205 if (IS_ERR(t->dev)) { |
|
206 printk(KERN_ERR PFX "Failed to register tty device.\n"); |
|
207 return PTR_ERR(t->dev); |
|
208 } |
|
209 |
|
210 // Tell the device-specific implementation about the initial cflags |
|
211 tty = tty_driver->ttys[minor]; |
|
212 |
|
213 if (tty && tty->termios) { // already opened before |
|
214 cflag = tty->termios->c_cflag; |
|
215 } else { |
|
216 cflag = tty_driver->init_termios.c_cflag; |
|
217 } |
|
218 ret = t->ops.cflag_changed(t->cb_data, cflag); |
|
219 if (ret) { |
|
220 printk(KERN_ERR PFX "ERROR: Initial cflag 0x%x not accepted.\n", |
|
221 cflag); |
|
222 tty_unregister_device(tty_driver, t->minor); |
|
223 return ret; |
|
224 } |
|
225 |
|
226 t->timer.function = ec_tty_wakeup; |
|
227 t->timer.data = (unsigned long) t; |
|
228 t->timer.expires = jiffies + 10; |
|
229 add_timer(&t->timer); |
|
230 return 0; |
|
231 } |
|
232 |
|
233 /*****************************************************************************/ |
|
234 |
|
235 void ec_tty_clear(ec_tty_t *tty) |
|
236 { |
|
237 del_timer_sync(&tty->timer); |
|
238 tty_unregister_device(tty_driver, tty->minor); |
|
239 } |
|
240 |
172 /*****************************************************************************/ |
241 /*****************************************************************************/ |
173 |
242 |
174 unsigned int ec_tty_tx_size(ec_tty_t *tty) |
243 unsigned int ec_tty_tx_size(ec_tty_t *tty) |
175 { |
244 { |
176 unsigned int ret; |
245 unsigned int ret; |
213 return EC_TTY_RX_BUFFER_SIZE - 1 - ec_tty_rx_size(tty); |
282 return EC_TTY_RX_BUFFER_SIZE - 1 - ec_tty_rx_size(tty); |
214 } |
283 } |
215 |
284 |
216 /*****************************************************************************/ |
285 /*****************************************************************************/ |
217 |
286 |
|
287 int ec_tty_get_serial_info(ec_tty_t *tty, struct serial_struct *data) |
|
288 { |
|
289 struct serial_struct tmp; |
|
290 |
|
291 if (!data) |
|
292 return -EFAULT; |
|
293 |
|
294 memset(&tmp, 0, sizeof(tmp)); |
|
295 |
|
296 if (copy_to_user(data, &tmp, sizeof(*data))) { |
|
297 return -EFAULT; |
|
298 } |
|
299 return 0; |
|
300 } |
|
301 |
|
302 /*****************************************************************************/ |
|
303 |
|
304 /** Timer function. |
|
305 */ |
218 void ec_tty_wakeup(unsigned long data) |
306 void ec_tty_wakeup(unsigned long data) |
219 { |
307 { |
220 ec_tty_t *tty = (ec_tty_t *) data; |
308 ec_tty_t *tty = (ec_tty_t *) data; |
221 size_t to_recv; |
309 size_t to_recv; |
222 |
310 |
255 printk(KERN_INFO PFX "Pushing %u bytes to TTY core.\n", to_recv); |
343 printk(KERN_INFO PFX "Pushing %u bytes to TTY core.\n", to_recv); |
256 #endif |
344 #endif |
257 |
345 |
258 for (i = 0; i < to_recv; i++) { |
346 for (i = 0; i < to_recv; i++) { |
259 cbuf[i] = tty->rx_buffer[tty->rx_read_idx]; |
347 cbuf[i] = tty->rx_buffer[tty->rx_read_idx]; |
260 tty->rx_read_idx = (tty->rx_read_idx + 1) % EC_TTY_RX_BUFFER_SIZE; |
348 tty->rx_read_idx = |
|
349 (tty->rx_read_idx + 1) % EC_TTY_RX_BUFFER_SIZE; |
261 } |
350 } |
262 tty_flip_buffer_push(tty->tty); |
351 tty_flip_buffer_push(tty->tty); |
263 } |
352 } |
264 } |
353 } |
265 |
354 |
266 tty->timer.expires += 1; |
355 tty->timer.expires += 1; |
267 add_timer(&tty->timer); |
356 add_timer(&tty->timer); |
268 } |
357 } |
269 |
358 |
270 /*****************************************************************************/ |
|
271 |
|
272 int ec_tty_init(ec_tty_t *tty, int minor) |
|
273 { |
|
274 tty->minor = minor; |
|
275 tty->tx_read_idx = 0; |
|
276 tty->tx_write_idx = 0; |
|
277 tty->wakeup = 0; |
|
278 tty->rx_read_idx = 0; |
|
279 tty->rx_write_idx = 0; |
|
280 init_timer(&tty->timer); |
|
281 tty->tty = NULL; |
|
282 |
|
283 tty->dev = tty_register_device(tty_driver, tty->minor, NULL); |
|
284 if (IS_ERR(tty->dev)) { |
|
285 printk(KERN_ERR PFX "Failed to register tty device.\n"); |
|
286 return PTR_ERR(tty->dev); |
|
287 } |
|
288 |
|
289 tty->timer.function = ec_tty_wakeup; |
|
290 tty->timer.data = (unsigned long) tty; |
|
291 tty->timer.expires = jiffies + 10; |
|
292 add_timer(&tty->timer); |
|
293 return 0; |
|
294 } |
|
295 |
|
296 /*****************************************************************************/ |
|
297 |
|
298 void ec_tty_clear(ec_tty_t *tty) |
|
299 { |
|
300 del_timer_sync(&tty->timer); |
|
301 tty_unregister_device(tty_driver, tty->minor); |
|
302 } |
|
303 |
|
304 /****************************************************************************** |
359 /****************************************************************************** |
305 * Device callbacks |
360 * Device callbacks |
306 *****************************************************************************/ |
361 *****************************************************************************/ |
307 |
362 |
308 static int ec_tty_open(struct tty_struct *tty, struct file *file) |
363 static int ec_tty_open(struct tty_struct *tty, struct file *file) |
445 static void ec_tty_flush_buffer(struct tty_struct *tty) |
500 static void ec_tty_flush_buffer(struct tty_struct *tty) |
446 { |
501 { |
447 #if EC_TTY_DEBUG >= 2 |
502 #if EC_TTY_DEBUG >= 2 |
448 printk(KERN_INFO PFX "%s().\n", __func__); |
503 printk(KERN_INFO PFX "%s().\n", __func__); |
449 #endif |
504 #endif |
|
505 |
|
506 // FIXME empty ring buffer |
450 } |
507 } |
451 |
508 |
452 /*****************************************************************************/ |
509 /*****************************************************************************/ |
453 |
510 |
454 static int ec_tty_ioctl(struct tty_struct *tty, struct file *file, |
511 static int ec_tty_ioctl(struct tty_struct *tty, struct file *file, |
455 unsigned int cmd, unsigned long arg) |
512 unsigned int cmd, unsigned long arg) |
456 { |
513 { |
|
514 ec_tty_t *t = (ec_tty_t *) tty->driver_data; |
|
515 int ret = -ENOTTY; |
|
516 |
|
517 #if EC_TTY_DEBUG >= 2 |
|
518 printk(KERN_INFO PFX "%s(tty=%p, file=%p, cmd=%08x, arg=%08lx).\n", |
|
519 __func__, tty, file, cmd, arg); |
|
520 printk(KERN_INFO PFX "decoded: type=%02x nr=%u\n", |
|
521 _IOC_TYPE(cmd), _IOC_NR(cmd)); |
|
522 #endif |
|
523 |
|
524 switch (cmd) { |
|
525 case TIOCGSERIAL: |
|
526 if (access_ok(VERIFY_WRITE, |
|
527 (void *) arg, sizeof(struct serial_struct))) { |
|
528 ret = ec_tty_get_serial_info(t, (struct serial_struct *) arg); |
|
529 } else { |
|
530 ret = -EFAULT; |
|
531 } |
|
532 break; |
|
533 |
|
534 case TIOCSSERIAL: // TODO |
|
535 break; |
|
536 |
|
537 default: |
|
538 #if EC_TTY_DEBUG >= 2 |
|
539 printk(KERN_INFO PFX "no ioctl() -> handled by tty core!\n"); |
|
540 #endif |
|
541 ret = -ENOIOCTLCMD; |
|
542 break; |
|
543 } |
|
544 |
|
545 return ret; |
|
546 } |
|
547 |
|
548 /*****************************************************************************/ |
|
549 |
|
550 static void ec_tty_set_termios(struct tty_struct *tty, |
|
551 struct ktermios *old_termios) |
|
552 { |
|
553 ec_tty_t *t = (ec_tty_t *) tty->driver_data; |
|
554 int ret; |
|
555 |
457 #if EC_TTY_DEBUG >= 2 |
556 #if EC_TTY_DEBUG >= 2 |
458 printk(KERN_INFO PFX "%s().\n", __func__); |
557 printk(KERN_INFO PFX "%s().\n", __func__); |
459 #endif |
558 #endif |
460 return -ENOTTY; |
559 |
461 } |
560 if (tty->termios->c_cflag == old_termios->c_cflag) |
462 |
561 return; |
463 /*****************************************************************************/ |
562 |
464 |
563 #if EC_TTY_DEBUG >= 2 |
465 static void ec_tty_throttle(struct tty_struct *tty) |
564 printk(KERN_INFO "cflag changed from %x to %x.\n", |
466 { |
565 old_termios->c_cflag, tty->termios->c_cflag); |
467 #if EC_TTY_DEBUG >= 2 |
566 #endif |
468 printk(KERN_INFO PFX "%s().\n", __func__); |
567 |
469 #endif |
568 ret = t->ops.cflag_changed(t->cb_data, tty->termios->c_cflag); |
470 } |
569 if (ret) { |
471 |
570 printk(KERN_ERR PFX "ERROR: cflag 0x%x not accepted.\n", |
472 /*****************************************************************************/ |
571 tty->termios->c_cflag); |
473 |
572 tty->termios->c_cflag = old_termios->c_cflag; |
474 static void ec_tty_unthrottle(struct tty_struct *tty) |
573 } |
475 { |
|
476 #if EC_TTY_DEBUG >= 2 |
|
477 printk(KERN_INFO PFX "%s().\n", __func__); |
|
478 #endif |
|
479 } |
|
480 |
|
481 /*****************************************************************************/ |
|
482 |
|
483 static void ec_tty_set_termios(struct tty_struct *tty, |
|
484 struct ktermios *old_termios) |
|
485 { |
|
486 #if EC_TTY_DEBUG >= 2 |
|
487 printk(KERN_INFO PFX "%s().\n", __func__); |
|
488 #endif |
|
489 } |
574 } |
490 |
575 |
491 /*****************************************************************************/ |
576 /*****************************************************************************/ |
492 |
577 |
493 static void ec_tty_stop(struct tty_struct *tty) |
578 static void ec_tty_stop(struct tty_struct *tty) |
550 #endif |
635 #endif |
551 } |
636 } |
552 |
637 |
553 /*****************************************************************************/ |
638 /*****************************************************************************/ |
554 |
639 |
555 static int ec_tty_tiocmget(struct tty_struct *tty, struct file *file) |
|
556 { |
|
557 #if EC_TTY_DEBUG >= 2 |
|
558 printk(KERN_INFO PFX "%s().\n", __func__); |
|
559 #endif |
|
560 return -EBUSY; |
|
561 } |
|
562 |
|
563 /*****************************************************************************/ |
|
564 |
|
565 static int ec_tty_tiocmset(struct tty_struct *tty, struct file *file, |
|
566 unsigned int set, unsigned int clear) |
|
567 { |
|
568 #if EC_TTY_DEBUG >= 2 |
|
569 printk(KERN_INFO PFX "%s(set=%u, clear=%u).\n", __func__, set, clear); |
|
570 #endif |
|
571 return -EBUSY; |
|
572 } |
|
573 |
|
574 /*****************************************************************************/ |
|
575 |
|
576 static const struct tty_operations ec_tty_ops = { |
640 static const struct tty_operations ec_tty_ops = { |
577 .open = ec_tty_open, |
641 .open = ec_tty_open, |
578 .close = ec_tty_close, |
642 .close = ec_tty_close, |
579 .write = ec_tty_write, |
643 .write = ec_tty_write, |
580 .put_char = ec_tty_put_char, |
644 .put_char = ec_tty_put_char, |
581 .write_room = ec_tty_write_room, |
645 .write_room = ec_tty_write_room, |
582 .chars_in_buffer = ec_tty_chars_in_buffer, |
646 .chars_in_buffer = ec_tty_chars_in_buffer, |
583 .flush_buffer = ec_tty_flush_buffer, |
647 .flush_buffer = ec_tty_flush_buffer, |
584 .ioctl = ec_tty_ioctl, |
648 .ioctl = ec_tty_ioctl, |
585 .throttle = ec_tty_throttle, |
649 .set_termios = ec_tty_set_termios, |
586 .unthrottle = ec_tty_unthrottle, |
650 .stop = ec_tty_stop, |
587 .set_termios = ec_tty_set_termios, |
651 .start = ec_tty_start, |
588 .stop = ec_tty_stop, |
652 .hangup = ec_tty_hangup, |
589 .start = ec_tty_start, |
653 .break_ctl = ec_tty_break, |
590 .hangup = ec_tty_hangup, |
654 .send_xchar = ec_tty_send_xchar, |
591 .break_ctl = ec_tty_break, |
655 .wait_until_sent = ec_tty_wait_until_sent, |
592 .send_xchar = ec_tty_send_xchar, |
|
593 .wait_until_sent = ec_tty_wait_until_sent, |
|
594 .tiocmget = ec_tty_tiocmget, |
|
595 .tiocmset = ec_tty_tiocmset, |
|
596 }; |
656 }; |
597 |
657 |
598 /****************************************************************************** |
658 /****************************************************************************** |
599 * Public functions and methods |
659 * Public functions and methods |
600 *****************************************************************************/ |
660 *****************************************************************************/ |
601 |
661 |
602 ec_tty_t *ectty_create(void) |
662 ec_tty_t *ectty_create(const ec_tty_operations_t *ops, void *cb_data) |
603 { |
663 { |
604 ec_tty_t *tty; |
664 ec_tty_t *tty; |
605 int minor, ret; |
665 int minor, ret; |
606 |
666 |
607 if (down_interruptible(&tty_sem)) { |
667 if (down_interruptible(&tty_sem)) { |