基于Linux2.6.22和s3c2440的串口驱动简析

来源:互联网 发布:js ajax表单验证码 编辑:程序博客网 时间:2024/05/03 03:39
Linux的终端设备这一块,涉及的面还是比较多的。经过几天的分析,有点收获,就写出来和大家分享分享,如果有不对的地方,还希望大家提出来。

s3c2440
的串口驱动部分,分为platform/tty/console三部分。




既然是分析s3c2440uart部分那么第一个该看的就是drivers\serial\s3c2410.c文件。在此文件中,可以找到下面的代码:

点击(此处)折叠或打开

  1. module_init(s3c24xx_serial_modinit);
  2. module_exit(s3c24xx_serial_modexit);

通过上面的两句代码,就很明确的告诉我们,首先需要关注的函数就是:s3c24xx_serial_modinit

点击(此处)折叠或打开

  1. static int __init s3c24xx_serial_modinit(void)
  2. {
  3.     int ret;

  4.     ret = uart_register_driver(&s3c24xx_uart_drv);
  5.     if (ret < 0) {
  6.         printk(KERN_ERR "failed to register UART driver\n");
  7.         return -1;
  8.     }

  9.     s3c2400_serial_init(); //不需要关注
  10.     s3c2410_serial_init(); //不需要关注
  11.     s3c2412_serial_init(); //不需要关注
  12.     s3c2440_serial_init();

  13.     return 0;
  14. }

既然分为三部分,那么这次就只说一部分,那就是platform这一部分,通过前面博文中对platform的分析,可知,会有platform_driverplatform_device两个结构体会被定义。那么就让我们把他们找出来:

platform_driver部分:

通过分析s3c24xx_serial_modinit函数,会发现如下的调用关系:

点击(此处)折叠或打开

  1. s3c2410_serial_init
  2.         s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
  3.                 platform_driver_register(drv);
  4.                         driver_register(&drv->driver);
  5.                                 bus_add_driver(drv);
  6.                                         driver_attach(drv);
  7.                                                 bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  8.                                                         fn(dev, data); ---> __driver_attach(dev, data);
  9.                                                                 driver_probe_device(drv, dev);
  10.                                                                         drv->bus->match(dev, drv) ---> platform_match
  11.                                                                                 (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0)
  12.                                                                         really_probe(dev, drv);
也就是说,s3c24xx_serial_modinits3c2410_serial_drv这个实例化的platform_driver结构体注册到了platform总线上。

点击(此处)折叠或打开

  1. static struct platform_driver s3c2440_serial_drv = {
  2.     .probe        = s3c2440_serial_probe,
  3.     .remove        = s3c24xx_serial_remove,
  4.     .suspend    = s3c24xx_serial_suspend,
  5.     .resume        = s3c24xx_serial_resume,
  6.     .driver        = {
  7.         .name    = "s3c2440-uart",
  8.         .owner    = THIS_MODULE,
  9.     },
  10. };


platform_driver部分:

platform_driver,自然就有platform_device。我们可以arch\arm\plat-s3c24xx\Cpu.c文件中,找到如下函数:

点击(此处)折叠或打开

  1.   
  2.   static int __init s3c_arch_init(void)
  3.   {
  4.       int ret;
  5.   
  6.       // do the correct init for cpu
  7.   
  8.       if (cpu == NULL)
  9.           panic("s3c_arch_init: NULL cpu\n");
  10.   
  11.       ret = (cpu->init)();
  12.       if (ret != 0)
  13.           return ret;
  14.   
  15.       ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
  16.       return ret;
  17.   }
  18.   
  19.   arch_initcall(s3c_arch_init);


在查找s3c24xx_uart_devs这个东东时,发现arch\arm\plat-s3c24xx\Devs.c中有如下定义

点击(此处)折叠或打开

  1. struct platform_device *s3c24xx_uart_devs[3] = {
  2. };

也就是它默认定义为指向platform_device的指针数组,那细想,自然会有地方会对其进行赋值。s3c24xx_uart_devs赋值的地方又在什么地方呢??在源码中搜索,会发下函数:

点击(此处)折叠或打开

  1.   void __init s3c24xx_init_uartdevs(char *name,
  2.                    struct s3c24xx_uart_resources *res,
  3.                    struct s3c2410_uartcfg *cfg, int no)
  4.   {
  5.       struct platform_device *platdev;
  6.       struct s3c2410_uartcfg *cfgptr = uart_cfgs;
  7.       struct s3c24xx_uart_resources *resp;
  8.       int uart;
  9.   
  10.       memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
  11.   
  12.       for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
  13.           platdev = s3c24xx_uart_src[cfgptr->hwport];
  14.   
  15.           resp = res + cfgptr->hwport;
  16.   
  17.           s3c24xx_uart_devs[uart] = platdev; //给s3c24xx_uart_devs进行赋值
  18.   
  19.           platdev->name = name;
  20.           platdev->resource = resp->resources;
  21.           platdev->num_resources = resp->nr_resources;
  22.   
  23.           platdev->dev.platform_data = cfgptr;
  24.       }
  25.   
  26.       nr_uarts = no; //给nr_uarts进行赋值
  27.   }

