linux2.6.28-tty設備驅動學習(二)

本次目標是要實現在用戶態下對tty驅動程序的數據讀寫。
首先來看一下tty設備的數據流通圖:
http://blog.chinaunix.net/photo/94212_100730152614.png
94212_100730152614.png

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<[email protected]>
 * 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!\n");
        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!\n");
    }
    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!\n");
    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 - \n", __FUNCTION__);
    printk("count :%d\n", count);
    printk("user write: %s ", buffer);
    printk("\n");
    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\n", 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!\n");
        return ;
    }
    printk("There is something to Change............\n");
    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!\n"};/*待發送的內容,以\n爲結
                                束標誌*/

    int retv;
    struct termios opt;
    int length=sizeof(sbuf);/*發送緩衝區數據寬度*/
/*******************************************************************/
    open_serial(0); /*打開設備1*/
/*******************************************************************/
    printf("ready for sending data...\n"); /*準備開始發送數據*/
    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\n",retv);
    flag_close =close(fd);
    if(flag_close == -1) /*判斷是否成功關閉文件*/
        printf("Close the Device failur!\n");
    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...\n");
    retv=read(fd,rbuf,1); /*接收數據*/
    if(retv==-1)
    {
        perror("read"); /*讀狀態標誌判斷*/
    }
/*************************開始接收數據******************************/
    while(*rbuf!='\n') /*判斷數據是否接收完畢*/
    {
        ncount+=1;
        rbuf++;
        retv=read(fd,rbuf,1);
        if(retv==-1)
        {
            perror("read");
        }
    }
/*******************************************************************/
    printf("The data received is:\n"); /*輸出接收到的數據*/
    for(i=0;i<ncount;i++)
    {
        printf("%c",hd[i]);
    }
    printf("\n");
    flag_close =close(fd);
    if(flag_close == -1) /*判斷是否成功關閉文件*/
        printf("Close the Device failur!\n");
    return 0;
}
/****************************結束***********************************/


<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script> <script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
閱讀(2219) | 評論(0) | 轉發(0) |
給主人留下些什麼吧!~~
評論熱議
發佈了93 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章