linux 线路规程

在嵌入式系统中大量用到了串口。
但是很多时候,在使用中串口是复用的,例如和MCU通讯,传的有触摸、按键,控制数据等等。
或者在通讯前要拉一些IO口进行握手之类的。
对于这些要求,单纯用串口就无法满足,那么我们就需要用到线路规程。
下面详细介绍下线路规程


1 线路规程的数据结构
struct tty_ldisc_ops {
        int        magic;  
        char        *name;
        int        num;
        int        flags;


        /*
         * The following routines are called from above.
         */
        int        (*open)(struct tty_struct *);
        void        (*close)(struct tty_struct *);
        void        (*flush_buffer)(struct tty_struct *tty);
        ssize_t        (*chars_in_buffer)(struct tty_struct *tty);
        ssize_t        (*read)(struct tty_struct *tty, struct file *file,
                        unsigned char __user *buf, size_t nr);
        ssize_t        (*write)(struct tty_struct *tty, struct file *file,
                         const unsigned char *buf, size_t nr);
        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);
        unsigned int (*poll)(struct tty_struct *, struct file *,
                             struct poll_table_struct *);
        int        (*hangup)(struct tty_struct *tty);


        /*
         * The following routines are called from below.
         */
        void        (*receive_buf)(struct tty_struct *, const unsigned char *cp,
                               char *fp, int count);
        void        (*write_wakeup)(struct tty_struct *);
        void        (*dcd_change)(struct tty_struct *, unsigned int);
        void        (*fasync)(struct tty_struct *tty, int on);
        int        (*receive_buf2)(struct tty_struct *, const unsigned char *cp,
                                char *fp, int count);


        struct  module *owner;


        int refcount;
};
常用的成员变量说明:
.owner :通常设置为THIS_MODULE
.name: 线路规程的名称,可以任何有意义的名称
.magic:通常设置为TTY_LDISC_MAGIC
.open :当线路规程附加到串口终端是会被调用。
.close:当线路规程从串口终端移除的时候被调用,关闭串口时,也会被调用
.read:应用程序读串口的时候被调用
.write:应用程序写串口的时候被调用
.ioctl:当ioctl交给tty层时被调用
.poll: 当调用poll\select 的时候检查资源的状态
.receive_buf:当串口接收缓冲区有数据的时候被调用,调用这个函数前会检查线路规程缓冲区的可用长度,如果长度为0,将不调用这个函数
.receive_buf2:当串口接收缓冲区有数据的时候被调用,这个函数调用前不检测线路规程缓冲区的可用长度。
.flush_buffer:清空线路规程的接收缓冲区
.chars_in_buffer:报告输入缓冲区中的字节数
 
2函数说明
int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
函数功能:注册一个线路规程
入口参数:disc 唯一标识一个线路规程,这个整数在系统中是唯一的,不能和其他的线路规程相同
          new_ldisc 上面介绍的tty_ldisc_ops结构的指针
返回值: 0   成功
         非0 失败
         
int tty_unregister_ldisc(int disc);
函数功能:注销一个线路规程        
入口参数:disc tty_register_ldisc注册时传的disc的值
返回值: 0   成功
         非0 失败
         
3 示例代码
#define DEV_NAME "ldisc-test"
#define MAX_BUF_LEN        32
#define N_LDISC_TEST 25
typedef struct
{
        u8  bReceivebuf[MAX_BUF_LEN];
        int iLen;
}stCtrl;
stCtrl MyData;
wait_queue_head_t wqReadOK;
static int ldisc_test_open (struct tty_struct *tty)
{
        stCtrl *data = ((stCtrl *) ((tty)->disc_data));
        
        /* There should not be an existing table for this slot. */
        if (data) {
                printk (KERN_ERR"n_tty_open:tty already associated!\n" );
                return -EEXIST;
        }
        
        data = &MyData;
         
        data->iLen= 0;
        tty->disc_data = data;//这里可以将自己的私有数据保存在这里
        
        tty->receive_room = MAX_BUF_LEN; //注意这里要指定receive_room,如果没有指定,默认为0 ,那么ldisc_test_receive不会被调用了
        
        /* flush receive data from driver */
        tty_driver_flush_buffer(tty);
        
        return 0;
        
}


static void ldisc_test_close(struct tty_struct *tty)
{
        stCtrl *data = ((stCtrl *) ((tty)->disc_data));
        data=NULL;
         
        printk("ldisc_close ok\n"); 
                
}


static ssize_t ldisc_test_read(struct tty_struct *tty, struct file *file,
                           __u8 __user *buf, size_t nr)
{
        stCtrl *data = ((stCtrl *) ((tty)->disc_data));
        int ret = 0;
         
        if(data->iLen==0)
        {
                if (file->f_flags & O_NONBLOCK) 
                {
                        return ret;
                }
                else
                {
                        wait_event_interruptible_timeout(wqReadOK, (data->iLen>0) ,10);
                }
        }
        //这里是示例代码,没有做判断是否会溢出,实际处理要判断nr的长度 
        if(copy_to_user(buf,data->bReceivebuf,data->iLen)==0)
    {
            ret= data->iLen;
    }
        tty->receive_room = MAX_BUF_LEN; 
        data->iLen = 0;
        return ret;
        
}
static ssize_t ldisc_test_write(struct tty_struct *tty, struct file *file,
                            const unsigned char *data, size_t count)
{
         
        int error; 
        ......这里可以做自己的一些相应的处理,例如拉IO口,进行握手等等
        error = tty->ops->write(tty, data, count);
        ......这里可以做自己的一些相应的处理         
        
        return error;
        
}


