Linux下的虚拟串口驱动(三)

欢迎转载,转载请注明出处。

前言

前面两章,对设备驱动和Linux下的字符设备驱动架构,做了简单的介绍;本章将结合实例,讲述虚拟串口实现过程。

驱动、总线和设备

谈到Linux的驱动架构(2.6内核),总线、设备和驱动这三个是必须提到的。总线将设备和驱动绑定,系统在注册设备时,会寻找与之匹配的驱动;同样,系统注册驱动的时候,会寻找与之匹配的设备。匹配操作就是总线来完成的(实际是通过probe()、match()函数,查找匹配项,这里先不做详细阐述)。
我们也会经常用一些总线去描述设备,例如USB设备,PCI设备,I2C设备等;从名字上也能看出这些设备所依附的总线。但在嵌入式系统里,并不是所有的总线都依附于此类总线,但设备和驱动又需要总线去绑定。例如本文需要实现的虚拟串口,既不属于PCI,也不属于USB,但总得挂接到相关总线上去。基于这种情况,Linux开发了一种虚拟总线,也叫platform总线,相应的设备被称为platform_device,驱动称为platform_driver,本次实现的虚拟串口就是挂接到platform总线的。

实例

一. platform_deviceplatform_driver
虽然叫虚拟串口,但为了蒙骗上层,底层就要把它当做一个真正的设备去注册进内核,让内核去欺骗上层应用。所以结合上面所说,一个设备驱动实例,需要总线、设备和驱动三个部分。总线匹配设备和驱动时,需要驱动信息和设备信息;因此就要用到platform_deviceplatform_driver
设备或驱动注册时,总线就会调用该设备(或驱动)的match函数,寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;
如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;
如果是驱动先注册,一样也会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。

  • platform_device

platform_device结构体用来描述设备的名称、资源信息等。

static struct platform_devicce virtual_serial_device = {
    .name       = "virtual_serial",                 /* 设备名                                  */
    .dev        = {                                 /* struct device dev 设备描述的详细信息      */
        .release = virtual_serial_relese,
        .platform_data = ( void *)( &virtual_serial_dev_data ),
    }
} ;

原2.6内核定义的结构如下:

struct platform_device {
    const char  * name;                             /* 设备名                                      */                  
    int     id;                                     /* 决定是否对name编号,值为-1表示,直接使用上诉    */
                                                    /* name命名,如果不是-1则对name进行编号          */
    struct device   dev;                            /* 设备的详细信息,碍于篇幅,这里就不做过多解释   */
    u32     num_resources;                          /* 设备用到的资源个数                            */
    struct resource * resource;                     /* 用到的资源                                    */

    struct platform_device_id   *id_entry;          /* 设备ID                                     */

    /* arch specific additions */
    struct pdev_archdata    archdata;
};

其中resource定义了设备需要使用到的资源, 在Linux中,系统资源包括I/O、Memory、Register、IRQ、DMA、Bus等多种类型。

struct resource {
    resource_size_t start;                          /* 资源起始地址                               */
    resource_size_t end;                            /* 资源的结束地址                             */
    const char *name;
    unsigned long flags;                            /* 资源类型                                  */
    struct resource *parent, *sibling, *child;
};

其中start和end的含义有flags决定,当flags表示Memory资源时,start 、end 表示该platform_device占用的内存的开始地址和结束地址;当flags表示IRQ资源时,start 、end 表示该platform_device使用的中断的开始地址和结束地址;

  • platform_driver

platform_driver是注册驱动时需要使用的数据结构,注册驱动之前要先注册设备,即上面的数据结构,因为驱动在注册时,会匹配内核中已经注册的设备名。

static struct platform_driver virtual_serial_driver = {
    .probe      = virtual_serial_probe,                 /* 设备检测函数,所以需要先注册platform_device    */
    .remove     = virtual_serial_remove,                /* 设备删除函数                                   */
    .driver     = {
        .name   = "virtual_serial"
        .owner  = THIS_MODULE,
    }
};

virtual_serial_driver 中的virtual_serial_probe()函数和virtual_serial_remove()函数,都是需要我们自己实现的。
原内核中platform_driver 定义如下:

struct platform_driver {
    int (*probe)(struct platform_device *);         /* 设备检测,软硬件匹配成功后调用              */
    int (*remove)(struct platform_device *);        /* 设备卸载时调用                              */
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
    struct platform_device_id *id_table;
};

二. 终端设备驱动结构

Linux内核中tty的层次结构如上图所示,tty核心层tty线路规程tty驱动。tty核心层是对tty设备的抽象,并为用户提供统一接口;tty驱动就是驱使tty设备工作的模块,面向的是tty设备;tty核心层主要负责数据交互,完成数据格式转换,这种操作通常采用某个协议进行转换;当用户向tty设备发送数据时,先将数据送到tty核心层,tty核心层收到数据后再将数据送到tty线路规程,接着数据会被传递到tty驱动层,最后tty驱动层再将数据转换成可以发送给硬件的格式。同样当硬件设备接收到数据时,会先向上交给tty驱动层,然后传递到tty线路规程,再进入ttty核心层,最后提交给用户。其实也正如图中描述,tty核心层也可以不经过tty线路规程进行格式转换,直接将数据传递到tty驱动层。

