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

原文地址:https://www.cnblogs.com/pengmn/p/14049128.html