1 /*
2 * Tiny TTY driver
3 *
4 * Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 2 of the License.
9 *
10 * This driver shows how to create a minimal tty driver. It does not rely on
11 * any backing hardware, but creates a timer that emulates data being received
12 * from some kind of hardware.
13 */
14
15 #include <linux/kernel.h>
16 #include <linux/errno.h>
17 #include <linux/init.h>
18 #include <linux/module.h>
19 #include <linux/slab.h>
20 #include <linux/wait.h>
21 #include <linux/tty.h>
22 #include <linux/tty_driver.h>
23 #include <linux/tty_flip.h>
24 #include <linux/serial.h>
25 #include <linux/sched.h>
26 #include <asm/uaccess.h>
27
28
29 #define DRIVER_VERSION "v2.0"
30 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
31 #define DRIVER_DESC "Tiny TTY driver"
32
33 /* Module information */
34 MODULE_AUTHOR( DRIVER_AUTHOR );
35 MODULE_DESCRIPTION( DRIVER_DESC );
36 MODULE_LICENSE("GPL");
37
38 #define DELAY_TIME HZ * 2 /* 2 seconds per character */
39 #define TINY_DATA_CHARACTER 't'
40
41 #define TINY_TTY_MAJOR 240 /* experimental range */
42 #define TINY_TTY_MINORS 4 /* only have 4 devices */
43
44 struct tiny_serial {
45 struct tty_struct *tty; /* pointer to the tty for this device */
46 int open_count; /* number of times this port has been opened */
47 struct semaphore sem; /* locks this structure */
48 struct timer_list *timer;
49
50 /* for tiocmget and tiocmset functions */
51 int msr; /* MSR shadow */
52 int mcr; /* MCR shadow */
53
54 /* for ioctl fun */
55 struct serial_struct serial;
56 wait_queue_head_t wait;
57 struct async_icount icount;
58 };
59
60 static struct tiny_serial *tiny_table[TINY_TTY_MINORS]; /* initially all NULL */
61
62
63 static void tiny_timer(unsigned long timer_data)
64 {
65 struct tiny_serial *tiny = (struct tiny_serial *)timer_data;
66 struct tty_struct *tty;
67 char data[1] = {TINY_DATA_CHARACTER};
68 int data_size = 1;
69
70 if (!tiny)
71 return;
72
73 tty = tiny->tty;
74
75 /* send the data to the tty layer for users to read. This doesn't
76 * actually push the data through unless tty->low_latency is set */
77 tty_buffer_request_room (tty, data_size);
78 tty_insert_flip_string(tty, data, data_size);
79 tty_flip_buffer_push(tty);
80
81 /* resubmit the timer again */
82 tiny->timer->expires = jiffies + DELAY_TIME;
83 add_timer(tiny->timer);
84 }
85
86 static int tiny_open(struct tty_struct *tty, struct file *file)
87 {
88 struct tiny_serial *tiny;
89 struct timer_list *timer;
90 int index;
91
92 /* initialize the pointer in case something fails */
93 tty->driver_data = NULL;
94
95 /* get the serial object associated with this tty pointer */
96 index = tty->index;
97 tiny = tiny_table[index];
98 if (tiny == NULL) {
99 /* first time accessing this device, let's create it */
100 tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
101 if (!tiny)
102 return -ENOMEM;
103
104 init_MUTEX(&tiny->sem);
105 tiny->open_count = 0;
106 tiny->timer = NULL;
107
108 tiny_table[index] = tiny;
109 }
110
111 down(&tiny->sem);
112
113 /* save our structure within the tty structure */
114 tty->driver_data = tiny;
115 tiny->tty = tty;
116
117 ++tiny->open_count;
118 if (tiny->open_count == 1) {
119 /* this is the first time this port is opened */
120 /* do any hardware initialization needed here */
121
122 /* create our timer and submit it */
123 if (!tiny->timer) {
124 timer = kmalloc(sizeof(*timer), GFP_KERNEL);
125 if (!timer) {
126 up(&tiny->sem);
127 return -ENOMEM;
128 }
129 tiny->timer = timer;
130 }
131 tiny->timer->data = (unsigned long )tiny;
132 tiny->timer->expires = jiffies + DELAY_TIME;
133 tiny->timer->function = tiny_timer;
134 add_timer(tiny->timer);
135 }
136
137 up(&tiny->sem);
138 return 0;
139 }
140
141 static void do_close(struct tiny_serial *tiny)
142 {
143 down(&tiny->sem);
144
145 if (!tiny->open_count) {
146 /* port was never opened */
147 goto exit;
148 }
149
150 --tiny->open_count;
151 if (tiny->open_count <= 0) {
152 /* The port is being closed by the last user. */
153 /* Do any hardware specific stuff here */
154
155 /* shut down our timer */
156 del_timer(tiny->timer);
157 }
158 exit:
159 up(&tiny->sem);
160 }
161
162 static void tiny_close(struct tty_struct *tty, struct file *file)
163 {
164 struct tiny_serial *tiny = tty->driver_data;
165
166 if (tiny)
167 do_close(tiny);
168 }
169
170 static int tiny_write(struct tty_struct *tty,
171 const unsigned char *buffer, int count)
172 {
173 struct tiny_serial *tiny = tty->driver_data;
174 int i;
175 int retval = -EINVAL;
176
177 if (!tiny)
178 return -ENODEV;
179
180 down(&tiny->sem);
181
182 if (!tiny->open_count)
183 /* port was not opened */
184 goto exit;
185
186 /* fake sending the data out a hardware port by
187 * writing it to the kernel debug log.
188 */
189 printk(KERN_DEBUG "%s - ", __FUNCTION__);
190 for (i = 0; i < count; ++i)
191 printk("%02x ", buffer[i]);
192 printk("\n");
193
194 exit:
195 up(&tiny->sem);
196 return retval;
197 }
198
199 static int tiny_write_room(struct tty_struct *tty)
200 {
201 struct tiny_serial *tiny = tty->driver_data;
202 int room = -EINVAL;
203
204 if (!tiny)
205 return -ENODEV;
206
207 down(&tiny->sem);
208
209 if (!tiny->open_count) {
210 /* port was not opened */
211 goto exit;
212 }
213
214 /* calculate how much room is left in the device */
215 room = 255;
216
217 exit:
218 up(&tiny->sem);
219 return room;
220 }
221
222 #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
223
224 static void tiny_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
225 {
226 unsigned int cflag;
227
228 cflag = tty->termios->c_cflag;
229
230 /* check that they really want us to change something */
231 if (old_termios) {
232 if ((cflag == old_termios->c_cflag) &&
233 (RELEVANT_IFLAG(tty->termios->c_iflag) ==
234 RELEVANT_IFLAG(old_termios->c_iflag))) {
235 printk(KERN_DEBUG " - nothing to change...\n");
236 return;
237 }
238 }
239
240 /* get the byte size */
241 switch (cflag & CSIZE) {
242 case CS5:
243 printk(KERN_DEBUG " - data bits = 5\n");
244 break;
245 case CS6:
246 printk(KERN_DEBUG " - data bits = 6\n");
247 break;
248 case CS7:
249 printk(KERN_DEBUG " - data bits = 7\n");
250 break;
251 default:
252 case CS8:
253 printk(KERN_DEBUG " - data bits = 8\n");
254 break;
255 }
256
257 /* determine the parity */
258 if (cflag & PARENB)
259 if (cflag & PARODD)
260 printk(KERN_DEBUG " - parity = odd\n");
261 else
262 printk(KERN_DEBUG " - parity = even\n");
263 else
264 printk(KERN_DEBUG " - parity = none\n");
265
266 /* figure out the stop bits requested */
267 if (cflag & CSTOPB)
268 printk(KERN_DEBUG " - stop bits = 2\n");
269 else
270 printk(KERN_DEBUG " - stop bits = 1\n");
271
272 /* figure out the hardware flow control settings */
273 if (cflag & CRTSCTS)
274 printk(KERN_DEBUG " - RTS/CTS is enabled\n");
275 else
276 printk(KERN_DEBUG " - RTS/CTS is disabled\n");
277
278 /* determine software flow control */
279 /* if we are implementing XON/XOFF, set the start and
280 * stop character in the device */
281 if (I_IXOFF(tty) || I_IXON(tty)) {
282 unsigned char stop_char = STOP_CHAR(tty);
283 unsigned char start_char = START_CHAR(tty);
284
285 /* if we are implementing INBOUND XON/XOFF */
286 if (I_IXOFF(tty))
287 printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "
288 "XON = %2x, XOFF = %2x", start_char, stop_char);
289 else
290 printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");
291
292 /* if we are implementing OUTBOUND XON/XOFF */
293 if (I_IXON(tty))
294 printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "
295 "XON = %2x, XOFF = %2x", start_char, stop_char);
296 else
297 printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");
298 }
299
300 /* get the baud rate wanted */
301 printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));
302 }
303
304 /* Our fake UART values */
305 #define MCR_DTR 0x01
306 #define MCR_RTS 0x02
307 #define MCR_LOOP 0x04
308 #define MSR_CTS 0x08
309 #define MSR_CD 0x10
310 #define MSR_RI 0x20
311 #define MSR_DSR 0x40
312
313 static int tiny_tiocmget(struct tty_struct *tty, struct file *file)
314 {
315 struct tiny_serial *tiny = tty->driver_data;
316
317 unsigned int result = 0;
318 unsigned int msr = tiny->msr;
319 unsigned int mcr = tiny->mcr;
320
321 result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | /* DTR is set */
322 ((mcr & MCR_RTS) ? TIOCM_RTS : 0) | /* RTS is set */
323 ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */
324 ((msr & MSR_CTS) ? TIOCM_CTS : 0) | /* CTS is set */
325 ((msr & MSR_CD) ? TIOCM_CAR : 0) | /* Carrier detect is set*/
326 ((msr & MSR_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */
327 ((msr & MSR_DSR) ? TIOCM_DSR : 0); /* DSR is set */
328
329 return result;
330 }
331
332 static int tiny_tiocmset(struct tty_struct *tty, struct file *file,
333 unsigned int set, unsigned int clear)
334 {
335 struct tiny_serial *tiny = tty->driver_data;
336 unsigned int mcr = tiny->mcr;
337
338 if (set & TIOCM_RTS)
339 mcr |= MCR_RTS;
340 if (set & TIOCM_DTR)
341 mcr |= MCR_RTS;
342
343 if (clear & TIOCM_RTS)
344 mcr &= ~MCR_RTS;
345 if (clear & TIOCM_DTR)
346 mcr &= ~MCR_RTS;
347
348 /* set the new MCR value in the device */
349 tiny->mcr = mcr;
350 return 0;
351 }
352
353 static int tiny_read_proc(char *page, char **start, off_t off, int count,
354 int *eof, void *data)
355 {
356 struct tiny_serial *tiny;
357 off_t begin = 0;
358 int length = 0;
359 int i;
360
361 length += sprintf(page, "tinyserinfo:1.0 driver:%s\n", DRIVER_VERSION);
362 for (i = 0; i < TINY_TTY_MINORS && length < PAGE_SIZE; ++i) {
363 tiny = tiny_table[i];
364 if (tiny == NULL)
365 continue;
366
367 length += sprintf(page+length, "%d\n", i);
368 if ((length + begin) > (off + count))
369 goto done;
370 if ((length + begin) < off) {
371 begin += length;
372 length = 0;
373 }
374 }
375 *eof = 1;
376 done:
377 if (off >= (length + begin))
378 return 0;
379 *start = page + (off-begin);
380 return (count < begin+length-off) ? count : begin + length-off;
381 }
382
383 #define tiny_ioctl tiny_ioctl_tiocgserial
384 static int tiny_ioctl(struct tty_struct *tty, struct file *file,
385 unsigned int cmd, unsigned long arg)
386 {
387 struct tiny_serial *tiny = tty->driver_data;
388
389 if (cmd == TIOCGSERIAL) {
390 struct serial_struct tmp;
391
392 if (!arg)
393 return -EFAULT;
394
395 memset(&tmp, 0, sizeof(tmp));
396
397 tmp.type = tiny->serial.type;
398 tmp.line = tiny->serial.line;
399 tmp.port = tiny->serial.port;
400 tmp.irq = tiny->serial.irq;
401 tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
402 tmp.xmit_fifo_size = tiny->serial.xmit_fifo_size;
403 tmp.baud_base = tiny->serial.baud_base;
404 tmp.close_delay = 5*HZ;
405 tmp.closing_wait = 30*HZ;
406 tmp.custom_divisor = tiny->serial.custom_divisor;
407 tmp.hub6 = tiny->serial.hub6;
408 tmp.io_type = tiny->serial.io_type;
409
410 if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct)))
411 return -EFAULT;
412 return 0;
413 }
414 return -ENOIOCTLCMD;
415 }
416 #undef tiny_ioctl
417
418 #define tiny_ioctl tiny_ioctl_tiocmiwait
419 static int tiny_ioctl(struct tty_struct *tty, struct file *file,
420 unsigned int cmd, unsigned long arg)
421 {
422 struct tiny_serial *tiny = tty->driver_data;
423
424 if (cmd == TIOCMIWAIT) {
425 DECLARE_WAITQUEUE(wait, current);
426 struct async_icount cnow;
427 struct async_icount cprev;
428
429 cprev = tiny->icount;
430 while (1) {
431 add_wait_queue(&tiny->wait, &wait);
432 set_current_state(TASK_INTERRUPTIBLE);
433 schedule();
434 remove_wait_queue(&tiny->wait, &wait);
435
436 /* see if a signal woke us up */
437 if (signal_pending(current))
438 return -ERESTARTSYS;
439
440 cnow = tiny->icount;
441 if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
442 cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
443 return -EIO; /* no change => error */
444 if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
445 ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
446 ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
447 ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
448 return 0;
449 }
450 cprev = cnow;
451 }
452
453 }
454 return -ENOIOCTLCMD;
455 }
456 #undef tiny_ioctl
457
458 #define tiny_ioctl tiny_ioctl_tiocgicount
459 static int tiny_ioctl(struct tty_struct *tty, struct file *file,
460 unsigned int cmd, unsigned long arg)
461 {
462 struct tiny_serial *tiny = tty->driver_data;
463
464 if (cmd == TIOCGICOUNT) {
465 struct async_icount cnow = tiny->icount;
466 struct serial_icounter_struct icount;
467
468 icount.cts = cnow.cts;
469 icount.dsr = cnow.dsr;
470 icount.rng = cnow.rng;
471 icount.dcd = cnow.dcd;
472 icount.rx = cnow.rx;
473 icount.tx = cnow.tx;
474 icount.frame = cnow.frame;
475 icount.overrun = cnow.overrun;
476 icount.parity = cnow.parity;
477 icount.brk = cnow.brk;
478 icount.buf_overrun = cnow.buf_overrun;
479
480 if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
481 return -EFAULT;
482 return 0;
483 }
484 return -ENOIOCTLCMD;
485 }
486 #undef tiny_ioctl
487
488 /* the real tiny_ioctl function. The above is done to get the small functions in the book */
489 static int tiny_ioctl(struct tty_struct *tty, struct file *file,
490 unsigned int cmd, unsigned long arg)
491 {
492 switch (cmd) {
493 case TIOCGSERIAL:
494 return tiny_ioctl_tiocgserial(tty, file, cmd, arg);
495 case TIOCMIWAIT:
496 return tiny_ioctl_tiocmiwait(tty, file, cmd, arg);
497 case TIOCGICOUNT:
498 return tiny_ioctl_tiocgicount(tty, file, cmd, arg);
499 }
500
501 return -ENOIOCTLCMD;
502 }
503
504 static struct tty_operations serial_ops = {
505 .open = tiny_open,
506 .close = tiny_close,
507 .write = tiny_write,
508 .write_room = tiny_write_room,
509 .set_termios = tiny_set_termios,
510 };
511
512 static struct tty_driver *tiny_tty_driver;
513
514 static int __init tiny_init(void)
515 {
516 int retval;
517 int i;
518
519 /* allocate the tty driver */
520 tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);
521 if (!tiny_tty_driver)
522 return -ENOMEM;
523
524 /* initialize the tty driver */
525 tiny_tty_driver->owner = THIS_MODULE;
526 tiny_tty_driver->driver_name = "tiny_tty";
527 tiny_tty_driver->name = "ttty";
528 /* no more devfs subsystem */
529 tiny_tty_driver->major = TINY_TTY_MAJOR,
530 tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
531 tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
532 tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW,
533 /* no more devfs subsystem */
534 tiny_tty_driver->init_termios = tty_std_termios;
535 tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
536 tty_set_operations(tiny_tty_driver, &serial_ops);
537
538 /* hack to make the book purty, yet still use these functions in the
539 * real driver. They really should be set up in the serial_ops
540 * structure above... */
541 tiny_tty_driver->read_proc = tiny_read_proc;
542 tiny_tty_driver->tiocmget = tiny_tiocmget;
543 tiny_tty_driver->tiocmset = tiny_tiocmset;
544 tiny_tty_driver->ioctl = tiny_ioctl;
545
546 /* register the tty driver */
547 retval = tty_register_driver(tiny_tty_driver);
548 if (retval) {
549 printk(KERN_ERR "failed to register tiny tty driver");
550 put_tty_driver(tiny_tty_driver);
551 return retval;
552 }
553
554 for (i = 0; i < TINY_TTY_MINORS; ++i)
555 tty_register_device(tiny_tty_driver, i, NULL);
556
557 printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION);
558 return retval;
559 }
560
561 static void __exit tiny_exit(void)
562 {
563 struct tiny_serial *tiny;
564 int i;
565
566 for (i = 0; i < TINY_TTY_MINORS; ++i)
567 tty_unregister_device(tiny_tty_driver, i);
568 tty_unregister_driver(tiny_tty_driver);
569
570 /* shut down all of the timers and free the memory */
571 for (i = 0; i < TINY_TTY_MINORS; ++i) {
572 tiny = tiny_table[i];
573 if (tiny) {
574 /* close the port */
575 while (tiny->open_count)
576 do_close(tiny);
577
578 /* shut down our timer and free the memory */
579 del_timer(tiny->timer);
580 kfree(tiny->timer);
581 kfree(tiny);
582 tiny_table[i] = NULL;
583 }
584 }
585 }
586
587 module_init(tiny_init);
588 module_exit(tiny_exit);
589
|
This page was automatically generated by the
LXR engine.
|