struct tty_driver {
    int magic;                              /* magic number for this structure  */
    struct kref kref;                       /* Reference management             */
    struct cdev cdev;
    struct module   *owner;
    const char  *driver_name;
    const char  *name;
    int name_base;                          /* offset of printed name           */
    int major;                              /* major device number              */
    int minor_start;                        /* start of minor device number     */
    int minor_num;                          /* number of *possible* devices     */
    int num;                                /* number of devices allocated      */
    short   type;                           /* type of tty driver               */
    short   subtype;                        /* subtype of tty driver            */
    struct ktermios init_termios;           /* Initial termios                  */
    int flags;                              /* tty driver flags                 */
    struct proc_dir_entry *proc_entry;      /* /proc fs entry                   */
    struct tty_driver *other;               /* only used for the PTY driver     */

    /* Pointer to the tty data structures                                       */
    struct tty_struct **ttys;
    struct ktermios **termios;
    struct ktermios **termios_locked;
    void *driver_state;

    /* Driver methods                                                           */
    const struct tty_operations *ops;
    struct list_head tty_drivers;
};

Linux内核对tty_driver结构体也做了较为详细的说明,其中主次设备号用来标识驱动和设备,tty_operations是驱动中的操作函数集,init_termios是一个termios结构体,用来提供一个线路设置集合,可以保存当前的线路设置,控制波特率、数据大小、数据流控等。
tty_operations是驱动中操作函数的集合,其成员通常在具体是tty驱动模块初始化函数中被赋值,这里就不列出其成员,感兴趣的可以自己去内核中查看。
所以按照之前谈到的思路,要实现一个串口驱动,我们需要做的是,定义tty_driver并实现tty_operations中的成员函数。但Linux已经在serial_core.c文件中实现了UART设备的通用tty驱动层,所以在实现UART驱动时,我们需要做的是实现serial_core.c文件中定义的一组uart_xxx接口,而不是tty_xxx接口。我们可以吧serial_core.c当成tty设备驱动的实例,它为我们实现了UART设备的tty驱动。
serial_core.c为串口驱动提供了uart_driveruart_portuart_ops三个结构体,我们需要做的就是定义并实现这三个结构体。

  • uart_driver
    包含设备的驱动名、主次设备号等信息。
struct uart_driver {
    struct module   *owner;                 /* 拥有该uart_driver的模块            */
    const char      *driver_name;           /* 驱动名                          */
    const char      *dev_name;              /* 设备名                          */
    int             major;                  /* 主设备号                         */
    int             minor;                  /* 次设备号                         */
    int             nr;                     /* 支持的串口个数                  */
    struct console  *cons;                  /* 若不支持serial console,则为NULL    */

    /* 私有数据,底层驱动不应该访问这些成员,应该被初始化为空                  */
    struct uart_state   *state;
    struct tty_driver   *tty_driver;
};
static struct uart_driver virtual_driver = {
    .owner          = THIS_MODULE,
    .driver_name    = "virtualSerial",
    .dev_name       = "virtualSerial",
    .major          = 0,                    /* 设为0,表示由系统分配设备号               */
    .minor          = 0,
    .nr             = virtual_SERIAL_NR,
    .cons           = NULL,                 /* 不支持serial console                        */
};
  • uart_port
    包含设备的IO端口地址、内存地址、中断号,端口类型等。
struct uart_port {  
    spinlock_t          lock;               /* port lock                        */  
    unsigned long       iobase;             /* io端口基地址(物理)              */  
    unsigned char __iomem   *membase;       /* io内存基地址(虚拟)              */  
    unsigned int        (*serial_in)(struct uart_port *, int);  
    void            (*serial_out)(struct uart_port *, int, int);  
    unsigned int        irq;                /* 中断号                          */  
    unsigned long       irqflags;           /* 中断标志                         */  
    unsigned int        uartclk;            /* 串口时钟                         */  
    unsigned int        fifosize;           /* 串口缓冲区大小                  */  
    unsigned char       x_char;             /* xon/xoff char                    */  
    unsigned char       regshift;           /* 寄存器位移                        */  
    unsigned char       iotype;             /* IO访问方式                       */  
    unsigned char       unused1;  

    unsigned int        read_status_mask;   /* 关心 Rx error status               */  
    unsigned int        ignore_status_mask; /* 忽略 Rx error status               */  
    struct uart_state   *state;             /* pointer to parent state          */  
    struct uart_icount  icount;             /* 串口信息计数器                  */  

    struct console      *cons;              /* struct console, if any           */  
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)  
    unsigned long       sysrq;              /* sysrq timeout                    */  
