Chinaunix首页 | 论坛 | 博客
  • 博客访问: 183169
  • 博文数量: 80
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 83
  • 用 户 组: 普通用户
  • 注册时间: 2016-03-23 13:37
文章分类

全部博文(80)

文章存档

2016年(80)

我的朋友

分类: LINUX

2016-03-23 13:45:20

原文地址:Linux TTY设备驱动 作者:星闪夜空

 首先需要说明的是,这篇文章是我根据lanlovehua的博客http://blog.chinaunix.net/uid-21273878-id-1828727.html与《LDD3》中的TTY驱动程序这章整理而成的。

一、tty设备的数据流通图


tty设备有三层:tty核心、tty线路规程、tty驱动。我们写驱动只负责最底层的tty驱动,线路规程的设置也是在底层的tty驱动,tty核心是封装好的。

用户空间主要是通过设备文件同tty核心交互,而tty核心根据用户空间的操作再选择跟tty线路规程或者tty驱动交互,例:设置硬件的ioctl指令就直接交给tty驱动处理,readwrite操作交给tty线路规程处理。

二、TTY驱动怎么没有read函数

主要是因为输入设备和输出设备不一样。由于这样的原因,tty驱动层和tty的线路规程层都有一个缓冲区(因为tty核心只能从tty线路规程层的缓冲区读取数据,所以tty线路规程层也需要一个缓冲区)。

三、驱动程序tiny_tty.c

