分类: LINUX
2010-05-19 16:17:21
基于内核版本2.6.16.14. 写于(美国)东部时区 2006年05月05日 22点49分 .
下层串行API
--------------------
$Id: driver,v 1.10 2002/07/22 15:27:30 rmk Exp $
本文简要介绍了新的串行驱动的某些方面。这并不完整,有任何问题请联系
在serial_amba.c可以找到所涉及的实现部分。
下层串行硬件驱动
--------------------------------
下层串行硬件驱动主要为核心串行驱动提供端口信息(在uart_port中定义的)和一组控制方法(在uart_port中定义的)。下层驱动同时也处理来自端口的中断和提供控制台支持。
控制台支持
---------------
串行核心提供了一些辅助函数。这包括识别正确的端口结构体(通过uart_get_console)和解析命令行参数(即uart_parse_options)。
锁定
-------
下层硬件驱动需使用port->lock执行必要的锁操作。也有一些例外(在下面的uart_ops列表中会描述到)。
有三种锁。单端口自旋锁,单端口临时缓冲区信号量和全局信号量。
从核心驱动来看,port->lock锁定以下数据:
port->mctrl
port->icount
info->xmit.head (circ->head)
info->xmit.tail (circ->tail)
下层驱动可以随意使用这种锁来提供任何锁定操作。
下层驱动使用info->tmpbuf_sem锁来防止多线程访问用作端口写的info->tmpbuf弹性缓冲区。
port_sem信号量是被用来保护端口的,防止在不恰当的时间添加删除或重新配置端口。
uart_ops
--------
struct uart_ops {
u_int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, u_int mctrl);
u_int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *, u_int from_tty);
void (*start_tx)(struct uart_port *, u_int nonempty, u_int from_tty);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *, struct uart_info *);
void (*shutdown)(struct uart_port *, struct uart_info *);
void (*change_speed)(struct uart_port *, u_int cflag, u_int iflag, u_int quot);
void (*pm)(struct uart_port *, u_int state, u_int oldstate);
int (*set_wake)(struct uart_port *, u_int state);
/*
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *);
/*
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void (*release_port)(struct uart_port *);
/*
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, u_int, u_long);
};
uart_ops结构体是串行核心serial_core和硬件专用驱动之间的主要接口。它包含控制硬件的所有方法。
u_int (*tx_empty)(struct uart_port *port);
该函数将检查参数port所对应的发送队列是否为空。如果为空,该函数应返回TIOCSER_TEMT,否则返回0。如果端口并不支持这一操作,那么他应返回TIOCSER_TEMT。
void (*set_mctrl)(struct uart_port *port, u_int mctrl);
该函数将参数port所对应的调制解调器控制线的值设为参数mctrl的值。mctrl相关的位如下所示:
- TIOCM_RTS RTS 信号.
- TIOCM_DTR DTR 信号.
- TIOCM_OUT1 OUT1 信号.
- TIOCM_OUT2 OUT2 信号.
如果其中某一位被置位,对应的信号将将被置为有效。如果被复位,对应的信号将被值为无效。
u_int (*get_mctrl)(struct uart_port *port);
返回调制解调器控制输入的现有状态。输出的状态则不应被返回,因为核心知道这些状态。这些状态信息包括:
- TIOCM_DCD DCD 信号状态
- TIOCM_CTS CTS 信号状态
- TIOCM_DSR DSR 信号状态
- TIOCM_RI RI 信号状态
如果信号被置为有效,则对应位将被置位。如果端口不支持CTS、DCD或DSR,驱动应指出这些信号将永久置为有效。如果RI不可用,其信号不应置为有效。
void (*stop_tx)(struct uart_port *port, u_int from_tty);
停止传输字符。这有可能因为CTS变成无效或tty层指示因为遇到Xoff字符而停止传输。
驱动应有可能就停止传输。
void (*start_tx)(struct uart_port *port, u_int nonempty, u_int from_tty);
开始传输字符。
void (*stop_rx)(struct uart_port *port);
停止接收字符;端口在关闭的过程中。
void (*enable_ms)(struct uart_port *port);
调制解调器状态中断使能。
void (*break_ctl)(struct uart_port *port, int ctl);
控制暂停信号的传输。如果ctl不为零,中断信号将被传输。当有凭借为零的ctl产生的其他调用时,该信号应终止。
int (*startup)(struct uart_port *port, struct uart_info *info);
抢占所有中断资源,初始化所有下层驱动状态。为接收使能端口。它不能使RTS和DTR有效;通过另外调用的set_mctrl可以做到这一点。
void (*shutdown)(struct uart_port *port, struct uart_info *info);
禁用端口,禁用任何有效果的暂停条件,并且释放所有的中断资源。它不能使RTS和DTR无效;通过另外调用的set_mctrl可以做到这一点。
void (*change_speed)(struct uart_port *port, u_int cflag, u_int iflag, u_int quot);
或set_termios(port,termios,oldtermios)
改变端口参数,包括字长,奇偶校验位,停止位。更新read_status_mask和ignore_status_mask来指示我们在接收时所感兴趣的事件类型。相关的termios->c_cflag位如下所示:
CSIZE - 字长
CSTOPB - 2个停止位
PARENB - 奇偶校验位使能
PARODD - 奇校验位(当PARENB被强制使能时)
CREAD - 字符接收使能(如果没有置位,仍然从端口接收字符,但这些字符都要被丢弃掉)
CRTSCTS - 如果被置位,使能CTS状态改变报告
CLOCAL - 如果没有置位,使能调制解调器状态改变报告
相关的termios->c_iflag bits位如下所示:
INPCK - 使能帧和奇偶校验错误事件,并传输到TTY层
BRKINT
PARMRK - 这两个都能使能暂停事件,并传输到TTY层。
IGNPAR - 忽略奇偶校验和帧错误
IGNBRK - 忽略暂停错误,如果IGNPAR也被置位,则忽略溢出错误。
iflag每一位的交互含义如下所示(以奇偶校验错误为例):
奇偶校验错误 INPCK IGNPAR
None n/a n/a 字符被接收
Yes n/a 0 字符被丢弃
Yes 0 1 字符被接受,并被标示为TTY_NORMAL
Yes 1 1 字符被接受,并被标示为TTY_PARITY
其它flags也会被使用(例如,xon/xoff字符),如果你的硬件支持硬件“软”流控。
void (*pm)(struct uart_port *port, u_int state, u_int oldstate);
执行任何特定端口操作的电源管理。参数state指示新的状态(在ACPI D0-D3中定义的),oldstate指示前一个状态。本质上,D0指全部开,D3指电源关闭。
该函数不能用来抢占任何资源。
该函数会在端口最初打开和最后关闭时调用,除了端口作为系统控制台的情况。这种事情总会发生,即使CONFIG_PM没有被置位。
const char *(*type)(struct uart_port *port);
返回一个描述特定端口的常量string的指针,或当遇到string为“unknown”时返回NULL。
void (*release_port)(struct uart_port *port);
释放任何被端口占用的内存及IO资源。
int (*request_port)(struct uart_port *port);
申请端口所需的内存和IO资源。如果失败,当函数返回时,应该没有任何资源被注册,并且在失败时-EBUSY。
void (*config_port)(struct uart_port *port, int type);
执行所有端口所需的自动配置步骤。‘type’包括所需配置的掩码。UART_CONFIG_TYPE指出端口需要探测和识别。port->type用被置位所发现的类型,或者是如果没有端口被发现,则置位为PORT_UNKNOWN。
UART_CONFIG_IRQ指出中断信号的自动配置,而这需使用标准内核自动探测技术来探测。在平台中固件内置有中断的端口是没有必要的(例如,片上系统实现)。
int (*verify_port)(struct uart_port *port, struct serial_struct *serinfo);
验证新的串行端口信息,包括serinfo是否适合这种端口类型。
int (*ioctl)(struct uart_port *port, u_int cmd, u_long arg);
执行任何特定的IOCTL操作。IOCTL命令必须使用标准编号系统中在
其它函数
---------------
uart_update_timeout(port,cflag,baud)
Update the FIFO drain timeout, port->timeout, according to the
number of bits, parity, stop bits and baud rate.
Locking: caller is expected to take port->lock
Interrupts: n/a
uart_get_baud_rate(port,termios,old,min,max)
Return the numeric baud rate for the specified termios, taking
account of the special 38400 baud "kludge". The B0 baud rate
is mapped to 9600 baud.
If the baud rate is not within min..max, then if old is non-NULL,
the original baud rate will be tried. If that exceeds the
min..max constraint, 9600 baud will be returned. termios will
be updated to the baud rate in use.
Note: min..max must always allow 9600 baud to be selected.
Locking: caller dependent.
Interrupts: n/a
uart_get_divisor(port,baud)
Return the divsor (baud_base / baud) for the specified baud
rate, appropriately rounded.
If 38400 baud and custom divisor is selected, return the
custom divisor instead.
Locking: caller dependent.
Interrupts: n/a
static int uart_match_port(struct uart_port *port1, struct uart_port *port2);
该效用函数用来确定两个uart_port结构体是否描述的是同一个端口。
uart_write_wakeup(port)
A driver is expected to call this function when the number of
characters in the transmit buffer have dropped below a threshold.
Locking: port->lock should be held.
Interrupts: n/a
int uart_register_driver(struct uart_driver *drv);
用核心驱动注册一个串行驱动。我们依次用tty层注册和初始化核心驱动的每个端口状态。
drv->port should be NULL, and the per-port structures should be
registered using uart_add_one_port after this call has succeeded.
Locking: none
Interrupts: enabled
void uart_unregister_driver(struct uart_driver *drv);
在驱动中移除从核心驱动中的所有涉及到的。下层驱动迎移除它所有的端口。
uart_suspend_port()
uart_resume_port()
uart_add_one_port()
uart_remove_one_port()
其它注解
-----------
有可能在某一天会丢弃掉uart_port中的‘unused’项,并允许下层驱动用核心注册它们自己的uart_port。这会允许驱动使用uart_port项,像用指向一个包含uart_port项和扩展项的结构体的指针一样,如此:
struct my_port {
struct uart_port port;
int my_stuff;
};