那么
s3c24xx_init_uartdevs又是在什么地方被调用的呢?其调用关系如下:

点击(此处)折叠或打开

  1.   smdk2440_map_io
  2.           s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
  3.                   cpu = s3c_lookup_cpu(idcode);
  4.                           tab = cpu_ids;
  5.                               for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) {
  6.                                   if ((idcode & tab->idmask) == tab->idcode)
  7.                                       return tab;
  8.                               }
  9.                   (cpu->map_io)(mach_desc, size); ---> s3c244x_map_io
  10.           s3c24xx_init_clocks(12000000);
  11.           s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
  12.                   (cpu->init_uarts)(cfg, no); ---> s3c244x_init_uarts
  13.                           s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no); //初始化struct platform_device *s3c24xx_uart_devs[3]

smdk2440_map_io
则是在内核启动的第二阶段被调用的:

点击(此处)折叠或打开

  1.   start_kernel
  2.           setup_arch(&command_line);
  3.                   mdesc = setup_machine(machine_arch_type);
  4.                           lookup_machine_type(nr);
  5.                   machine_name = mdesc->name;
  6.                   paging_init(&meminfo, mdesc);
  7.                           devicemaps_init(mdesc);
  8.                                   mdesc->map_io();
  9.                   init_arch_irq = mdesc->init_irq;
  10.                   system_timer = mdesc->timer;
  11.                   init_machine = mdesc->init_machine;
  12.   
  13.                   
  14.   MACHINE_START(S3C2440, "SMDK2440")
  15.       /* Maintainer: Ben Dooks <ben@fluff.org> */
  16.       .phys_io    = S3C2410_PA_UART,
  17.       .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
  18.       .boot_params    = S3C2410_SDRAM_PA + 0x100,
  19.   
  20.       .init_irq    = s3c24xx_init_irq,
  21.       .map_io        = smdk2440_map_io,
  22.       .init_machine    = smdk2440_machine_init,
  23.       .timer        = &s3c24xx_timer,
  24.   MACHINE_END

到此,s3c2440的串口的platform部分就说完了,系统在注册s3c24xx_init_uartdevs,又注册了s3c2410_serial_drv,之后就会自动调用s3c2440_serial_probe,其作用将在 基于Linux2.6.22和s3c2440的串口驱动简析---(2)中进行分析。


上文说的是串口的platform部分,自然这次说的就是串口的tty部分

现在就要追溯到上文s3c24xx_serial_modinit函数中的uart_register_driver(&s3c24xx_uart_drv)函数:

点击(此处)折叠或打开

  1. static struct uart_driver s3c24xx_uart_drv = {
  2.     .owner        = THIS_MODULE,
  3.     .dev_name    = "s3c2410_serial",
  4.     .nr        = 3,
  5.     .cons        = S3C24XX_SERIAL_CONSOLE,
  6.     .driver_name    = S3C24XX_SERIAL_NAME,  //#define S3C24XX_SERIAL_NAME "ttySAC"
  7.     .major        = S3C24XX_SERIAL_MAJOR,   //#define S3C24XX_SERIAL_MAJOR 204
  8.     .minor        = S3C24XX_SERIAL_MINOR,   //#define S3C24XX_SERIAL_MINOR 64
  9. };

