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