前些天给新的板子修改BUG的时候,发现这块板子的串口是接在板载MCU上,我们的主SOC(海思HI3520D)上已经没有多余的串口。于是问题来了… 我们很多的上层应用都是通过串口和传感器通讯的,而且程序都是默认使用SOC上的串口,即打开/dev/ttyAMAx,然后调用Linux的select系统调用去和传感器交互数据的,代码都是C++写的,如果要做到兼容,必须修改基类的方法,这不符合软件的开闭原则…. 所以我打算写一个虚拟串口驱动,让内核去欺骗上层应用:”喂,你打开的真的是一个串口哦”。然后再定义一个虚拟串口管理进程,让这个进程去和单片机通讯进行实际串口收发。
驱动代码
今天先上一个尚未测试的驱动代码,按照linux uart框架写的,并且添加了一个给虚拟串口管理进程使用设备节点。这段代码可能会因为内核版本不同而编译不过,目前在3.16.0-30-generic内核版本下编译成功。
明天开始调试…. (2015-11-28)
今天终于完成了单元测试,虚拟串口的select读写均正常,另外使用了异步通知的方式来通知管理者进程,去更新实际的串口,先上一串调试通过的代码。(2015-12-1)
添加了多串口的支持,修正了资源竞争的问题。(2015-12-2)
/*
* Dummy serial driver by sdliu <sdliu@hongdian.com>
* The real hardware was installed in the MCU which is attached to our SOC(HI3520D).
* This driver depends on a serial manager which will process in the userspace
*/
#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>
// #include <asm/sizes.h>
#define prt_dbg
#ifdef prt_dbg
#define drintk printk
#else
#define drintk(...)
#endif
#define DUMMY_SERIAL_NR 4
static int dummy_serial_major = 0;
static int dummy_serial_minor_start = 0;
static struct dummy_uart_port *dummy_array[DUMMY_SERIAL_NR];
unsigned int dummy_serial_nr = 1;
module_param(dummy_serial_nr, uint, S_IRUGO);
// static DECLARE_WAIT_QUEUE_HEAD(dummy_wq);
int dummy_fasync(int fd, struct file *filp, int mode);
struct dummy_port_data {
// unsigned char port_idx;
unsigned long tx_fifo_size;
unsigned long rx_fifo_size;
};
struct dummy_uart_port {
struct uart_port port;
struct dummy_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 *dummy_class;
static unsigned int dummy_tx_empty(struct uart_port *port)
{
struct dummy_uart_port *dummy = (struct dummy_uart_port *)port;
drintk("dummy_tx_empty %d\n", dummy->tx_len);
// return TIOCSER_TEMT;
return dummy->tx_len > 0 ? 0 : TIOCSER_TEMT;
}
static void dummy_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct dummy_ua