点击(此处)折叠或打开

  1. int uart_register_driver(struct uart_driver *drv)
  2. {
  3.     struct tty_driver *normal = NULL;
  4.     int i, retval;

  5.     BUG_ON(drv->state);

  6.     /*
  7.      * Maybe we should be using a slab cache for this, especially if
  8.      * we have a large number of ports to handle.
  9.      */
  10.     drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
  11.     retval = -ENOMEM;
  12.     if (!drv->state)
  13.         goto out;

  14.     normal = alloc_tty_driver(drv->nr);  //分配tty_driver
  15.     if (!normal)
  16.         goto out;

  17.     drv->tty_driver = normal;

  18.     normal->owner        = drv->owner;  //将uart_driver中的相关信息赋值给tty_driver
  19.     normal->driver_name    = drv->driver_name;
  20.     normal->name        = drv->dev_name;
  21.     normal->major        = drv->major;
  22.     normal->minor_start    = drv->minor;
  23.     normal->type        = TTY_DRIVER_TYPE_SERIAL;
  24.     normal->subtype        = SERIAL_TYPE_NORMAL;
  25.     normal->init_termios    = tty_std_termios;  //给tty_driver设置默认的线路设置 tty_std_termios,并在后续进行赋值修改
  26.     normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
  27.     normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
  28.     normal->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
  29.     normal->driver_state = drv;
  30.     tty_set_operations(normal, &uart_ops);  //给tty_driver设置默认的操作函数uart_ops,这是由内核为我们实现的一个tty_operations结构体实例。

  31.     /*
  32.      * Initialise the UART state(s).
  33.      */
  34.     for (= 0; i < drv->nr; i++) {
  35.         struct uart_state *state = drv->state + i;

  36.         state->close_delay = 500;    /* .5 seconds */
  37.         state->closing_wait = 30000;    /* 30 seconds */

  38.         mutex_init(&state->mutex);
  39.     }

  40.     retval = tty_register_driver(normal);  //调用tty_register_driver
  41.  out:
  42.     if (retval < 0) {
  43.         put_tty_driver(normal);
  44.         kfree(drv->state);
  45.     }
  46.     return retval;
  47. }

那么我们就继续分析下tty_register_driver,整个函数的作用是分配字符设备,并进行初始化和注册。

点击(此处)折叠或打开

  1. int tty_register_driver(struct tty_driver *driver)
  2. {
  3.   …… …… ……
  4.     /*driver->minor_start=*/
  5.     if (!driver->major) {

  6.         error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
  7.                         driver->name);  
  8.         if (!error) {
  9.             driver->major = MAJOR(dev);
  10.             driver->minor_start = MINOR(dev);
  11.         }
  12.     } else {
  13.         dev = MKDEV(driver->major, driver->minor_start);
  14.         error = register_chrdev_region(dev, driver->num, driver->name);
  15.     }
  16.   …… …… ……
  17.     cdev_init(&driver->cdev, &tty_fops);  //设备绑定了tty_fops这个file_operations结构体实例
  18.     driver->cdev.owner = driver->owner;
  19.     error = cdev_add(&driver->cdev, dev, driver->num);
  20.   …… …… ……
  21. }

这样,以后在用户层对串口设备进行读写等操作时,就会执行tty_fops中的相关函数,即:

点击(此处)折叠或打开

  1. static const struct file_operations tty_fops = {
  2.     .llseek        = no_llseek,
  3.     .read        = tty_read,
  4.     .write        = tty_write,
  5.     .poll        = tty_poll,
  6.     .ioctl        = tty_ioctl,
  7.     .compat_ioctl    = tty_compat_ioctl,
  8.     .open        = tty_open,
  9.     .release    = tty_release,
  10.     .fasync        = tty_fasync,
  11. };

例如其中的tty_open函数:

点击(此处)折叠或打开

  1. static int tty_open(struct inode * inode, struct file * filp)
  2. {
  3.     ………… ………… …………
  4.         retval = tty->driver->open(tty, filp);  //这个就是在调用tty_driver中的open函数,即uart_ops实例中的uart_open
  5.     ………… ………… …………
  6. }

uart_ops实例的定义如下:

点击(此处)折叠或打开

  1. static const struct tty_operations uart_ops = {
  2.     .open        = uart_open,
  3.     .close        = uart_close,
  4.     .write        = uart_write,
  5.     .put_char    = uart_put_char,
  6.     .flush_chars    = uart_flush_chars,
  7.     .write_room    = uart_write_room,
  8.     .chars_in_buffer= uart_chars_in_buffer,
  9.     .flush_buffer    = uart_flush_buffer,
  10.     .ioctl        = uart_ioctl,
  11.     .throttle    = uart_throttle,
  12.     .unthrottle    = uart_unthrottle,
  13.     .send_xchar    = uart_send_xchar,
  14.     .set_termios    = uart_set_termios,
  15.     .stop        = uart_stop,
  16.     .start        = uart_start,
  17.     .hangup        = uart_hangup,
  18.     .break_ctl    = uart_break_ctl,
  19.     .wait_until_sent= uart_wait_until_sent,
  20. #ifdef CONFIG_PROC_FS
  21.     .read_proc    = uart_read_proc,
  22. #endif
  23.     .tiocmget    = uart_tiocmget,
  24.     .tiocmset    = uart_tiocmset,
  25. };

uart_ops中的uart_oepn进行分析,发现如下调用关系:

点击(此处)折叠或打开

  1. uart_open
  2.         uart_startup(state, 0);
  3.                 port->ops->startup(port); //uart_ops结构体中的startup函数 (通tty_operations的uart_ops实例不同)

s3c2440uart_ops结构体定义如下:

