√串口时钟, 串口时钟用来转换计算须要设置到配置寄存器当中的波特率比值,其计算方法为:quot = (port->uartclk / (16 * baud)); baud为当前设置的波特率,可为115200等值, 取决于所选的串口时钟源, quot即为要设置到寄存器当中的比值.
√串口基址, 即串口所有配置寄存器基础址.
√串口次设备号(由驱动中的最低次设备号依次累加)
前面已经讲过了六个串口中断,这里详细列出对应情况如下,方便查找:
w83697的三个串口对应中断如下:
uart 1: 读写数据寄存器偏移为00x3F8, 对应系统外部中断INT_EXT[0].
uart 2: 读写数据寄存器偏移为00x2F8, 对应系统外部中断INT_EXT[1].
uart 3: 读写数据寄存器偏移为00x3e8, 对应系统外部中断INT_EXT[2].
uart 4: 读写数据寄存器偏移为00x3e8, 对应EGPIO[8].
w83977的两个串口对应中断如下:
uart 1: 读写数据寄存器偏移为00x3F8, 对应EGPIO[1].
uart 2: 读写数据寄存器偏移为00x2F8, 对应EGPIO[2].
下面列出其中一个具体的串口port的定义如下:
{
.port = {
.membase = (void *)W83697_UART4_BASE,
.mapbase = W83697_UART4_BASE,
.iotype = SERIAL_IO_MEM,
.IRQ = W83697_IRQ_UART4, //串口中断号
.uartclk = 1846100, //uart时钟,默认.
.fifosize = 8, //硬件fifo大小.
.ops = &amba_pops, //底层驱动的硬件操作集,如开关中断等.
.flags = ASYNC_BOOT_AUTOCONF,
.line = 3, //串口在次设备数组中的索引号,须注意从0计起…
},
.dtr_mask = 0,
.rts_mask = 0,
}
4. 串口驱动的底层接口函数
驱动文件:linux-2.4.21/drivers/serial/Ep93xx_w83697.c
相关文件: linux-2.4.21/drivers/serial/core.c 下面详述.
函数: w83697uart_rx_chars(struct uart_port *port, struct pt_regs *regs)
描述: 串口接收数据中断, 此函数中应当注意的要点如下:
接收数据时,要注意判断FIFO是否读空(参见上述2点中说明).
接收数据放入flip缓冲区,此缓冲区专供缓存中断中接收到的数据,是最原始的串口数据,为更上一层中各种终端处理模式的原始数据,可以进行各种加工处理。
接收数据到flip缓冲区中时,须根据硬件接收状态,置每一个接收到的字符的接收标志,放在flag_buf_ptr当中, 标志类型有TTY_NORMAL/ TTY_PARITY/ TTY_FRAME等,分别表示正常/校验出错/帧出错(无停止位)等.
每接收数据之后,会通过在退出中断前调用tty_flip_buffer_push()来往tq_timer任务列表中加一个队列任务,串口的队列任务主要是负责将中断接收到flip缓冲区中的数据往上传输至终端终冲区, 队列任务的机制将在后面介绍,它是一种异步执行机制,在软中断中触发执行.
函数: static void w83697uart_tx_chars(struct uart_port *port)
描述: 串口发送数据中断, 发送中断中要做的事比较少,比起接收中断简单了好多,注意事项如下:
当上层要发送数据时,就会打开串口发送中断,此时FIFO为空,传送半个FIFO大小数据即退出, 通常打开中断是通过更上一层的uart_flush_chars()调用,最终调用的是w83697uart_start_tx().
检测当没有数据要传输的时候,关闭传送中断,在传送之前与传送完之后都有检测.
最重要的一点是如果传送缓冲区当中的字符数已经小于WAKEUP_CHARS, 则可以唤醒当前正在使用串口进行传送的进程,这里是通过tasklet机制来完成,这也是一异步执行机制.
顺带介绍开关中断接口:
statIC void w83697UART_start_tx(struct uart_port *port, unsigned int tty_start)
static void w83697uart_stop_tx(struct uart_port *port, unsigned int tty_stop)
函数: static void w83697uart_int(int IRQ, void *dev_id, struct pt_regs *regs)
描述: 中断处理函数,为3个使用系统外部中断的的串口的中断入口,其中必须处理的中断状态分为如下几种, 注意必须在处理中断时根据手册中的说明来清除中断,通常是读或写某些寄存器即可。
接收中断.
传送中断.
FIFO超时中断.
其它不具体处理的中断,必须读相应寄存器清中断.
函数: static void w83697uart_int2(int irq, void *dev_id, struct pt_regs *regs)
描述: 中断处理函数,为另外几个使用串口使用的GPIO中断入口,GPIO中断共享同一个系统中断向量, 必须根据GPIO的中断状态寄存器的相应位来判断对应的中断是属哪一个串口的,从而进行相应的处理,其实这个判断也是无所谓的,因为中断产生时传进来的参数已经含有了相应串口的参数, 在判断完中断产生的GPIO口后立即调用w83697uart_int2 完成具体的中断处理.
函数: static int w83697uart_startup(struct uart_port *port)
描述: 串口开启后的初始化函数,主要完成初始化配置,以及安装中断处理了函数,初始化配置包括打开中断使能标志。
函数: static void w83697uart_shutdown(struct uart_port *port)
描述: 串口关闭函数,清除配置,半闭中断.
函数: static void w83697uart_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot)
描述: 配置函数,经由上次调用下来,主要配制串口的波特率比,以及各种容错处理,在串口打开初始化时会被调用,在必变串口波特率/校验方式/停止位/传送位数等参数时会被调用.
5. 串口驱动与上层的接口关联
文件: linux-2.4.21/drivers/serial/core.c
这一层接口是串口驱动中的共用部分代码, 核心结构为struct uart_driver. 这一层上承TTY终端,下启串口底层,串口底层主要处理了与串口硬件相关的部分,并向上提供uart中间层向下的接口. Uart coar向下与底层驱动的接口,通过一个static struct uart_ops amba_pops结构完成? 这个结构直接赋值给串口struct uart_amba_port amba_ports 的.ops成员,最后将串口的port加入到uart_driver当中完成关联, 通过uart_add_one_port加入.
static int __init w83697uart_init(void)
{
int ret, i;
ret = uart_register_driver(&amba_reg);
if (ret == 0) {
for (i = 0; i < UART_NR; i++)
uart_add_one_port(&amba_reg, &amba_ports[i].port);
}
return ret;
}
二. Linux的中断机制及中断共享机制.
前面讲到了有6个串口,除了w83697中的前三个串使用的是独立的系统外部中断之外,其它的在个串口是共享一个系统中断向量的,现在我们来看看多个中断是如何挂在一个系统中断向量表当中的,共享中断到底是什么样的一种机制?
进行分析代码可知,linux下的中断采用的是中断向量的方式,每一个中断对应一个中断描述数组当中的一项, 结构为struct irqdesc,其当中对应一成员结构为struct irqactionr 的成员action, 这个即表示此中断向量对应的中断处理动作,这里引用从网上下载的一幅图讲明中断向量表与中断动作之间的关系:
struct irqaction {
void (*handler)(int, void *, struct pt_regs *);
unsigned long flags;
unsigned long mask;
const char *name;
void *dev_id;
struct irqaction *next;
};
从上面的结构体与图当中,我们就可以很清楚的看到,一个中断向量表可以对应一个irqaction,也可能对应多个由链表链在一起的一个链表irqaction, 这当中主要在安装中断的时候通过中断的标志位来决定:
安装中断处理,不可共享:
retval = request_irq(port->irq, w83697uart_int, 0, "w83697_uart3", port);
安装中断处理,可共享:
retval = request_irq(port->irq, w83697uart_int2, SA_SHIRQ, "w83977_uart5", port);
上一篇:ARM7启动代码编写的分析与设计