关闭

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

341人阅读 评论(0) 收藏 举报
在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;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:168854次
    • 积分:2105
    • 等级:
    • 排名:第18772名
    • 原创:9篇
    • 转载:175篇
    • 译文:1篇
    • 评论:1条
    最新评论