zoukankan      html  css  js  c++  java
  • tty设备驱动程序 基于:linux2.6.28

     

    2014年09月05日 ⁄ 综合 ⁄ 共 10482字 ⁄ 字号 ⁄ 评论关闭
     

    本文源自:http://blog.chinaunix.net/uid-21273878-id-1828727.html

    tty设备的数据流通图:


    tty设备有三层:tty核心,tty线路规程,tty驱动。
    我们写驱动还是只负责最底层的tty驱动。线路规程的设置也是在底层的tty驱动。
    tty核心是封装好的。
    来看一下tty设备的操作函数:

    struct tty_operations
    {
           int (*open)(struct
    tty_struct * tty,
    struct file
    * filp);
           void (*close)(struct
    tty_struct * tty,
    struct file
    * filp);
           int (*write)(struct
    tty_struct * tty,
                        const
    unsigned char
    *buf, int
    count);
           void (*put_char)(struct tty_struct
    *tty,
    unsigned char ch);
           void (*flush_chars)(struct tty_struct
    *tty);
           int (*write_room)(struct tty_struct
    *tty);
           int (*chars_in_buffer)(struct tty_struct
    *tty);
           int (*ioctl)(struct tty_struct
    *tty,
    struct file
    * file,
                      unsigned
    int cmd, unsigned
    long arg);
           long (*compat_ioctl)(struct tty_struct
    *tty,
    struct file
    * file,
                              unsigned
    int cmd, unsigned
    long arg);
           void (*set_termios)(struct tty_struct
    *tty,
    struct ktermios * old);
           void (*throttle)(struct tty_struct
    * tty);
           void (*unthrottle)(struct tty_struct
    * tty);
           void (*stop)(struct tty_struct
    *tty);
           void (*start)(struct tty_struct
    *tty);
           void (*hangup)(struct tty_struct
    *tty);
           void (*break_ctl)(struct tty_struct
    *tty,
    int state);
           void (*flush_buffer)(struct tty_struct
    *tty);
           void (*set_ldisc)(struct tty_struct
    *tty);
           void (*wait_until_sent)(struct tty_struct
    *tty,
    int timeout);
           void (*send_xchar)(struct tty_struct
    *tty,
    char ch);
           int (*read_proc)(char
    *page,
    char **start, off_t off,
                           int
    count, int
    *eof,
    void *data);
           int (*write_proc)(struct
    file *file,
    const char __user
    *buffer,
                           unsigned
    long count,
    void *data);
           int (*tiocmget)(struct tty_struct
    *tty,
    struct file
    *file);
           int (*tiocmset)(struct tty_struct
    *tty,
    struct file
    *file,
                         unsigned
    int set,
    unsigned int clear);
    };

    tty设备没有read函数,是因为大部分tty的输入设备和输出设备不一样。例如我们的虚拟终端设备,它的输入是键盘,输出是显示器。
    由于这样的原因,tty的驱动层和tty的线路规程层都有一个缓冲区。
    tty驱动层的缓冲区用来保存硬件发过来的数据。在驱动程序里使用  tty_insert_flip_string 函数可以实现将硬件的数据存入到驱动层的缓冲区。
    其实一个缓冲区就够了,为什么线路规程层还是有一个缓冲区呢?
    那是因为tty核心无法直接读取驱动层的缓冲区的数据。tty核心读不到数据,用户也就无法获取数据。用户的read函数只能从tty核心读取数据。而tty核心只能从tty线路规程层的缓冲区读取数据。
    因为是层层读写的关系,所以tty线路规程也是需要一个缓冲区的。
    在驱动程序里使用 tty_flip_buffer_push()  函数将tty驱动层缓冲区的数据推到tty线路规程层的缓冲区。这样就完成了数据的流通。
    因为全是缓冲区操作,所以需要两个进程:写数据进程和读数据进程。
    如果缓冲区内没有数据,运行读进程的话,tty核心就会把读进程加入到等待队列。
     
    完整的代码如下:

    /*
     * Copyright (c) 2009-~ Lan Peng
     *
     * This source code is released for free distribution under the terms of the
     * GNU General Public License
     *
     * Author: Lan Peng<lanpeng722@gmail.com>
     * Created Time: 2010年07月26日 星期一 10时12分32秒
     * File Name: tty_lan.c
     *
     * Description:
     */
    #include
    <linux/module.h>
    #include
    <linux/kernel.h>
    #include
    <linux/init.h>
    #include
    <linux/cdev.h>
    #include
    <linux/tty.h>
    #include
    <linux/fs.h>
    #include
    <linux/tty_driver.h>
    #include
    <linux/tty_flip.h>
    #include
    <linux/ioport.h>
    #include
    <linux/serial_reg.h>

    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("lan");

    #define TTY_LAN_MINORS_NUM    5
    #define TTY_LAN_MAJOR        202
    static int open_count
    = 0;
    static unsigned
    char *to;
    static struct tty_driver
    *tty_lan_driver;
    static struct tty_struct
    *tty_lan_struct;

    static int tty_lan_open(struct tty_struct
    *tty,
    struct file
    *filp);
    static void tty_lan_close(struct tty_struct
    *tty,
    struct file
    *filp);
    static int tty_lan_write(struct tty_struct
    *tty,
    const unsigned
    char *buffer,
    int count);
    static int tty_lan_write_room(struct tty_struct
    *tty);
    static void tty_lan_set_termios(struct tty_struct
    *tty,
    struct ktermios * old);
    static int tty_lan_put_char(struct tty_struct
    *tty,
    unsigned char ch);

    static struct tty_operations tty_lan_ops
    = {
        .open
    = tty_lan_open,
        .close
    = tty_lan_close,
        .write
    = tty_lan_write,
        .put_char = tty_lan_put_char,
        .write_room = tty_lan_write_room,
        .set_termios = tty_lan_set_termios,
    };

    static int __init tty_lan_init(void)
    {
        int i;
        int retval;
        
        tty_lan_driver = alloc_tty_driver(TTY_LAN_MINORS_NUM);
        if(!tty_lan_driver)
            return -ENOMEM;
        
        tty_lan_driver->owner
    = THIS_MODULE;
        tty_lan_driver->driver_name
    = "tty_lan";
        tty_lan_driver->name
    = "ttty_lan";
        tty_lan_driver->major
    = TTY_LAN_MAJOR,
        tty_lan_driver->minor_start
    = 0;    
        tty_lan_driver->type
    = TTY_DRIVER_TYPE_SERIAL;
        tty_lan_driver->subtype
    = SERIAL_TYPE_NORMAL;
        tty_lan_driver->flags
    = TTY_DRIVER_REAL_RAW;
        tty_lan_driver->init_termios
    = tty_std_termios;
        tty_lan_driver->init_termios.c_cflag
    = B115200 | CS8
    | CREAD | HUPCL
    | CLOCAL;
        tty_set_operations(tty_lan_driver,
    &tty_lan_ops);

        retval = tty_register_driver(tty_lan_driver);
        if(retval){
            printk(KERN_ERR"Failed to register tty_lan_driver! ");
            put_tty_driver(tty_lan_driver);
            return retval;
        }

        for(i
    = 0; i < TTY_LAN_MINORS_NUM; i++)
            tty_register_device(tty_lan_driver, i,
    NULL);
        return 0;
    }

    static int tty_lan_open(struct tty_struct
    *tty,
    struct file
    *filp)
    {
        if(open_count
    == 0){
            printk("Open OK! ");
        }
        tty_lan_struct = kmalloc(sizeof(struct tty_struct),
    GFP_KERNEL);
        tty->low_latency
    = 1;
        tty_lan_struct = tty;
        return 0;
    }

    static void tty_lan_close(struct tty_struct
    *tty,
    struct file
    *filp)
    {
        printk("ClOSE OK! ");
        kfree(tty_lan_struct);
        return;
    }

    static int tty_lan_write(struct tty_struct
    *tty,
    const unsigned
    char *buffer,
    int count)
    {
        int i;
        int retval =
    count;
        tty = tty_lan_struct;
        printk(KERN_DEBUG
    "%s - ",
    __FUNCTION__);
        printk("count :%d ",
    count);
        printk("user write: %s ", buffer);
        printk(" ");
        tty_insert_flip_string(tty, buffer,
    count);
        tty_flip_buffer_push(tty);
        
        return retval;
    }

    static int tty_lan_put_char(struct tty_struct
    *tty,
    unsigned char ch)
    {
        
        printk("put_char :%c ", ch);
        return 0;
    }

    static int tty_lan_write_room(struct tty_struct
    *tty)
    {
        int room;
        room = 255;
        return room;
    }

    static void tty_lan_set_termios(struct tty_struct
    *tty,
    struct ktermios *old)
    {
        tty = tty_lan_struct;
        if(tty->termios->c_cflag
    == old->c_cflag){
            printk("Nothing to change! ");
            return ;
        }
        printk("There is something to Change............ ");
        return ;
    }

    static void __exit tty_lan_exit(void)
    {
        int i;
        for(i
    = 0; i < TTY_LAN_MINORS_NUM; i++)
            tty_unregister_device(tty_lan_driver, i);
        tty_unregister_driver(tty_lan_driver);
    }

    module_init(tty_lan_init);
    module_exit(tty_lan_exit);

    将此模块编译后加入到内核,再运行以下两个程序:
    receive.c : 读取tty设备缓冲区的数据。
    send.c    : 将数据写入到tty设备驱动。
    首先运行 receive
    $ sudo ./receive
    open /dev/ttty_lan0: Success
    ready for receiving data...
    The data received is:
    等待数据状态。
    再打开另一个终端,运行 send
    $ sudo ./send
    open /dev/ttty_lan0: Success
    ready for sending data...
    the number of char sent is 35
    $
    这个程序运行结束后,我们来看一下receive是否接受到了数据:
    $sudo ./receive
    open /dev/ttty_lan0: Success
    ready for receiving data...
    The data received is:
    Hello,this is a Serial_Port test!
    $
    说明接受到了缓冲区的数据。
    具体测试程序的代码如下:
    send.c:

    /*******************************************************
    * File Name: send.c
    * Description: send data to serial_Port
    * Date:
    *******************************************************/
    /******************头文件定义******************/
    #include
    <stdio.h>
    #include
    <string.h>
    #include
    <malloc.h>
    #include
    <sys/types.h>
    #include
    <sys/stat.h>
    #include
    <fcntl.h>
    #include
    <unistd.h>
    #include
    <termios.h>
    #define max_buffer_size 100
    /*定义缓冲区最大宽度*/
    /*******************************************/
    int fd;
    /*定义设备文件描述符*/
    int flag_close;
    int open_serial(int k)
    {
        if(k==0)
    /*tty设备选择*/
        {
            fd = open("/dev/ttty_lan0",O_RDWR|O_NOCTTY);
    /*读写方式打开设备*/
            perror("open /dev/ttty_lan0");
        }
        else
        {
            fd = open("/dev/ttty_lan1",O_RDWR|O_NOCTTY);
            perror("open /dev/ttty_lan1");
        }
        if(fd
    == -1)
    /*打开失败*/
            return -1;
        else
            return 0;
    }
    /********************************************************************/
    int main(int argc,
    char *argv[])
    {
        char sbuf[]={"Hello,this is a Serial_Port test! "};/*待发送的内容,以 为结
                                    束标志*/
        int retv;
        struct termios opt;
        int length=sizeof(sbuf);/*发送缓冲区数据宽度*/
    /*******************************************************************/
        open_serial(0);
    /*打开设备1*/
    /*******************************************************************/
        printf("ready for sending data... ");
    /*准备开始发送数据*/
        tcgetattr(fd,&opt);
        cfmakeraw(&opt);
    /*****************************************************************/
        //cfsetispeed(&opt,B9600); /*波特率设置为9600bps*/

        //cfsetospeed(&opt,B9600);

        /*******************************************************************/
        tcsetattr(fd,TCSANOW,&opt);
        retv=write(fd,sbuf,length);
    /*接收数据*/
        if(retv==-1)
        {
            perror("write");
        }
        printf("the number of char sent is %d ",retv);
        flag_close =close(fd);
        if(flag_close
    ==
    -1) /*判断是否成功关闭文件*/
            printf("Close the Device failur! ");
        return 0;
    }
    /****************************结束***********************************/

    receive.c:

    /*******************************************************
    *ilename:receive.c
    * Description:Receive data from Serial_Port
    * Date:
    *******************************************************/
    /*********************头文件定义***********************/
    #include
    <stdio.h>
    #include
    <string.h>
    #include
    <malloc.h>
    #include
    <sys/types.h>
    #include
    <sys/stat.h>
    #include
    <fcntl.h>
    #include
    <unistd.h>
    #include
    <termios.h>
    #include
    "math.h"
    #define max_buffer_size 100
    /*定义缓冲区最大宽度*/
    /*********************************************************/
    int fd, s;
    int open_serial(int k)
    {
        if(k==0)
    /*tty设备选择*/
        {
            fd = open("/dev/ttty_lan0",O_RDWR|O_NOCTTY);
    /*读写方式打开设备*/
            perror("open /dev/ttty_lan0");
        }
        else
        {
            fd = open("/dev/ttty_lan1",O_RDWR|O_NOCTTY);
            perror("open /dev/ttty_lan1");
        }
        if(fd
    == -1)
    /*打开失败*/
            return -1;
        else
            return 0;
    }
    /********************************************************************/
    int main()
    {
        char hd[max_buffer_size],*rbuf;
    /*定义接收缓冲区*/
        int flag_close, retv,i,ncount=0;
        struct termios opt;
    /*******************************************************************/
        open_serial(0);
    /*打开设备1*/
    /*******************************************************************/
        tcgetattr(fd,&opt);
        cfmakeraw(&opt);
    /*****************************************************************/
        //cfsetispeed(&opt,B9600);
    /*波特率设置为9600bps*/
         //cfsetospeed(&opt,B9600);
    /*******************************************************************/
        tcsetattr(fd,TCSANOW,&opt);
        rbuf=hd;
    /*数据保存*/
        printf("ready for receiving data... ");
        retv=read(fd,rbuf,1);
    /*接收数据*/
        if(retv==-1)
        {
            perror("read");
    /*读状态标志判断*/
        }
    /*************************开始接收数据******************************/
        while(*rbuf!=' ')
    /*判断数据是否接收完毕*/
        {
            ncount+=1;
            rbuf++;
            retv=read(fd,rbuf,1);
            if(retv==-1)
            {
                perror("read");
            }
        }
    /*******************************************************************/
        printf("The data received is: ");
    /*输出接收到的数据*/
        for(i=0;i<ncount;i++)
        {
            printf("%c",hd[i]);
        }
        printf(" ");
        flag_close =close(fd);
        if(flag_close
    ==
    -1) /*判断是否成功关闭文件*/
            printf("Close the Device failur! ");
        return 0;
    }
    /****************************结束***********************************/

    https://www.xuebuyuan.com/2129887.html

  • 相关阅读:
    java鼠标操控小程序
    java多线程三之线程协作与通信实例
    java线程一之创建线程、线程池以及多线程运行时间统计
    newCachedThreadPool的使用
    java多线程 三种实现方式
    Oracle查询练手1
    aspose word for java去除目录文字蓝色样式以及文字下方蓝色下划线
    aspose word for java解决Word中部分表格跨页线框缺失问题
    aspose word for java添加页码并自动生成目录
    aspose word for java调整表格适应页面宽度
  • 原文地址:https://www.cnblogs.com/pengmn/p/14049128.html
Copyright © 2011-2022 走看看