自己实现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);
- 自己实现linux串口驱动
- Linux串口驱动程序(6)-串口驱动实现
- 自己写linux usb转串口 驱动
- 6410 实现 linux 串口驱动详解
- linux UART串口驱动
- linux串口驱动分析
- linux串口驱动分析
- linux串口驱动分析
- linux串口驱动分析
- linux串口驱动分析
- linux串口驱动分析
- linux串口驱动分析
- linux 串口驱动
- 五 linux 串口驱动
- linux串口驱动初始化
- linux串口驱动
- 五 linux 串口驱动
- linux 串口驱动
- 微信公众帐号开发教程第1篇-引言
- BCB一打开时就出现了这个unable to resource Only Dll Projects.......
- Android4.2.2 Gallery2源码分析(3)——分析Gallery.java
- ContentProvider和Uri详解
- iphone之点击UIWebView中链接调用Safari打开目标链接
- 自己实现linux串口驱动
- Java中终止线程的一种方式
- 删数问题
- java数组
- 打开自带性能监控器提示“性能监视器控制,不能添加这些计数器”
- 微信公众帐号开发教程第2篇-微信公众帐号的类型(普通和会议)
- 利用钩子技术控制进程创建(附源代码)
- linux常用命令
- 为什么Arduino独占鳌头并站稳脚跟?