Uart驱动小结

Uart驱动小结

前段时间由于工作需要,接触了下ATMEL 的sam d20g18, cortex-m0,用到了片子上的i2c和uart,使用SDK,对一些低层的细节可以考虑的少一点,开发进度也快了不少;由于正在看Linux,所以顺便也看了下Linux的tty框架,记录下来打个标记。

Uart驱动是紧紧围绕数据结构tty_driver的。

一.UART字符设备

1.1UART字符设备操作函数

Tty设备也算是字符设备,设备的操作函数在驱动加载的时候被注册,具体的操作函数如下:

static const struct file_operations tty_fops = {

         .llseek                = no_llseek,

         .read                  = tty_read,

         .write                 = tty_write,

         .poll           = tty_poll,

         .unlocked_ioctl         = tty_ioctl,

         .compat_ioctl  = tty_compat_ioctl,

         .open                 = tty_open,

         .release   = tty_release,

         .fasync               = tty_fasync,

};

在以上的函数中有一个重要的数据结构tty_struct

struct tty_struct {

         int    magic;

         structkref kref;

         structdevice *dev;

         structtty_driver *driver;

         conststruct tty_operations *ops;

         structtty_ldisc *ldisc;

……

struct tty_port*port;

}

该结构是在使用设备打开时候被初始化分配的

 

static inttty_open(struct inode *inode, struct file *filp)

{

         struct tty_struct *tty;

         int noctty, retval;

         dev_t device = inode->i_rdev;

         unsigned saved_flags =filp->f_flags;

 

         nonseekable_open(inode, filp);

 

retry_open:

         retval = tty_alloc_file(filp);  //分配file->private_data=tty_file_private

         if (retval)

                   return -ENOMEM;

 

         tty = tty_open_current_tty(device,filp);

         if (!tty)

                   tty =tty_open_by_driver(device, inode, filp);

//内部有1.tty_lookup_driver根据设备节点,在链表上取得tty_driver 2.初始化tty的函数tty = tty_init_dev(driver, index);

 

         if (IS_ERR(tty)) {

                   tty_free_file(filp);

                   retval = PTR_ERR(tty);

                   if (retval != -EAGAIN ||signal_pending(current))

                            return retval;

                   schedule();

                   goto retry_open;

         }

 

         tty_add_file(tty, filp); //赋值,file->private_data->tty= tty;

 

         check_tty_count(tty, __func__);

         tty_debug_hangup(tty, "opening(count=%d)\n", tty->count);

 

         if (tty->ops->open)

                   retval =tty->ops->open(tty, filp);

         else

                   retval = -ENODEV;

         filp->f_flags = saved_flags;

 

         if (retval) {

                   tty_debug_hangup(tty,"open error %d, releasing\n", retval);

 

                   tty_unlock(tty); /* need tocall tty_release without BTM */

                   tty_release(inode, filp);

                   if (retval != -ERESTARTSYS)

                            return retval;

 

                   if (signal_pending(current))

                            return retval;

 

                   schedule();

                   /*

                    * Need to reset f_op in case a hanguphappened.

                    */

                   if (tty_hung_up_p(filp))

                            filp->f_op =&tty_fops;

                   goto retry_open;

         }

         clear_bit(TTY_HUPPED,&tty->flags);

 

         noctty = (filp->f_flags & O_NOCTTY)||

                    (IS_ENABLED(CONFIG_VT) && device ==MKDEV(TTY_MAJOR, 0)) ||

                    device == MKDEV(TTYAUX_MAJOR, 1) ||

                    (tty->driver->type ==TTY_DRIVER_TYPE_PTY &&

                     tty->driver->subtype ==PTY_TYPE_MASTER);

         if (!noctty)

                   tty_open_proc_set_tty(filp,tty);

         tty_unlock(tty);

         return 0;

}

1.2 tty_struct的初始化

structtty_struct *tty_init_dev(struct tty_driver *driver, int idx)

{

         struct tty_struct *tty;

         int retval;

         tty = alloc_tty_struct(driver, idx);//初始化tty_struct

         if (!tty) {

                   retval = -ENOMEM;

                   goto err_module_put;

         }

……

         tty_lock(tty);

         retval = tty_driver_install_tty(driver,tty);

         if (retval < 0)

                   goto err_free_tty;

 

         if (!tty->port)

                   tty->port =driver->ports[idx];//将driver定义的port和tty_struct的port联系起来,tty_port结构中有串口的收发数据的缓存

……

         tty->port->itty = tty;

 

}

 

structtty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)

