Linux串口(serial、uart)驱动程序设计

一、核心数据结构串口驱动有3个核心数据结构,它们都定义在<#include linux/serial_core.h>1、uart_driveruart_driver包含了串口设备名、串口驱动名、主次设备号、串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver)。

structuart_driver{structmodule*owner;/* 拥有该uart_driver的模块,一般为THIS_MODULE */constchar*driver_name;/*串口驱动名,串口设备文件名以驱动名为基础 */constchar*dev_name;/* 串口设备名 */int major; /* 主设备号 */intminor; /* 次设备号 */intnr; /* 该uart_driver支持的串口个数(最大) */structconsole*cons;/* 其对应的console.若该uart_driver支持serial console,否则为NULL *//* * these are private; the low level driver should not * touch these; they should be initialised to NULL */structuart_state*state;structtty_driver*tty_driver;};

2、uart_portuart_port用于描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。实际上,一个uart_port实例对应一个串口设备

structuart_port{spinlock_t lock;/* 串口端口锁 */unsignedint iobase;/* IO端口基地址 */unsignedchar__iomem*membase;/* IO内存基地址,经映射(如ioremap)后的IO内存虚拟基地址 */unsignedint irq;/* 中断号 */unsignedint uartclk;/* 串口时钟 */unsignedintfifosize;/* 串口FIFO缓冲大小 */unsignedchar x_char;/* xon/xoff字符 */unsignedchar regshift;/* 寄存器位移 */unsignedchar iotype;/* IO访问方式 */unsignedchar unused1;#defineUPIO_PORT(0)/* IO端口 */#defineUPIO_HUB6(1)#defineUPIO_MEM(2) /* IO内存 */#defineUPIO_MEM32(3)#defineUPIO_AU(4)/* Au1x00 type IO */#defineUPIO_TSI(5)/* Tsi108/109 type IO */#defineUPIO_DWAPB(6)/* DesignWare APB UART */#defineUPIO_RM9000(7)/* RM9000 type IO */unsignedintread_status_mask;/* 关心的Rx error status */unsignedintignore_status_mask;/* 忽略的Rx error status */structuart_info*info;/* pointer to parent info */structuart_icount icount; /* 计数器 */structconsole*cons; /*console结构体 */#ifdefCONFIG_SERIAL_CORE_CONSOLEunsignedlongsysrq; /* sysrq timeout */#endifupf_t flags;#defineUPF_FOURPORT((__force upf_t)(1<<1))#defineUPF_SAK((__force upf_t)(1<<2))#defineUPF_SPD_MASK ((__force upf_t)(0x1030))#defineUPF_SPD_HI((__force upf_t)(0x0010))#defineUPF_SPD_VHI((__force upf_t)(0x0020))#defineUPF_SPD_CUST((__force upf_t)(0x0030))#defineUPF_SPD_SHI((__force upf_t)(0x1000))#defineUPF_SPD_WARP((__force upf_t)(0x1010))#defineUPF_SKIP_TEST((__force upf_t)(1<<6))#defineUPF_AUTO_IRQ((__force upf_t)(1<<7))#defineUPF_HARDPPS_CD((__force upf_t)(1<<11))#defineUPF_LOW_LATENCY((__force upf_t)(1<<13))#defineUPF_BUGGY_UART((__force upf_t)(1<<14))#defineUPF_MAGIC_MULTIPLIER((__force upf_t)(1<<16))#defineUPF_CONS_FLOW((__force upf_t)(1<<23))#defineUPF_SHARE_IRQ((__force upf_t)(1<<24))#defineUPF_BOOT_AUTOCONF((__force upf_t)(1<<28))#defineUPF_FIXED_PORT((__force upf_t)(1<<29))#defineUPF_DEAD((__force upf_t)(1<<30))#defineUPF_IOREMAP((__force upf_t)(1<<31))#defineUPF_CHANGE_MASK((__force upf_t)(0x17fff))#defineUPF_USR_MASK((__force upf_t)(UPF_SPD_MASK|UPF_LOW_LATENCY))unsignedint mctrl;/* 当前的moden设置 */unsignedint timeout;/* character-based timeout */unsignedint type;/* 端口类型 */conststructuart_ops*ops;/* 串口端口操作函数集 */unsignedint custom_divisor;unsignedintline;/* 端口索引 */resource_size_tmapbase;/*IO内存物理基地址,可用于ioremap */structdevice *dev;/* 父设备 */unsignedchar hub6;/* this should be in the 8250 driver */unsignedchar suspended;unsignedchar unused[2];void*private_data;/* 端口私有数据,一般为platform数据指针 */};

uart_iconut为串口信息计数器,包含了发送字符计数、接收字符计数等。在串口的发送中断处理函数和接收中断处理函数中,我们需要管理这些计数。

structuart_icount{__u32cts;__u32dsr;__u32rng;__u32dcd;__u32rx;/*发送字符计数 */__u32tx;/*接受字符计数 */__u32frame;/*帧错误计数 */__u32overrun;/*Rx FIFO溢出计数 */__u32parity;/*帧校验错误计数 */__u32brk;/*break计数 */__u32buf_overrun;};

uart_info有两个成员在底层串口驱动会用到:xmit和tty。用户空间程序通过串口发送数据时,上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它们发送出去。串口接收中断处理函数需要通过tty将接收到的数据传递给行规则层。

/* uart_info实例仅在串口端口打开时有效,它可能在串口关闭时被串口核心层释放。因此,在使用uart_port的uart_info成员时必须保证串口已打开。底层驱动和核心层驱动都可以修改uart_info实例。* This is the state information which is only valid when the port* is open; it may be freed by the core driver once the device has* been closed. Either the low level driver or the core can modify* stuff here.*/structuart_info{structtty_struct*tty;structcirc_bufxmit;uif_t flags;/** Definitions for info->flags. These are _private_ to serial_core, and* are specific to this structure. They may be queried by low level drivers.*/#defineUIF_CHECK_CD((__force uif_t)(1<<25))#defineUIF_CTS_FLOW((__force uif_t)(1<<26))#defineUIF_NORMAL_ACTIVE((__force uif_t)(1<<29))#defineUIF_INITIALIZED((__force uif_t)(1<<31))#defineUIF_SUSPENDED((__force uif_t)(1<<30))int blocked_open;structtasklet_structtlet;wait_queue_head_t open_wait;wait_queue_head_t delta_msr_wait;};

3、uart_ops

uart_ops涵盖了串口驱动可对串口设备进行的所有操作。

/** This structure describes all the operations that can be* done on the physical hardware.*/structuart_ops{unsignedint(*tx_empty)(structuart_port*);/*串口的Tx FIFO缓存是否为空*/void(*set_mctrl)(structuart_port*,unsignedintmctrl);/*设置串口modem控制*/unsignedint(*get_mctrl)(structuart_port*);/*获取串口modem控制*/void(*stop_tx)(structuart_port*);/*禁止串口发送数据*/void(*start_tx)(structuart_port*);/*使能串口发送数据*/void(*send_xchar)(structuart_port*,charch);/*发送xChar*/void(*stop_rx)(structuart_port*);/*禁止串口接收数据*/void(*enable_ms)(structuart_port*);/*使能modem的状态信号*/void(*break_ctl)(structuart_port*,intctl);/*设置break信号*/int(*startup)(structuart_port*);/*启动串口,应用程序打开串口设备文件时,该函数会被调用*/void(*shutdown)(structuart_port*);/*关闭串口,应用程序关闭串口设备文件时,该函数会被调用*/void(*set_termios)(structuart_port*,structktermios*new,structktermios*old);/*设置串口参数*/void(*pm)(structuart_port*,unsignedintstate,unsignedintoldstate);/*串口电源管理*/int(*set_wake)(structuart_port*,unsignedintstate);/**/constchar*(*type)(structuart_port*);/*返回一描述串口类型的字符串*/void(*release_port)(structuart_port*);/*释放串口已申请的IO端口/IO内存资源,必要时还需iounmap*/int(*request_port)(structuart_port*);/*申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口*/void(*config_port)(structuart_port*,int);/*执行串口所需的自动配置*/int(*verify_port)(structuart_port*,structserial_struct*);/*核实新串口的信息*/int(*ioctl)(structuart_port*,unsignedint,unsignedlong);/*IO控制*/};

二、串口驱动API1、uart_register_driver

/* 功能: uart_register_driver用于将串口驱动uart_driver注册到内核(串口核心层)中,通常在模块初始化函数调用该函数。* 参数 drv:要注册的uart_driver

*返回值: 成功,返回0;否则返回错误码*/intuart_register_driver(structuart_driver*drv)

2、uart_unregister_driver

/* 功能: uart_unregister_driver用于注销我们已注册的uart_driver,通常在模块卸载函数调用该函数*参数 drv:要注销的uart_driver

*返回值: 成功,返回0;否则返回错误码*/voiduart_unregister_driver(structuart_driver*drv)

3、uart_add_one_port

/* 功能: uart_add_one_port用于为串口驱动添加一个串口端口,通常在探测到设备后(驱动的设备probe方法)调用该函数* 参数drv:串口驱动* port:要添加的串口端口

* 返回值:成功,返回0;否则返回错误码*/intuart_add_one_port(structuart_driver*drv,structuart_port*port)

4、uart_remove_one_port

/* 功能:uart_remove_one_port用于删除一个已添加到串口驱动中的串口端口,通常在驱动卸载时调用该函数* 参数drv: 串口驱动* port: 要删除的串口端口* 返回值:成功,返回0;否则返回错误码*/intuart_remove_one_port(structuart_driver*drv,structuart_port*port)

5、uart_write_wakeup

/* 功能: uart_write_wakeup唤醒上层因向串口端口写数据而阻塞的进程,通常在串口发送中断处理函数中调用该函数* 参数 port:需要唤醒写阻塞进程的串口端口*/voiduart_write_wakeup(structuart_port*port)

6、uart_suspend_port

/* 功能: uart_suspend_port用于挂起特定的串口端口*参数drv: 要挂起的串口端口所属的串口驱动*port:要挂起的串口端口* 返回值: 成功返回0;否则返回错误码*/intuart_suspend_port(structuart_driver*drv,structuart_port*port)

7、uart_resume_port

/* 功能: uart_resume_port用于恢复某一已挂起的串口*参数drv: 要恢复的串口端口所属的串口驱动*port:要恢复的串口端口* 返回值: 成功返回0;否则返回错误码*/intuart_resume_port(structuart_driver*drv,structuart_port*port)

8、uart_get_baud_rate

/* 功能: uart_get_baud_rate通过解码termios结构体来获取指定串口的波特率* 参数 port: 要获取波特率的串口端口* termios:当前期望的termios配置(包含串口波特率)* old: 以前的termios配置,可以为NULL* min: 可接受的最小波特率* max:可接受的最大波特率* 返回值: 串口的波特率*/unsignedintuart_get_baud_rate(structuart_port*port,structktermios*termios,structktermios*old,unsignedintmin,unsignedintmax)

9、uart_get_divisor

/* 功能: uart_get_divisor用于计算某一波特率的串口时钟分频数(串口波特率除数)* 参数 port:要计算时钟分频数的串口端口* baud:期望的波特率*返回值: 串口时钟分频数*/unsignedintuart_get_divisor(structuart_port*port,unsignedintbaud)

10、uart_update_timeout

/* 功能: uart_update_timeout用于更新(设置)串口FIFO超时时间* 参数 port: 要更新超时时间的串口端口* cflag:termios结构体的cflag值* baud: 串口的波特率*/voiduart_update_timeout(structuart_port*port,unsignedintcflag,unsignedintbaud)

11、uart_match_port

/* 功能:uart_match_port用于判断两串口端口是否为同一端口* 参数 port1、port2:要判断的串口端口* 返回值:不同返回0;否则返回非0*/intuart_match_port(structuart_port*port1,structuart_port*port2)

12、uart_console_write

/* 功能: uart_console_write用于向串口端口写一控制台信息

*参数 port: 要写信息的串口端口* s:要写的信息* count: 信息的大小* putchar: 用于向串口端口写字符的函数,该函数函数有两个参数:串口端口和要写的字符*/voiduart_console_write(structuart_port*port,constchar*s,unsignedintcount,void(*putchar)(structuart_port*,int))

三、串口驱动例子该串口驱动例子是我针对s3c2410处理器的串口2(uart2)独立开发的。因为我通过博创2410s开发板的GRPS扩展板来测试该驱动(已通过测试),所以我叫该串口为gprs_uart。该驱动将串口看作平台(platform)设备。platform可以看作一伪总线,用于将集成于片上系统的轻量级设备与Linux设备驱动模型联系到一起,它包含以下两部分(有关platform的声明都在#include <linux/platform_device.h>,具体实现在drivers/base/platform.c):1、platform设备。我们需要为每个设备定义一个platform_device实例

structplatform_device{constchar*name;/*设备名 */int id;/*设备的id号 */structdevice dev;/*其对应的device */u32num_resources;/*该设备用有的资源数 */structresource*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方法会被调用来做一些清理工作,如移除该设备的一些实例、注销一些已注册到系统中去的东西。

structplatform_driver{int(*probe)(structplatform_device*);int(*remove)(structplatform_device*);void(*shutdown)(structplatform_device*);int(*suspend)(structplatform_device*,pm_message_t state);int(*suspend_late)(structplatform_device*,pm_message_t state);int(*resume_early)(structplatform_device*);int(*resume)(structplatform_device*);structdevice_driver driver;};

例子驱动中申请和释放IO内存区的整个过程如下:

insmodgprs_uart.ko→gprs_init_module()→uart_register_driver()→gprs_uart_probe()→uart_add_one_port()→gprs_uart_config_port()→gprs_uart_request_port()→request_mem_region()

rmmodgprs_uart.ko→gprs_exit_module()→uart_unregister_driver()→gprs_uart_remove()→uart_remove_one_port()→gprs_uart_release_port()→release_mem_region()

例子驱动中申请和释放IRQ资源的整个过程如下:

open/dev/gprs_uart→gprs_uart_startup()→request_irq()

close/dev/gprs_uart→gprs_uart_shutdown()→free_irq()

想了解更详细的调用过程可以在驱动模块各函数头插入printk(KERN_DEBUG "%s\n", __FUNCTION__);并在函数尾插入printk(KERN_DEBUG "%s done\n", __FUNCTION__);

下面是串口驱动例子和其GPRS测试程序源码下载地址:

http://www.pudn.com/downloads258/sourcecode/unix_linux/detail1192104.html

#include<linux/module.h>#include<linux/init.h>#include<linux/kernel.h>/* printk() */#include<linux/slab.h>/* kmalloc() */#include<linux/fs.h>/* everything... */#include<linux/errno.h>/* error codes */#include<linux/types.h>/* size_t */#include<linux/fcntl.h>/* O_ACCMODE */#include<asm/system.h>/* cli(), *_flags */#include<asm/uaccess.h>/* copy_*_user */#include<linux/ioctl.h>#include<linux/device.h>#include<linux/platform_device.h>#include<linux/sysrq.h>#include<linux/tty.h>#include<linux/tty_flip.h>#include<linux/serial_core.h>#include<linux/serial.h>#include<linux/delay.h>#include<linux/clk.h>#include<linux/console.h>#include<asm/io.h>#include<asm/irq.h>#include<asm/hardware.h>#include<asm/plat-s3c/regs-serial.h>#include<asm/arch/regs-gpio.h>#defineDEV_NAME"gprs_uart"/* 设备名 *//* 这里将串口的主设备号设为0,则串口设备编号由内核动态分配;你也可指定串口的设备编号 */#defineGPRS_UART_MAJOR0/* 主设备号 */#defineGPRS_UART_MINOR0/* 次设备号 */#defineGPRS_UART_FIFO_SIZE16/* 串口FIFO的大小 */#defineRXSTAT_DUMMY_READ(0x10000000)#defineMAP_SIZE (0x100)/* 要映射的串口IO内存区大小 *//* 串口发送中断号 */#defineTX_IRQ(port)((port)->irq+1)/* 串口接收中断号 */#defineRX_IRQ(port)((port)->irq)/* 允许串口接收字符的标志 */#definetx_enabled(port)((port)->unused[0])/* 允许串口发送字符的标志 */#definerx_enabled(port)((port)->unused[1])/* 获取寄存器地址 */#defineportaddr(port,reg)((port)->membase+(reg))/* 读8位宽的寄存器 */#definerd_regb(port,reg)(ioread8(portaddr(port,reg)))/* 读32位宽的寄存器 */#definerd_regl(port,reg)(ioread32(portaddr(port,reg)))/* 写8位宽的寄存器 */#definewr_regb(port,reg,val)\do{iowrite8(val,portaddr(port,reg));}while(0)/* 写32位宽的寄存器 */#definewr_regl(port,reg,val)\do{iowrite32(val,portaddr(port,reg));}while(0)/* 禁止串口发送数据 */staticvoidgprs_uart_stop_tx(structuart_port*port){if(tx_enabled(port))/* 若串口已启动发送 */{disable_irq(TX_IRQ(port));/* 禁止发送中断 */tx_enabled(port)=0;/* 设置串口为未启动发送 */}}/* 使能串口发送数据 */staticvoidgprs_uart_start_tx(structuart_port*port){if(!tx_enabled(port))/* 若串口未启动发送 */{enable_irq(TX_IRQ(port));/* 使能发送中断 */tx_enabled(port)=1;/* 设置串口为已启动发送 */}}/* 禁止串口接收数据 */staticvoidgprs_uart_stop_rx(structuart_port*port){if(rx_enabled(port))/* 若串口已启动接收 */{disable_irq(RX_IRQ(port));/* 禁止接收中断 */rx_enabled(port)=0;/* 设置串口为未启动接收 */}}/* 使能modem的状态信号 */staticvoidgprs_uart_enable_ms(structuart_port*port){}/* 串口的Tx FIFO缓存是否为空 */staticunsignedintgprs_uart_tx_empty(structuart_port*port){intret=1;unsignedlongufstat=rd_regl(port,S3C2410_UFSTAT);unsignedlongufcon=rd_regl(port,S3C2410_UFCON);if(ufcon&S3C2410_UFCON_FIFOMODE)/* 若使能了FIFO */{if((ufstat&S3C2410_UFSTAT_TXMASK)!=0||/* 0 <FIFO <=15 */(ufstat&S3C2410_UFSTAT_TXFULL))/* FIFO满 */ret=0;}else/* 若未使能了FIFO,则判断发送缓存和发送移位寄存器是否均为空 */{ret=rd_regl(port,S3C2410_UTRSTAT)&S3C2410_UTRSTAT_TXE;}returnret;}/* 获取串口modem控制,因为uart2无modem控制,所以CTS、DSR直接返回有效 */staticunsignedintgprs_uart_get_mctrl(structuart_port*port){return(TIOCM_CTS|TIOCM_DSR|TIOCM_CAR);}/* 设置串口modem控制 */staticvoidgprs_uart_set_mctrl(structuart_port*port,unsignedintmctrl){}/* 设置break信号 */staticvoidgprs_uart_break_ctl(structuart_port*port,intbreak_state){unsignedlongflags;unsignedintucon;spin_lock_irqsave(&port->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已存多少数据 */staticintgprs_uart_rx_fifocnt(unsignedlongufstat){/* 若Rx FIFO已满,返回FIFO的大小 */if(ufstat&S3C2410_UFSTAT_RXFULL)returnGPRS_UART_FIFO_SIZE;/* 若FIFO未满,返回Rx FIFO已存了多少字节数据 */return(ufstat&S3C2410_UFSTAT_RXMASK)>>S3C2410_UFSTAT_RXSHIFT;}#defineS3C2410_UERSTAT_PARITY(0x1000)/* 串口接收中断处理函数,获取串口接收到的数据,并将这些数据递交给行规则层 */staticirqreturn_t gprs_uart_rx_chars(intirq,void*dev_id){structuart_port*port=dev_id;structtty_struct*tty=port->info->tty;unsignedintufcon,ch,flag,ufstat,uerstat;intmax_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))gotoignore_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;elseif(uerstat&S3C2410_UERSTAT_PARITY)flag=TTY_PARITY;elseif(uerstat&(S3C2410_UERSTAT_FRAME|S3C2410_UERSTAT_OVERRUN))flag=TTY_FRAME;}/* 处理sys字符 */if(uart_handle_sysrq_char(port,ch))gotoignore_char;/* 将接收到的字符插入到tty设备的flip缓冲 */uart_insert_char(port,uerstat,S3C2410_UERSTAT_OVERRUN,ch,flag);ignore_char:continue;}/* 刷新tty设备的flip缓冲,将接受到的数据传给行规则层 */tty_flip_buffer_push(tty);returnIRQ_HANDLED;}/* 串口发送中断处理函数,将用户空间的数据(保存在环形缓冲xmit里)发送出去 */staticirqreturn_t gprs_uart_tx_chars(intirq,void*dev_id){structuart_port*port=dev_id;structcirc_buf*xmit=&port->info->xmit;/* 获取环线缓冲 */intcount=256;/* 若设置了xChar字符 */if(port->x_char){/* 将该xChar发送出去 */wr_regb(port,S3C2410_UTXH,port->x_char);/* 递增发送计数 */port->icount.tx++;/* 清除xChar */port->x_char=0;/* 退出中断处理 */gotoout;}/* 如果没有更多的字符需要发送(环形缓冲为空), * 或者uart Tx已停止, * 则停止uart并退出中断处理函数 */if(uart_circ_empty(xmit)||uart_tx_stopped(port)){gprs_uart_stop_tx(port);gotoout;}/* 循环发送数据,直到环形缓冲为空,最多一次中断发送256字节数据 */while(!uart_circ_empty(xmit)&&count-->0){/* 若Tx FIFO已满,退出循环 */if(rd_regl(port,S3C2410_UFSTAT)&S3C2410_UFSTAT_TXFULL)break;/* 将要发送的数据写入Tx FIFO */wr_regb(port,S3C2410_UTXH,xmit->buf[xmit->tail]);/* 移向循环缓冲中下一要发送的数据 */xmit->tail=(xmit->tail+1)&(UART_XMIT_SIZE-1);port->icount.tx++;}/* 如果环形缓冲区中剩余的字符少于WAKEUP_CHARS,唤醒上层 */if(uart_circ_chars_pending(xmit)<WAKEUP_CHARS)uart_write_wakeup(port);/* 如果环形缓冲为空,则停止发送 */if(uart_circ_empty(xmit))gprs_uart_stop_tx(port);out:returnIRQ_HANDLED;}/* 启动串口端口,在打开该驱动的设备文件时会调用该函数来申请串口中断,并设置串口为可接受,也可发送 */staticintgprs_uart_startup(structuart_port*port){unsignedlongflags;intret;constchar*portname=to_platform_device(port->dev)->name;/* 设置串口为不可接受,也不可发送 */rx_enabled(port)=0;tx_enabled(port)=0;spin_lock_irqsave(&port->lock,flags);/* 申请接收中断 */ret=request_irq(RX_IRQ(port),gprs_uart_rx_chars,0,portname,port);if(ret!=0){printk(KERN_ERR"cannot get irq %d\n",RX_IRQ(port));returnret;}/* 设置串口为允许接收 */rx_enabled(port)=1;/* 申请发送中断 */ret=request_irq(TX_IRQ(port),gprs_uart_tx_chars,0,portname,port);if(ret){printk(KERN_ERR"cannot get irq %d\n",TX_IRQ(port));rx_enabled(port)=0;free_irq(RX_IRQ(port),port);gotoerr;}/* 设置串口为允许发送 */tx_enabled(port)=1;err:spin_unlock_irqrestore(&port->lock,flags);returnret;}/* 关闭串口,在关闭驱动的设备文件时会调用该函数,释放串口中断 */staticvoidgprs_uart_shutdown(structuart_port*port){rx_enabled(port)=0;/* 设置串口为不允许接收*/free_irq(RX_IRQ(port),port);/* 释放接收中断*/tx_enabled(port)=0;/* 设置串口为不允许发送*/free_irq(TX_IRQ(port),port);/* 释放发送中断*/}/* 设置串口参数 */staticvoidgprs_uart_set_termios(structuart_port*port,structktermios*termios,structktermios*old){unsignedlongflags;unsignedintbaud,quot;unsignedintulcon,ufcon=0;/* 不支持moden控制信号线 * HUPCL:关闭时挂断moden * CMSPAR:mark or space (stick) parity * CLOCAL:忽略任何moden控制线 */termios->c_cflag&=~(HUPCL|CMSPAR);termios->c_cflag|=CLOCAL;/* 获取用户设置的串口波特率,并计算分频数(串口波特率除数quot) */baud=uart_get_baud_rate(port,termios,old,0,115200*8);if(baud==38400&&(port->flags&UPF_SPD_MASK)==UPF_SPD_CUST)quot=port->custom_divisor;elsequot=port->uartclk/baud/16-1;/* 设置数据字长 */switch(termios->c_cflag&CSIZE){caseCS5:ulcon=S3C2410_LCON_CS5;break;caseCS6:ulcon=S3C2410_LCON_CS6;break;caseCS7:ulcon=S3C2410_LCON_CS7;break;caseCS8:default:ulcon=S3C2410_LCON_CS8;break;}/* 是否要求设置两个停止位(CSTOPB) */if(termios->c_cflag&CSTOPB)ulcon|=S3C2410_LCON_STOPB;/* 是否使用奇偶检验 */if(termios->c_cflag&PARENB){if(termios->c_cflag&PARODD)/* 奇校验 */ulcon|=S3C2410_LCON_PODD;else/* 偶校验 */ulcon|=S3C2410_LCON_PEVEN;}else/* 无校验 */{ulcon|=S3C2410_LCON_PNONE;}if(port->fifosize>1)ufcon|=S3C2410_UFCON_FIFOMODE|S3C2410_UFCON_RXTRIG8;spin_lock_irqsave(&port->lock,flags);/* 设置FIFO控制寄存器、线控制寄存器和波特率除数寄存器 */wr_regl(port,S3C2410_UFCON,ufcon);wr_regl(port,S3C2410_ULCON,ulcon);wr_regl(port,S3C2410_UBRDIV,quot);/* 更新串口FIFO的超时时限 */uart_update_timeout(port,termios->c_cflag,baud);/* 设置我们感兴趣的Rx error */port->read_status_mask=S3C2410_UERSTAT_OVERRUN;if(termios->c_iflag&INPCK)port->read_status_mask|=S3C2410_UERSTAT_FRAME|S3C2410_UERSTAT_PARITY;/* 设置我们忽略的Rx error */port->ignore_status_mask=0;if(termios->c_iflag&IGNPAR)port->ignore_status_mask|=S3C2410_UERSTAT_OVERRUN;if(termios->c_iflag&IGNBRK&&termios->c_iflag&IGNPAR)port->ignore_status_mask|=S3C2410_UERSTAT_FRAME;/* 若未设置CREAD(使用接收器),则忽略所有Rx error*/if((termios->c_cflag&CREAD)==0)port->ignore_status_mask|=RXSTAT_DUMMY_READ;spin_unlock_irqrestore(&port->lock,flags);}/* 获取串口类型 */staticconstchar*gprs_uart_type(structuart_port*port){/* 返回描述串口类型的字符串指针 */returnport->type==PORT_S3C2410?"gprs_uart:s3c2410_uart2":NULL;}/* 申请串口一些必要的资源,如IO端口/IO内存资源,必要时还可以重新映射串口端口 */staticintgprs_uart_request_port(structuart_port*port){structresource*res;constchar*name=to_platform_device(port->dev)->name;/* request_mem_region请求分配IO内存,从开始port->mapbase,大小MAP_SIZE * port->mapbase保存当前串口的寄存器基地址(物理) * uart2: 0x50008000 */res=request_mem_region(port->mapbase,MAP_SIZE,name);if(res==NULL){printk(KERN_ERR"request_mem_region error: %p\n",res);return-EBUSY;}return0;}/* 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap */staticvoidgprs_uart_release_port(structuart_port*port){/* 释放已分配IO内存 */release_mem_region(port->mapbase,MAP_SIZE);}/* 执行串口所需的自动配置 */staticvoidgprs_uart_config_port(structuart_port*port,intflags){intretval;/* 请求串口 */retval=gprs_uart_request_port(port);/* 设置串口类型 */if(flags&UART_CONFIG_TYPE&&retval==0)port->type=PORT_S3C2410;}/* The UART operations structure */staticstructuart_ops gprs_uart_ops={.start_tx=gprs_uart_start_tx,/* Start transmitting */.stop_tx=gprs_uart_stop_tx,/* Stop transmission */.stop_rx=gprs_uart_stop_rx,/* Stop reception */.enable_ms=gprs_uart_enable_ms,/* Enable modem status signals */.tx_empty=gprs_uart_tx_empty,/* Transmitter busy? */.get_mctrl=gprs_uart_get_mctrl,/* Get modem control */.set_mctrl=gprs_uart_set_mctrl,/* Set modem control */.break_ctl=gprs_uart_break_ctl,/* Set break signal */.startup=gprs_uart_startup,/* App opens GPRS_UART */.shutdown=gprs_uart_shutdown,/* App closes GPRS_UART */.set_termios=gprs_uart_set_termios,/* Set termios */.type=gprs_uart_type,/* Get UART type */.request_port=gprs_uart_request_port,/* Claim resources associated with a GPRS_UART port */.release_port=gprs_uart_release_port,/* Release resources associated with a GPRS_UART port */.config_port=gprs_uart_config_port,/* Configure when driver adds a GPRS_UART port */};/* Uart driver for GPRS_UART */staticstructuart_driver gprs_uart_driver={.owner=THIS_MODULE,/* Owner */.driver_name=DEV_NAME,/* Driver name */.dev_name=DEV_NAME,/* Device node name */.major=GPRS_UART_MAJOR,/* Major number */.minor=GPRS_UART_MINOR,/* Minor number start */.nr=1,/* Number of UART ports */};/* Uart port for GPRS_UART port */staticstructuart_port gprs_uart_port={.irq=IRQ_S3CUART_RX2,/* IRQ */.fifosize=GPRS_UART_FIFO_SIZE,/* Size of the FIFO */.iotype=UPIO_MEM,/* IO memory */.flags=UPF_BOOT_AUTOCONF,/* UART port flag */.ops=&gprs_uart_ops,/* UART operations */.line=0,/* UART port number */.lock=__SPIN_LOCK_UNLOCKED(gprs_uart_port.lock),};/* 初始化指定串口端口 */staticintgprs_uart_init_port(structuart_port*port,structplatform_device*platdev){unsignedlongflags;unsignedintgphcon;if(platdev==NULL)return-ENODEV;port->dev=&platdev->dev;/* 设置串口波特率时钟频率 */port->uartclk=clk_get_rate(clk_get(&platdev->dev,"pclk"));/* 设置串口的寄存器基地址(物理): 0x50008000 */port->mapbase=S3C2410_PA_UART2;/* 设置当前串口的寄存器基地址(虚拟): 0xF5008000 */port->membase=S3C24XX_VA_UART+(S3C2410_PA_UART2-S3C24XX_PA_UART);spin_lock_irqsave(&port->lock,flags);wr_regl(port,S3C2410_UCON,S3C2410_UCON_DEFAULT);wr_regl(port,S3C2410_ULCON,S3C2410_LCON_CS8|S3C2410_LCON_PNONE);wr_regl(port,S3C2410_UFCON,S3C2410_UFCON_FIFOMODE|S3C2410_UFCON_RXTRIG8|S3C2410_UFCON_RESETBOTH);/* 将I/O port H的gph6和gph7设置为TXD2和RXD2 */gphcon=readl(S3C2410_GPHCON);gphcon&=~((0x5)<<12);writel(gphcon,S3C2410_GPHCON);spin_unlock_irqrestore(&port->lock,flags);return0;}/* Platform driver probe */staticint__init gprs_uart_probe(structplatform_device*dev){intret;/* 初始化串口 */ret=gprs_uart_init_port(&gprs_uart_port,dev);if(ret<0){printk(KERN_ERR"gprs_uart_probe: gprs_uart_init_port error: %d\n",ret);returnret;}/* 添加串口 */ret=uart_add_one_port(&gprs_uart_driver,&gprs_uart_port);if(ret<0){printk(KERN_ERR"gprs_uart_probe: uart_add_one_port error: %d\n",ret);returnret;}/* 将串口uart_port结构体保存在platform_device->dev->driver_data中 */platform_set_drvdata(dev,&gprs_uart_port);return0;}/* Called when the platform driver is unregistered */staticintgprs_uart_remove(structplatform_device*dev){platform_set_drvdata(dev,NULL);/* 移除串口 */uart_remove_one_port(&gprs_uart_driver,&gprs_uart_port);return0;}/* Suspend power management event */staticintgprs_uart_suspend(structplatform_device*dev,pm_message_t state){uart_suspend_port(&gprs_uart_driver,&gprs_uart_port);return0;}/* Resume after a previous suspend */staticintgprs_uart_resume(structplatform_device*dev){uart_resume_port(&gprs_uart_driver,&gprs_uart_port);return0;}/* Platform driver for GPRS_UART */staticstructplatform_driver gprs_plat_driver={.probe=gprs_uart_probe,/* Probe method */.remove=__exit_p(gprs_uart_remove),/* Detach method */.suspend=gprs_uart_suspend,/* Power suspend */.resume=gprs_uart_resume,/* Resume after a suspend */.driver={.owner=THIS_MODULE,.name=DEV_NAME,/* Driver name */},};/* Platform device for GPRS_UART */structplatform_device*gprs_plat_device;staticint__init gprs_init_module(void){intretval;/* Register uart_driver for GPRS_UART */retval=uart_register_driver(&gprs_uart_driver);if(0!=retval){printk(KERN_ERR"gprs_init_module: can't register the GPRS_UART driver %d\n",retval);returnretval;}/* Register platform device for GPRS_UART. Usually calledduring architecture-specific setup */gprs_plat_device=platform_device_register_simple(DEV_NAME,0,NULL,0);if(IS_ERR(gprs_plat_device)){retval=PTR_ERR(gprs_plat_device);printk(KERN_ERR"gprs_init_module: can't register platform device %d\n",retval);gotofail_reg_plat_dev;}/* Announce a matching driver for the platformdevices registered above */retval=platform_driver_register(&gprs_plat_driver);if(0!=retval){printk(KERN_ERR"gprs_init_module: can't register platform driver %d\n",retval);gotofail_reg_plat_drv;}return0;/* succeed */fail_reg_plat_drv:platform_device_unregister(gprs_plat_device);fail_reg_plat_dev:uart_unregister_driver(&gprs_uart_driver);returnretval;}staticvoid__exit gprs_exit_module(void){/* The order of unregistration is important. Unregistering theUART driver before the platform driver will crash the system *//* Unregister the platform driver */platform_driver_unregister(&gprs_plat_driver);/* Unregister the platform devices */platform_device_unregister(gprs_plat_device);/* Unregister the GPRS_UART driver */uart_unregister_driver(&gprs_uart_driver);}module_init(gprs_init_module);module_exit(gprs_exit_module);MODULE_AUTHOR("lingd");MODULE_LICENSE("Dual BSD/GPL");

失败是什么?没有什么,只是更走近成功一步;

Linux串口(serial、uart)驱动程序设计

相关文章:

你感兴趣的文章:

标签云: