首页 > 解决方案 > 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

dmesg 中的输出

标签: linuxlinux-device-driverembedded-linux

解决方案


通过在 raw 模式下设置 ttyAMFD0 解决了不读取数据的问题sudo stty -F /dev/ttyAMFD0 raw

start_tx并且通过使用关闭回声解决了调用函数的问题sudo stty -F /dev/ttyAMFD0 -echo -echoe -echok -echoctl -echoke


推荐阅读