ARM-Linux S5PV210 UART驱动(5)----串口的open操作(tty_open、uart_open)
串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了。
那接下来uart的操作是如何进行的呢?
操作硬件之前都是要先open设备,先来分析下这里的open函数具体做了那些工作。
s3c24xx_serial_modinit -->uart_register_driver -->tty_register_driver 中有如下语句:
cdev_init(&driver->cdev, &tty_fops);
此处将 driver->cdev->ops=&tty_fops
而tty_fops如下:
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系统调用open(“/dev/s3c2410_serial0”,)一层一层调用到会调用到tty_open。
/** * tty_open - open a tty device * @inode: inode of device file * @filp: file pointer to tty * * tty_open and tty_release keep up the tty count that contains the * number of opens done on a tty. We cannot use the inode-count, as * different inodes might point to the same tty. * * Open-counting is needed for pty masters, as well as for keeping * track of serial lines: DTR is dropped when the last close happens. * (This is not done solely through tty->count, now. - Ted 1/27/92) * * The termios state of a pty is reset on first open so that * settings don't persist across reuse. * * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand */ 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; nonseekable_open(inode, filp);//通知内核设备不支持 llseek retry_open: noctty = filp->f_flags & O_NOCTTY; index = -1; retval = 0; mutex_lock(&tty_mutex); lock_kernel(); if (device == MKDEV(TTYAUX_MAJOR, 0)) {//判断打开的设备是否是5 0(/dev/tty) tty = get_current_tty(); if (!tty) { unlock_kernel(); 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)) {// 4 0(/dev/tty0) 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)) {//5 1(/dev/console) 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; } } unlock_kernel(); mutex_unlock(&tty_mutex); return -ENODEV; } /********************若都没有,则执行下面这句******************************/ /* 此函数的作用就是通过设备号来找到设备对应的tty_driver,并且将索引号码保存在index中 因为一个tty_driver对应的是所有此种类型的tty设备,比如所有的串口设备,所以需要通过这个索引号 index来判断打开的是具体哪个设备。并且每个具体的设备对应着一个用来描述自己的tty_struct。 而系统后面的操作全部和这个tty_struct相关。 */ driver = get_tty_driver(device, &index); if (!driver) { unlock_kernel(); mutex_unlock(&tty_mutex); return -ENODEV; } got_driver: if (!tty) { /* check whether we're reopening an existing tty */ tty = tty_driver_lookup_tty(driver, inode, index); if (IS_ERR(tty)) { unlock_kernel(); mutex_unlock(&tty_mutex); return PTR_ERR(tty); } } if (tty) { retval = tty_reopen(tty);//判断是否有tty_struct if (retval) tty = ERR_PTR(retval); } else tty = tty_init_dev(driver, index, 0);//不存在则创建并初始化一个tty_struct mutex_unlock(&tty_mutex); tty_driver_kref_put(driver); if (IS_ERR(tty)) { unlock_kernel(); return PTR_ERR(tty); } filp->private_data = tty; file_move(filp, &tty->tty_files); check_tty_count(tty, "tty_open"); if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) noctty = 1; #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "opening %s...", tty->name); #endif if (!retval) { if (tty->ops->open) /********************************************************/ /*调用uart_open*/ retval = tty->ops->open(tty, filp); // ===============>>>>>>>>> /********************************************************/ else retval = -ENODEV; } filp->f_flags = saved_flags; if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) retval = -EBUSY; if (retval) { #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "error %d in opening %s...", retval, tty->name); #endif tty_release(inode, filp); if (retval != -ERESTARTSYS) { unlock_kernel(); return retval; } if (signal_pending(current)) { unlock_kernel(); return retval; } schedule(); /* * Need to reset f_op in case a hangup happened. */ if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; unlock_kernel(); goto retry_open; } unlock_kernel(); mutex_lock(&tty_mutex); lock_kernel(); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && !current->signal->tty && tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); unlock_kernel(); mutex_unlock(&tty_mutex); return 0; }
/* * calls to uart_open are serialised by the BKL in * fs/char_dev.c:chrdev_open() * Note that if this fails, then uart_close() _will_ be called. * * In time, we want to scrap the "opening nonpresent ports" * behaviour and implement an alternative way for setserial * to set base addresses/ports/types. This will allow us to * get rid of a certain amount of extra tests. */ static int uart_open(struct tty_struct *tty, struct file *filp) { struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; struct uart_state *state; struct tty_port *port; int retval, line = tty->index; BUG_ON(!kernel_locked()); pr_debug("uart_open(%d) called\n", line); /* * tty->driver->num won't change, so we won't fail here with * tty->driver_data set to something non-NULL (and therefore * we won't get caught by uart_close()). */ retval = -ENODEV; if (line >= tty->driver->num) goto fail; /* * 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. */ /* 找到保存在tty_driver中的uart_state。 最后将其值赋值给tty_struct。此处特别注意一下,这个uart_state会被放到tty_struct的driver_data中的! 因为后面的write、read都是从driver_data中找到这个uart_state的! */ 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); /* * If the port is in the middle of closing, bail out now. */ if (tty_hung_up_p(filp)) { retval = -EAGAIN; port->count--; mutex_unlock(&port->mutex); goto fail; } /* * Make sure the device is in D0 state. */ if (port->count == 1) uart_change_pm(state, 0); /* * Start up the serial port. */ retval = uart_startup(state, 0);//初始化串口硬件 ==========>>>>>>>>>>>>>>> /* * If we succeeded, wait until the port is ready. */ if (retval == 0) retval = uart_block_til_ready(filp, state); mutex_unlock(&port->mutex); /* * If this is the first open to succeed, adjust things to suit. */ if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) { set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); uart_update_termios(state); } fail: return retval; }
/* * Startup the port. This will be called once per open. All calls * will be serialised by the per-port mutex. */ static int uart_startup(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; if (port->flags & ASYNC_INITIALIZED) return 0; /* * Set the TTY IO error marker - we will only clear this * once we have successfully opened the port. Also set * up the tty->alt_speed kludge */ set_bit(TTY_IO_ERROR, &port->tty->flags); if (uport->type == PORT_UNKNOWN) return 0; /* * Initialise and allocate the transmit and temporary * buffer. */ if (!state->xmit.buf) { /* This is protected by the per port mutex */ page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; state->xmit.buf = (unsigned char *) page; uart_circ_clear(&state->xmit); } retval = uport->ops->startup(uport); ==========>>>>>>>>>调用s3c24xx_serial_startup() if (retval == 0) { if (init_hw) { /* * Initialise the hardware port settings. */ uart_change_speed(state, NULL); /* * Setup the RTS and DTR signals once the * port is open and ready to respond. */ if (port->tty->termios->c_cflag & CBAUD) uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } if (port->flags & ASYNC_CTS_FLOW) { spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) port->tty->hw_stopped = 1; spin_unlock_irq(&uport->lock); } set_bit(ASYNCB_INITIALIZED, &port->flags); clear_bit(TTY_IO_ERROR, &port->tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) retval = 0; return retval; }
至此,通过open函数后s3c24xx的uart硬件部分也初始化好了,接着可以通过write、read函数收发数据了。
《《《《=============总结===============》》》》
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硬件。
open的流程大致如下:
open
--tty_open
|
--get_tty_driver
--tty_init_dev
--tty->ops->open(uart_open)
|
--uart_startup
|
--uport->ops->startup(s3c24xx_serial_startup())
|
--request_irq(rx_irq)
--request_irq(tx_irq)
参考:http://blog.csdn.net/rockrockwu/article/details/7897283