{

         struct tty_struct *tty;

 

         tty = kzalloc(sizeof(*tty),GFP_KERNEL);

         if (!tty)

                   return NULL;

 

         kref_init(&tty->kref);

         tty->magic = TTY_MAGIC;

         tty_ldisc_init(tty);//初始化线路规程,/////////////////////////////////////

         tty->session = NULL;

         tty->pgrp = NULL;

         mutex_init(&tty->legacy_mutex);

         mutex_init(&tty->throttle_mutex);

         init_rwsem(&tty->termios_rwsem);

         mutex_init(&tty->winsize_mutex);

         init_ldsem(&tty->ldisc_sem);

         init_waitqueue_head(&tty->write_wait);

         init_waitqueue_head(&tty->read_wait);

         INIT_WORK(&tty->hangup_work,do_tty_hangup);

         mutex_init(&tty->atomic_write_lock);

         spin_lock_init(&tty->ctrl_lock);

         spin_lock_init(&tty->flow_lock);

         spin_lock_init(&tty->files_lock);

         INIT_LIST_HEAD(&tty->tty_files);

         INIT_WORK(&tty->SAK_work,do_SAK_work);

 

         tty->driver = driver;

         tty->ops = driver->ops;  // struct tty_operations *ops;

 //即为在intuart_register_driver(struct uart_driver *drv)初始化的static const struct tty_operations uart_ops

         tty->index = idx;

         tty_line_name(driver, idx,tty->name);

         tty->dev = tty_get_device(tty);

 

         return tty;

}

 void tty_ldisc_init(struct tty_struct *tty)

 {
       struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
       if (IS_ERR(ld))
        panic("n_tty: init_tty");
        tty->ldisc = ld;
}

1.3写数据 

ssize_t tty_write(struct file *file, const char __user *buf,  size_t count, loff_t *ppos)

{

        struct tty_struct *tty = file_tty(file);

        ld = tty_ldisc_ref_wait(tty);

        ret = do_tty_write(ld->ops->write, tty, file, buf, count);  //发送数据

}

ld是这样注册的

 void tty_ldisc_init(struct tty_struct *tty)

 {
       struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
       if (IS_ERR(ld))
        panic("n_tty: init_tty");
        tty->ldisc = ld;
}

static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)//此处disc是N_TTY

{

         ldops = get_ldops(disc);

         ld->ops = ldops;

}

N_TTY的注册的ldops结构是

static struct tty_ldisc_ops n_tty_ops = {//lieye
    .magic           = TTY_LDISC_MAGIC,
    .name            = "n_tty",
    .open            = n_tty_open,
    .close           = n_tty_close,
    .write           = n_tty_write,
};

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
               const unsigned char *buf, size_t nr)

{

      c = tty->ops->write(tty, b, nr);   

//  此处为上文提到的tty->ops = driver->ops;  struct tty_operations *ops;

// driver->ops又来自于下文提到的normal->ops = uart_ops, normal的类型是tty_driver,  uart_ops的类型是tty_operations

//uart_ops结构中的函数int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)

}

二.UART以平台设备形式注册的驱动

2.1 UART平台设备驱动

static struct platform_driversamsung_serial_driver = {

         .probe                = s3c24xx_serial_probe,

         .remove            = s3c24xx_serial_remove,

         .id_table  = s3c24xx_serial_driver_ids,

         .driver                = {

                   .name       = "samsung-uart",

                   .pm  = SERIAL_SAMSUNG_PM_OPS,

                   .of_match_table      = of_match_ptr(s3c24xx_uart_dt_match),

         },

};

 

2.2 设备驱动的注册

static int s3c24xx_serial_probe(structplatform_device *pdev)

{

         struct s3c24xx_uart_port *ourport;

         ourport= &s3c24xx_serial_ports[index];                              

ret = s3c24xx_serial_init_port(ourport,pdev);

         if(ret < 0)

                   returnret;

 

         if(!s3c24xx_uart_drv.state) {

                   ret= uart_register_driver(&s3c24xx_uart_drv);//里面会层层调用,注册真实的字符设备。

                   if(ret < 0) {

                            pr_err("Failedto register Samsung UART driver\n");

                            returnret;

                   }

         }

 

         dbg("%s:adding port\n", __func__);

         uart_add_one_port(&s3c24xx_uart_drv,&ourport->port);//////////////////////////////////////////注册个port

 

}

s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
    [0] = {
        .port = {
            .lock        = __PORT_LOCK_UNLOCKED(0),
            .iotype        = UPIO_MEM,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,/////////////////////////////////////////////////////port的ops
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 0,
        }
    },

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)

{

         state = drv->state + uport->line;
         port = &state->port;

         state->uart_port = uport;
         uport->state = state;

}

 

 

2.3 uart_register_driver

 

int uart_register_driver(struct uart_driver*drv)