#endif  

    upf_t           flags;  

    unsigned int        mctrl;              /* 当前的Moden 设置                  */  
    unsigned int        timeout;            /* character-based timeout          */  
    unsigned int        type;               /* 端口类型                         */  
    const struct uart_ops   *ops;           /* 串口端口操作函数                 */  
    unsigned int        custom_divisor;  
    unsigned int        line;               /* 端口索引                         */  
    resource_size_t     mapbase;            /* io内存物理基地址                    */  
    struct device       *dev;               /* 父设备                          */  
    unsigned char       hub6;               /* this should be in the 8250 driver */  
    unsigned char       suspended;  
    unsigned char       unused[2];  
    void            *private_data;          /* generic platform data pointer    */  
};
  • uart_ops
    串口相关的操作集合,相应函数需要我们自己去实现,这些函数一般厂家都会帮我们实现,很多都是跟操作寄存器相关,有点类似裸机驱动。
static struct uart_ops virtual_uart_ops = {
    .tx_empty       = virtual_tx_empty,     /* 检测发送FIFO缓冲区是否为空              */
    .set_mctrl      = virtual_set_mctrl,    /* 设置串口流控                               */
    .get_mctrl      = virtual_get_mctrl,    /* 检测串口流控                               */
    .stop_tx        = virtual_stop_tx,      /* 停止发送                                 */
    .start_tx       = virtual_start_tx,     /* 开始发送                                 */
    .stop_rx        = virtual_stop_rx,      /* 停止接收                                 */
    .enable_ms      = virtual_enable_ms,    /* 空函数                                  */
    .break_ctl      = virtual_break_ctl,    /* 发送break信号                            */
    .startup        = virtual_startup,      /* 串口发送/接收,以及中断初始函数         */
    .shutdown       = virtual_shutdown,     /* 关闭串口                                 */
    .flush_buffer   = virtual_flush_buffer, /* 刷新缓冲区,并丢弃任何剩下的数据         */
    .set_termios    = virtual_set_termios,  /* 设置串口波特率,数据位等                 */
    .type           = virtual_type,         /* 串口类型                                 */
    .release_port   = virtual_release_port, /* 释放串口                                 */
    .request_port   = virtual_request_port, /* 申请串口                                 */
    .config_port    = virtual_config_port,  /* 串口的一些配置                          */
    .verify_port    = virtual_verify_port,  /* 串口检测                                 */
#ifdef CONFIG_CONSOLE_POLL
    .poll_get_char  = NULL,                 /* 设备阻塞与非阻塞访问相关                 */
    .poll_put_char  = NULL,
#endif
};

这是串口定义的相关函数集,但前面说过,注册驱动时,还需要一组提供给上层应用的操作集file_operations,同样这里也需要。

struct file_operations virtual_fops = {
    .open           = virtual_open,         /* 对应上层系统调用,打开串口                */
    .release        = virtual_release,      /* 对应上层系统调用,释放串口                */
    .read           = virtual_read,         /* 对应上层系统调用,从串口读数据          */
    .write          = virtual_write,        /* 对应上层系统调用,往串口写入               */
    .unlocked_ioctl = virtual_ioctl,        /* 对应上层系统调用,串口控制                */
    .poll           = virtual_poll,
    .fasync         = virtual_fasync,
};

有前面的介绍可知,除了需要定义uart_driver、uart_port两个结构体外,串口驱动的主要工作是实现uart_ops和file_operations 结构中的函数。


#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/amba/bus.h>
#include <linux/amba/serial.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/tty_flip.h>
#include <linux/circ_buf.h>

#include <asm/io.h>
#include <linux/platform_device.h>

#define drintk printk

#define virtual_SERIAL_NR         4

typedef     char                CHAR;
typedef     unsigned char       BYTE;
typedef     short               SHORT;
typedef     unsigned short      WORD;
typedef     long                LONG;
typedef     unsigned long       DWORD;
typedef     int                 INT;

