欢迎转载,转载请注明出处。
前言
前面两章,对设备驱动和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 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");