自己实现linux串口驱动

来源:互联网 发布:java框架的书籍 编辑:程序博客网 时间:2024/06/15 00:48

其实driver/tty/serial目录下有很多厂家自己的uart驱动,还有些是spi转串口的驱动。

不过这些都设计到外围硬件,我这里没有外围设备,只是自己模拟来玩的,以后用到硬件时候往里面加就行了,驱动如下:

/* * drivers/tty/serial/sw_uart.c * (C) Copyright 2007-2011 * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com> * Aaron.Maoye <leafy.myeh@reuuimllatech.com> * * description for this code * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */#include <linux/kernel.h>#include <linux/module.h>#include <linux/io.h>#include <linux/irq.h>#include <linux/clk.h>#include <linux/gpio.h>#include <linux/delay.h>#include <linux/ctype.h>#include <linux/slab.h>#include <linux/proc_fs.h>#include <linux/platform_device.h>#include <linux/console.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial_core.h>#include <linux/workqueue.h>#include <linux/serial.h>#include <linux/module.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/tty.h>#include <linux/ratelimit.h>#include <linux/tty_flip.h>#include <linux/serial_reg.h>#include <linux/serial_core.h>#include <linux/serial.h>#include <linux/nmi.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/clk.h>#include <linux/timer.h>#include <linux/workqueue.h>#include <linux/dma-mapping.h>#include <asm/io.h>#include <asm/irq.h>#define SW_UART_NR8static struct timer_list *timer;struct platform_device *pdev;struct work_struct q_work;struct workqueue_struct *workqueue;volatile static int force_enable_work;//当有硬件数据到来时,要手动调用该函数接收数据并上报static void sw_uart_handle_rx(unsigned long data){#if 1    struct uart_port * port;    struct tty_struct *tty;    unsigned char ch = 0;    int i;    int flag;    char *send_buff = "i am sclu, this is from serial rx";    flag = TTY_NORMAL;    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    port = (struct uart_port *)data;    if (!port)        return;    if (!port->state)        return;    tty = port->state->port.tty;    //tty->flags |= TTY_HW_COOK_IN;    //tty = port->info->tty;    if (!tty)        return;  //  for (i = 0; i < strlen(send_buff) + 1; i++) {        //用tty_insert_flip_char函数也可以    //    ch = send_buff[i];      //  uart_insert_char(port, 0, 0, ch, flag);   // }tty_insert_flip_string(tty, send_buff, strlen(send_buff) + 1);//    uart_insert_char(port, 0, 0, '\n', flag);  //  uart_insert_char(port, 0, 0, '\t', 0);    tty_flip_buffer_push(tty);#endif    timer->expires = jiffies + 3 * HZ;    add_timer(timer);}static void sw_uart_stop_tx(struct uart_port *port){    //del_timer(timer);    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}//上层用户空间写的数据最终调用该函数完成发送static void sw_uart_start_tx(struct uart_port *port){    struct circ_buf *xmit = &port->state->xmit;   // printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    //开始发送数据    if(!force_enable_work || uart_circ_empty(xmit) || uart_tx_stopped(port))        queue_work(workqueue, &q_work);}static unsigned int sw_uart_tx_empty(struct uart_port *port){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    //当硬件fifo中的数据发送完成后,要返回TIOCSER_TEMI,否则上层一直写等待,如echo数据一直没有返回    return TIOCSER_TEMT;}static void sw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}static unsigned int sw_uart_get_mctrl(struct uart_port *port){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    return 0;}static void sw_uart_stop_rx(struct uart_port *port){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}static void sw_uart_enable_ms(struct uart_port *port){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}static void sw_uart_break_ctl(struct uart_port *port, int break_state){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}static int sw_uart_startup(struct uart_port *port){    /* this is the first time this port is opened */    /* do any hardware initialization needed here */    /* create our timer and submit it */    force_enable_work = 0;    if (!timer) {        timer = kmalloc(sizeof(*timer), GFP_KERNEL);        if (!timer)            return -ENOMEM;    }       setup_timer(timer, sw_uart_handle_rx, (unsigned long)port);    timer->expires = jiffies + HZ * 3;#if 0       timer->data = (unsigned long)port;    timer->expires = jiffies + DELAY_TIME;    timer->function = tiny_timer;#endif      add_timer(timer);    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    return 0;}static void sw_uart_shutdown(struct uart_port *port){    /* The port is being closed by the last user. */    /* Do any hardware specific stuff here */    /* shut down our timer */    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    force_enable_work = 1;    cancel_work_sync(&q_work);  }static void sw_uart_set_termios(struct uart_port *port, struct ktermios *termios,        struct ktermios *old){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}static const char *sw_uart_type(struct uart_port *port){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    return "SW";}static void sw_uart_release_port(struct uart_port *port){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}static int sw_uart_request_port(struct uart_port *port){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    return 0;}static void sw_uart_config_port(struct uart_port *port, int flags){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}static int sw_uart_verify_port(struct uart_port *port, struct serial_struct *ser){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    return 0;}static void sw_uart_pm(struct uart_port *port, unsigned int state,        unsigned int oldstate){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);}static struct uart_driver sw_uart_driver;static struct uart_ops sw_uart_ops = {#if 1    .tx_empty = sw_uart_tx_empty,    .set_mctrl = sw_uart_set_mctrl,    .get_mctrl = sw_uart_get_mctrl,    .stop_tx = sw_uart_stop_tx,    .enable_ms = sw_uart_enable_ms,    .break_ctl = sw_uart_break_ctl,    .set_termios = sw_uart_set_termios,    .type = sw_uart_type,    .release_port = sw_uart_release_port,    .request_port = sw_uart_request_port,    .config_port = sw_uart_config_port,    .verify_port = sw_uart_verify_port,    .pm = sw_uart_pm,#endif    .startup = sw_uart_startup,    .start_tx = sw_uart_start_tx,    .stop_rx = sw_uart_stop_rx,    .shutdown = sw_uart_shutdown,};static struct uart_port tiny_port = {    .ops = &sw_uart_ops,    .type = PORT_8250,    .fifosize = 64,    .flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF,};#ifdef CONFIG_CONSOLE_POLLstatic void sw_console_write(struct console *co, const char *s,        unsigned int count){}static int __init sw_console_setup(struct console *co, char *options){    struct uart_port *port;    int baud = 115200;    int bits = 8;    int parity = 'n';    int flow = 'n';    port = &tiny_port;    if (options)        uart_parse_options(options, &baud, &parity, &bits, &flow);    return uart_set_options(port, co, baud, parity, bits, flow);}#endifstatic void tx_work(struct work_struct *work){#if 1    struct uart_port *port = &tiny_port;    struct circ_buf *xmit = &port->state->xmit;    int count;   // printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    if (port->x_char) {        printk("x_char %2x", port->x_char);        port->icount.tx++;        port->x_char = 0;        return;    }    if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {        return;    }    count = port->fifosize / 2;   // printk("sclu %s, count = %d\n", __func__, count);    //发送数据    //serial_out(&sw_uport->port, xmit->buf[xmit->tail], SW_UART_THR);    printk("get data:\n");    do {        printk("%c", xmit->buf[xmit->tail]);        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);        port->icount.tx++;        if (uart_circ_empty(xmit)) {            break;        }    } while (--count > 0);    printk("\n");    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {       // spin_unlock(&port->lock);        uart_write_wakeup(port);       // spin_lock(&port->lock);    }    if (uart_circ_empty(xmit))        sw_uart_stop_tx(port);#endif}#ifdef CONFIG_CONSOLE_POLLstatic struct console sw_console = {    .name = "ttyVirtual",    .write = sw_console_write,    .device = uart_console_device,    .setup = sw_console_setup,    .flags = CON_PRINTBUFFER | CON_ANYTIME,    .index = -1,    .data = &sw_uart_driver,};#define SERIAL_CONSOLE  &sw_console#else #define SERIAL_CONSOLE  NULL#endifstatic struct uart_driver sw_uart_driver = {    .owner = THIS_MODULE,    .driver_name = "Virtual_serial",    .dev_name = "ttyVirtual",    .nr = SW_UART_NR,    .cons = SERIAL_CONSOLE,};static int __devinit sw_uart_probe(struct platform_device *pdev){    printk("%s, %d, %s\n", __FILE__, __LINE__, __func__);    workqueue = create_singlethread_workqueue("ttyVirtual_work");    INIT_WORK(&q_work, tx_work);    return uart_add_one_port(&sw_uart_driver, &tiny_port);}static int __devexit sw_uart_remove(struct platform_device *pdev){    return 0;}#define SERIAL_SW_PM_OPSNULLstatic struct platform_driver sw_uport_platform_driver = {    .probe = sw_uart_probe,    .remove = __devexit_p(sw_uart_remove),    .driver.name = "ttyVirtual",    .driver.pm = SERIAL_SW_PM_OPS,    .driver.owner = THIS_MODULE,};static int __init sw_uart_init(void){    int ret;    printk("serial driver initializied\n");    ret = uart_register_driver(&sw_uart_driver);    if (unlikely(ret)) {        printk("serial driver initializied err\n");        return ret;    }    pdev = platform_device_register_simple("ttyVirtual", 0, NULL, 0);    return platform_driver_register(&sw_uport_platform_driver);}static void __exit sw_uart_exit(void){    if (timer) {        del_timer(timer);        timer = NULL;    }    if (workqueue) {        flush_workqueue(workqueue);        destroy_workqueue(workqueue);        workqueue = NULL;    }    printk("driver exit\n");    uart_remove_one_port(&sw_uart_driver, &tiny_port);    platform_driver_unregister(&sw_uport_platform_driver);    if (pdev)        platform_device_unregister(pdev);    uart_unregister_driver(&sw_uart_driver);}module_init(sw_uart_init);module_exit(sw_uart_exit);MODULE_AUTHOR("Aaron<leafy.myeh@allwinnertech.com>");MODULE_DESCRIPTION("Driver for SW serial device");MODULE_LICENSE("GPL");

