標籤:
串口驅動是由tty_driver架構實現的。一個應用程式中的函數要操作硬體,首先會經過tty,級級調用之後才會到達驅動之中。本文先介紹應用程式中開啟裝置的open函數的整個曆程。
首先在串口初始化中會先註冊一個串口驅動,函數原型為
int uart_register_driver(struct uart_driver *drv)
在這個函數中會調用註冊tty驅動的函數
int tty_register_driver(struct tty_driver *driver)
{
...
cdev_init(&driver->cdev, &tty_fops);
...
}
從這一句代碼可以看出串口實質上也是一個字元裝置。用soucesight對參數tty_fops進行回溯,可以找出應用程式與tty架構的函數調用關係表file_operations
static const struct file_operations tty_fops = { .llseek = no_llseek, .read = tty_read, .write = tty_write, .poll = tty_poll, .unlocked_ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync,};
可以看出應用程式中的open函數實際上是tty架構中的tty_open函數,查看該函數
static int tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty = NULL;
int noctty, retval;
struct tty_driver *driver;
int index;
dev_t device = inode->i_rdev;
unsigned saved_flags = filp->f_flags;
...
if (tty->ops->open)
...
}
這裡調用到了tty->ops中的open函數,是struct tty_operations類型的,實際上是uart_ops這一結構
static const struct tty_operations uart_ops = { .open = uart_open, ...};
可以看出這裡又調用到了uart_open函數
static int uart_open(struct tty_struct *tty, struct file *filp)
{
...
retval = uart_startup(tty, state, 0);
...
}
static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw){ struct uart_port *uport = state->uart_port; struct tty_port *port = &state->port; unsigned long page; int retval = 0; ... retval = uport->ops->startup(uport); ...}
層層調用之後到這裡,調用到uport結構中的函數,uport為struct uart_port類型,每一個uart_port對應一個串口裝置,也就是說這裡已經調用到了底層驅動的startup函數。在串口初始化時用數組來初始化uart_port
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { [0] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX0, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 0, } }, ...}
函數操作集
static struct uart_ops s3c24xx_serial_ops = { .pm = s3c24xx_serial_pm, .tx_empty = s3c24xx_serial_tx_empty, .get_mctrl = s3c24xx_serial_get_mctrl, .set_mctrl = s3c24xx_serial_set_mctrl, .stop_tx = s3c24xx_serial_stop_tx, .start_tx = s3c24xx_serial_start_tx, .stop_rx = s3c24xx_serial_stop_rx, .enable_ms = s3c24xx_serial_enable_ms, .break_ctl = s3c24xx_serial_break_ctl, .startup = s3c24xx_serial_startup, .shutdown = s3c24xx_serial_shutdown, .set_termios = s3c24xx_serial_set_termios, .type = s3c24xx_serial_type, .release_port = s3c24xx_serial_release_port, .request_port = s3c24xx_serial_request_port, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port,};
所以,retval = uport->ops->startup(uport);這裡最終調用了s3c24xx_serial_startup函數,真相基本上已經浮出水面。應用程式中的open函數通過tty架構,層層調用,最後調用到了samsung.c驅動檔案中的s3c24xx_serial_startup函數。
static int s3c24xx_serial_startup(struct uart_port *port){ struct s3c24xx_uart_port *ourport = to_ourport(port); int ret; dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n", port->mapbase, port->membase); rx_enabled(port) = 1; ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, s3c24xx_serial_portname(port), ourport); if (ret != 0) { printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); return ret; } ourport->rx_claimed = 1; dbg("requesting tx irq...\n"); tx_enabled(port) = 1; ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, s3c24xx_serial_portname(port), ourport); if (ret) { printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); goto err; } ourport->tx_claimed = 1; dbg("s3c24xx_serial_startup ok\n"); /* the port reset code should have done the correct * register setup for the port controls */ return ret; err: s3c24xx_serial_shutdown(port); return ret;
這個函數主要做了四件事情,代碼已高亮標出:
1、開啟接收使能
2、註冊資料接收中斷
3、開啟發送使能
4、註冊資料發送中斷
至此,linux串口驅動程式開啟裝置的實現已分析完畢。如果有疑問或建議,歡迎指出。
linux串口驅動分析——開啟裝置