在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;
}