Linux串口serial、uart驱动程序设计.doc_第1页
Linux串口serial、uart驱动程序设计.doc_第2页
Linux串口serial、uart驱动程序设计.doc_第3页
Linux串口serial、uart驱动程序设计.doc_第4页
Linux串口serial、uart驱动程序设计.doc_第5页
已阅读5页,还剩33页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux串口(serial、uart)驱动程序设计/space.php?uid=23089249&do=blog&id=34481一、核心数据结构串口驱动有3个核心数据结构,它们都定义在1、uart_driveruart_driver包含了串口设备名、串口驱动名、主次设备号、串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver)。struct uart_driver struct module *owner; /* 拥有该uart_driver的模块,一般为THIS_MODULE */const char *driver_name; /* 串口驱动名,串口设备文件名以驱动名为基础 */const char *dev_name; /* 串口设备名 */int major; /* 主设备号 */int minor; /* 次设备号 */int nr; /* 该uart_driver支持的串口个数(最大) */struct console *cons; /* 其对应的console.若该uart_driver支持serial console,否则为NULL */* these are private; the low level driver should not* touch these; they should be initialised to NULL*/struct uart_state *state;struct tty_driver *tty_driver;2、uart_portuart_port用于描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。实际上,一个uart_port实例对应一个串口设备struct uart_port spinlock_t lock; /* 串口端口锁 */unsigned int iobase; /* IO端口基地址 */unsigned char _iomem *membase; /* IO内存基地址,经映射(如ioremap)后的IO内存虚拟基地址 */unsigned int irq; /* 中断号 */unsigned int uartclk; /* 串口时钟 */unsigned int fifosize; /* 串口FIFO缓冲大小 */unsigned char x_char; /* xon/xoff字符 */unsigned char regshift; /* 寄存器位移 */unsigned char iotype; /* IO访问方式 */unsigned char unused1;#define UPIO_PORT (0) /* IO端口 */#define UPIO_HUB6 (1)#define UPIO_MEM (2) /* IO内存 */#define UPIO_MEM32 (3)#define UPIO_AU (4) /* Au1x00 type IO */#define UPIO_TSI (5) /* Tsi108/109 type IO */#define UPIO_DWAPB (6) /* DesignWare APB UART */#define UPIO_RM9000 (7) /* RM9000 type IO */unsigned int read_status_mask; /* 关心的Rx error status */unsigned int ignore_status_mask;/* 忽略的Rx error status */struct uart_info *info; /* pointer to parent info */struct uart_icount icount; /* 计数器 */struct console *cons; /* console结构体 */#ifdef CONFIG_SERIAL_CORE_CONSOLEunsigned long sysrq; /* sysrq timeout */#endifupf_t flags;#define UPF_FOURPORT (_force upf_t) (1 1)#define UPF_SAK (_force upf_t) (1 2)#define UPF_SPD_MASK (_force upf_t) (0x1030)#define UPF_SPD_HI (_force upf_t) (0x0010)#define UPF_SPD_VHI (_force upf_t) (0x0020)#define UPF_SPD_CUST (_force upf_t) (0x0030)#define UPF_SPD_SHI (_force upf_t) (0x1000)#define UPF_SPD_WARP (_force upf_t) (0x1010)#define UPF_SKIP_TEST (_force upf_t) (1 6)#define UPF_AUTO_IRQ (_force upf_t) (1 7)#define UPF_HARDPPS_CD (_force upf_t) (1 11)#define UPF_LOW_LATENCY (_force upf_t) (1 13)#define UPF_BUGGY_UART (_force upf_t) (1 14)#define UPF_MAGIC_MULTIPLIER (_force upf_t) (1 16)#define UPF_CONS_FLOW (_force upf_t) (1 23)#define UPF_SHARE_IRQ (_force upf_t) (1 24)#define UPF_BOOT_AUTOCONF (_force upf_t) (1 28)#define UPF_FIXED_PORT (_force upf_t) (1 29)#define UPF_DEAD (_force upf_t) (1 30)#define UPF_IOREMAP (_force upf_t) (1 flags. These are _private_ to serial_core, and* are specific to this structure. They may be queried by low level drivers.*/#define UIF_CHECK_CD (_force uif_t) (1 25)#define UIF_CTS_FLOW (_force uif_t) (1 26)#define UIF_NORMAL_ACTIVE (_force uif_t) (1 29)#define UIF_INITIALIZED (_force uif_t) (1 31)#define UIF_SUSPENDED (_force uif_t) (1 30)int blocked_open;struct tasklet_struct tlet;wait_queue_head_t open_wait;wait_queue_head_t delta_msr_wait;3、uart_opsuart_ops涵盖了串口驱动可对串口设备进行的所有操作。/* This structure describes all the operations that can be* done on the physical hardware.*/struct uart_ops unsigned int (*tx_empty)(struct uart_port *); /* 串口的Tx FIFO缓存是否为空 */void (*set_mctrl)(struct uart_port *, unsigned int mctrl); /* 设置串口modem控制 */unsigned int (*get_mctrl)(struct uart_port *); /* 获取串口modem控制 */void (*stop_tx)(struct uart_port *); /* 禁止串口发送数据 */void (*start_tx)(struct uart_port *); /* 使能串口发送数据 */void (*send_xchar)(struct uart_port *, char ch);/* 发送xChar */void (*stop_rx)(struct uart_port *); /* 禁止串口接收数据 */void (*enable_ms)(struct uart_port *); /* 使能modem的状态信号 */void (*break_ctl)(struct uart_port *, int ctl); /* 设置break信号 */int (*startup)(struct uart_port *); /* 启动串口,应用程序打开串口设备文件时,该函数会被调用 */void (*shutdown)(struct uart_port *); /* 关闭串口,应用程序关闭串口设备文件时,该函数会被调用 */void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); /* 设置串口参数 */void (*pm)(struct uart_port *, unsigned int state,unsigned int oldstate); /* 串口电源管理 */int (*set_wake)(struct uart_port *, unsigned int state); /* */const char *(*type)(struct uart_port *); /* 返回一描述串口类型的字符串 */void (*release_port)(struct uart_port *); /* 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap */int (*request_port)(struct uart_port *); /* 申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口 */void (*config_port)(struct uart_port *, int); /* 执行串口所需的自动配置 */int (*verify_port)(struct uart_port *, struct serial_struct *); /* 核实新串口的信息 */int (*ioctl)(struct uart_port *, unsigned int, unsigned long); /* IO控制 */;二、串口驱动API1、uart_register_driver/* 功能: uart_register_driver用于将串口驱动uart_driver注册到内核(串口核心层)中,通常在模块初始化函数调用该函数。* 参数 drv:要注册的uart_driver* 返回值: 成功,返回0;否则返回错误码*/int uart_register_driver(struct uart_driver *drv)2、uart_unregister_driver/* 功能: uart_unregister_driver用于注销我们已注册的uart_driver,通常在模块卸载函数调用该函数* 参数 drv:要注销的uart_driver* 返回值: 成功,返回0;否则返回错误码*/void uart_unregister_driver(struct uart_driver *drv)3、uart_add_one_port/* 功能: uart_add_one_port用于为串口驱动添加一个串口端口,通常在探测到设备后(驱动的设备probe方法)调用该函数* 参数 drv:串口驱动* port:要添加的串口端口* 返回值: 成功,返回0;否则返回错误码*/int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)4、uart_remove_one_port/* 功能: uart_remove_one_port用于删除一个已添加到串口驱动中的串口端口,通常在驱动卸载时调用该函数* 参数 drv: 串口驱动* port: 要删除的串口端口* 返回值: 成功,返回0;否则返回错误码*/int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)5、uart_write_wakeup/* 功能: uart_write_wakeup唤醒上层因向串口端口写数据而阻塞的进程,通常在串口发送中断处理函数中调用该函数* 参数 port:需要唤醒写阻塞进程的串口端口*/void uart_write_wakeup(struct uart_port *port)6、uart_suspend_port/* 功能: uart_suspend_port用于挂起特定的串口端口* 参数 drv: 要挂起的串口端口所属的串口驱动* port:要挂起的串口端口* 返回值: 成功返回0;否则返回错误码*/int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)7、uart_resume_port/* 功能: uart_resume_port用于恢复某一已挂起的串口* 参数 drv: 要恢复的串口端口所属的串口驱动* port:要恢复的串口端口* 返回值: 成功返回0;否则返回错误码*/int uart_resume_port(struct uart_driver *drv, struct uart_port *port)8、uart_get_baud_rate/* 功能: uart_get_baud_rate通过解码termios结构体来获取指定串口的波特率* 参数 port: 要获取波特率的串口端口* termios:当前期望的termios配置(包含串口波特率)* old: 以前的termios配置,可以为NULL* min: 可接受的最小波特率* max: 可接受的最大波特率* 返回值: 串口的波特率*/unsigned intuart_get_baud_rate(struct uart_port *port, struct ktermios *termios,struct ktermios *old, unsigned int min, unsigned int max)9、uart_get_divisor/* 功能: uart_get_divisor用于计算某一波特率的串口时钟分频数(串口波特率除数)* 参数 port:要计算时钟分频数的串口端口* baud:期望的波特率*返回值: 串口时钟分频数*/unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud)10、uart_update_timeout/* 功能: uart_update_timeout用于更新(设置)串口FIFO超时时间* 参数 port: 要更新超时时间的串口端口* cflag:termios结构体的cflag值* baud: 串口的波特率*/void uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud)11、uart_match_port/* 功能:uart_match_port用于判断两串口端口是否为同一端口* 参数 port1、port2:要判断的串口端口* 返回值:不同返回0;否则返回非0*/int uart_match_port(struct uart_port *port1, struct uart_port *port2)12、uart_console_write/* 功能: uart_console_write用于向串口端口写一控制台信息* 参数 port: 要写信息的串口端口* s: 要写的信息* count: 信息的大小* putchar: 用于向串口端口写字符的函数,该函数函数有两个参数:串口端口和要写的字符*/void uart_console_write(struct uart_port *port, const char *s,unsigned int count,void (*putchar)(struct uart_port *, int)三、串口驱动例子该串口驱动例子是我针对s3c2410处理器的串口2(uart2)独立开发的。因为我通过博创2410s开发板的GRPS扩展板来测试该驱动(已通过测试),所以我叫该串口为gprs_uart。 该驱动将串口看作平台(platform)设备。platform可以看作一伪总线,用于将集成于片上系统的轻量级设备与Linux设备驱动模型联系到一起,它包含以下两部分(有关platform的声明都在#include ,具体实现在drivers/base/platform.c):1、platform设备。我们需要为每个设备定义一个platform_device实例struct platform_device const char *name; /* 设备名 */int id; /* 设备的id号 */struct device dev; /* 其对应的device */u32 num_resources;/* 该设备用有的资源数 */struct resource *resource; /* 资源数组 */;为我们的设备创建platform_device实例有两种方法:填充一个platform_device结构体后用platform_device_register(一次注册一个)或platform_add_devices(一次可以注册多个platform设备)将platform_device注册到内核;更简单的是使用platform_device_register_simple来建立并注册我们的platform_device。2、platform驱动。platform设备由platform驱动进行管理。当设备加入到系统中时,platform_driver的probe方法会被调用来见对应的设备添加或者注册到内核;当设备从系统中移除时,platform_driver的remove方法会被调用来做一些清理工作,如移除该设备的一些实例、注销一些已注册到系统中去的东西。struct platform_driver int (*probe)(struct platform_device *);int (*remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*suspend_late)(struct platform_device *, pm_message_t state);int (*resume_early)(struct platform_device *);int (*resume)(struct platform_device *);struct device_driver driver;更详细platform资料可参考网上相关文章。例子驱动中申请和释放IO内存区的整个过程如下:insmod gprs_uart.kogprs_init_module()uart_register_driver()gprs_uart_probe() uart_add_one_port()gprs_uart_config_port()gprs_uart_request_port()request_mem_region()rmmod gprs_uart.kogprs_exit_module()uart_unregister_driver()gprs_uart_remove()uart_remove_one_port()gprs_uart_release_port()release_mem_region()例子驱动中申请和释放IRQ资源的整个过程如下:open /dev/gprs_uartgprs_uart_startup()request_irq()close /dev/gprs_uartgprs_uart_shutdown()free_irq()想了解更详细的调用过程可以在驱动模块各函数头插入printk(KERN_DEBUG %sn, _FUNCTION_);并在函数尾插入printk(KERN_DEBUG %s donen, _FUNCTION_);下面是串口驱动例子和其GPRS测试程序源码下载地址:/downloads258/sourcecode/unix_linux/detail1192104.html#include #include #include /* printk() */#include /* kmalloc() */#include /* everything. */#include /* error codes */#include /* size_t */#include /* O_ACCMODE */#include /* cli(), *_flags */#include /* copy_*_user */#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEV_NAME gprs_uart /* 设备名 */* 这里将串口的主设备号设为0,则串口设备编号由内核动态分配;你也可指定串口的设备编号 */#define GPRS_UART_MAJOR 0 /* 主设备号 */#define GPRS_UART_MINOR 0 /* 次设备号 */#define GPRS_UART_FIFO_SIZE 16 /* 串口FIFO的大小 */#define RXSTAT_DUMMY_READ (0x10000000)#define MAP_SIZE (0x100) /* 要映射的串口IO内存区大小 */* 串口发送中断号 */#define TX_IRQ(port) (port)-irq + 1)/* 串口接收中断号 */#define RX_IRQ(port) (port)-irq)/* 允许串口接收字符的标志 */#define tx_enabled(port) (port)-unused0)/* 允许串口发送字符的标志 */#define rx_enabled(port) (port)-unused1)/* 获取寄存器地址 */#define portaddr(port, reg) (port)-membase + (reg)/* 读8位宽的寄存器 */#define rd_regb(port, reg) (ioread8(portaddr(port, reg)/* 读32位宽的寄存器 */#define rd_regl(port, reg) (ioread32(portaddr(port, reg)/* 写8位宽的寄存器 */#define wr_regb(port, reg, val) do iowrite8(val, portaddr(port, reg); while(0)/* 写32位宽的寄存器 */ #define wr_regl(port, reg, val) do iowrite32(val, portaddr(port, reg); while(0)/* 禁止串口发送数据 */static void gprs_uart_stop_tx(struct uart_port *port)if (tx_enabled(port) /* 若串口已启动发送 */ disable_irq(TX_IRQ(port); /* 禁止发送中断 */tx_enabled(port) = 0; /* 设置串口为未启动发送 */* 使能串口发送数据 */static void gprs_uart_start_tx(struct uart_port *port)if (!tx_enabled(port) /* 若串口未启动发送 */enable_irq(TX_IRQ(port); /* 使能发送中断 */tx_enabled(port) = 1; /* 设置串口为已启动发送 */ /* 禁止串口接收数据 */static void gprs_uart_stop_rx(struct uart_port *port)if (rx_enabled(port) /* 若串口已启动接收 */disable_irq(RX_IRQ(port); /* 禁止接收中断 */rx_enabled(port) = 0; /* 设置串口为未启动接收 */* 使能modem的状态信号 */static void gprs_uart_enable_ms(struct uart_port *port)/* 串口的Tx FIFO缓存是否为空 */static unsigned int gprs_uart_tx_empty(struct uart_port *port)int ret = 1;unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);unsigned long ufcon = rd_regl(port, S3C2410_UFCON);if (ufcon & S3C2410_UFCON_FIFOMODE) /* 若使能了FIFO */if (ufstat & S3C2410_UFSTAT_TXMASK) != 0 | /* 0 FIFO lock, flags);ucon = rd_regl(port, S3C2410_UCON);if (break_state)ucon |= S3C2410_UCON_SBREAK;elseucon &= S3C2410_UCON_SBREAK;wr_regl(port, S3C2410_UCON, ucon);spin_unlock_irqrestore(&port-lock, flags);/* 返回Rx FIFO已存多少数据 */static int gprs_uart_rx_fifocnt(unsigned long ufstat)/* 若Rx FIFO已满,返回FIFO的大小 */if (ufstat & S3C2410_UFSTAT_RXFULL)return GPRS_UART_FIFO_SIZE;/* 若FIFO未满,返回Rx FIFO已存了多少字节数据 */return (ufstat & S3C2410_UFSTAT_RXMASK) S3C2410_UFSTAT_RXSHIFT;#define S3C2410_UERSTAT_PARITY (0x1000)/* 串口接收中断处理函数,获取串口接收到的数据,并将这些数据递交给行规则层 */static irqreturn_t gprs_uart_rx_chars(int irq, void *dev_id)struct uart_port *port = dev_id;struct tty_struct *tty = port-info-tty;unsigned int ufcon, ch, flag, ufstat, uerstat;int max_count = 64;/* 循环接收数据,最多一次中断接收64字节数据 */while (max_count- 0)ufcon = rd_regl(port, S3C2410_UFCON);ufstat = rd_regl(port, S3C2410_UFSTAT);/* 若Rx FIFO无数据了,跳出循环 */if (gprs_uart_rx_fifocnt(ufstat) = 0)break;/* 读取Rx error状态寄存器 */uerstat = rd_regl(port, S3C2410_UERSTAT);/* 读取已接受到的数据 */ch = rd_regb(port, S3C2410_URXH);/* insert the character into the buffer */* 先将tty标志设为正常 */flag = TTY_NORMAL;/* 递增接收字符计数器 */port-icount.rx+;/* 判断是否存在Rx error* if (unlikely(uerstat & S3C2410_UERSTAT_ANY)等同于* if (uerstat & S3C2410_UERSTAT_ANY)* 只是unlikely表示uerstat & S3C2410_UERSTAT_ANY的值为假的可能性大一些* 另外还有一个likely(value)表示value的值为真的可能性更大一些*/if (unlikely(uerstat & S3C2410_UERSTAT_ANY)/* 若break错误,递增icount.brk计算器 */if (uerstat & S3C2410_UERSTAT_BREAK)port-icount.brk+;if (uart_handle_break(port)goto ignore_char;/* 若frame错误,递增icount.frame计算器 */if (uerstat & S3C2410_UERSTAT_FRAME)port-icount.frame+;/* 若overrun错误,递增icount.overrun计算器 */if (uerstat & S3C2410_UERSTAT_OVERRUN)port-icount.overrun+;/* 查看我们是否关心该Rx error* port-read_status_mask保存着我们感兴趣的Rx error status*/uerstat &= port-read_status_mask;/* 若我们关心该Rx error,则将flag设置为对应的error flag */if (uerstat & S3C2410_UERSTAT_BREAK)flag = TTY_BREAK;else if (uerstat & S3C2410_UERSTAT_PARITY)flag = TTY_PARITY;else if (uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN)flag = TTY_FRAME;/* 处理sys字符 */if (uart_handle_sysrq_char(port, ch)goto ignore_char;/* 将接收到的字符插入到tty设备的flip缓冲 */uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);ignore_char:continue;/* 刷新tty设备的flip缓冲,将接受到的数据传给行规则层 */tty_flip_buffer_push(tty);return IRQ_HANDLED;/* 串口发送中断处理函数,将用户空间的数据(保存在环形缓冲xmit里)发送出去 */static irqreturn_t gprs_uart_tx_chars(int irq, void *dev_id)struct uart_port *port = dev_id;struct circ_buf *xmit = &port-info-xmit; /* 获取环线缓冲 */int count = 256;/* 若设置了xChar字符 */if (port-x_char)/* 将该xChar发送出去 */wr_regb(port, S3C2410_UTXH, port-x_char);/* 递增发送计数 */port-icount.tx+;/* 清除xChar */ port-x_char = 0;/* 退出中断处理 */ goto out;/* 如果没有更多的字符需要发送(环形缓冲为空),* 或者uart Tx已停止,* 则停止uart并退出中断处理函数*/if (uart_circ_empty(xmit) | uart_tx_stopped(port)gprs_uart_stop_tx(port);goto out;/* 循环发送数据,直到环形缓冲为空,最多一次中断发送256字节

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论