上层测试代码如下:

#include <termios.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#include <jni.h>#include <assert.h>#include <errno.h>#include <sys/epoll.h>#include <pthread.h>#include <sys/socket.h>#include <utils/Log.h>#include <stdlib.h>static int epoll_register(int  epoll_fd, int  fd) {    struct epoll_event  ev;    int  ret, flags;    /* important: make the fd non-blocking */    flags = fcntl(fd, F_GETFL);    fcntl(fd, F_SETFL, flags | O_NONBLOCK);    //fcntl(fd, F_SETOWN, getpid()); //sclu add    ev.events  = EPOLLIN;    ev.data.fd = fd;    do {        ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);    } while (ret < 0 && errno == EINTR);    return ret;}int main(int argc, char **argv){    struct termios uart_termios;    int baudrate = atoi(argv[2]);    int  epoll_fd = epoll_create(1);    int ret, err;    char read_buf[1024];    int fd = open(argv[1], O_RDWR|O_NOCTTY|O_NDELAY);    if(fd < 0) {        LOGE("open %s failure, err = %s\n", argv[1], strerror(errno));        return fd;    }    fcntl(fd, F_SETFL, 0);    tcflush(fd, TCIOFLUSH);    if ((err = tcgetattr(fd, &uart_termios)) != 0) {        LOGE("tcgetattr %s, err = %s\n", argv[1], strerror(errno));        close(fd);        return err;    }    uart_termios.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);    uart_termios.c_oflag &= ~OPOST;    uart_termios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);    uart_termios.c_cflag &= ~(CSIZE|PARENB);    uart_termios.c_cflag |= CS8;    uart_termios.c_cflag &= ~CRTSCTS;//no flow control    tcsetattr(fd, TCSANOW, &uart_termios);    tcflush(fd, TCIOFLUSH);    tcsetattr(fd, TCSANOW, &uart_termios);    tcflush(fd, TCIOFLUSH);    if (cfsetispeed(&uart_termios, baudrate)) {        LOGE("cfsetispeed, err = %s\n", strerror(errno));        close(fd);        return fd;    }    if (cfsetospeed(&uart_termios, baudrate)) {        LOGE("cfsetospeed err = %s\n", strerror(errno));        close(fd);        return fd;    }    tcsetattr (fd, TCSANOW, &uart_termios);    epoll_register(epoll_fd, fd);    ret = write(fd, "$$$$", 4);    LOGE("ret = %d", ret);    if ((ret = read(fd, read_buf, sizeof(read_buf))) > 0) {        int i;        for (i = 0; i < ret; i++) {            LOGE("read:%c", read_buf[i]);        }        LOGE("\n");    }    for (;;) {        struct epoll_event   events[1];        int ne, nevents, ret, size;        LOGE("poll start");        nevents = epoll_wait(epoll_fd, events, 1, -1);        LOGE("poll get data");        if (nevents < 0) {            if (errno != EINTR)                LOGE("epoll_wait() unexpected error: %s", strerror(errno));            continue;        }        for (ne = 0; ne < nevents; ne++) {            if ((events[ne].events & (EPOLLERR | EPOLLHUP)) != 0) {                LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?\n");                return -1;            }            if ((events[ne].events & EPOLLIN) != 0) {                 int  event_fd = events[ne].data.fd;                int i;                if (event_fd == fd) {                    ret = read(fd, read_buf, sizeof(read_buf));                    if (ret) {                        LOGE("data:%s", read_buf);                        char *p = "this is from userspace!";                        write(fd, p, strlen(p) + 1);                    }                }            }        }    }    return 0;}