点击(此处)折叠或打开

  1. static struct uart_ops s3c24xx_serial_ops = {
  2.     .pm        = s3c24xx_serial_pm,
  3.     .tx_empty    = s3c24xx_serial_tx_empty,
  4.     .get_mctrl    = s3c24xx_serial_get_mctrl,
  5.     .set_mctrl    = s3c24xx_serial_set_mctrl,
  6.     .stop_tx    = s3c24xx_serial_stop_tx,
  7.     .start_tx    = s3c24xx_serial_start_tx,
  8.     .stop_rx    = s3c24xx_serial_stop_rx,
  9.     .enable_ms    = s3c24xx_serial_enable_ms,
  10.     .break_ctl    = s3c24xx_serial_break_ctl,
  11.     .startup    = s3c24xx_serial_startup,
  12.     .shutdown    = s3c24xx_serial_shutdown,
  13.     .set_termios    = s3c24xx_serial_set_termios,
  14.     .type        = s3c24xx_serial_type,
  15.     .release_port    = s3c24xx_serial_release_port,
  16.     .request_port    = s3c24xx_serial_request_port,
  17.     .config_port    = s3c24xx_serial_config_port,
  18.     .verify_port    = s3c24xx_serial_verify_port,
  19. };

其被赋值于:

点击(此处)折叠或打开

  1. static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
  2.     [0] = {
  3.         .port = {
  4.             .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
  5.             .iotype        = UPIO_MEM,
  6.             .irq        = IRQ_S3CUART_RX0,
  7.             .uartclk    = 0,
  8.             .fifosize    = 16,
  9.             .ops        = &s3c24xx_serial_ops,  //见这里
  10.             .flags        = UPF_BOOT_AUTOCONF,
  11.             .line        = 0,
  12.         }
  13.     },
  14.     [1] = {
  15.         .port = {
  16.             .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
  17.             .iotype        = UPIO_MEM,
  18.             .irq        = IRQ_S3CUART_RX1,
  19.             .uartclk    = 0,
  20.             .fifosize    = 16,
  21.             .ops        = &s3c24xx_serial_ops,
  22.             .flags        = UPF_BOOT_AUTOCONF,
  23.             .line        = 1,
  24.         }
  25.     },
  26. #if NR_PORTS > 2

  27.     [2] = {
  28.         .port = {
  29.             .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
  30.             .iotype        = UPIO_MEM,
  31.             .irq        = IRQ_S3CUART_RX2,
  32.             .uartclk    = 0,
  33.             .fifosize    = 16,
  34.             .ops        = &s3c24xx_serial_ops,
  35.             .flags        = UPF_BOOT_AUTOCONF,
  36.             .line        = 2,
  37.         }
  38.     }
  39. #endif
  40. };



到这个时候,我们就需要分析下,在上文 基于Linux2.6.22s3c2440的串口驱动简析---(1) 最后说到 s3c2440_serial_probe,那么这个函数的功能是什么呢?对其分析会发现如下的调用关系:

点击(此处)折叠或打开

  1. s3c2410_serial_probe
  2.         s3c24xx_serial_probe(dev, &s3c2410_uart_inf);
  3.                 ourport = &s3c24xx_serial_ports[probe_index];  //此处引用了上面的s3c24xx_serial_ports
  4.                 s3c24xx_serial_init_port(ourport, info, dev); //完成硬件寄存器初始化
  5.                         s3c24xx_serial_resetport(port, cfg);
  6.                                 (info->reset_port)(port, cfg); ---> s3c2410_serial_resetport
  7.                 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
  8.                         state = drv->state + port->line;  //将s3c24xx_serial_ports中的uart_port赋给了uart_driver中的uart_state->port
  9.                         state->port = port;
  10.                         uart_configure_port(drv, state, port);
  11.                                 port->ops->config_port(port, flags); //port->flags & UPF_BOOT_AUTOCONF
  12.                         tty_register_device(drv->tty_driver, port->line, port->dev);  //这个是关键, tty_register_device
  13.                                 device_create(tty_class, device, dev, name);
  14.                                         device_register(dev);
  15.                                                 device_add(dev);


tty部分总结:
    当用户对tty驱动所分配的设备节点进行系统调用时,tty_driver所拥有的tty_operations中的成员函数会被tty核心调用,之后串口核心层的uart_ops中的成员函数会被tty_operations中的成员函数再次调用。

    这样,由于内核已经将tty核心层和tty驱动层实现了,那么我们需要做的就是对具体的设备,实现串口核心层的uart_ops、uart_ports、uart_driver等结构体实例。


至此,tty_register_driver和tty_register_device都被调用了。整个过程还是比较繁琐的,不知道有没有写清楚~~

0 0