/****************************************************************************************************/
/*struct uart_ops {                                                                                 */
/*  unsigned int    (*tx_empty)(struct uart_port *);                                                */
/*  void        (*set_mctrl)(struct uart_port *, unsigned int mctrl);                               */
/*  unsigned int    (*get_mctrl)(struct uart_port *);                                               */
/*  void        (*stop_tx)(struct uart_port *);                                                     */
/*  void        (*start_tx)(struct uart_port *);                                                    */
/*  void        (*send_xchar)(struct uart_port *, char ch);                                         */
/*  void        (*stop_rx)(struct uart_port *);                                                     */
/*  void        (*enable_ms)(struct uart_port *);                                                   */
/*  void        (*break_ctl)(struct uart_port *, int ctl);                                          */
/*  int     (*startup)(struct uart_port *);                                                         */
/*  void        (*shutdown)(struct uart_port *);                                                    */
/*  void        (*flush_buffer)(struct uart_port *);                                                */
/*  void        (*set_termios)(struct uart_port *, struct ktermios *new,                            */
/*                     struct ktermios *old);                                                       */
/*  void        (*set_ldisc)(struct uart_port *);                                                   */
/*  void        (*pm)(struct uart_port *, unsigned int state,                                       */
/*                unsigned int oldstate);                                                           */
/*  int     (*set_wake)(struct uart_port *, unsigned int state);                                    */
/*                                                                                                  */
/*  /*                                                                                              */
/*    Return a string describing the type of the port                                               */
/*                                                                                                  */
/*  const char *(*type)(struct uart_port *);                                                        */
/*                                                                                                  */
/*  /*                                                                                              */
/*   * Release IO and memory resources used by the port.                                            */
/*   * This includes iounmap if necessary.                                                          */
/*                                                                                                  */
/*  void        (*release_port)(struct uart_port *);                                                */
/*                                                                                                  */
/*  /*                                                                                              */
/*   * Request IO and memory resources used by the port.                                            */
/*   * This includes iomapping the port if necessary.                                               */
/*                                                                                                  */
/*  int     (*request_port)(struct uart_port *);                                                    */
/*  void        (*config_port)(struct uart_port *, int);                                            */
/*  int     (*verify_port)(struct uart_port *, struct serial_struct *);                             */
/*  int     (*ioctl)(struct uart_port *, unsigned int, unsigned long);                              */
/*#ifdef CONFIG_CONSOLE_POLL                                                                        */
/*  void    (*poll_put_char)(struct uart_port *, unsigned char);                                    */
/*  int     (*poll_get_char)(struct uart_port *);                                                   */
/*#endif                                                                                            */
/****************************************************************************************************/

static unsigned int virtual_tx_empty(struct uart_port *port);
static void virtual_set_mctrl(struct uart_port *port, unsigned int mctrl);
static unsigned int virtual_get_mctrl(struct uart_port *port);
static void virtual_stop_tx(struct uart_port *port);
static void virtual_start_tx(struct uart_port *port);
static void virtual_stop_rx(struct uart_port *port);
static void virtual_enable_ms(struct uart_port *port);
static void virtual_break_ctl(struct uart_port *port, int break_state);
static int virtual_startup(struct uart_port *port);
static void virtual_shutdown(struct uart_port *port);
static void virtual_flush_buffer(struct uart_port *port);
static void
virtual_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old);
static const char *virtual_type(struct uart_port *port);
static void virtual_release_port(struct uart_port *port);
static int virtual_request_port(struct uart_port *port);
static void virtual_config_port(struct uart_port *port, int flags);
static int virtual_verify_port(struct uart_port *port, struct serial_struct *ser);

/****************************************************************************************************/
/*struct file_operations {                                                                          */
/*struct module *owner;                                                                             */
/*loff_t (*llseek) (struct file *, loff_t, int);                                                    */
/*ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);                                 */
/*ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);                          */
/*ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);                */
/*ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);               */
/*int (*readdir) (struct file *, void *, filldir_t);                                                */
/*unsigned int (*poll) (struct file *, struct poll_table_struct *);                                 */
/*int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);                        */
/*long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);                              */
/*long (*compat_ioctl) (struct file *, unsigned int, unsigned long);                                */
/*int (*mmap) (struct file *, struct vm_area_struct *);                                             */
/*int (*open) (struct inode *, struct file *);                                                      */
/*int (*flush) (struct file *, fl_owner_t id);                                                      */
/*int (*release) (struct inode *, struct file *);                                                   */
/*int (*fsync) (struct file *, struct dentry *, int datasync);                                      */
/*int (*aio_fsync) (struct kiocb *, int datasync);                                                  */
/*int (*fasync) (int, struct file *, int);                                                          */
/*int (*lock) (struct file *, int, struct file_lock *);                                             */
/*ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);                   */
/*unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);*/
/*int (*check_flags)(int);                                                                          */
/*int (*flock) (struct file *, int, struct file_lock *);                                            */
/*ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); */
/*ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);  */
/*int (*setlease)(struct file *, long, struct file_lock **);                                        */
/*}                                                                                                 */
/****************************************************************************************************/

int virtual_open(struct inode *i, struct file *file);
int virtual_release(struct inode *i, struct file *file);
ssize_t virtual_read(struct file *file, char __user *buf, size_t size, loff_t *offset);
ssize_t virtual_write(struct file *file, const char __user *buf, size_t size, loff_t *offset);
unsigned int virtual_poll(struct file *file, struct poll_table_struct *pwait);
int virtual_fasync(int fd, struct file *filp, int mode) ;
int create_manager_device(struct platform_device *pdev, int index);
static int serial_virtual_probe(struct platform_device *pdev);
static int serial_virtual_remove(struct platform_device *dev);
void virtual_serial_release(struct device *dev);
long virtual_ioctl(struct file *file, unsigned int cmd, unsigned long arg);

static int virtual_serial_major = 0;
static int virtual_serial_minor_start = 0;
static struct virtual_uart_port *virtual_array[virtual_SERIAL_NR];

unsigned int virtual_serial_nr = 1;
module_param(virtual_serial_nr, uint, S_IRUGO);

int virtual_fasync(int fd, struct file *filp, int mode);
static struct virtual_serial_data
{
    DWORD tx_fifo_size;
    DWORD rx_fifo_size;
};

