diff --git a/IMX6ULL/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.md b/IMX6ULL/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.md new file mode 100644 index 0000000..d36bc34 --- /dev/null +++ b/IMX6ULL/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.md @@ -0,0 +1,136 @@ +## 编写虚拟UART驱动程序\_实现数据传输 + +* 参考代码 + + ```shell + 硬件相关: + drivers/tty/serial/imx.c + drivers/tty/serial/stm32-usart.c + + 串口核心层: + drivers/tty/serial/serial_core.c + + TTY层: + drivers/tty/tty_io.c + ``` + + + +* 本节课程源码在GIT仓库里 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\09_UART + 06_virtual_uart_driver_txrx + doc_and_source_for_drivers\STM32MP157\source\A7\09_UART + 06_virtual_uart_driver_txrx + ``` + + + +### 1. 虚拟UART的驱动组成 + +![image-20210730142813809](pic/09_UART/39_uart_driver.png) + + + +### 2. 虚拟UART的数据流程 + +![image-20210730150445225](pic/09_UART/40_virtual_uart.png) + +为了做实验,我们还要创建一个虚拟文件:/proc/virt_uart_buf + +* 要发数据给虚拟串口时,执行:echo "xxx" > /proc/virt_uart_buf +* 要读取虚拟串口的数据时,执行:cat /proc/virt_uart_buf + + + +### 3. 实现/proc文件 + +参考`/proc/cmdline`,怎么找到它对应的驱动?在Linux内核源码下执行以下命令搜索: + +```shell +grep "cmdline" * -nr | grep proc +``` + +得到: + +```shell +fs/proc/cmdline.c:26: proc_create("cmdline", 0, NULL, &cmdline_proc_fops); +``` + + + +### 4. 触发中断 + +使用如下函数: + +```c +int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, + bool val); +``` + +怎么找到它的?在中断子系统中,我们知道往GIC寄存器GICD_ISPENDRn写入某一位就可以触发中断。内核代码中怎么访问这些寄存器? +在`drivers\irqchip\irq-gic.c`中可以看到irq_chip中的"irq_set_irqchip_state"被用来设置中断状态: + +```c +static struct irq_chip gic_chip = { + .irq_mask = gic_mask_irq, + .irq_unmask = gic_unmask_irq, + .irq_eoi = gic_eoi_irq, + .irq_set_type = gic_set_type, + .irq_get_irqchip_state = gic_irq_get_irqchip_state, + .irq_set_irqchip_state = gic_irq_set_irqchip_state, /* 2. 继续搜"irq_set_irqchip_state" */ + .flags = IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE | + IRQCHIP_MASK_ON_SUSPEND, +}; + +static int gic_irq_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool val) +{ + u32 reg; + + switch (which) { + case IRQCHIP_STATE_PENDING: + reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR; /* 1. 找到寄存器 */ + break; + + case IRQCHIP_STATE_ACTIVE: + reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR; + break; + + case IRQCHIP_STATE_MASKED: + reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET; + break; + + default: + return -EINVAL; + } + + gic_poke_irq(d, reg); + return 0; +} +``` + + + +继续搜"irq_set_irqchip_state",在`drivers\irqchip\irq-gic.c`中可以看到: + +```c +int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, + bool val) +{ + ...... +} + +EXPORT_SYMBOL_GPL(irq_set_irqchip_state); +``` + + + +以后就可与使用如下代码触发某个中断: + +```c +irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, 1); +``` + diff --git a/IMX6ULL/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.tif b/IMX6ULL/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.tif new file mode 100644 index 0000000..7333f25 Binary files /dev/null and b/IMX6ULL/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.tif differ diff --git a/IMX6ULL/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.md b/IMX6ULL/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.md new file mode 100644 index 0000000..0a75d98 --- /dev/null +++ b/IMX6ULL/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.md @@ -0,0 +1,211 @@ +## 编写虚拟UART驱动程序_调试 + +* 参考代码 + + ```shell + 硬件相关: + drivers/tty/serial/imx.c + drivers/tty/serial/stm32-usart.c + + 串口核心层: + drivers/tty/serial/serial_core.c + + TTY层: + drivers/tty/tty_io.c + ``` + + + +* 本节课程源码在GIT仓库里 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\09_UART + 07_virtual_uart_driver_ok + doc_and_source_for_drivers\STM32MP157\source\A7\09_UART + 07_virtual_uart_driver_ok + ``` + + + + +### 1. 实验流程 + +![image-20210806120309603](pic/09_UART/41_data_flow.png) + + + +### 2. 上机实验 + +#### 2.1 设置工具链 + +##### 1. STM32MP157 + + ```shell + export ARCH=arm + export CROSS_COMPILE=arm-buildroot-linux-gnueabihf- + export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin + ``` + +##### 2. IMX6ULL + + ```shell + export ARCH=arm + export CROSS_COMPILE=arm-linux-gnueabihf- + export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin + ``` + + + +#### 2.2 编译、替换设备树 + +##### 1. STM32MP157 + + * 修改`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`,添加如下代码: + + ```shell + / { + virtual_uart: virtual_uart_100ask { + compatible = "100ask,virtual_uart"; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + }; + ``` + + + + * 编译设备树: + 在Ubuntu的STM32MP157内核目录下执行如下命令, + 得到设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v2.dtb` + + ```shell + make dtbs + ``` + + * 复制到NFS目录: + + ```shell + $ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v2.dtb ~/nfs_rootfs/ + ``` + + * 开发板上挂载NFS文件系统 + + * vmware使用NAT(假设windowsIP为192.168.2.100) + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 + 192.168.2.100:/home/book/nfs_rootfs /mnt + ``` + + * vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.2.137 + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.2.137:/home/book/nfs_rootfs /mnt + ``` + +* 更新设备树 + + ```shell + [root@100ask:~]# mount /dev/mmcblk2p2 /boot + [root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + + + +##### 2. IMX6ULL + + * 修改`arch/arm/boot/dts/100ask_imx6ull-14x14.dts`,添加如下代码: + + ```shell + / { + virtual_uart: virtual_uart_100ask { + compatible = "100ask,virtual_uart"; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + + }; + ``` + + + + * 编译设备树: + 在Ubuntu的IMX6ULL内核目录下执行如下命令, + 得到设备树文件:`arch/arm/boot/dts/100ask_imx6ull-14x14.dtb` + + ```shell + make dtbs + ``` + + * 复制到NFS目录: + + ```shell + $ cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/ + ``` + +* 开发板上挂载NFS文件系统 + + * vmware使用NAT(假设windowsIP为192.168.2.100) + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 + 192.168.2.100:/home/book/nfs_rootfs /mnt + ``` + + * vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.2.137 + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.2.137:/home/book/nfs_rootfs /mnt + ``` + + * 更新设备树 + + ```shell + [root@100ask:~]# cp /mnt/100ask_imx6ull-14x14.dtb /boot + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + +#### 2.3 编译、安装驱动程序 + +* 编译: + + * 在Ubuntu上 + * 修改`07_virtual_uart_driver_ok`中的Makefile,指定内核路径`KERN_DIR`,在执行`make`命令即可。 + +* 安装: + + * 在开发板上 + + * 挂载NFS,复制文件,insmod,类似如下命令: + + ```shell + mount -t nfs -o nolock,vers=3 192.168.2.137:/home/book/nfs_rootfs /mnt + // 对于IMX6ULL,想看到驱动打印信息,需要先执行 + echo "7 4 1 7" > /proc/sys/kernel/printk + + insmod -f /mnt/virtual_uart.ko + + ``` + +* 观察内核打印的信息 + + + +### 3. 调试 + +根据框架、数据流程来调试: + +![](pic/09_UART/24_tty_driver_level_2.png) \ No newline at end of file diff --git a/IMX6ULL/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.tif b/IMX6ULL/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.tif new file mode 100644 index 0000000..3a62339 Binary files /dev/null and b/IMX6ULL/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.tif differ diff --git a/IMX6ULL/doc_pic/09_UART/pic/09_UART/41_data_flow.png b/IMX6ULL/doc_pic/09_UART/pic/09_UART/41_data_flow.png new file mode 100644 index 0000000..4bcd220 Binary files /dev/null and b/IMX6ULL/doc_pic/09_UART/pic/09_UART/41_data_flow.png differ diff --git a/IMX6ULL/source/09_UART/06_virtual_uart_driver_txrx/virtual_uart.c b/IMX6ULL/source/09_UART/06_virtual_uart_driver_txrx/virtual_uart.c new file mode 100644 index 0000000..e52a0bf --- /dev/null +++ b/IMX6ULL/source/09_UART/06_virtual_uart_driver_txrx/virtual_uart.c @@ -0,0 +1,288 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BUF_LEN 1024 +#define NEXT_PLACE(i) ((i+1)&0x3FF) + +static struct uart_port *virt_port; +static unsigned char txbuf[BUF_LEN]; +static int tx_buf_r = 0; +static int tx_buf_w = 0; + +static unsigned char rxbuf[BUF_LEN]; +static int rx_buf_r = 0; +static int rx_buf_w = 0; + +static struct proc_dir_entry *uart_proc_file; + + +static struct uart_driver virt_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 = g_events[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; +} + + + +ssize_t virt_uart_buf_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) +{ + /* 把txbuf中的数据copy_to_user */ + int cnt = txbuf_count(); + int i; + unsigned char val; + + cnt = (cnt > size)? size: cnt; + + for (i = 0; i < cnt; i++) + { + txbuf_get(&val); + copy_to_user(buf+i, &val, 1); + } + + return cnt; +} + +static ssize_t virt_uart_buf_write (struct file *file, const char __user *buf, size_t size, loff_t *off) +{ + /* get data */ + copy_from_user(rxbuf, buf, size); + rx_buf_w = size; + + /* 模拟产生RX中断 */ + irq_set_irqchip_state(virt_port->irq, IRQCHIP_STATE_PENDING, 1); + + return size; +} + +static const struct file_operations virt_uart_buf_fops = { + .read = virt_uart_buf_read, + .write = virt_uart_buf_write, +}; + + +static unsigned int virt_tx_empty(struct uart_port *port) +{ + /* 因为要发送的数据瞬间存入buffer */ + return 1; +} + + +/* + * interrupts disabled on entry + */ +static void virt_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + while (!uart_circ_empty(xmit) && + !uart_tx_stopped(port)) { + /* send xmit->buf[xmit->tail] + * out the port here */ + + /* 把circ buffer中的数据全部存入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 +virt_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + return; +} + +static const struct uart_ops virt_pops = { + .tx_empty = virt_tx_empty, + //.set_mctrl = imx_set_mctrl, + //.get_mctrl = imx_get_mctrl, + //.stop_tx = imx_stop_tx, + .start_tx = virt_start_tx, + //.stop_rx = imx_stop_rx, + //.enable_ms = imx_enable_ms, + //.break_ctl = imx_break_ctl, + //.startup = imx_startup, + //.shutdown = imx_shutdown, + //.flush_buffer = imx_flush_buffer, + .set_termios = virt_set_termios, + //.type = imx_type, + //.config_port = imx_config_port, + //.verify_port = imx_verify_port, +}; + + +static irqreturn_t virt_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++; + + /* get data from hardware/rxbuf */ + + /* put data to ldisc */ + tty_insert_flip_char(port, rxbuf[i], TTY_NORMAL) + } + rx_buf_w = 0; + + spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(port); + + return IRQ_HANDLED; +} + + +static int virtual_uart_probe(struct platform_device *pdev) +{ + int rxirq; + int ret; + + /* create proc file */ + uart_proc_file = proc_create("virt_uart_buf", 0, NULL, &virt_uart_buf_fops); + + //uart_add_one_port(struct uart_driver * drv, struct uart_port * uport); + + /* 从设备树获得硬件信息 */ + rxirq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, rxirq, virt_uart_rxint, 0, + dev_name(&pdev->dev), virt_port); + + /* 分配设置注册uart_port */ + virt_port = devm_kzalloc(&pdev->dev, sizeof(*virt_port), GFP_KERNEL); + + virt_port->dev = &pdev->dev; + virt_port->iotype = UPIO_MEM; + virt_port->irq = rxirq; + virt_port->fifosize = 32; + virt_port->ops = &virt_pops; + virt_port->flags = UPF_BOOT_AUTOCONF; + + return uart_add_one_port(&virt_uart_drv, virt_port); + +} +static int virtual_uart_remove(struct platform_device *pdev) +{ + 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_driver = { + .probe = virtual_uart_probe, + .remove = virtual_uart_remove, + .driver = { + .name = "100ask_virtual_uart", + .of_match_table = of_match_ptr(virtual_uart_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_uart_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + int ret = uart_register_driver(&virt_uart_drv); + + if (ret) + return ret; + + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_uart_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_uart_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_uart_driver); +} + +module_init(virtual_uart_init); +module_exit(virtual_uart_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/IMX6ULL/source/09_UART/06_virtual_uart_driver_txrx/virtual_uart.dts b/IMX6ULL/source/09_UART/06_virtual_uart_driver_txrx/virtual_uart.dts new file mode 100644 index 0000000..50b380a --- /dev/null +++ b/IMX6ULL/source/09_UART/06_virtual_uart_driver_txrx/virtual_uart.dts @@ -0,0 +1,13 @@ + + +/{ + virtual_uart: virtual_uart_100ask { + compatible = "100ask,virtual_uart"; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + + +}; diff --git a/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/Makefile b/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/Makefile new file mode 100644 index 0000000..91e9a18 --- /dev/null +++ b/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/Makefile @@ -0,0 +1,20 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += virtual_uart.o + diff --git a/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/virtual_uart.c b/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/virtual_uart.c new file mode 100644 index 0000000..17a64dd --- /dev/null +++ b/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/virtual_uart.c @@ -0,0 +1,334 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BUF_LEN 1024 +#define NEXT_PLACE(i) ((i+1)&0x3FF) + +static struct uart_port *virt_port; +static unsigned char txbuf[BUF_LEN]; +static int tx_buf_r = 0; +static int tx_buf_w = 0; + +static unsigned char rxbuf[BUF_LEN]; +static int rx_buf_w = 0; + +static struct proc_dir_entry *uart_proc_file; + + +static struct uart_driver virt_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; +} + + + +ssize_t virt_uart_buf_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) +{ + /* 把txbuf中的数据copy_to_user */ + int cnt = txbuf_count(); + int i; + unsigned char val; + int ret; + + cnt = (cnt > size)? size: cnt; + + for (i = 0; i < cnt; i++) + { + txbuf_get(&val); + ret = copy_to_user(buf+i, &val, 1); + } + + return cnt; +} + +static ssize_t virt_uart_buf_write (struct file *file, const char __user *buf, size_t size, loff_t *off) +{ + int ret; + + /* get data */ + ret = copy_from_user(rxbuf, buf, size); + rx_buf_w = size; + + /* 模拟产生RX中断 */ + irq_set_irqchip_state(virt_port->irq, IRQCHIP_STATE_PENDING, 1); + + return size; +} + +static const struct file_operations virt_uart_buf_fops = { + .read = virt_uart_buf_read, + .write = virt_uart_buf_write, +}; + + +static unsigned int virt_tx_empty(struct uart_port *port) +{ + /* 因为要发送的数据瞬间存入buffer */ + return 1; +} + + +/* + * interrupts disabled on entry + */ +static void virt_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + while (!uart_circ_empty(xmit) && + !uart_tx_stopped(port)) { + /* send xmit->buf[xmit->tail] + * out the port here */ + + /* 把circ buffer中的数据全部存入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 +virt_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + return; +} + +static int virt_startup(struct uart_port *port) +{ + return 0; +} + +static void virt_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int virt_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void virt_stop_tx(struct uart_port *port) +{ +} + +static void virt_stop_rx(struct uart_port *port) +{ +} + +static void virt_shutdown(struct uart_port *port) +{ +} + +static const char *virt_type(struct uart_port *port) +{ + return "100ASK_VIRT_UART"; +} + + +static const struct uart_ops virt_pops = { + .tx_empty = virt_tx_empty, + .set_mctrl = virt_set_mctrl, + .get_mctrl = virt_get_mctrl, + .stop_tx = virt_stop_tx, + .start_tx = virt_start_tx, + .stop_rx = virt_stop_rx, + //.enable_ms = imx_enable_ms, + //.break_ctl = imx_break_ctl, + .startup = virt_startup, + .shutdown = virt_shutdown, + //.flush_buffer = imx_flush_buffer, + .set_termios = virt_set_termios, + .type = virt_type, + //.config_port = imx_config_port, + //.verify_port = imx_verify_port, +}; + + + +static irqreturn_t virt_uart_rxint(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct tty_port *tport = &port->state->port; + unsigned long flags; + int i; + + spin_lock_irqsave(&port->lock, flags); + + for (i = 0; i < rx_buf_w; i++) { + port->icount.rx++; + + /* get data from hardware/rxbuf */ + + /* put data to ldisc */ + tty_insert_flip_char(tport, rxbuf[i], TTY_NORMAL); + } + rx_buf_w = 0; + + spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(tport); + + return IRQ_HANDLED; +} + + +static int virtual_uart_probe(struct platform_device *pdev) +{ + int rxirq; + int ret; + + /* create proc file */ + uart_proc_file = proc_create("virt_uart_buf", 0, NULL, &virt_uart_buf_fops); + + //uart_add_one_port(struct uart_driver * drv, struct uart_port * uport); + + /* 从设备树获得硬件信息 */ + rxirq = platform_get_irq(pdev, 0); + + /* 分配设置注册uart_port */ + virt_port = devm_kzalloc(&pdev->dev, sizeof(*virt_port), GFP_KERNEL); + + virt_port->dev = &pdev->dev; + virt_port->iotype = UPIO_MEM; + virt_port->irq = rxirq; + virt_port->fifosize = 32; + virt_port->ops = &virt_pops; + virt_port->flags = UPF_BOOT_AUTOCONF; + virt_port->type = PORT_8250; + + ret = devm_request_irq(&pdev->dev, rxirq, virt_uart_rxint, 0, + dev_name(&pdev->dev), virt_port); + + return uart_add_one_port(&virt_uart_drv, virt_port); + +} +static int virtual_uart_remove(struct platform_device *pdev) +{ + uart_remove_one_port(&virt_uart_drv, virt_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_driver = { + .probe = virtual_uart_probe, + .remove = virtual_uart_remove, + .driver = { + .name = "100ask_virtual_uart", + .of_match_table = of_match_ptr(virtual_uart_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_uart_init(void) +{ + int ret; + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + ret = uart_register_driver(&virt_uart_drv); + + if (ret) + return ret; + + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_uart_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_uart_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_uart_driver); + + + uart_unregister_driver(&virt_uart_drv); +} + +module_init(virtual_uart_init); +module_exit(virtual_uart_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/virtual_uart.dts b/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/virtual_uart.dts new file mode 100644 index 0000000..50b380a --- /dev/null +++ b/IMX6ULL/source/09_UART/07_virtual_uart_driver_ok/virtual_uart.dts @@ -0,0 +1,13 @@ + + +/{ + virtual_uart: virtual_uart_100ask { + compatible = "100ask,virtual_uart"; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + + +}; diff --git a/README.md b/README.md index 464f300..c2efe8a 100644 --- a/README.md +++ b/README.md @@ -367,6 +367,18 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git 14_编写虚拟UART驱动程序_实现uart_ops ``` +* 2021.08.04 发布"UART子系统" + + ```shell + 15_编写虚拟UART驱动程序_实现数据传输 + ``` +* 2021.08.06 发布"UART子系统" + + ```shell + 16_编写虚拟UART驱动程序_调试 + ``` + + ## 6. 联系方式 * 官网:http://www.100ask.net diff --git a/STM32MP157/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.md b/STM32MP157/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.md new file mode 100644 index 0000000..d36bc34 --- /dev/null +++ b/STM32MP157/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.md @@ -0,0 +1,136 @@ +## 编写虚拟UART驱动程序\_实现数据传输 + +* 参考代码 + + ```shell + 硬件相关: + drivers/tty/serial/imx.c + drivers/tty/serial/stm32-usart.c + + 串口核心层: + drivers/tty/serial/serial_core.c + + TTY层: + drivers/tty/tty_io.c + ``` + + + +* 本节课程源码在GIT仓库里 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\09_UART + 06_virtual_uart_driver_txrx + doc_and_source_for_drivers\STM32MP157\source\A7\09_UART + 06_virtual_uart_driver_txrx + ``` + + + +### 1. 虚拟UART的驱动组成 + +![image-20210730142813809](pic/09_UART/39_uart_driver.png) + + + +### 2. 虚拟UART的数据流程 + +![image-20210730150445225](pic/09_UART/40_virtual_uart.png) + +为了做实验,我们还要创建一个虚拟文件:/proc/virt_uart_buf + +* 要发数据给虚拟串口时,执行:echo "xxx" > /proc/virt_uart_buf +* 要读取虚拟串口的数据时,执行:cat /proc/virt_uart_buf + + + +### 3. 实现/proc文件 + +参考`/proc/cmdline`,怎么找到它对应的驱动?在Linux内核源码下执行以下命令搜索: + +```shell +grep "cmdline" * -nr | grep proc +``` + +得到: + +```shell +fs/proc/cmdline.c:26: proc_create("cmdline", 0, NULL, &cmdline_proc_fops); +``` + + + +### 4. 触发中断 + +使用如下函数: + +```c +int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, + bool val); +``` + +怎么找到它的?在中断子系统中,我们知道往GIC寄存器GICD_ISPENDRn写入某一位就可以触发中断。内核代码中怎么访问这些寄存器? +在`drivers\irqchip\irq-gic.c`中可以看到irq_chip中的"irq_set_irqchip_state"被用来设置中断状态: + +```c +static struct irq_chip gic_chip = { + .irq_mask = gic_mask_irq, + .irq_unmask = gic_unmask_irq, + .irq_eoi = gic_eoi_irq, + .irq_set_type = gic_set_type, + .irq_get_irqchip_state = gic_irq_get_irqchip_state, + .irq_set_irqchip_state = gic_irq_set_irqchip_state, /* 2. 继续搜"irq_set_irqchip_state" */ + .flags = IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE | + IRQCHIP_MASK_ON_SUSPEND, +}; + +static int gic_irq_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool val) +{ + u32 reg; + + switch (which) { + case IRQCHIP_STATE_PENDING: + reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR; /* 1. 找到寄存器 */ + break; + + case IRQCHIP_STATE_ACTIVE: + reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR; + break; + + case IRQCHIP_STATE_MASKED: + reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET; + break; + + default: + return -EINVAL; + } + + gic_poke_irq(d, reg); + return 0; +} +``` + + + +继续搜"irq_set_irqchip_state",在`drivers\irqchip\irq-gic.c`中可以看到: + +```c +int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, + bool val) +{ + ...... +} + +EXPORT_SYMBOL_GPL(irq_set_irqchip_state); +``` + + + +以后就可与使用如下代码触发某个中断: + +```c +irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, 1); +``` + diff --git a/STM32MP157/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.tif b/STM32MP157/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.tif new file mode 100644 index 0000000..7333f25 Binary files /dev/null and b/STM32MP157/doc_pic/09_UART/15_编写虚拟UART驱动程序_实现数据传输.tif differ diff --git a/STM32MP157/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.md b/STM32MP157/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.md new file mode 100644 index 0000000..0a75d98 --- /dev/null +++ b/STM32MP157/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.md @@ -0,0 +1,211 @@ +## 编写虚拟UART驱动程序_调试 + +* 参考代码 + + ```shell + 硬件相关: + drivers/tty/serial/imx.c + drivers/tty/serial/stm32-usart.c + + 串口核心层: + drivers/tty/serial/serial_core.c + + TTY层: + drivers/tty/tty_io.c + ``` + + + +* 本节课程源码在GIT仓库里 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\09_UART + 07_virtual_uart_driver_ok + doc_and_source_for_drivers\STM32MP157\source\A7\09_UART + 07_virtual_uart_driver_ok + ``` + + + + +### 1. 实验流程 + +![image-20210806120309603](pic/09_UART/41_data_flow.png) + + + +### 2. 上机实验 + +#### 2.1 设置工具链 + +##### 1. STM32MP157 + + ```shell + export ARCH=arm + export CROSS_COMPILE=arm-buildroot-linux-gnueabihf- + export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin + ``` + +##### 2. IMX6ULL + + ```shell + export ARCH=arm + export CROSS_COMPILE=arm-linux-gnueabihf- + export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin + ``` + + + +#### 2.2 编译、替换设备树 + +##### 1. STM32MP157 + + * 修改`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`,添加如下代码: + + ```shell + / { + virtual_uart: virtual_uart_100ask { + compatible = "100ask,virtual_uart"; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + }; + ``` + + + + * 编译设备树: + 在Ubuntu的STM32MP157内核目录下执行如下命令, + 得到设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v2.dtb` + + ```shell + make dtbs + ``` + + * 复制到NFS目录: + + ```shell + $ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v2.dtb ~/nfs_rootfs/ + ``` + + * 开发板上挂载NFS文件系统 + + * vmware使用NAT(假设windowsIP为192.168.2.100) + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 + 192.168.2.100:/home/book/nfs_rootfs /mnt + ``` + + * vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.2.137 + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.2.137:/home/book/nfs_rootfs /mnt + ``` + +* 更新设备树 + + ```shell + [root@100ask:~]# mount /dev/mmcblk2p2 /boot + [root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + + + +##### 2. IMX6ULL + + * 修改`arch/arm/boot/dts/100ask_imx6ull-14x14.dts`,添加如下代码: + + ```shell + / { + virtual_uart: virtual_uart_100ask { + compatible = "100ask,virtual_uart"; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + + }; + ``` + + + + * 编译设备树: + 在Ubuntu的IMX6ULL内核目录下执行如下命令, + 得到设备树文件:`arch/arm/boot/dts/100ask_imx6ull-14x14.dtb` + + ```shell + make dtbs + ``` + + * 复制到NFS目录: + + ```shell + $ cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/ + ``` + +* 开发板上挂载NFS文件系统 + + * vmware使用NAT(假设windowsIP为192.168.2.100) + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 + 192.168.2.100:/home/book/nfs_rootfs /mnt + ``` + + * vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.2.137 + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.2.137:/home/book/nfs_rootfs /mnt + ``` + + * 更新设备树 + + ```shell + [root@100ask:~]# cp /mnt/100ask_imx6ull-14x14.dtb /boot + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + +#### 2.3 编译、安装驱动程序 + +* 编译: + + * 在Ubuntu上 + * 修改`07_virtual_uart_driver_ok`中的Makefile,指定内核路径`KERN_DIR`,在执行`make`命令即可。 + +* 安装: + + * 在开发板上 + + * 挂载NFS,复制文件,insmod,类似如下命令: + + ```shell + mount -t nfs -o nolock,vers=3 192.168.2.137:/home/book/nfs_rootfs /mnt + // 对于IMX6ULL,想看到驱动打印信息,需要先执行 + echo "7 4 1 7" > /proc/sys/kernel/printk + + insmod -f /mnt/virtual_uart.ko + + ``` + +* 观察内核打印的信息 + + + +### 3. 调试 + +根据框架、数据流程来调试: + +![](pic/09_UART/24_tty_driver_level_2.png) \ No newline at end of file diff --git a/STM32MP157/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.tif b/STM32MP157/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.tif new file mode 100644 index 0000000..3a62339 Binary files /dev/null and b/STM32MP157/doc_pic/09_UART/16_编写虚拟UART驱动程序_调试.tif differ diff --git a/STM32MP157/doc_pic/09_UART/pic/09_UART/41_data_flow.png b/STM32MP157/doc_pic/09_UART/pic/09_UART/41_data_flow.png new file mode 100644 index 0000000..4bcd220 Binary files /dev/null and b/STM32MP157/doc_pic/09_UART/pic/09_UART/41_data_flow.png differ diff --git a/STM32MP157/source/A7/09_UART/06_virtual_uart_driver_txrx/virtual_uart.c b/STM32MP157/source/A7/09_UART/06_virtual_uart_driver_txrx/virtual_uart.c new file mode 100644 index 0000000..e52a0bf --- /dev/null +++ b/STM32MP157/source/A7/09_UART/06_virtual_uart_driver_txrx/virtual_uart.c @@ -0,0 +1,288 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BUF_LEN 1024 +#define NEXT_PLACE(i) ((i+1)&0x3FF) + +static struct uart_port *virt_port; +static unsigned char txbuf[BUF_LEN]; +static int tx_buf_r = 0; +static int tx_buf_w = 0; + +static unsigned char rxbuf[BUF_LEN]; +static int rx_buf_r = 0; +static int rx_buf_w = 0; + +static struct proc_dir_entry *uart_proc_file; + + +static struct uart_driver virt_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 = g_events[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; +} + + + +ssize_t virt_uart_buf_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) +{ + /* 把txbuf中的数据copy_to_user */ + int cnt = txbuf_count(); + int i; + unsigned char val; + + cnt = (cnt > size)? size: cnt; + + for (i = 0; i < cnt; i++) + { + txbuf_get(&val); + copy_to_user(buf+i, &val, 1); + } + + return cnt; +} + +static ssize_t virt_uart_buf_write (struct file *file, const char __user *buf, size_t size, loff_t *off) +{ + /* get data */ + copy_from_user(rxbuf, buf, size); + rx_buf_w = size; + + /* 模拟产生RX中断 */ + irq_set_irqchip_state(virt_port->irq, IRQCHIP_STATE_PENDING, 1); + + return size; +} + +static const struct file_operations virt_uart_buf_fops = { + .read = virt_uart_buf_read, + .write = virt_uart_buf_write, +}; + + +static unsigned int virt_tx_empty(struct uart_port *port) +{ + /* 因为要发送的数据瞬间存入buffer */ + return 1; +} + + +/* + * interrupts disabled on entry + */ +static void virt_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + while (!uart_circ_empty(xmit) && + !uart_tx_stopped(port)) { + /* send xmit->buf[xmit->tail] + * out the port here */ + + /* 把circ buffer中的数据全部存入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 +virt_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + return; +} + +static const struct uart_ops virt_pops = { + .tx_empty = virt_tx_empty, + //.set_mctrl = imx_set_mctrl, + //.get_mctrl = imx_get_mctrl, + //.stop_tx = imx_stop_tx, + .start_tx = virt_start_tx, + //.stop_rx = imx_stop_rx, + //.enable_ms = imx_enable_ms, + //.break_ctl = imx_break_ctl, + //.startup = imx_startup, + //.shutdown = imx_shutdown, + //.flush_buffer = imx_flush_buffer, + .set_termios = virt_set_termios, + //.type = imx_type, + //.config_port = imx_config_port, + //.verify_port = imx_verify_port, +}; + + +static irqreturn_t virt_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++; + + /* get data from hardware/rxbuf */ + + /* put data to ldisc */ + tty_insert_flip_char(port, rxbuf[i], TTY_NORMAL) + } + rx_buf_w = 0; + + spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(port); + + return IRQ_HANDLED; +} + + +static int virtual_uart_probe(struct platform_device *pdev) +{ + int rxirq; + int ret; + + /* create proc file */ + uart_proc_file = proc_create("virt_uart_buf", 0, NULL, &virt_uart_buf_fops); + + //uart_add_one_port(struct uart_driver * drv, struct uart_port * uport); + + /* 从设备树获得硬件信息 */ + rxirq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, rxirq, virt_uart_rxint, 0, + dev_name(&pdev->dev), virt_port); + + /* 分配设置注册uart_port */ + virt_port = devm_kzalloc(&pdev->dev, sizeof(*virt_port), GFP_KERNEL); + + virt_port->dev = &pdev->dev; + virt_port->iotype = UPIO_MEM; + virt_port->irq = rxirq; + virt_port->fifosize = 32; + virt_port->ops = &virt_pops; + virt_port->flags = UPF_BOOT_AUTOCONF; + + return uart_add_one_port(&virt_uart_drv, virt_port); + +} +static int virtual_uart_remove(struct platform_device *pdev) +{ + 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_driver = { + .probe = virtual_uart_probe, + .remove = virtual_uart_remove, + .driver = { + .name = "100ask_virtual_uart", + .of_match_table = of_match_ptr(virtual_uart_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_uart_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + int ret = uart_register_driver(&virt_uart_drv); + + if (ret) + return ret; + + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_uart_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_uart_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_uart_driver); +} + +module_init(virtual_uart_init); +module_exit(virtual_uart_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/STM32MP157/source/A7/09_UART/06_virtual_uart_driver_txrx/virtual_uart.dts b/STM32MP157/source/A7/09_UART/06_virtual_uart_driver_txrx/virtual_uart.dts new file mode 100644 index 0000000..50b380a --- /dev/null +++ b/STM32MP157/source/A7/09_UART/06_virtual_uart_driver_txrx/virtual_uart.dts @@ -0,0 +1,13 @@ + + +/{ + virtual_uart: virtual_uart_100ask { + compatible = "100ask,virtual_uart"; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + + +}; diff --git a/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/Makefile b/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/Makefile new file mode 100644 index 0000000..2b6a3ad --- /dev/null +++ b/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/Makefile @@ -0,0 +1,20 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += virtual_uart.o + diff --git a/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/virtual_uart.c b/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/virtual_uart.c new file mode 100644 index 0000000..17a64dd --- /dev/null +++ b/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/virtual_uart.c @@ -0,0 +1,334 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BUF_LEN 1024 +#define NEXT_PLACE(i) ((i+1)&0x3FF) + +static struct uart_port *virt_port; +static unsigned char txbuf[BUF_LEN]; +static int tx_buf_r = 0; +static int tx_buf_w = 0; + +static unsigned char rxbuf[BUF_LEN]; +static int rx_buf_w = 0; + +static struct proc_dir_entry *uart_proc_file; + + +static struct uart_driver virt_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; +} + + + +ssize_t virt_uart_buf_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) +{ + /* 把txbuf中的数据copy_to_user */ + int cnt = txbuf_count(); + int i; + unsigned char val; + int ret; + + cnt = (cnt > size)? size: cnt; + + for (i = 0; i < cnt; i++) + { + txbuf_get(&val); + ret = copy_to_user(buf+i, &val, 1); + } + + return cnt; +} + +static ssize_t virt_uart_buf_write (struct file *file, const char __user *buf, size_t size, loff_t *off) +{ + int ret; + + /* get data */ + ret = copy_from_user(rxbuf, buf, size); + rx_buf_w = size; + + /* 模拟产生RX中断 */ + irq_set_irqchip_state(virt_port->irq, IRQCHIP_STATE_PENDING, 1); + + return size; +} + +static const struct file_operations virt_uart_buf_fops = { + .read = virt_uart_buf_read, + .write = virt_uart_buf_write, +}; + + +static unsigned int virt_tx_empty(struct uart_port *port) +{ + /* 因为要发送的数据瞬间存入buffer */ + return 1; +} + + +/* + * interrupts disabled on entry + */ +static void virt_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + while (!uart_circ_empty(xmit) && + !uart_tx_stopped(port)) { + /* send xmit->buf[xmit->tail] + * out the port here */ + + /* 把circ buffer中的数据全部存入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 +virt_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + return; +} + +static int virt_startup(struct uart_port *port) +{ + return 0; +} + +static void virt_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int virt_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void virt_stop_tx(struct uart_port *port) +{ +} + +static void virt_stop_rx(struct uart_port *port) +{ +} + +static void virt_shutdown(struct uart_port *port) +{ +} + +static const char *virt_type(struct uart_port *port) +{ + return "100ASK_VIRT_UART"; +} + + +static const struct uart_ops virt_pops = { + .tx_empty = virt_tx_empty, + .set_mctrl = virt_set_mctrl, + .get_mctrl = virt_get_mctrl, + .stop_tx = virt_stop_tx, + .start_tx = virt_start_tx, + .stop_rx = virt_stop_rx, + //.enable_ms = imx_enable_ms, + //.break_ctl = imx_break_ctl, + .startup = virt_startup, + .shutdown = virt_shutdown, + //.flush_buffer = imx_flush_buffer, + .set_termios = virt_set_termios, + .type = virt_type, + //.config_port = imx_config_port, + //.verify_port = imx_verify_port, +}; + + + +static irqreturn_t virt_uart_rxint(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct tty_port *tport = &port->state->port; + unsigned long flags; + int i; + + spin_lock_irqsave(&port->lock, flags); + + for (i = 0; i < rx_buf_w; i++) { + port->icount.rx++; + + /* get data from hardware/rxbuf */ + + /* put data to ldisc */ + tty_insert_flip_char(tport, rxbuf[i], TTY_NORMAL); + } + rx_buf_w = 0; + + spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(tport); + + return IRQ_HANDLED; +} + + +static int virtual_uart_probe(struct platform_device *pdev) +{ + int rxirq; + int ret; + + /* create proc file */ + uart_proc_file = proc_create("virt_uart_buf", 0, NULL, &virt_uart_buf_fops); + + //uart_add_one_port(struct uart_driver * drv, struct uart_port * uport); + + /* 从设备树获得硬件信息 */ + rxirq = platform_get_irq(pdev, 0); + + /* 分配设置注册uart_port */ + virt_port = devm_kzalloc(&pdev->dev, sizeof(*virt_port), GFP_KERNEL); + + virt_port->dev = &pdev->dev; + virt_port->iotype = UPIO_MEM; + virt_port->irq = rxirq; + virt_port->fifosize = 32; + virt_port->ops = &virt_pops; + virt_port->flags = UPF_BOOT_AUTOCONF; + virt_port->type = PORT_8250; + + ret = devm_request_irq(&pdev->dev, rxirq, virt_uart_rxint, 0, + dev_name(&pdev->dev), virt_port); + + return uart_add_one_port(&virt_uart_drv, virt_port); + +} +static int virtual_uart_remove(struct platform_device *pdev) +{ + uart_remove_one_port(&virt_uart_drv, virt_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_driver = { + .probe = virtual_uart_probe, + .remove = virtual_uart_remove, + .driver = { + .name = "100ask_virtual_uart", + .of_match_table = of_match_ptr(virtual_uart_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_uart_init(void) +{ + int ret; + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + ret = uart_register_driver(&virt_uart_drv); + + if (ret) + return ret; + + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_uart_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_uart_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_uart_driver); + + + uart_unregister_driver(&virt_uart_drv); +} + +module_init(virtual_uart_init); +module_exit(virtual_uart_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/virtual_uart.dts b/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/virtual_uart.dts new file mode 100644 index 0000000..50b380a --- /dev/null +++ b/STM32MP157/source/A7/09_UART/07_virtual_uart_driver_ok/virtual_uart.dts @@ -0,0 +1,13 @@ + + +/{ + virtual_uart: virtual_uart_100ask { + compatible = "100ask,virtual_uart"; + + interrupt-parent = <&intc>; + interrupts = ; + + }; + + +};