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;   

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

         tty->index = idx;

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

         tty->dev = tty_get_device(tty);

         return tty;

}

二.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)

{

         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

}

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

         /*

          * 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

};



猜你喜欢

转载自blog.csdn.net/lieye_leaves/article/details/79063541