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

    技术2024-06-12  66

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