/*
* uart 打开分析
*
* 问题: 当应用程序调用open系统调用函数,那么是如何open串口的?
*
* */
/*Samsung.c 函数的模块入口函数
*它是一个公用的借口,不管2440/6410都会调用该函数
* */
static int __init s3c24xx_serial_modinit(void)
{
int ret;
ret = uart_register_driver(&s3c24xx_uart_drv);
if (ret < 0) {
printk(KERN_ERR "failed to register UART driver\n");
return -1;
}
return 0;
}
int uart_register_driver(struct uart_driver *drv)
{
/*无关的代码忽略**/
struct tty_driver *normal;
normal = alloc_tty_driver(drv->nr);
if (!normal)
goto out_kfree;
/*把normal 赋值给 uart驱动的tty_driver*/
drv->tty_driver = normal;
/*设置normal的operations*/
tty_set_operations(normal, &uart_ops);
/*注册tty驱动*/
tty_register_driver(normal);
}
int tty_register_driver(struct tty_driver *driver)
{
/*分配主设备号*/
if (!driver->major) {
error = alloc_chrdev_region(&dev, driver->minor_start,
driver->num, driver->name);
if (!error) {
driver->major = MAJOR(dev);
driver->minor_start = MINOR(dev);
}
} else {
dev = MKDEV(driver->major, driver->minor_start);
error = register_chrdev_region(dev, driver->num, driver->name);
/*初始化字符设备,注册字符设备*/
cdev_init(&driver->cdev, &tty_fops);
driver->cdev.owner = driver->owner;
error = cdev_add(&driver->cdev, dev, driver->num);
}
/*
* 当应用程序调用open系统调用后,sys_open就会调用字符驱动的file_operations中的open函数
* 也就是tty_fops中的open函数
* */
static int tty_open(struct inode *inode, struct file *filp)
{
/*首先判断打开的设备是否是:
*MAJOR = 5, MINOR = 0
*MAJOR = 5, MINOR = 1
*MAJOR = 4, MINOR = 0
* 其实打开的是2440_Serial0设备
*
* MAJOR = 204 MINOR = 64
* */
if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {
tty_unlock();
mutex_unlock(&tty_mutex);
return -ENXIO;
}
driver = tty_driver_kref_get(tty->driver);
index = tty->index;
filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
/* noctty = 1; */
/* FIXME: Should we take a driver reference ? */
tty_kref_put(tty);
goto got_driver;
}
#ifdef CONFIG_VT
if (device == MKDEV(TTY_MAJOR, 0)) {
extern struct tty_driver *console_driver;
driver = tty_driver_kref_get(console_driver);
index = fg_console;
noctty = 1;
goto got_driver;
}
#endif
if (device == MKDEV(TTYAUX_MAJOR, 1)) {
struct tty_driver *console_driver = console_device(&index);
if (console_driver) {
driver = tty_driver_kref_get(console_driver);
if (driver) {
/* Don't let /dev/console block */
filp->f_flags |= O_NONBLOCK;
noctty = 1;
goto got_driver;
}
}
tty_unlock();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
/*当上面的判断不正确时,就执行这句*/
driver = get_tty_driver(device, &index);
if (!driver) {
tty_unlock();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
/*如果tty存在,不存在则重新初始化一个tty_struct的结构*/
if (tty) {
retval = tty_reopen(tty);
if (retval)
tty = ERR_PTR(retval);
} else
/*该函数主要是分配tty,然后初始化tty.
* 接着将tty_struct存放到tty_driver的tty_struct[]数组中
* 根据tty_struct->index*/
tty = tty_init_dev(driver, index, 0);
/*它接着会调用tty->ops->open。 其实这个open函数就是
* uart_opsz中的uart_open函数
*
* */
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
}
/* 通过device设备号来找到设备对应的tty_driver.并且将索引保存到index中
*
* **/
static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
struct tty_driver *p;
list_for_each_entry(p, &tty_drivers, tty_drivers) {
dev_t base = MKDEV(p->major, p->minor_start);
if (device < base || device >= base + p->num)
continue;
*index = device - base;
return tty_driver_kref_get(p);
}
return NULL;
}
/*初始化tty中的重要的几条语句
*
* 1. 将tty和线路规程联系在一起.其中通过索引号N_TTY。将来
* tty_struct中的ldisc成员将为tty_ldisc_N_TTY
* 2. 将tty_struct中的ops设置为tty_driver中的ops。而tty_driver中的ops
* 是通过tty_set_operations(normal, &uart_ops);设置进去的。 也就是uart_ops
* */
void initialize_tty_struct(struct tty_struct *tty,
struct tty_driver *driver, int idx)
{
tty_ldisc_init(tty);
tty->driver = driver;
tty->ops = driver->ops;
tty->index = idx;
}
/*
* 找到保存在tty_driver中的uart_state.
* 其实uart_state就是在初始化保存进uart_driver中
*
* 然后调用uart_start初始化serial port
*
* */
static int uart_open(struct tty_struct *tty, struct file *filp)
{
/*
* We take the semaphore inside uart_get to guarantee that we won't
* be re-entered while allocating the state structure, or while we
* request any IRQs that the driver may need. This also has the nice
* side-effect that it delays the action of uart_hangup, so we can
* guarantee that state->port.tty will always contain something
* reasonable.
*/
state = uart_get(drv, line);
if (IS_ERR(state)) {
retval = PTR_ERR(state);
goto fail;
}
port = &state->port;
/*
* Once we set tty->driver_data here, we are guaranteed that
* uart_close() will decrement the driver module use count.
* Any failures from here onwards should not touch the count.
*/
tty->driver_data = state;
state->uart_port->state = state;
tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 0;
tty_port_tty_set(port, tty);
/*
* Start up the serial port.
*/
retval = uart_startup(tty, state, 0);
}
static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)
{
/*其中最重要的一句就是调用ops->startup. 也就是初始化uart_port中的ops. 也就是s3c24xx_serial_ops*/
uport->ops->startup(uport);
}
/*
* 此函数就是去打开rx和tx的中断,然后使能中断
* 然后就是一直等待中断的到来。然后跳到中断处理程序去处理中断
*/
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;
}
总结:
open的主要作用是 在内核通过创建并初始化一个tty_struct来描述具体对应的一个硬件设备,比如这里就是用一个tty_struct来描述s3c24xx上的uart0的,然后找到uart_port
中ops的startup方法初始化uart的硬件。
具体的tty_struct初始化过程中最重要的几步如下
1.初始化tty-struct的ops,就是将tty_driver中的ops赋值给tty_struct
2.初始化tty线路规程操作集
3.初始化tty_struct中的uart_state,uart_state中包含uart_port信息,这一步通过步骤1中ops中的open方法来完成。
4.根据步骤3中找到的uart_state,找到里面的uart_port的ops中的startup方法来初始化uart硬件