编写虚拟UART驱动程序-框架

一、框架回顾

编写虚拟UART驱动程序-框架_第1张图片

二、编写UART驱动要做的事

编写虚拟UART驱动程序-框架_第2张图片

1.注册一个uart_driver
2. 对于每一个port,都会在设备树里面有一个节点
3. 设备树里的节点和platform_driver节点匹配
4. 当platform_dirver的probe函数被调用时,可以获得设备树里的信息,从而把每个串口设置成对应的uart_driver

三、虚拟的UART

编写虚拟UART驱动程序-框架_第3张图片

为了做实验,我们还要创建一个虚拟文件:/proc/virt_uart_buf

  • 要发送数据给虚拟串口时,执行:echo "xxx" > /proc/virt_uart_buf
  • 要读取虚拟串口的数据时,执行:cat /proc/virt_uart_buf
    虚拟串口需要有接收中断(写入buff时),查看6ull的手册,第三章中断中,使用99是可157里面一样是保留的。
    编写虚拟UART驱动程序-框架_第4张图片

编写代码的思路:
在platform_driver的probe函数里,需要设置uart_port(提供uart_ops),然后注册uart_port
uart_ops需要提供设置串口的信息,需要设置读数据,需要设置写输入的方法

四、编程

4.1 编写设备树

/ {
        virtual_uart: virtual_uart_100ask {
                compatible = "100ask,virtual_uart";
                interrupt-controller;
                #interrupt-cells = <2>;

                interrupt-parent = <&intc>;
                interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HGIH>;
        };
};

4.2 编写uart_driver

4.3 编写platform_driver

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static struct uart_port *virtual_port;
static struct proc_dir_entry *uart_proc_file;

#define BUF_LEN 1024
#define NEXT_PLACE(i) ((i+1)&0x3ff)
static unsigned char txbuf[1024];
static int tx_buf_r = 0;
static int tx_buf_w = 0;

static unsigned char rxbuf[1024];
//static int rx_buf_r = 0;
static int rx_buf_w = 0;

static struct uart_driver virtual_uart_drv = {
	.owner = THIS_MODULE,
	.driver_name = "VIRT_UART",
	.dev_name = "ttyVIRT",
	.major = 0,
	.minor = 0,
	.nr = 1,
};

/* circle buffer */
static int is_txbuf_empty(void)
{
	return tx_buf_r == tx_buf_w;
}

static int is_txbuf_full(void)
{
	return NEXT_PLACE(tx_buf_w) == tx_buf_r;
}

static int txbuf_put(unsigned char val)
{
	if(is_txbuf_full())
		return -1;
	txbuf[tx_buf_w] = val;
	tx_buf_w = NEXT_PLACE(tx_buf_w);
	return 0;
}

static int txbuf_get(unsigned char *pval)
{
	if(is_txbuf_empty())
		return -1;
	*pval =txbuf[tx_buf_r];
	tx_buf_r = NEXT_PLACE(tx_buf_r);
	return 0;
}

static int txbuf_count(void)
{
	if(tx_buf_w >= tx_buf_r)
		return tx_buf_w - tx_buf_r;
	else
		return BUF_LEN + tx_buf_w - tx_buf_r;
}

static ssize_t virtual_uart_buf_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	int cnt = txbuf_count();
	int i;
	unsigned char val;

	cnt = (cnt > size)?size:cnt;

	for(i=0;i<cnt;i++) {
		int ret;
		txbuf_get(&val);
		ret = copy_to_user(buf+i, &val, 1);
	}

	return cnt;
}

static ssize_t virtual_uart_buf_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{
	rx_buf_w = copy_from_user(rxbuf, buf, size);

	irq_set_irqchip_state(virtual_port->irq, IRQCHIP_STATE_PENDING, 1);

	return size;
}

static const struct file_operations virtual_uart_buf_fops = {
	.read  = virtual_uart_buf_read,
	.write = virtual_uart_buf_write,
};

static unsigned int virtual_uart_tx_empty(struct uart_port *port)
{
	return 1;
}

static void virtual_uart_start_tx(struct uart_port *port)
{
	struct circ_buf *xmit = &port->state->xmit;

	while(!uart_circ_empty(xmit) &&
			!uart_tx_stopped(port)) {

		/* save circ buffer into the txbuf */

		//txbuf[tx_buf_w++] = xmit->buf[xmit->tail];
		txbuf_put(xmit->buf[xmit->tail]);
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1);
		port->icount.tx++;
	}

   if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
       uart_write_wakeup(port);
}

