LINUX
虽然UART驱动程序也是字符驱动程序,但它们并不像之前的通常的字符驱动程序那样直接暴露给内核系统调用。相反,UART驱动程序提供服务给另一个内核层:tty层。I/O系统调用的旅程首先从顶层的线路规程开始,通过tty层,最后到达UART驱动程序层。
这三个数据结构都定义于include/linux/serial_core.h中。
1. 特定UART相关的驱动程序结构 struct uart_driver :
struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
struct tty_driver *tty_driver;
};
结构体中每个域的注释解释了其作用。owner域的作用和前面章节中讨论的file_operations结构中owner域相同
2. Struct uart_port
uart_port结构。UART驱动拥有的每个端口,都存在uart_port结构的一个实例。
struct uart_port {
spinlock_t lock;
unsigned int iobase;
unsigned char __iomem *membase;
unsigned int irq;
unsigned int uartclk;
unsigned char fifosize;
unsigned char x_char;
};
3. uart_ops结构。这个结构是每个UART驱动必须支持的物理硬件上可完成的操作的入口函数的集合
struct uart_ops {
uint (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *,
unsigned int mctrl);
uint (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *);
void (*start_tx)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*set_termios)(struct uart_port *,
struct termios *new,
struct termios *old);
void (*config_port)(struct uart_port *,
int);
};
1. 通过调用uart_register_driver(struct uart_driver *)向串口核心层注册。
2. 调用uart_add_one_port(struct uart_driver *, struct uart_port *)注册其支持的每个端口。如果你的串口硬件支持热插拔,探测到设备存在后,从入口点向内核注册。第10章“PCI”的清单10.4中的CardBus Modem驱动,就是串口设备热插拔的例子。需要注意的是一些驱动使用封装的注册函数serial8250_register_port(struct uart_port *),在其内部调用了uart_add_one_port()。
初始化过程中,USB_UART驱动程序首先用uart_register_driver()向串行核心注册自身。
2. platform驱动。platform驱动使用platform_driver_register()将自身注册进平台中。结构体platform_driver亦定义于include/linux/platform_device.h中,代表了platform驱动:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
struct device_driver driver;
};
关于platform设备和platform驱动更详细的文档可参考Documentation/driver-model/platform.txt。为了讨论简单,我们的例子驱动注册了platform设备和platform驱动。
在初始化过程中,首先USB_UART驱动用uart_register_driver()向串口核心层注册自身。初始化成功后,在/proc/tty/drivers中你将会发现以usb_uart开始的新行。其次,驱动用platform_device_register_simple()注册两个platform设备(每个USB_UART一个)。正如前面提到的,platform设备通常在电路板引导过程中注册。随后,驱动用platform_driver_register()注册platform驱动入口点(probe(), remove(),suspend(), and resume())。USB_UARTplatform驱动和前面的platform设备联系在一起,并有一个相应的名字(usb_uart)。在以上步骤完成后,你将会发现在sysfs下出现两个新目录,每一个和相应的USB_UART端口相对应:/sys/devices/platform/usb_uart.0/ 和 /sys/devices/platform/usb_uart.1/
因为Linux设备层开始检测到和注册的USB_UART platform设备相匹配的platform驱动,它调用属于platform驱动的probe()入口点[](usb_uart_probe()),每个USB_UART一次。probe入口点用uart_add_one_port()添加相应的USB_UART端口。以上步骤会触发config_port()入口点(前面讨论的uart_ops结构的一部分)的调用:声明并映射USB_UART寄存器空间。
直到某个应用程序打开USB_UART端口,才会占用中断号。当应用程序关闭USB_UART端口时,中断号被释放。下表表示了驱动程序代码中声明和释放内存区域与中断号的整个过程。
Table 6.2. Claiming and Freeing Memory and IRQ Resources |
||||||
Module Insert模块插入 |
usb_uart_init() |
uart_register_driver() |
usb_uart_probe() |
uart_add_one_port() |
usb_uart_config_port() |
request_mem_region() |
Module Unload模块卸载 |
usb_uart_exit() |
usb_unregister_driver() |
usb_uart_remove() |
uart_remove_one_port() |
usb_uart_release_port() |
release_mem_region() |
打开Open/dev/ttyUUX |
usb_uart_startup() |
request_irq() |
|
|
|
|
关闭Close/dev/ttyUUX |
usb_uart_shutdown() |
free_irq() |
|
|
|
|
在发送过程中,驱动收集和UART端口相关的循环缓冲区中的待发送数据。在UART驱动的start_tx()的入口函数usb_uart_start_tx()可以看到,数据存放于port->info->xmit.buf[port->info->xmit.tail]。
在接收过程中,驱动使用tty_insert_flip_char()和tty_flip_buffer_push()将从USB_UART收到的数据推出至tty驱动。这些在收中断处理例程usb_uart_rxint()中完成的。读者可在读完下一节“TTY驱动”后,再重读此例程。
Code View:
#include
#include
#include
#include
#include
#include
#include
#include
#define USB_UART_MAJOR 200 /* You’ve to get this assigned */
#define USB_UART_MINOR_START 70 /* Start minor numbering here */
#define USB_UART_PORTS 2 /* The phone has 2 USB_UARTs */
#define PORT_USB_UART 30 /* UART type. Add this to
include/linux/serial_core.h */
/* Each USB_UART has a 3-byte register set consisting of
UU_STATUS_REGISTER at offset 0, UU_READ_DATA_REGISTER at
offset 1, and UU_WRITE_DATA_REGISTER at offset 2 as shown
in Table 6.1 */
#define USB_UART1_BASE 0xe8000000 /* Memory base for USB_UART1 */
#define USB_UART2_BASE 0xe9000000 /* Memory base for USB_UART2 */
#define USB_UART_REGISTER_SPACE 0×3
/* Semantics of bits in the status register */
#define USB_UART_TX_FULL 0×20 /* TX FIFO is full */
#define USB_UART_RX_EMPTY 0×10 /* TX FIFO is empty */
#define USB_UART_STATUS 0x0F /* Parity/frame/overruns? */
#define USB_UART1_IRQ 3 /* USB_UART1 IRQ */
#define USB_UART2_IRQ 4 /* USB_UART2 IRQ */
#define USB_UART_FIFO_SIZE 32 /* FIFO size */
#define USB_UART_CLK_FREQ 16000000
static struct uart_port usb_uart_port[]; /* Defined later on */
/* Write a character to the USB_UART port */
static void
usb_uart_putc(struct uart_port *port, unsigned char c)
{
/* Wait until there is space in the TX FIFO of the USB_UART.
Sense this by looking at the USB_UART_TX_FULL bit in the
status register */
while (__raw_readb(port->membase) & USB_UART_TX_FULL);
/* Write the character to the data port*/
__raw_writeb(c, (port->membase+1));
}
/* Read a character from the USB_UART */
static unsigned char
usb_uart_getc(struct uart_port *port)
{
/* Wait until data is available in the RX_FIFO */
while (__raw_readb(port->membase) & USB_UART_RX_EMPTY);
/* Obtain the data */
return(__raw_readb(port->membase+2));
}
/* Obtain USB_UART status */
static unsigned char
usb_uart_status(struct uart_port *port)
{
return(__raw_readb(port->membase) & USB_UART_STATUS);
}
/*
* Claim the memory region attached to USB_UART port. Called
* when the driver adds a USB_UART port via uart_add_one_port().
*/
static int
usb_uart_request_port(struct uart_port *port)
{
if (!request_mem_region(port->mapbase, USB_UART_REGISTER_SPACE,
"usb_uart")) {
return -EBUSY;
}
return 0;
}
/* Release the memory region attached to a USB_UART port.
* Called when the driver removes a USB_UART port via
* uart_remove_one_port().
*/
static void
usb_uart_release_port(struct uart_port *port)
{
release_mem_region(port->mapbase, USB_UART_REGISTER_SPACE);
}
/* * Configure USB_UART. Called when the driver adds a USB_UART port.
*/
static void
usb_uart_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE && usb_uart_request_port(port) == 0)
{
port->type = PORT_USB_UART;
}
}
/* Receive interrupt handler */
static irqreturn_t
usb_uart_rxint(int irq, void *dev_id)
{
struct uart_port *port = (struct uart_port *) dev_id;
struct tty_struct *tty = port->info->tty;
unsigned int status, data;
/* … */
do {
/* … */
/* Read data */
data = usb_uart_getc(port);
/* Normal, overrun, parity, frame error? */
status = usb_uart_status(port);
/* Dispatch to the tty layer */
tty_insert_flip_char(tty, data, status);
/* … */
} while (more_chars_to_be_read()); /* More chars */
/* … */
tty_flip_buffer_push(tty);
return IRQ_HANDLED;
}
/* Called when an application opens a USB_UART */
static int
usb_uart_startup(struct uart_port *port)
{
int retval = 0;
/* … */
/* Request IRQ */
if ((retval = request_irq(port->irq, usb_uart_rxint, 0,
"usb_uart", (void *)port))) {
return retval;
}
/* … */
return retval;
}
/* Called when an application closes a USB_UART */
static void
usb_uart_shutdown(struct uart_port *port)
{
/* … */
/* Free IRQ */
free_irq(port->irq, port);
/* Disable interrupts by writing to appropriate
registers */
/* … */
}
/* Set UART type to USB_UART */
static const char *
usb_uart_type(struct uart_port *port)
{
return port->type == PORT_USB_UART ? "USB_UART" : NULL;
}
/* Start transmitting bytes */
static void
usb_uart_start_tx(struct uart_port *port)
{
while (1) {
/* Get the data from the UART circular buffer and
write it to the USB_UART’s WRITE_DATA register */
usb_uart_putc(port,
port->info->xmit.buf[port->info->xmit.tail]);
/* Adjust the tail of the UART buffer */
port->info->xmit.tail = (port->info->xmit.tail + 1) &
(UART_XMIT_SIZE – 1);
/* Statistics */
port->icount.tx++;
/* Finish if no more data available in the UART buffer */
if (uart_circ_empty(&port->info->xmit)) break;
}
/* … */
}
/* The UART operations structure */
static struct uart_ops usb_uart_ops = {
.start_tx = usb_uart_start_tx, /* Start transmitting */
.startup = usb_uart_startup, /* App opens USB_UART */
.shutdown = usb_uart_shutdown, /* App closes USB_UART */
.type = usb_uart_type, /* Set UART type */
.config_port = usb_uart_config_port, /* Configure when driver
adds a USB_UART port */
.request_port = usb_uart_request_port,/* Claim resources
associated with a
USB_UART port */
.release_port = usb_uart_release_port,/* Release resources
associated with a
USB_UART port */
#if 0 /* Left unimplemented for the USB_UART */
.tx_empty = usb_uart_tx_empty, /* Transmitter busy? */
.set_mctrl = usb_uart_set_mctrl, /* Set modem control */
.get_mctrl = usb_uart_get_mctrl, /* Get modem control
.stop_tx = usb_uart_stop_tx, /* Stop transmission */
.stop_rx = usb_uart_stop_rx, /* Stop reception */
.enable_ms = usb_uart_enable_ms, /* Enable modem status
signals */
.set_termios = usb_uart_set_termios, /* Set termios */
#endif
};
static struct uart_driver usb_uart_reg = {
.owner = THIS_MODULE, /* Owner */
.driver_name = "usb_uart", /* Driver name */
.dev_name = "ttyUU", /* Node name */
.major = USB_UART_MAJOR, /* Major number */
.minor = USB_UART_MINOR_START, /* Minor number start */
.nr = USB_UART_PORTS, /* Number of UART ports */
.cons = &usb_uart_console, /* Pointer to the console
structure. Discussed in Chapter
12, "Video Drivers" */
};
/* Called when the platform driver is unregistered */
static int
usb_uart_remove(struct platform_device *dev)
{
platform_set_drvdata(dev, NULL);
/* Remove the USB_UART port from the serial core */
uart_remove_one_port(&usb_uart_reg, &usb_uart_port[dev->id]);
return 0;
}
/* Suspend power management event */
static int
usb_uart_suspend(struct platform_device *dev, pm_message_t state)
{
uart_suspend_port(&usb_uart_reg, &usb_uart_port[dev->id]);
return 0;
}
/* Resume after a previous suspend */
static int
usb_uart_resume(struct platform_device *dev)
{
uart_resume_port(&usb_uart_reg, &usb_uart_port[dev->id]);
return 0;
}
/* Parameters of each supported USB_UART port */
static struct uart_port usb_uart_port[] = {
{
.mapbase = (unsigned int) USB_UART1_BASE,
.iotype = UPIO_MEM, /* Memory mapped */
.irq = USB_UART1_IRQ, /* IRQ */
.uartclk = USB_UART_CLK_FREQ, /* Clock HZ */
.fifosize = USB_UART_FIFO_SIZE, /* Size of the FIFO */
.ops = &usb_uart_ops, /* UART operations */
.flags = UPF_BOOT_AUTOCONF, /* UART port flag */
.line = 0, /* UART port number */
},
{
.mapbase = (unsigned int)USB_UART2_BASE,
.iotype = UPIO_MEM, /* Memory mapped */
.irq = USB_UART2_IRQ, /* IRQ */
.uartclk = USB_UART_CLK_FREQ, /* CLock HZ */
.fifosize = USB_UART_FIFO_SIZE, /* Size of the FIFO */
.ops = &usb_uart_ops, /* UART operations */
.flags = UPF_BOOT_AUTOCONF, /* UART port flag */
.line = 1, /* UART port number */
}
};
/* Platform driver probe */
static int __init
usb_uart_probe(struct platform_device *dev)
{
/* … */
/* Add a USB_UART port. This function also registers this device
with the tty layer and triggers invocation of the config_port()
entry point */
uart_add_one_port(&usb_uart_reg, &usb_uart_port[dev->id]);
platform_set_drvdata(dev, &usb_uart_port[dev->id]);
return 0;
}
struct platform_device *usb_uart_plat_device1; /* Platform device
for USB_UART 1 */
struct platform_device *usb_uart_plat_device2; /* Platform device
for USB_UART 2 */
static struct platform_driver usb_uart_driver = {
.probe = usb_uart_probe, /* Probe method */
.remove = __exit_p(usb_uart_remove), /* Detach method */
.suspend = usb_uart_suspend, /* Power suspend */
.resume = usb_uart_resume, /* Resume after a suspend */
.driver = {
.name = "usb_uart", /* Driver name */
},
};
/* Driver Initialization */
static int __init
usb_uart_init(void)
{
int retval;
/* Register the USB_UART driver with the serial core */
if ((retval = uart_register_driver(&usb_uart_reg))) {
return retval;
}
/* Register platform device for USB_UART 1. Usually called
during architecture-specific setup */
usb_uart_plat_device1 =
platform_device_register_simple("usb_uart", 0, NULL, 0);
if (IS_ERR(usb_uart_plat_device1)) {
uart_unregister_driver(&usb_uart_reg);
return PTR_ERR(usb_uart_plat_device1);
}
/* Register platform device for USB_UART 2. Usually called
during architecture-specific setup */
usb_uart_plat_device2 =
platform_device_register_simple("usb_uart", 1, NULL, 0);
if (IS_ERR(usb_uart_plat_device2)) {
uart_unregister_driver(&usb_uart_reg);
platform_device_unregister(usb_uart_plat_device1);
return PTR_ERR(usb_uart_plat_device2);
}
/* Announce a matching driver for the platform
devices registered above */
if ((retval = platform_driver_register(&usb_uart_driver))) {
uart_unregister_driver(&usb_uart_reg);
platform_device_unregister(usb_uart_plat_device1);
platform_device_unregister(usb_uart_plat_device2);
}
return 0;
}
/* Driver Exit */
static void __exit
usb_uart_exit(void)
{
/* The order of unregistration is important. Unregistering the
UART driver before the platform driver will crash the system */
/* Unregister the platform driver */
platform_driver_unregister(&usb_uart_driver);
/* Unregister the platform devices */
platform_device_unregister(usb_uart_plat_device1);
platform_device_unregister(usb_uart_plat_device2);
/* Unregister the USB_UART driver */
uart_unregister_driver(&usb_uart_reg);
}
module_init(usb_uart_init);
module_exit(usb_uart_exit);