点击(此处)折叠或打开

  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. #include <linux/kernel.h>
  15. #include <linux/errno.h>
  16. #include <linux/init.h>
  17. #include <linux/module.h>
  18. #include <linux/slab.h>
  19. #include <linux/sched.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 <asm/uaccess.h>


  26. #define DRIVER_VERSION "v2.0"
  27. #define DRIVER_AUTHOR "Greg Kroah-Hartman "
  28. #define DRIVER_DESC "Tiny TTY driver"

  29. /* Module information */
  30. MODULE_AUTHOR( DRIVER_AUTHOR );
  31. MODULE_DESCRIPTION( DRIVER_DESC );
  32. MODULE_LICENSE("GPL");

  33. #define TTY_FLIPBUF_SIZE 512

  34. #define DELAY_TIME        HZ * 2    /* 2 seconds per character */
  35. #define TINY_DATA_CHARACTER    't'

  36. #define TINY_TTY_MAJOR        240    /* experimental range */
  37. #define TINY_TTY_MINORS        4    /* only have 4 devices */

  38. struct tiny_serial {
  39.     struct tty_struct    *tty;        /* pointer to the tty for this device */
  40.     int            open_count;    /* number of times this port has been opened */
  41.     struct semaphore    sem;        /* locks this structure */
  42.     struct timer_list    *timer;

  43.     /* for tiocmget and tiocmset functions */
  44.     int            msr;        /* MSR shadow */
  45.     int            mcr;        /* MCR shadow */

  46.     /* for ioctl fun */
  47.     struct serial_struct    serial;
  48.     wait_queue_head_t    wait;
  49.     struct async_icount    icount;
  50. };

  51. static struct tiny_serial *tiny_table[TINY_TTY_MINORS];    /* initially all NULL */


  52. static void tiny_timer(unsigned long timer_data)
  53. {
  54.     struct tiny_serial *tiny = (struct tiny_serial *)timer_data;
  55.     struct tty_struct *tty;
  56.     int i;
  57.     char data[1] = {TINY_DATA_CHARACTER};
  58.     int data_size = 1;

  59.     if (!tiny)
  60.         return;

  61.     tty = tiny->tty;

  62.     /* send the data to the tty layer for users to read. This doesn't
  63.      * actually push the data through unless tty->low_latency is set */
  64.     for (i = 0; i < data_size; ++i) {
  65.         if (tty->count >= TTY_FLIPBUF_SIZE)
  66.             tty_flip_buffer_push(tty);
  67.         tty_insert_flip_char(tty, data[i], TTY_NORMAL);
  68.     }
  69.     tty_flip_buffer_push(tty);

  70.     /* resubmit the timer again */
  71.     tiny->timer->expires = jiffies + DELAY_TIME;
  72.     add_timer(tiny->timer);
  73. }

  74. static int tiny_open(struct tty_struct *tty, struct file *file)
  75. {
  76.     struct tiny_serial *tiny;
  77.     struct timer_list *timer;
  78.     int index;

  79.     /* initialize the pointer in case something fails */
  80.     tty->driver_data = NULL;

  81.     /* get the serial object associated with this tty pointer */
  82.     index = tty->index;
  83.     tiny = tiny_table[index];
  84.     if (tiny == NULL) {
  85.         /* first time accessing this device, let's create it */
  86.         tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
  87.         if (!tiny)
  88.             return -ENOMEM;

  89.         //init_MUTEX(&tiny->sem);
  90.                 sema_init(&tiny->sem, 1);
  91.         tiny->open_count = 0;
  92.         tiny->timer = NULL;

  93.         tiny_table[index] = tiny;
  94.     }

  95.     down(&tiny->sem);

  96.     /* save our structure within the tty structure */
  97.     tty->driver_data = tiny;
  98.     tiny->tty = tty;

  99.     ++tiny->open_count;
  100.     if (tiny->open_count == 1) {
  101.         /* this is the first time this port is opened */
  102.         /* do any hardware initialization needed here */

  103.         /* create our timer and submit it */
  104.         if (!tiny->timer) {
  105.             timer = kmalloc(sizeof(*timer), GFP_KERNEL);
  106.             if (!timer) {
  107.                 up(&tiny->sem);
  108.                 return -ENOMEM;
  109.             }
  110.             tiny->timer = timer;
  111.         }
  112.         tiny->timer->data = (unsigned long )tiny;
  113.         tiny->timer->function = tiny_timer;
  114.                 init_timer(tiny->timer);
  115.         tiny->timer->expires = jiffies + DELAY_TIME;
  116.         add_timer(tiny->timer);
  117.     }

  118.     up(&tiny->sem);
  119.     return 0;
  120. }

  121. static void do_close(struct tiny_serial *tiny)
  122. {

  123.     down(&tiny->sem);

  124.     if (!tiny->open_count) {
  125.         /* port was never opened */
  126.         goto exit;
  127.     }

  128.     --tiny->open_count;
  129.     if (tiny->open_count <= 0) {
  130.         /* The port is being closed by the last user. */
  131.         /* Do any hardware specific stuff here */

  132.         /* shut down our timer */
  133.         del_timer(tiny->timer);
  134.     }
  135. exit:
  136.     up(&tiny->sem);
  137. }

  138. static void tiny_close(struct tty_struct *tty, struct file *file)
  139. {
  140.     struct tiny_serial *tiny = tty->driver_data;

  141.     if (tiny)
  142.         do_close(tiny);
  143. }    

  144. static int tiny_write(struct tty_struct *tty,
  145.          const unsigned char *buffer, int count)
  146. {
  147.     struct tiny_serial *tiny = tty->driver_data;
  148.     int i;
  149.     int retval = -EINVAL;
  150.   
  151.         printk("Enter tiny_write\n");

  152.     if (!tiny)
  153.         return -ENODEV;

  154.     down(&tiny->sem);

  155.     if (!tiny->open_count)
  156.         /* port was not opened */
  157.         goto exit;

  158.     /* fake sending the data out a hardware port by
  159.      * writing it to the kernel debug log.
  160.      */
  161.     printk(KERN_DEBUG "%s - ", __FUNCTION__);
  162.     for (i = 0; i < count; ++i)
  163.         printk("%02x ", buffer[i]);
  164.     printk("\n");
  165.      
  166.         //up(&tiny->sem);
  167.         //return count;
  168.         
  169. exit:
  170.     up(&tiny->sem);
  171.     return retval;
  172. }

  173. static int tiny_write_room(struct tty_struct *tty)
  174. {
  175.     struct tiny_serial *tiny = tty->driver_data;
  176.     int room = -EINVAL;

  177.     if (!tiny)
  178.         return -ENODEV;

  179.     down(&tiny->sem);
  180.     
  181.     if (!tiny->open_count) {
  182.         /* port was not opened */
  183.         goto exit;
  184.     }

  185.     /* calculate how much room is left in the device */
  186.     room = 255;

  187. exit:
  188.     up(&tiny->sem);
  189.     return room;
  190. }

  191. #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

  192. static void tiny_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
  193. {
  194.     unsigned int cflag;

  195.     cflag = tty->termios->c_cflag;

  196.     /* check that they really want us to change something */
  197.     if (old_termios) {
  198.         if ((cflag == old_termios->c_cflag) &&
  199.          (RELEVANT_IFLAG(tty->termios->c_iflag) ==
  200.          RELEVANT_IFLAG(old_termios->c_iflag))) {
  201.             printk(KERN_DEBUG " - nothing to change...\n");
  202.             return;
  203.         }
  204.     }

  205.     /* get the byte size */
  206.     switch (cflag & CSIZE) {
  207.         case CS5:
  208.             printk(KERN_DEBUG " - data bits = 5\n");
  209.             break;
  210.         case CS6:
  211.             printk(KERN_DEBUG " - data bits = 6\n");
  212.             break;
  213.         case CS7:
  214.             printk(KERN_DEBUG " - data bits = 7\n");
  215.             break;
  216.         default:
  217.         case CS8:
  218.             printk(KERN_DEBUG " - data bits = 8\n");
  219.             break;
  220.     }
  221.     
  222.     /* determine the parity */
  223.     if (cflag & PARENB)
  224.         if (cflag & PARODD)
  225.             printk(KERN_DEBUG " - parity = odd\n");
  226.         else
  227.             printk(KERN_DEBUG " - parity = even\n");
  228.     else
  229.         printk(KERN_DEBUG " - parity = none\n");

  230.     /* figure out the stop bits requested */
  231.     if (cflag & CSTOPB)
  232.         printk(KERN_DEBUG " - stop bits = 2\n");
  233.     else
  234.         printk(KERN_DEBUG " - stop bits = 1\n");

  235.     /* figure out the hardware flow control settings */
  236.     if (cflag & CRTSCTS)
  237.         printk(KERN_DEBUG " - RTS/CTS is enabled\n");
  238.     else
  239.         printk(KERN_DEBUG " - RTS/CTS is disabled\n");
  240.     
  241.     /* determine software flow control */
  242.     /* if we are implementing XON/XOFF, set the start and
  243.      * stop character in the device */
  244.     if (I_IXOFF(tty) || I_IXON(tty)) {
  245.         unsigned char stop_char = STOP_CHAR(tty);
  246.         unsigned char start_char = START_CHAR(tty);

  247.         /* if we are implementing INBOUND XON/XOFF */
  248.         if (I_IXOFF(tty))
  249.             printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "
  250.                 "XON = %2x, XOFF = %2x", start_char, stop_char);
  251.         else
  252.             printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");

  253.         /* if we are implementing OUTBOUND XON/XOFF */
  254.         if (I_IXON(tty))
  255.             printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "
  256.                 "XON = %2x, XOFF = %2x", start_char, stop_char);
  257.         else
  258.             printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");
  259.     }

  260.     /* get the baud rate wanted */
  261.     printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));
  262. }

  263. static struct tty_operations serial_ops = {
  264.     .open = tiny_open,
  265.     .close = tiny_close,
  266.     .write = tiny_write,
  267.     .write_room = tiny_write_room,
  268.     .set_termios = tiny_set_termios,
  269. };

  270. static struct tty_driver *tiny_tty_driver;

  271. static int __init tiny_init(void)
  272. {
  273.     int retval;

  274.     /* allocate the tty driver */
  275.     tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);
  276.     if (!tiny_tty_driver)
  277.         return -ENOMEM;

  278.     /* initialize the tty driver */
  279.     tiny_tty_driver->owner = THIS_MODULE;
  280.     tiny_tty_driver->driver_name = "tiny_tty";
  281.     tiny_tty_driver->name = "ttty";
  282.     tiny_tty_driver->major = TINY_TTY_MAJOR,
  283.     tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
  284.     tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
  285.     tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW,
  286.     tiny_tty_driver->init_termios = tty_std_termios;
  287.     tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
  288.     tty_set_operations(tiny_tty_driver, &serial_ops);

  289.     /* register the tty driver */
  290.     retval = tty_register_driver(tiny_tty_driver);
  291.     if (retval) {
  292.         printk(KERN_ERR "failed to register tiny tty driver");
  293.         put_tty_driver(tiny_tty_driver);
  294.         return retval;
  295.     }

  296.     printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION);

  297.     return retval;
  298. }

  299. static void __exit tiny_exit(void)
  300. {
  301.     struct tiny_serial *tiny;
  302.     int i;

  303.         for(i = 0; i < TINY_TTY_MINORS; ++i)
  304.             tty_unregister_device(tiny_tty_driver, i);
  305.     tty_unregister_driver(tiny_tty_driver);

  306.     /* shut down all of the timers and free the memory */
  307.     for (i = 0; i < TINY_TTY_MINORS; ++i) {
  308.         tiny = tiny_table[i];
  309.         if (tiny) {
  310.             /* close the port */
  311.             while (tiny->open_count)
  312.                 do_close(tiny);

  313.             /* shut down our timer and free the memory */
  314.             del_timer(tiny->timer);
  315.             kfree(tiny->timer);
  316.             kfree(tiny);
  317.             tiny_table[i] = NULL;
  318.         }
  319.     }
  320. }

  321. module_init(tiny_init);
  322. module_exit(tiny_exit);



