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/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.