static void
virtual_uart_set_termios(struct uart_port *port, struct ktermios *termios,
             struct ktermios *old)
{
	return;
}

static int virtual_startup(struct uart_port *port)
{
	return 0;
}

static void virtual_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
}

static unsigned int virtual_get_mctrl(struct uart_port *port)
{
	return 0;
}

static void virtual_stop_tx(struct uart_port *port)
{
}

static void virtual_stop_rx(struct uart_port *port)
{
}

static void virtual_shutdown(struct uart_port *port)
{
}

static const char *virtual_type(struct uart_port *port)
{
	return "100ASK_VIRT_UART";
}

static const struct uart_ops  virtual_port_ops = {
    .tx_empty   = virtual_uart_tx_empty,
	.set_mctrl	= virtual_set_mctrl,
	.get_mctrl	= virtual_get_mctrl,
    .start_tx   = virtual_uart_start_tx,
	.stop_tx 	= virtual_stop_tx,
	.stop_rx	= virtual_stop_rx,
//	.enable_ms	= imx_enable_ms,
//	.break_ctl	= imx_break_ctl,
	.startup	= virtual_startup,
	.shutdown	= virtual_shutdown,
//	.flush_buffer	= imx_flush_buffer,
    .set_termios    = virtual_uart_set_termios,
	.type			= virtual_type,
//	.config_port	= imx_config_port,
//	.verify_port	= imx_verify_port,
};

static irqreturn_t virtual_uart_rxint(int irq, void *dev_id)
{
	struct uart_port *port = dev_id;
	unsigned long flags;
	int i;

	spin_lock_irqsave(&port->lock, flags);

	for(i=0;i<rx_buf_w;i++) {
		port->icount.rx++;

		tty_insert_flip_char(&port->state->port, rxbuf[i], TTY_NORMAL);
	}
	rx_buf_w = 0;

	spin_unlock_irqrestore(&port->lock, flags);
	tty_flip_buffer_push(&port->state->port);

	return IRQ_HANDLED;
}

static int virtual_uart_probe(struct platform_device *pdev)
{
	int rx_irq;
	int ret;

	uart_proc_file = proc_create("virtual_uart_buf", 0, NULL, &virtual_uart_buf_fops);

	virtual_port = devm_kzalloc(&pdev->dev, sizeof(struct uart_port), GFP_KERNEL);
	if(!virtual_port) {
		return -ENOMEM;
	}
	
	rx_irq = platform_get_irq(pdev, 0);
	ret = devm_request_irq(&pdev->dev, rx_irq, virtual_uart_rxint, 0,
			dev_name(&pdev->dev), virtual_port);


	virtual_port->dev = &pdev->dev;
	virtual_port->iotype = UPIO_MEM;
	virtual_port->irq = rx_irq;
	virtual_port->fifosize = 32;
	virtual_port->ops = &virtual_port_ops;
	virtual_port->flags = UPF_BOOT_AUTOCONF;

	return uart_add_one_port(&virtual_uart_drv, virtual_port);
}

static int virtual_uart_remove(struct platform_device *pdev)
{
	uart_remove_one_port(&virtual_uart_drv, virtual_port);
	proc_remove(uart_proc_file);
	return 0;
}

static const struct of_device_id virtual_uart_of_match[] = {
	{ .compatible = "100ask,virtual_uart", },
	{ },
};

static struct platform_driver virtual_uart_platform_drv = {
	.probe  = virtual_uart_probe,
	.remove = virtual_uart_remove,
	.driver = {
		.name = "virtual_uart",
		.of_match_table = of_match_ptr(virtual_uart_of_match),
	},
};

static int __init virtual_uart_init(void)
{
	int ret = uart_register_driver(&virtual_uart_drv);
	if(ret) return ret;

	ret = platform_driver_register(&virtual_uart_platform_drv);
	if(ret != 0)
		uart_unregister_driver(&virtual_uart_drv);

	return ret;
}

static void __exit virtual_uart_exit(void)
{
	platform_driver_unregister(&virtual_uart_platform_drv);
	uart_unregister_driver(&virtual_uart_drv);
}

module_init(virtual_uart_init);
module_exit(virtual_uart_exit);
MODULE_LICENSE("GPL");

4.4 实现uart_ops

4.5 实现/proc/virt_uart_buf

你可能感兴趣的:(#,UART,uart,imx6ull,kernel,driver)