自己实现linux串口驱动

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

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

/*
 * drivers/tty/serial/sw_uart.c
 * (C) Copyright 2007-2011
 * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
 * Aaron.Maoye <[email protected]>
 *
 * 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_NR	8
static 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_POLL
static 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);


}
#endif
static 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_POLL
static 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
#endif
static 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_OPS	NULL

static 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<[email protected]>");
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        240
static 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);











你可能感兴趣的:(自己实现linux串口驱动)