串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/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); // ===============>>>>>>>>> /********************************************************/ 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(&current->sighand->siglock); if (!noctty && current->signal->leader && !current->signal->tty && tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(&current->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的流程大致如下:

--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