调试中遇到的问题有sw_uart_tx_empty函数中不能返回0,要返回TIOCSER_TEMT,否则上层一直写等待

还有就是打开节点时候要设置串口信息,入上面测试代码所示,刚开始自己用 cat /dev/ttyVirtual来看信息,发现底层发送上来的数据到了tty核心层后,原样的发送回去了,虽然上层一样可以接收到,但不是想要的结果。



还有,linux系用已经为我们写好了serial_core.c架构,我们的串口设备只学要按照其格式实现相应的函数即可,不过我们也可以自己实现一个,只要实现相应的tty_operations结构体函数即可,然后把它注册到tty核心层中,以后自己的串口驱动如果不需要linux默认的serial_core,注册时候指定自己的tty driver即可。

代码如下,功能是往tty driver写入数据,然后原样发送回来:

#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/cdev.h>#include <linux/tty.h>#include <linux/fs.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/ioport.h>#include <linux/serial_reg.h>#include <linux/slab.h>#include <linux/serial_core.h>#include <linux/serial.h>#include <linux/tty.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("sclu");#define TTY_LAN_MINORS_NUM    1#define TTY_LAN_MAJOR        240static int open_count = 0;static struct tty_driver *tty_lan_driver;static struct tty_struct *tty_lan_struct;static int tty_lan_open(struct tty_struct *tty, struct file *filp);static void tty_lan_close(struct tty_struct *tty, struct file *filp);static int tty_lan_write(struct tty_struct *tty, const unsigned char *buffer, int count);static int tty_lan_write_room(struct tty_struct *tty);static void tty_lan_set_termios(struct tty_struct *tty, struct ktermios * old);static int tty_lan_put_char(struct tty_struct *tty, unsigned char ch);static void uart_wait_until_sent(struct tty_struct *tty, int timeout);static struct tty_operations tty_lan_ops = {    .open = tty_lan_open,    .close = tty_lan_close,    .write = tty_lan_write,    .put_char = tty_lan_put_char,    .write_room = tty_lan_write_room,    .set_termios = tty_lan_set_termios,    .wait_until_sent = uart_wait_until_sent,};static int __init tty_lan_init(void){    int i;    int retval;    tty_lan_driver = alloc_tty_driver(TTY_LAN_MINORS_NUM);    if(!tty_lan_driver)        return -ENOMEM;    tty_lan_driver->owner = THIS_MODULE;    tty_lan_driver->driver_name = "tty_sclu";    tty_lan_driver->name = "ttty_sclu";    tty_lan_driver->major = TTY_LAN_MAJOR,        tty_lan_driver->minor_start = 0;        tty_lan_driver->type = TTY_DRIVER_TYPE_SERIAL;    tty_lan_driver->subtype = SERIAL_TYPE_NORMAL;    //TTY_DRIVER_DYNAMIC_DEV标志表示手动注册device设备,在tty_io.c中会判断,如果没有该标志,会调用tty_register_device,    //后续就不用再次注册device设备了    tty_lan_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;    tty_lan_driver->init_termios = tty_std_termios;    tty_lan_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;    tty_set_operations(tty_lan_driver, &tty_lan_ops);    retval = tty_register_driver(tty_lan_driver);    if(retval){        printk(KERN_ERR"Failed to register tty_lan_driver!\n");        put_tty_driver(tty_lan_driver);        return retval;    }        tty_register_device(tty_lan_driver, 0, NULL);    return 0;}static int tty_lan_open(struct tty_struct *tty, struct file *filp){    if(open_count == 0){        printk("Open OK!\n");    }    tty_lan_struct = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);    tty->low_latency = 1;    tty_lan_struct = tty;    return 0;}static void tty_lan_close(struct tty_struct *tty, struct file *filp){    printk("ClOSE OK!\n");    kfree(tty_lan_struct);    return;}static int tty_lan_write(struct tty_struct *tty, const unsigned char *buffer, int count){    int retval = count;    tty = tty_lan_struct;    printk(KERN_DEBUG "%s - \n", __FUNCTION__);    printk("count :%d\n", count);    printk("user write: %s ", buffer);    printk("\n");    tty_insert_flip_string(tty, buffer, count);    tty_flip_buffer_push(tty);    return retval;}static int tty_lan_put_char(struct tty_struct *tty, unsigned char ch){    printk("%c", ch);    return 0;}static int tty_lan_write_room(struct tty_struct *tty){    int room;    room = 255;    return room;}static void tty_lan_set_termios(struct tty_struct *tty, struct ktermios *old){    tty = tty_lan_struct;    if(tty->termios->c_cflag == old->c_cflag){        printk("Nothing to change!\n");        return ;    }    printk("There is something to Change............\n");    return ;}static void uart_wait_until_sent(struct tty_struct *tty, int timeout){    struct uart_state *state = tty->driver_data;    struct uart_port *port = state->uart_port;    printk("%s, %d\n", __func__, __LINE__);}static void __exit tty_lan_exit(void){    int i;        tty_unregister_device(tty_lan_driver, 0);    tty_unregister_driver(tty_lan_driver);}module_init(tty_lan_init);











原创粉丝点击