/****************************************************************************************************/
/*struct platform_driver {                                                                          */
/*  int (*probe)(struct platform_device *);         /* 设备检测,软硬件匹配成功后调用              */
/*  int (*remove)(struct platform_device *);        /* 设备卸载时调用                              */
/*  void (*shutdown)(struct platform_device *);                                                     */
/*  int (*suspend)(struct platform_device *, pm_message_t state);                                   */
/*  int (*resume)(struct platform_device *);                                                        */
/*  struct device_driver driver;                                                                    */
/*  struct platform_device_id *id_table;                                                            */
/*};                                                                                                */
/****************************************************************************************************/

static struct platform_driver virtual_serial_driver = {
    .probe      = serial_virtual_probe,             /* 设备检测函数,所以需要先注册platform_device    */
    .remove     = serial_virtual_probe,             /* 设备删除函数                                   */
    .driver     = {
        .name   = "virtual_serial"
        .owner  = THIS_MODULE,
    }
};

static struct virtual_serial_data virtual_serial_dev_data = {
        .tx_fifo_size = 2 * 1024,
        .rx_fifo_size = 2 * 1024,
};

/****************************************************************************************************/
/*struct platform_device {                                                                          */
/*  const char  * name;                             /* 设备名                                      */                  
/*  int     id;                                     /* 决定是否对name编号,值为-1表示,直接使用上诉    */
/*                                                  /* name命名,如果不是-1则对name进行编号          */
/*  struct device   dev;                            /* 设备的详细信息,碍于篇幅,这里就不做过多解释   */
/*  u32     num_resources;                          /* 设备用到的资源个数                            */
/*  struct resource * resource;                     /* 用到的资源                                    */
/*
/*  struct platform_device_id   *id_entry;          /* 设备ID                                     */
/*
/*  /* arch specific additions                                                                      */
/*  struct pdev_archdata    archdata;                                                               */
/*};                                                                                                */
/****************************************************************************************************/
static struct platform_devicce virtual_serial_device = 
{
    .name       = "virtual_serial",                 /* 设备名                                      */
    .dev        = {                                 /* struct device dev 设备描述的详细信息          */
        .release = virtual_serial_relese,
        .platform_data = ( void *)( &virtual_serial_dev_data ),
    }
} ;

/****************************************************************************************************/
/*struct uart_driver {                                                                              */
/*  struct module   *owner;                         /* 拥有该uart_driver的模块                        */
/*  const char      *driver_name;                   /* 驱动名                                      */
/*  const char      *dev_name;                      /* 设备名                                      */
/*  int             major;                          /* 主设备号                                     */
/*  int             minor;                          /* 次设备号                                     */
/*  int             nr;                             /* 支持的串口个数                              */
/*  struct console  *cons;                          /* 若不支持serial console,则为NULL                */
/*  /* 私有数据,底层驱动不应该访问这些成员,应该被初始化为空                                      */
/*  struct uart_state   *state;                                                                     */
/*  struct tty_driver   *tty_driver;                                                                */
/*};                                                                                                */
/****************************************************************************************************/

static struct uart_driver virtual_driver = {
    .owner          = THIS_MODULE,
    .driver_name    = "virtualSerial",
    .dev_name       = "virtualSerial",
    .major          = 0,                            /* 设为0,表示由系统分配设备号                   */
    .minor          = 0,
    .nr             = virtual_SERIAL_NR,
    .cons           = NULL,                         /* 不支持serial console                            */
};

static struct uart_ops virtual_uart_ops = {
    .tx_empty       = virtual_tx_empty,             /* 检测发送FIFO缓冲区是否为空                  */
    .set_mctrl      = virtual_set_mctrl,            /* 设置串口流控                                   */
    .get_mctrl      = virtual_get_mctrl,            /* 检测串口流控                                   */
    .stop_tx        = virtual_stop_tx,              /* 停止发送                                     */
    .start_tx       = virtual_start_tx,             /* 开始发送                                     */
    .stop_rx        = virtual_stop_rx,              /* 停止接收                                     */
    .enable_ms      = virtual_enable_ms,            /* 空函数                                      */
    .break_ctl      = virtual_break_ctl,            /* 发送break信号                                */
    .startup        = virtual_startup,              /* 串口发送/接收,以及中断初始函数             */
    .shutdown       = virtual_shutdown,             /* 关闭串口                                     */
    .flush_buffer   = virtual_flush_buffer,         /* 刷新缓冲区,并丢弃任何剩下的数据             */
    .set_termios    = virtual_set_termios,          /* 设置串口波特率,数据位等                     */
    .type           = virtual_type,                 /* 串口类型                                     */
    .release_port   = virtual_release_port,         /* 释放串口                                     */
    .request_port   = virtual_request_port,         /* 申请串口                                     */
    .config_port    = virtual_config_port,          /* 串口的一些配置                              */
    .verify_port    = virtual_verify_port,          /* 串口检测                                     */
#ifdef CONFIG_CONSOLE_POLL                          /*                                              */
    .poll_get_char  = NULL,                         /* 设备阻塞与非阻塞访问相关                     */
    .poll_put_char  = NULL,
#endif
};

