您的位置:首页 > 运维架构 > Linux

linux serial构架分析及驱动开发(4)

2015-06-05 16:20 591 查看
原文地址:http://blog.csdn.net/sirzjp/article/details/6169984

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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息