一、框架回顾
二、编写UART驱动要做的事
1.注册一个uart_driver
2. 对于每一个port,都会在设备树里面有一个节点
3. 设备树里的节点和platform_driver节点匹配
4. 当platform_dirver的probe函数被调用时,可以获得设备树里的信息,从而把每个串口设置成对应的uart_driver
三、虚拟的UART
为了做实验,我们还要创建一个虚拟文件:/proc/virt_uart_buf
- 要发送数据给虚拟串口时,执行:
echo "xxx" > /proc/virt_uart_buf
- 要读取虚拟串口的数据时,执行:
cat /proc/virt_uart_buf
虚拟串口需要有接收中断(写入buff时),查看6ull的手册,第三章中断中,使用99是可157里面一样是保留的。
编写代码的思路:
在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 <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/sysrq.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/proc_fs.h>
#include <linux/tty_flip.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
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");