四、
测试程序

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <termios.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>

  8. #define max_buffer_size 100 /*recv buffer size*/


  9. int open_serial(int k)
  10. {
  11.     char pathname[20] = {0};
  12.     int ret;

  13.     sprintf(pathname, "/dev/ttty%d", k);
  14.     ret = open(pathname, O_RDWR|O_NOCTTY);
  15.     if(ret == -1)
  16.     {
  17.         perror("open error");
  18.         exit(-1);
  19.     }
  20.     else
  21.         printf("Open %s success\n", pathname);

  22.     return ret;
  23. }

  24. int main()
  25. {
  26.     int fd;
  27.     ssize_t n;
  28.     char recv[max_buffer_size] = {0};
  29.     struct termios opt;

  30.     fd = open_serial(0); /*open device 0*/

  31.     tcgetattr(fd, &opt);
  32.     cfmakeraw(&opt);
  33.     tcsetattr(fd, TCSANOW, &opt);


  34.     printf("ready for receiving data...\n");
  35.     n = read(fd, recv, sizeof(recv));
  36.     if(n == -1)
  37.     {
  38.         perror("read error");
  39.         exit(-1);
  40.     }
  41.     
  42.     printf("The data received is %s", recv);
  43.     if(close(fd) == -1)
  44.         perror("close error");
  45.     
  46.     return 0;
  47. }

五、测试结果

用户测试信息:


dmesg信息:


阅读(1028) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~