在uart_register_driver函数中有这样的一个函数:tty_set_operations(normal,&uart_ops),这个uart_ops就是tty_operations函数集,这里是串口操作的公用函数接口,本节及后面章节将结合串口操作的流程,来介绍上面的操作函数。
static const struct tty_operations uart_ops = { .open = uart_open, .close = uart_close, .write = uart_write, .put_char = uart_put_char, .flush_chars = uart_flush_chars, .write_room = uart_write_room, .chars_in_buffer= uart_chars_in_buffer, .flush_buffer = uart_flush_buffer, .ioctl = uart_ioctl, .throttle = uart_throttle, .unthrottle = uart_unthrottle, .send_xchar = uart_send_xchar, .set_termios = uart_set_termios, .set_ldisc = uart_set_ldisc, .stop = uart_stop, .start = uart_start, .hangup = uart_hangup, .break_ctl = uart_break_ctl, .wait_until_sent= uart_wait_until_sent,#ifdef CONFIG_PROC_FS .proc_fops = &uart_proc_fops,#endif .tiocmget = uart_tiocmget, .tiocmset = uart_tiocmset,#ifdef CONFIG_CONSOLE_POLL .poll_init = uart_poll_init, .poll_get_char = uart_poll_get_char, .poll_put_char = uart_poll_put_char,#endif};
当用户程序调用open函数打开串口设备首先执行tty_open(前面tty分析中已经介绍过),tty_open中tty->ops->open即是串口核心中对应的uart_open函数。
/* * 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){
//先前在uart_register_driver中已经让tty_driver->driver_state指向uart_driver;我们知道tty_driver是用tty_drivers链表来
//管理的,而uart_driver只是tty_driver的一个扩展,因此也是保存在此链表中 struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; struct uart_state *state; 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 info 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 info->port.tty will always contain something reasonable. */
//当串口打开时必然已经调用uart_add_one_port将uart_port和uart_driver绑定,因此这里我们就要分配一个uart_info并作
//相应的初始化来表示一个打开的串口设备uart_get函数就做相应的操作
state = uart_get(drv, line); if (IS_ERR(state)) { retval = PTR_ERR(state); goto fail; }
/* * 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->port->info = &state->info;
//后面是否用工作队列操作的需要 tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; state->info.port.tty = tty;
/* * If the port is in the middle of closing, bail out now. */ if (tty_hung_up_p(filp)) { retval = -EAGAIN; state->count--; mutex_unlock(&state->mutex); goto fail; }
/* * Make sure the device is in D0 state. */ if (state->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(&state->mutex);
/* * If this is the first open to succeed, adjust things to suit. */ if (retval == 0 && !(state->info.flags & UIF_NORMAL_ACTIVE)) { state->info.flags |= UIF_NORMAL_ACTIVE;
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_info *info = &state->info; struct uart_port *port = state->port; unsigned long page; int retval = 0;
if (info->flags & UIF_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, &info->port.tty->flags); //设备未完成初始化好时设置标志,后面初始化好后清除标志
if (port->type == PORT_UNKNOWN) return 0;
/* * Initialise and allocate the transmit and temporary * buffer. */
//为串口分配环形缓存并初始化 if (!info->xmit.buf) { /* This is protected by the per port mutex */ page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM;
info->xmit.buf = (unsigned char *) page; uart_circ_clear(&info->xmit); }
retval = port->ops->startup(port);//启动串口 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 (info->port.tty->termios->c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); }
if (info->flags & UIF_CTS_FLOW) { spin_lock_irq(&port->lock); if (!(port->ops->get_mctrl(port) & TIOCM_CTS)) info->port.tty->hw_stopped = 1; spin_unlock_irq(&port->lock); }
info->flags |= UIF_INITIALIZED;
clear_bit(TTY_IO_ERROR, &info->port.tty->flags); }
if (retval && capable(CAP_SYS_ADMIN)) retval = 0;
return retval;}