Linux下的虚拟串口驱动(三)
来源:互联网 发布:怎么查看淘宝店家电话 编辑:程序博客网 时间:2024/05/21 17:04
欢迎转载,转载请注明出处。
前言
前面两章,对设备驱动和Linux下的字符设备驱动架构,做了简单的介绍;本章将结合实例,讲述虚拟串口实现过程。
驱动、总线和设备
谈到Linux的驱动架构(2.6内核),总线、设备和驱动这三个是必须提到的。总线将设备和驱动绑定,系统在注册设备时,会寻找与之匹配的驱动;同样,系统注册驱动的时候,会寻找与之匹配的设备。匹配操作就是总线来完成的(实际是通过probe()、match()函数,查找匹配项,这里先不做详细阐述)。
我们也会经常用一些总线去描述设备,例如USB设备,PCI设备,I2C设备等;从名字上也能看出这些设备所依附的总线。但在嵌入式系统里,并不是所有的总线都依附于此类总线,但设备和驱动又需要总线去绑定。例如本文需要实现的虚拟串口,既不属于PCI,也不属于USB,但总得挂接到相关总线上去。基于这种情况,Linux开发了一种虚拟总线,也叫platform总线,相应的设备被称为platform_device,驱动称为platform_driver,本次实现的虚拟串口就是挂接到platform总线的。
实例
一. platform_device和platform_driver
虽然叫虚拟串口,但为了蒙骗上层,底层就要把它当做一个真正的设备去注册进内核,让内核去欺骗上层应用。所以结合上面所说,一个设备驱动实例,需要总线、设备和驱动三个部分。总线匹配设备和驱动时,需要驱动信息和设备信息;因此就要用到platform_device和platform_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_driver、uart_port和uart_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 4typedef 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 voidvirtual_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 voidvirtual_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");
- Linux下的虚拟串口驱动(三)
- Linux下的虚拟串口驱动(一)
- Linux下的虚拟串口驱动(二)
- Linux下的串口总线驱动(三)
- Linux下自定义虚拟串口驱动
- linux 串口驱动(三)
- Linux下串口虚拟终端的配置
- linux下串口驱动
- Linux下的串口总线驱动(一)
- Linux下的串口总线驱动(二)
- Linux下的串口总线驱动(四)
- Linux下USB转串口的驱动
- Linux下USB转串口的驱动
- Linux下USB转串口的驱动
- Linux下USB转串口的驱动
- Linux下USB转串口的驱动
- WINCE下虚拟串口驱动设计
- WINCE下虚拟串口驱动设计
- C:\Windows\System32>net start mysql MySQL 服务正在启动 . MySQL 服务无法启动。 服务没有报告任何错误。 请键入 NET HELPMSG 3534
- 配置samba服务--centos7.x
- 软件JDK的安装及变量参数设置
- Hadoop单机安装-yellowcong
- XML_Dom4j的使用
- Linux下的虚拟串口驱动(三)
- 剑指offer:第一个只出现一次的字符位置
- 任总在新员工入职培训座谈会上的讲话
- Android hybrid 开发实践(android webview)
- 码位(code position/point)Unicode 编码与 Python 2/3 编码兼容性问题
- 【Java】EL表达式
- RecyclerView回收原理分析
- 设计模式(五)--装饰模式
- 二分图最大匹配(HihoCoder