static int ldisc_test_ioctl(struct tty_struct *tty, struct file *file,
                            unsigned int cmd, unsigned long arg)
{
        stCtrl *data = ((stCtrl *) ((tty)->disc_data));
        int error = 0; 
        int count;
        switch (cmd) {
        case FIONREAD: //获取缓冲区的数据长度
                
                error = put_user(data->iLen, (int __user *)arg);
                break;


        case TIOCOUTQ://返回输出队列中还未送出的字符数。
                count = tty_chars_in_buffer(tty);
                error = put_user(count, (int __user *)arg); 
                break;


         
                /* fall through to default */


        default:
                error = n_tty_ioctl_helper(tty, file, cmd, arg);
                break;
        }
        return error;
        
}


static unsigned int ldisc_test_poll(struct tty_struct *tty, struct file *filp,
                                    poll_table *wait)
{
        stCtrl *data = ((stCtrl *) ((tty)->disc_data));
        unsigned int mask = 0;
        poll_wait(filp, &wqReadOK, wait);
        if(data->iLen >0)
        {
                mask |= POLLIN | POLLRDNORM;        /* 缓冲区有数据 */
        }
        return mask;
}
static void ldisc_test_receive(struct tty_struct *tty, const __u8 *data,
                               char *flags, int count)
{
        stCtrl *Mydata = ((stCtrl *) ((tty)->disc_data));
        int i; 
        Mydata->iLen = 0;
        //这里只是示例代码,没有判断是否会数组越界,也没有考虑是否缓冲区有数据
    for(i=0;i<count;i++)
    {
                    if(flags[i]==TTY_NORMAL)//flags 是标志位,判断是否是有效数据
                    {
                            Mydata->bReceivebuf[Mydata->iLen++]=data[i];
                    }
    }
    tty->receive_room = 0;         //这里没有判断实际缓冲区已经满了,直接认为满了,实际可以根据我们缓冲区的剩余空间,修改        receive_room的值。
                     
    .......这里可以根据自己的规则处理数据,或者对IO的操作,处理接收到的按键数据或者触摸数据等等                                
    wake_up_interruptible(&wqReadOK);
                                     
                             
     
    
}
static void ldisc_test_wakeup(struct tty_struct *tty)
{
        stCtrl *data = ((stCtrl *) ((tty)->disc_data));
        
}


static void flush_rx_queue(struct tty_struct *tty)
{
        stCtrl *data = ((stCtrl *) ((tty)->disc_data));
         data->iLen = 0; //缓冲区清零
}


static struct tty_ldisc_ops ldisc_test_ldisc = {
        .owner                = THIS_MODULE,
        .magic                = TTY_LDISC_MAGIC,
        .name                = DEV_NAME,
        .open                = ldisc_test_open,
        .close                = ldisc_test_close,
        .read                = ldisc_test_read,
        .write                = ldisc_test_write,
        .ioctl                = ldisc_test_ioctl,
        .poll                = ldisc_test_poll,
        .receive_buf        = ldisc_test_receive,
        .write_wakeup        = ldisc_test_wakeup,
        .flush_buffer   = flush_rx_queue,
};


static int __init ldisc_test_init(void)
{
        int status;
        init_waitqueue_head(&wqReadOK);
        //注册线路规程 
        status = tty_register_ldisc(N_LDISC_TEST, &ldisc_test_ldisc);
        if (!status)
                printk("ldisc register ok\n");
        else
                printk("ldisc register fail, status=%d\n",status);
                
         
        return status;
        
}


static void __exit ldisc_test_exit(void)
{
         
        int status = tty_unregister_ldisc(N_LDISC_TEST);


        if (status)
                printk(KERN_ALERT"ldisc unregister fail, status=%d\n",status);
         
}


module_init(ldisc_test_init);
module_exit(ldisc_test_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("TEST@qq.com");


MODULE_ALIAS_LDISC(N_LDISC_TEST);




4 应用程序调用示例
int OpenLinBus(  )
{
        struct termios Opt; 
        int iLdisc = N_ITAS_LIN;
        int i;
         
         
        int fd = open(DEV, O_RDWR|O_NOCTTY);
        
         
        if(fd<0)
        {
                 
                return -1 ;
        }
         
        ioctl(fd , TIOCSETD, &iLdisc); //这里注册线路规程
        
        //下面都是通用的串口设置了 
        tcgetattr(fd, &Opt) ;
        cfsetispeed(&Opt,  Bautrate);
        cfsetospeed(&Opt,  Bautrate);
        tcsetattr(fd, TCSANOW, &Opt);
        tcflush(fd, TCIOFLUSH); 
        tcgetattr(fd, &Opt) ;
        Opt.c_cflag &= ~CSTOPB;
        Opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                | INLCR | IGNCR | ICRNL | IXON);
        Opt.c_oflag &= ~OPOST;
        Opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
        Opt.c_cflag &= ~(CSIZE | PARENB);
        Opt.c_cflag |= CS8;
        Opt.c_cflag &= ~CRTSCTS;
        tcsetattr(fd, TCSANOW, &Opt);
        tcflush(fd, TCIOFLUSH); 
        return 0;
}
之后应用就可以像操作普通串口一样调用read write等接口了。最后就会调到前面线路规程驱动中的read write了
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值