Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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.