struct file_operations virtual_fops = {
    .open           = virtual_open,                 /* 对应上层系统调用,打开串口                    */
    .release        = virtual_release,              /* 对应上层系统调用,释放串口                    */
    .read           = virtual_read,                 /* 对应上层系统调用,从串口读数据              */
    .write          = virtual_write,                /* 对应上层系统调用,往串口写入                   */
    .unlocked_ioctl = virtual_ioctl,                /* 对应上层系统调用,串口控制                    */
    .poll           = virtual_poll,
    .fasync         = virtual_fasync,
};

struct virtual_uart_port {
    struct uart_port port;

    struct virtual_port_data *port_data;

    char type[12];

    unsigned char *tx_fifo;
    unsigned char *rx_fifo;

    unsigned long tx_len;
    unsigned long rx_len;

    unsigned int mctrl;
    unsigned int baud;

    struct ktermios termios;

    struct fasync_struct *async_queue;
    struct semaphore async_sem;

    struct completion manager_activie;
    struct completion write_ok; 
    wait_queue_head_t poll_wq; 

    int manager_reset;

    int is_default_termios : 1;
    unsigned long status;

    struct cdev c_dev;
    int index;
};

static struct class *virtual_class;

static unsigned int virtual_tx_empty(struct uart_port *port)
{

    struct virtual_uart_port *virtual = (struct virtual_uart_port *)port;

    drintk("virtual_tx_empty %d\n", virtual->tx_len);

    return virtual->tx_len > 0 ? 0 : TIOCSER_TEMT;
}

static void virtual_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port *)port;
    virtual->mctrl = mctrl;

    drintk("virtual_set_mctrl!\n");   
}

static unsigned int virtual_get_mctrl(struct uart_port *port)
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port *)port;

    drintk("virtual_get_mctrl!\n");
    return virtual->mctrl;
}

static void virtual_stop_tx(struct uart_port *port)
{
    drintk("virtual_stop_tx!\n");
}

static void virtual_start_tx(struct uart_port *port)
{   
    struct virtual_uart_port *virtual = (struct virtual_uart_port*)port;
    struct circ_buf *xmit = &port->state->xmit;
    int i = 0;

    drintk("virtual_start_tx!\n");

    // virtual->tx_len = 0;
    do {        
        virtual->tx_fifo[virtual->tx_len++] = xmit->buf[xmit->tail];

        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        port->icount.tx++;  

        if (uart_circ_empty(xmit)) {
            break;
        }

    } while (virtual->tx_len < virtual->port_data->tx_fifo_size); 

    for (i = 0; i < virtual->tx_len; i++) {
        drintk("%c ", virtual->tx_fifo[i]);
    }
    drintk("\n");

    wake_up(&virtual->poll_wq);

    init_completion(&virtual->write_ok);
    wait_for_completion(&virtual->write_ok);
}

static void virtual_stop_rx(struct uart_port *port)
{
    drintk("virtual_stop_rx!\n");
}

static void virtual_enable_ms(struct uart_port *port)
{
    drintk("virtual_enable_ms!\n");
}

static void virtual_break_ctl(struct uart_port *port, int break_state)
{
    drintk("virtual_break_ctl!\n");
}

static int virtual_startup(struct uart_port *port)
{
    drintk("virtual_startup!\n");

    return 0;
}

static void virtual_shutdown(struct uart_port *port)
{
    drintk("virtual_shutdown!\n");
}

static void virtual_flush_buffer(struct uart_port *port)
{
    drintk("virtual_flush_buffer!\n");
}


static void
virtual_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old)
{
    unsigned int baud = 0;
    struct virtual_uart_port *virtual = (struct virtual_uart_port*)port;

    baud = uart_get_baud_rate(port, termios, old, 0, 460800);
    virtual->baud = baud;

    memcpy(&virtual->termios, termios, sizeof(struct ktermios));

    drintk("set baudrate to %d\n", baud);

#if 1
    down(&virtual->async_sem);
    if (virtual->async_queue == NULL) {   
        up(&virtual->async_sem);

        init_completion(&virtual->manager_activie);
        wait_for_completion(&virtual->manager_activie);

    } else {
        up(&virtual->async_sem);
    }

    kill_fasync(&virtual->async_queue, SIGIO, POLL_IN);
#endif

}

static const char *virtual_type(struct uart_port *port)
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port*)port;
    drintk("virtual_type\n");             
    return virtual->type;
}

static void virtual_release_port(struct uart_port *port)
{
    drintk("virtual_release_port\n");
}

static int virtual_request_port(struct uart_port *port)
{
    drintk("virtual_request_port\n");
    return 0;
}

static void virtual_config_port(struct uart_port *port, int flags)
{
    drintk("virtual_config_port\n");
    virtual_request_port(port);
    drintk("port->type = %d\n", port->type);

    port->type = PORT_AMBA;
}

