linux - Linux 串行驱动程序接收
问题描述
我想在 Linux 中开发一个串行驱动程序
下面的代码通过定时器模拟 Rx 中断,并在其触发时,将一个字符添加到 Rx FIFO,就好像接收到一个新字符一样......
#include <linux/module.h>
#include <linux/serial_core.h>
#include <linux/tty_flip.h>
#define DRIVER_NAME "AMFD_TTY"
#define PAMFD_NAME "ttyAMFD"
#define MAX_UART_PORTS 2
#define PORT_PAMFD 118
#define FIFO_BASE 0x100
#define FIFO_SIZE 256
#define BPC 1 /* bytes per char */
//Received character
#define AMFD_DATA_CHARACTER 't'
struct pamfd_port {
struct uart_port port;
unsigned char __iomem *tx_fifo;
unsigned char __iomem *rx_fifo;
};
union fifo_pos {
u16 pos;
struct {
u8 tail; /* read pointer */
u8 head; /* write pointer */
} s;
};
/* Instantiate for each serial port, i.e. ttyAMFD0 and ttyAMFD1 */
static struct pamfd_port pports[MAX_UART_PORTS];
/* Rx and Tx Buffers in kernel */
unsigned char rxFifo[MAX_UART_PORTS][FIFO_SIZE];
unsigned char txFifo[MAX_UART_PORTS][FIFO_SIZE];
/* Timer variables */
#define TIMEOUT 1000 //milliseconds
static struct timer_list etx_timer;
static unsigned int count = 0;
//Timer Callback function. This will be called when timer expires
void timer_callback(struct timer_list * data)
{
/* Port0, i.e. /dev/ttyAMFD0 */
struct uart_port *up = &pports[0].port;
printk(KERN_INFO "Timer Callback function Called [%d]\n",count++);
/* Add a character 't' to the receive FIFO */
uart_insert_char(up, 0, 0, AMFD_DATA_CHARACTER, TTY_NORMAL);
up->icount.rx += 1;
tty_flip_buffer_push(&up->state->port);
mod_timer(&etx_timer, jiffies + msecs_to_jiffies(TIMEOUT));
}
static inline struct pamfd_port *up_to_pport(struct uart_port *up)
{
return container_of(up, struct pamfd_port, port);
}
static inline int pport_is_fifo_empty(union fifo_pos *pos)
{
return pos->s.head == pos->s.tail;
}
static void pport_rx_chars(struct pamfd_port *pp)
{
printk("This is pport_rx_chars function\n");
}
static void pport_tx_chars(struct pamfd_port *pp)
{
printk("This is pport_tx_chars function\n");
}
static irqreturn_t pport_handle_irq(int irq, void *pru_port)
{
printk("This is pport_handle_irq function\n");
return IRQ_HANDLED;
}
static unsigned int pport_tx_empty(struct uart_port *up)
{
printk("This is pport_tx_empty function\n");
return TIOCSER_TEMT;
}
static unsigned int pport_get_mctrl(struct uart_port *up)
{
printk("This is pport_get_mctrl function\n");
return up->mctrl;
}
/* the hardware flow control doesn't require any software assistance */
static void pport_set_mctrl(struct uart_port *up, unsigned int mctrl)
{
printk("This is pport_set_mctrl function\n");
};
static void pport_stop_tx(struct uart_port *up)
{
printk("This is pport_stop_tx function\n");
}
static void pport_start_tx(struct uart_port *up)
{
printk("This is pport_start_tx function\n");
return;
}
static void pport_stop_rx(struct uart_port *up)
{
printk("This is pport_stop_rx function\n");
}
static void pport_start_rx(struct uart_port *up)
{
printk("This is pport_start_rx function\n");
}
static void pport_throttle(struct uart_port *up)
{
printk("This is pport_throttle function\n");
}
static void pport_unthrottle(struct uart_port *up)
{
printk("This is pport_unthrottle function\n");
}
/* line break is not supported */
static void pport_break_ctl(struct uart_port *up, int break_state)
{
printk("This is pport_break_ctl function\n");
}
/* software flow control currently not supported */
static void pport_set_termios(struct uart_port *up, struct ktermios *termios, struct ktermios *old)
{
printk("This is pport_set_termios function\n");
}
static int pport_startup(struct uart_port *up)
{
printk("This is pport_startup function\n");
return 0;
};
static void pport_shutdown(struct uart_port *up)
{
printk("This is pport_shutdown function\n");
};
static void pport_config_port(struct uart_port *up, int flags)
{
printk("This is pport_config_port function\n");
}
/* rs485 is unsupported */
static int pport_rs485_config(struct uart_port *up, struct serial_rs485 *rs485)
{
printk("This is pport_rs485_config function\n");
return rs485->flags & SER_RS485_ENABLED ? -EOPNOTSUPP : 0;
}
static const struct uart_ops pamfd_port_ops = {
.tx_empty = pport_tx_empty,
.get_mctrl = pport_get_mctrl,
.set_mctrl = pport_set_mctrl,
.stop_tx = pport_stop_tx,
.start_tx = pport_start_tx,
.throttle = pport_throttle,
.unthrottle = pport_unthrottle,
.stop_rx = pport_stop_rx,
.break_ctl = pport_break_ctl,
.startup = pport_startup,
.shutdown = pport_shutdown,
.set_termios = pport_set_termios,
.config_port = pport_config_port,
};
static struct uart_driver pamfd_port_drv = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = PAMFD_NAME,
.nr = MAX_UART_PORTS,
};
static void pamfd_init_port(int portNum)
{
int ret;
struct pamfd_port *pp;
printk("This is pamfd_init_port function\n");
pp = &pports[portNum];
/* Setting FIFOs */
pp->tx_fifo = &txFifo[portNum][0];
pp->rx_fifo = &rxFifo[portNum][0];
pp->port.type = PORT_PAMFD;
pp->port.iotype = UPIO_MEM;
pp->port.fifosize = FIFO_SIZE;
pp->port.ops = &pamfd_port_ops;
pp->port.line = portNum;
pp->port.rs485_config = pport_rs485_config;
ret = uart_add_one_port(&pamfd_port_drv, &pp->port);
if (ret)
{
printk("adding port[%d] failed (%d)\n", portNum, ret);
}
return;
}
static int pamfd_probe(void)
{
int port, ret;
printk("This is pamfd_probe function\n");
/* UART Driver */
ret = uart_register_driver(&pamfd_port_drv);
if (ret)
{
return ret;
}
/* UART Ports */
for(port=0; port<MAX_UART_PORTS; port++)
{
pamfd_init_port(port);
}
return 0;
}
static int pamfd_remove(void)
{
struct pamfd_port *pp;
int port;
printk("This is pamfd_remove function\n");
/* UART Ports */
for(port=0; port<MAX_UART_PORTS; port++)
{
pp = &pports[port];
uart_remove_one_port(&pamfd_port_drv, &pp->port);
}
/* UART Driver */
uart_unregister_driver(&pamfd_port_drv);
return 0;
}
static int __init pamfd_init(void)
{
int ret;
printk("This is pamfd_init function\n");
ret = pamfd_probe();
timer_setup(&etx_timer, timer_callback, 0);
mod_timer(&etx_timer, jiffies + msecs_to_jiffies(TIMEOUT));
return ret;
}
module_init(pamfd_init);
static void __exit pamfd_exit(void)
{
printk("This is pamfd_exit function\n");
pamfd_remove();
del_timer(&etx_timer);
}
module_exit(pamfd_exit);
MODULE_AUTHOR("Example Author <example@example.com");
MODULE_DESCRIPTION("AMFD TTY Linux Driver");
MODULE_LICENSE("GPL");
在我的 PC 上测试代码(内核 4.15.0-106-generic 的 ubuntu 16.04),我没有得到正确的输出。
我期望的是:如果我运行“$sudo cat /dev/ttyAMFD0”,我应该看到每秒添加一个“t”字符以通过计时器接收 FIFO,即在终端上每秒打印“t”......但是事实并非如此 ...
会发生什么:在运行“$sudo cat /dev/ttyAMFD0”之前,计时器只是在其中打印语句,而在我运行“$sudo cat /dev/ttyAMFD0”之后它调用 start_tx 而不在终端上打印任何内容......正如我测试的那样,由于在插入字符后在代码中调用了 tty_flip_buffer_push ,所以调用了 start_tx
解决方案
通过在 raw 模式下设置 ttyAMFD0 解决了不读取数据的问题sudo stty -F /dev/ttyAMFD0 raw
start_tx
并且通过使用关闭回声解决了调用函数的问题sudo stty -F /dev/ttyAMFD0 -echo -echoe -echok -echoctl -echoke
推荐阅读
- python - 如何在 logging.info 中传递多个参数
- jquery - 如果选中任何其他复选框,请取消选择特定复选框
- c++ - 将第 k 个打印到链表中最后一个元素的算法
- python - plt.subplot2grid() 命令的形状尺寸----Matplotlib
- mysql - 在 laravel 中有两个条件的两个表的多重连接
- c# - 调用静态类属性表示对象引用未设置为对象的实例
- c# - 如何检查 win32 本机 copyfile() 的工作情况,或者我在哪里可以获得文档以检查 win32 本机 copyfile() 的工作方式?
- elasticsearch - ELK - 从日志中获取日期时间
- php - Composer 给我 [ErrorException] 未定义索引:安装后的用户名
- php - 更改 PHP 数组中的键