{

         structtty_driver *normal;

         inti, retval;

 

         BUG_ON(drv->state);

 

         /*

          * Maybe we should be using a slab cache forthis, especially if

          * we have a large number of ports to handle.

          */

         drv->state= kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);

         if(!drv->state)

                   gotoout;

 

         normal= alloc_tty_driver(drv->nr);

         if(!normal)

                   gotoout_kfree;

 

         drv->tty_driver= normal;

 

         normal->driver_name      = drv->driver_name;

         normal->name          = drv->dev_name;

         normal->major                   = drv->major;

         normal->minor_start       = drv->minor;

         normal->type            = TTY_DRIVER_TYPE_SERIAL;

         normal->subtype               = SERIAL_TYPE_NORMAL;

         normal->init_termios       = tty_std_termios;

         normal->init_termios.c_cflag= B9600 | CS8 | CREAD | HUPCL | CLOCAL;

         normal->init_termios.c_ispeed= normal->init_termios.c_ospeed = 9600;

         normal->flags           = TTY_DRIVER_REAL_RAW |TTY_DRIVER_DYNAMIC_DEV;

         normal->driver_state    = drv;

         tty_set_operations(normal,&uart_ops);//此处初始化tty_driver->ops

         //normal->ops = uart_ops, normal的类型是tty_driver,  uart_ops的类型是tty_operations

         /*

          * Initialise the UART state(s).

          */

         for(i = 0; i < drv->nr; i++) {

                  struct uart_state *state =drv->state + i;

                   structtty_port *port = &state->port;

 

                   tty_port_init(port);

                   port->ops= &uart_port_ops;

         }

 

         retval= tty_register_driver(normal);//注册字符驱动

         if(retval >= 0)

                   returnretval;

 

         for(i = 0; i < drv->nr; i++)

                   tty_port_destroy(&drv->state[i].port);

         put_tty_driver(normal);

out_kfree:

         kfree(drv->state);

out:

         return-ENOMEM;

}

 

2.4 struct tty_operations uart_ops

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,

         .get_icount      = uart_get_icount,

#ifdef CONFIG_CONSOLE_POLL

         .poll_init  = uart_poll_init,

         .poll_get_char = uart_poll_get_char,

         .poll_put_char = uart_poll_put_char,

#endif

};

uart_write()一个实际的发送函数:

static int uart_write(struct tty_struct *tty,
                    const unsigned char *buf, int count)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port;
    struct circ_buf *circ;
    unsigned long flags;
    int c, ret = 0;

    /*
     * This means you called this function _after_ the port was
     * closed.  No cookie for you.
     */
    if (!state) {
        WARN_ON(1);
        return -EL3HLT;
    }

    circ = &state->xmit;
    if (!circ->buf)
        return 0;

    port = uart_port_lock(state, flags);
    while (port) {
        c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
        if (count < c)
            c = count;
        if (c <= 0)
            break;
        memcpy(circ->buf + circ->head, buf, c);
        circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
        buf += c;
        count -= c;
        ret += c;
    }

    __uart_start(tty);
    uart_port_unlock(port, flags);
    return ret;
}
 

static void __uart_start(struct tty_struct *tty)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->uart_port;

    if (port && !uart_tx_stopped(port))
        port->ops->start_tx(port);
}
 

 struct uart_ops s3c24xx_serial_ops = {
    .pm        = s3c24xx_serial_pm,
    .tx_empty    = s3c24xx_serial_tx_empty,
    .get_mctrl    = s3c24xx_serial_get_mctrl,
    .set_mctrl    = s3c24xx_serial_set_mctrl,
    .stop_tx    = s3c24xx_serial_stop_tx,
    .start_tx    = s3c24xx_serial_start_tx,
    .stop_rx    = s3c24xx_serial_stop_rx,
    .break_ctl    = s3c24xx_serial_break_ctl,
    .startup    = s3c24xx_serial_startup,
    .shutdown    = s3c24xx_serial_shutdown,
    .set_termios    = s3c24xx_serial_set_termios,
    .type        = s3c24xx_serial_type,
    .release_port    = s3c24xx_serial_release_port,
    .request_port    = s3c24xx_serial_request_port,
    .config_port    = s3c24xx_serial_config_port,
    .verify_port    = s3c24xx_serial_verify_port,
#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL)
    .poll_get_char = s3c24xx_serial_get_poll_char,
    .poll_put_char = s3c24xx_serial_put_poll_char,
#endif
};

static void s3c24xx_serial_start_tx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    struct circ_buf *xmit = &port->state->xmit;

    if (!tx_enabled(port)) {
        if (port->flags & UPF_CONS_FLOW)
            s3c24xx_serial_rx_disable(port);

        tx_enabled(port) = 1;
        if (!ourport->dma || !ourport->dma->tx_chan)
            s3c24xx_serial_start_tx_pio(ourport);
    }

    if (ourport->dma && ourport->dma->tx_chan) {
        if (!uart_circ_empty(xmit) && !ourport->tx_in_progress)
            s3c24xx_serial_start_next_tx(ourport);
    }
}

 

 

 

你可能感兴趣的:(linux内核驱动)