static int virtual_verify_port(struct uart_port *port, struct serial_struct *ser)
{

    drintk("virtual_verify_port\n");

    return 0;
}

int virtual_open(struct inode *i, struct file *file)
{
    int minor = iminor(i);
    int index = minor - virtual_serial_minor_start;

    file->private_data = virtual_array[index];

    drintk("minor %d index %d private_data %p\n", minor, index, file->private_data);

    return 0;
}

int virtual_release(struct inode *i, struct file *file)
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port *)file->private_data;

    virtual_fasync(-1, file, 0);

    down(&virtual->async_sem);    
    virtual->async_queue = NULL;
    virtual->manager_reset = 1;
    up(&virtual->async_sem);

    file->private_data = NULL;
    return 0;
}

ssize_t virtual_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port *)file->private_data;
    unsigned long tx_len = virtual->tx_len;
    int read_len = 0;

    read_len = size > tx_len ? tx_len : size;
    copy_to_user(buf, virtual->tx_fifo, read_len);
    virtual->tx_len -= read_len;

    if (virtual->tx_len == 0)
        complete(&virtual->write_ok);

    return read_len;
}

ssize_t virtual_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port *)file->private_data;
    // struct tty_struct *tty = NULL;
    struct uart_port *port = NULL;
    unsigned char ch = 0;
    int i = 0;  

    if (virtual == NULL)
    {
        printk("virtual is nullptr\n");
        return -EIO;
    }

    port = &virtual->port;
    if (virtual->port.state == NULL)
    {
        printk("virtual->port.state is nullptr\n");
        return -EIO;    
    }   

    struct tty_port *tty = NULL;
    tty = &virtual->port.state->port;

    // tty = virtual->port.state->port.tty;
    if (tty == NULL)
    {
        printk("tty is nullptr\n");
        return -EIO;    
    }   

    copy_from_user(virtual->rx_fifo, buf, size);
    virtual->rx_len = size;


    // insert chars
    for (i = 0; i < size ; i++) {
        ch = *(virtual->rx_fifo + i);
        port->icount.rx++;

        tty_insert_flip_char(tty, ch, 0);
    }

    tty_flip_buffer_push(tty);  


    return size;
}

long virtual_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port *)file->private_data;

    switch (cmd){
    case 0xde:
        printk("virtual_ioctl baud %d\n", virtual->baud);
        copy_to_user((void *)arg, &virtual->baud, sizeof(unsigned int));
        break;

    default:
        break;
    }   

    return 0;
}

unsigned int virtual_poll(struct file *file, struct poll_table_struct *pwait)
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port *)file->private_data;
    unsigned int mask = 0;

    printk("%d tx_len %d\n", virtual->index, virtual->tx_len);
    poll_wait(file, &virtual->poll_wq, pwait);

    if (virtual->tx_len)
        mask |= POLLIN | POLLRDNORM;

    return mask;
}

int virtual_fasync(int fd, struct file *filp, int mode) 
{
    struct virtual_uart_port *virtual = (struct virtual_uart_port *)filp->private_data;
    int ret = 0;

    down(&virtual->async_sem);
    ret = fasync_helper(fd, filp, mode, &virtual->async_queue);

    // 如果管理进程重置过,则需要主动通知其更新termios
    if (virtual->manager_reset) {
        virtual->manager_reset = 0;
        kill_fasync(&virtual->async_queue, SIGIO, POLL_IN);       
    }

    up(&virtual->async_sem);

    complete(&virtual->manager_activie);

    return ret;
}

int create_manager_device(struct platform_device *pdev, int index)
{
    struct virtual_uart_port *virtual = NULL;
    struct virtual_port_data *data = NULL;
    struct device *tmp = NULL;
    int ret = 0;
    dev_t dev = 0;
    char dev_name[64] = {0};

    data = (struct virtual_port_data *)pdev->dev.platform_data;
    if (!data) {
        printk("not platform data\n");
        return -EINVAL;
    }


    virtual = (struct virtual_uart_port *)\
                kmalloc(sizeof(struct virtual_uart_port), GFP_KERNEL);
    if (!virtual) {
        printk("malloc virtual error\n");
        return -ENOMEM;
    }

    memset(virtual, 0, sizeof(struct virtual_uart_port));
    virtual->index = index;
    virtual->is_default_termios = 1;  
    sema_init(&virtual->async_sem, 1);
    init_completion(&virtual->manager_activie);
    init_waitqueue_head(&virtual->poll_wq);

    virtual->tx_fifo = (unsigned char*)kmalloc(data->tx_fifo_size, GFP_KERNEL);
    virtual->rx_fifo = (unsigned char*)kmalloc(data->rx_fifo_size, GFP_KERNEL);
    if (!virtual->tx_fifo || !virtual->rx_fifo) {
        printk("virtual fifo kmalloc err rx=%p tx=%p\n", \
                virtual->rx_fifo, virtual->tx_fifo);
        ret = -ENOMEM;
        goto FIFO_ERR;
    }

    virtual->port_data = data;    

    virtual->port.dev = &(pdev->dev);
    virtual->port.mapbase = 0;
    virtual->port.membase = (unsigned char *)(0xdeadbeef);
    virtual->port.iotype = UPIO_MEM;
    virtual->port.irq = 0;
    virtual->port.fifosize = 16;
    virtual->port.ops = &virtual_uart_ops;
    virtual->port.flags = UPF_BOOT_AUTOCONF;
    virtual->port.line = index;
    virtual->port.type = PORT_AMBA;

    ret = uart_add_one_port(&virtual_driver, &virtual->port);
    if (ret) {
        printk("uart drv add one port err. ret = 0x%08x\n", ret);
        goto PORT_ERR;
    }

    virtual_array[index] = virtual;

    if (!virtual_serial_major) {
        sprintf(dev_name, "valueserial%d", 0);
        ret = alloc_chrdev_region(&dev, 0, virtual_SERIAL_NR, dev_name);
        if (!ret) {
            virtual_serial_major = MAJOR(dev);
            virtual_serial_minor_start = MINOR(dev);
            // printk("major %d minor_start %d\n", virtual_serial_major, virtual_serial_minor_start);
        } else {
            printk("register cdev err, ret=%d\n", ret);
            goto DEV_ERR;   
        }   
    } else {
        dev = MKDEV(virtual_serial_major, virtual_serial_minor_start + index);
        sprintf(dev_name, "valueserial%d", index);
    }   

    cdev_init(&virtual->c_dev, &virtual_fops);
    virtual->c_dev.owner = THIS_MODULE;   
    cdev_add(&virtual->c_dev, dev, virtual_SERIAL_NR);

    tmp = device_create(virtual_class, NULL, dev, NULL, dev_name);
    if (NULL == tmp) {
        printk("create device err! %d, %s\n", dev, dev_name);
    }

    printk("create virtual serial manager device /dev/%s\n", dev_name);

    // platform_set_drvdata(pdev, virtual);

    return ret;

DEV_ERR:
    uart_remove_one_port(&virtual_driver, &virtual->port);

PORT_ERR:
    if (virtual->rx_fifo)
        kfree(virtual->rx_fifo);

    if (virtual->tx_fifo)
        kfree(virtual->tx_fifo);

FIFO_ERR:
    kfree(virtual);

    return ret;
}


static int serial_virtual_probe(struct platform_device *pdev)
{
    int i = 0;
    int ret = 0;

    for(i = 0; i < virtual_serial_nr; i++) {
        ret = create_manager_device(pdev, i);
        if (ret) {
            printk("create virtual manager device err, index = %d\n", i);
        }
    }   
    return 0;
}

static int serial_virtual_remove(struct platform_device *dev)
{   
    struct virtual_uart_port *virtual =  NULL;
    int i = 0;
    dev_t dev_num = 0;

    for (i = 0; i < virtual_serial_nr; i++) {
        virtual = virtual_array[i];

        dev_num = MKDEV(virtual_serial_major, virtual_serial_minor_start + i);
        device_destroy(virtual_class, dev_num);
        cdev_del(&virtual->c_dev);

        uart_remove_one_port(&virtual_driver, &virtual->port);

        if (virtual->rx_fifo)
            kfree(virtual->rx_fifo);

        if (virtual->tx_fifo)
            kfree(virtual->tx_fifo);

        kfree(virtual);
    }

    dev_num = MKDEV(virtual_serial_major, virtual_serial_minor_start);
    unregister_chrdev_region(dev_num, virtual_SERIAL_NR);

    return 0;
}

void virtual_serial_release(struct device *dev)
{


}

static int __init serial_virtual_init(void)
{   
    int ret = 0;   

    if (virtual_serial_nr > virtual_SERIAL_NR) {
        printk("virtual serial nr(%d) is more than max(%d)\n", \
            virtual_serial_nr, virtual_SERIAL_NR);
        return -EINVAL;
    }

    // 注册平台设备
    ret = platform_device_register(&virtual_serial_driver);
    virtual_class  = class_create(THIS_MODULE, "dumtty");

    ret = uart_register_driver(&virtual_driver);
    if (ret) {
        printk("virtual drv register err ret = 0x%08x\n", ret);
        return -EINVAL;
    }

    ret = platform_driver_register(&virtual_serial_dirver);
    if (ret) {
        printk("virtual platform drv register err ret = %d\n", ret);
        platform_device_unregister(&virtual_serial_driver);
        uart_unregister_driver(&virtual_driver);
    }

    return ret;
}

static void __exit serial_virtual_exit(void)
{   
    platform_driver_unregister(&virtual_serial_dirver);
    uart_unregister_driver(&virtual_driver);
    class_destroy(virtual_class);

    platform_device_unregister(&virtual_serial_driver);
}

module_init(serial_virtual_init);
module_exit(serial_virtual_exit);

MODULE_AUTHOR("xxxxx");
MODULE_DESCRIPTION("virtual serial driver");
MODULE_LICENSE("GPL");
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值