s3c2440 的串口驱动部分,分为 platform/tty/console 三部分。
既然是分析s3c2440的uart部分,那么第一个该看的就是drivers\serial\s3c2410.c文件。在此文件中,可以找到下面的代码:
点击(此处)折叠或打开
- module_init(s3c24xx_serial_modinit);
- module_exit(s3c24xx_serial_modexit);
通过上面的两句代码,就很明确的告诉我们,首先需要关注的函数就是:s3c24xx_serial_modinit
点击(此处)折叠或打开
- static int __init s3c24xx_serial_modinit(void)
- {
- int ret;
-
- ret = uart_register_driver(&s3c24xx_uart_drv);
- if (ret < 0) {
- printk(KERN_ERR "failed to register UART driver\n");
- return -1;
- }
-
- s3c2400_serial_init(); //不需要关注
- s3c2410_serial_init(); //不需要关注
- s3c2412_serial_init(); //不需要关注
- s3c2440_serial_init();
-
- return 0;
- }
既然分为三部分,那么这次就只说一部分,那就是platform这一部分,通过前面博文中对platform的分析,可知,会有platform_driver和platform_device两个结构体会被定义。那么就让我们把他们找出来:
platform_driver部分:
通过分析 s3c24xx_serial_modinit 函数,会发现如下的调用关系:点击(此处)折叠或打开
- s3c2410_serial_init
- s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
- platform_driver_register(drv);
- driver_register(&drv->driver);
- bus_add_driver(drv);
- driver_attach(drv);
- bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- fn(dev, data); ---> __driver_attach(dev, data);
- driver_probe_device(drv, dev);
- drv->bus->match(dev, drv) ---> platform_match
- (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0)
- really_probe(dev, drv);
点击(此处)折叠或打开
- static struct platform_driver s3c2440_serial_drv = {
- .probe = s3c2440_serial_probe,
- .remove = s3c24xx_serial_remove,
- .suspend = s3c24xx_serial_suspend,
- .resume = s3c24xx_serial_resume,
- .driver = {
- .name = "s3c2440-uart",
- .owner = THIS_MODULE,
- },
- };
platform_driver部分:
有 platform_driver,自然就有 platform_device。我们可以 在 arch\arm\plat-s3c24xx\Cpu.c 文件中,找到如下函数:点击(此处)折叠或打开
-
- static int __init s3c_arch_init(void)
- {
- int ret;
-
- // do the correct init for cpu
-
- if (cpu == NULL)
- panic("s3c_arch_init: NULL cpu\n");
-
- ret = (cpu->init)();
- if (ret != 0)
- return ret;
-
- ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
- return ret;
- }
-
- arch_initcall(s3c_arch_init);
在查找s3c24xx_uart_devs这个东东时,发现arch\arm\plat-s3c24xx\Devs.c中有如下定义
点击(此处)折叠或打开
- struct platform_device *s3c24xx_uart_devs[3] = {
- };
也就是它默认定义为指向platform_device的指针数组,那细想,自然会有地方会对其进行赋值。 s3c24xx_uart_devs赋值的地方又在什么地方呢??在源码中搜索,会发下函数:
点击(此处)折叠或打开
- void __init s3c24xx_init_uartdevs(char *name,
- struct s3c24xx_uart_resources *res,
- struct s3c2410_uartcfg *cfg, int no)
- {
- struct platform_device *platdev;
- struct s3c2410_uartcfg *cfgptr = uart_cfgs;
- struct s3c24xx_uart_resources *resp;
- int uart;
-
- memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
-
- for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
- platdev = s3c24xx_uart_src[cfgptr->hwport];
-
- resp = res + cfgptr->hwport;
-
- s3c24xx_uart_devs[uart] = platdev; //给s3c24xx_uart_devs进行赋值
-
- platdev->name = name;
- platdev->resource = resp->resources;
- platdev->num_resources = resp->nr_resources;
-
- platdev->dev.platform_data = cfgptr;
- }
-
- nr_uarts = no; //给nr_uarts进行赋值
- }
那么 s3c24xx_init_uartdevs又是在什么地方被调用的呢?其调用关系如下:
点击(此处)折叠或打开
- smdk2440_map_io
- s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
- cpu = s3c_lookup_cpu(idcode);
- tab = cpu_ids;
- for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) {
- if ((idcode & tab->idmask) == tab->idcode)
- return tab;
- }
- (cpu->map_io)(mach_desc, size); ---> s3c244x_map_io
- s3c24xx_init_clocks(12000000);
- s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
- (cpu->init_uarts)(cfg, no); ---> s3c244x_init_uarts
- s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no); //初始化struct platform_device *s3c24xx_uart_devs[3]
smdk2440_map_io 则是在内核启动的第二阶段被调用的:
点击(此处)折叠或打开
- start_kernel
- setup_arch(&command_line);
- mdesc = setup_machine(machine_arch_type);
- lookup_machine_type(nr);
- machine_name = mdesc->name;
- paging_init(&meminfo, mdesc);
- devicemaps_init(mdesc);
- mdesc->map_io();
- init_arch_irq = mdesc->init_irq;
- system_timer = mdesc->timer;
- init_machine = mdesc->init_machine;
-
-
- MACHINE_START(S3C2440, "SMDK2440")
- /* Maintainer: Ben Dooks <ben@fluff.org> */
- .phys_io = S3C2410_PA_UART,
- .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
- .boot_params = S3C2410_SDRAM_PA + 0x100,
-
- .init_irq = s3c24xx_init_irq,
- .map_io = smdk2440_map_io,
- .init_machine = smdk2440_machine_init,
- .timer = &s3c24xx_timer,
- 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)函数:
点击(此处)折叠或打开
- static struct uart_driver s3c24xx_uart_drv = {
- .owner = THIS_MODULE,
- .dev_name = "s3c2410_serial",
- .nr = 3,
- .cons = S3C24XX_SERIAL_CONSOLE,
- .driver_name = S3C24XX_SERIAL_NAME, //#define S3C24XX_SERIAL_NAME "ttySAC"
- .major = S3C24XX_SERIAL_MAJOR, //#define S3C24XX_SERIAL_MAJOR 204
- .minor = S3C24XX_SERIAL_MINOR, //#define S3C24XX_SERIAL_MINOR 64
- };
点击(此处)折叠或打开
- int uart_register_driver(struct uart_driver *drv)
- {
- struct tty_driver *normal = NULL;
- int i, retval;
-
- BUG_ON(drv->state);
-
- /*
- * Maybe we should be using a slab cache for this, especially if
- * we have a large number of ports to handle.
- */
- drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
- retval = -ENOMEM;
- if (!drv->state)
- goto out;
-
- normal = alloc_tty_driver(drv->nr); //分配tty_driver
- if (!normal)
- goto out;
-
- drv->tty_driver = normal;
-
- normal->owner = drv->owner; //将uart_driver中的相关信息赋值给tty_driver
- 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; //给tty_driver设置默认的线路设置 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设置默认的操作函数uart_ops,这是由内核为我们实现的一个tty_operations结构体实例。
-
- /*
- * Initialise the UART state(s).
- */
- for (i = 0; i < drv->nr; i++) {
- struct uart_state *state = drv->state + i;
-
- state->close_delay = 500; /* .5 seconds */
- state->closing_wait = 30000; /* 30 seconds */
-
- mutex_init(&state->mutex);
- }
-
- retval = tty_register_driver(normal); //调用tty_register_driver
- out:
- if (retval < 0) {
- put_tty_driver(normal);
- kfree(drv->state);
- }
- return retval;
- }
那么我们就继续分析下tty_register_driver,整个函数的作用是分配字符设备,并进行初始化和注册。
点击(此处)折叠或打开
- int tty_register_driver(struct tty_driver *driver)
- {
- …… …… ……
- /*driver->minor_start=*/
- if (!driver->major) {
-
- error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
- driver->name);
- if (!error) {
- driver->major = MAJOR(dev);
- driver->minor_start = MINOR(dev);
- }
- } else {
- dev = MKDEV(driver->major, driver->minor_start);
- error = register_chrdev_region(dev, driver->num, driver->name);
- }
- …… …… ……
- cdev_init(&driver->cdev, &tty_fops); //设备绑定了tty_fops这个file_operations结构体实例
- driver->cdev.owner = driver->owner;
- error = cdev_add(&driver->cdev, dev, driver->num);
- …… …… ……
- }
这样,以后在用户层对串口设备进行读写等操作时,就会执行 tty_fops中的相关函数,即:
点击(此处)折叠或打开
- static const struct file_operations tty_fops = {
- .llseek = no_llseek,
- .read = tty_read,
- .write = tty_write,
- .poll = tty_poll,
- .ioctl = tty_ioctl,
- .compat_ioctl = tty_compat_ioctl,
- .open = tty_open,
- .release = tty_release,
- .fasync = tty_fasync,
- };
例如其中的 tty_open函数:
点击(此处)折叠或打开
- static int tty_open(struct inode * inode, struct file * filp)
- {
- ………… ………… …………
- retval = tty->driver->open(tty, filp); //这个就是在调用tty_driver中的open函数,即uart_ops实例中的uart_open
- ………… ………… …………
- }
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,
- .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
- .read_proc = uart_read_proc,
- #endif
- .tiocmget = uart_tiocmget,
- .tiocmset = uart_tiocmset,
- };
对 uart_ops中的 uart_oepn进行分析,发现如下调用关系:
点击(此处)折叠或打开
- uart_open
- uart_startup(state, 0);
- port->ops->startup(port); //uart_ops结构体中的startup函数 (通tty_operations的uart_ops实例不同)
而 s3c2440的 uart_ops结构体定义如下:
点击(此处)折叠或打开
- static 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,
- .enable_ms = s3c24xx_serial_enable_ms,
- .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,
- };
其被赋值于:
点击(此处)折叠或打开
- static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
- [0] = {
- .port = {
- .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
- .iotype = UPIO_MEM,
- .irq = IRQ_S3CUART_RX0,
- .uartclk = 0,
- .fifosize = 16,
- .ops = &s3c24xx_serial_ops, //见这里
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- }
- },
- [1] = {
- .port = {
- .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
- .iotype = UPIO_MEM,
- .irq = IRQ_S3CUART_RX1,
- .uartclk = 0,
- .fifosize = 16,
- .ops = &s3c24xx_serial_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- }
- },
- #if NR_PORTS > 2
-
- [2] = {
- .port = {
- .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
- .iotype = UPIO_MEM,
- .irq = IRQ_S3CUART_RX2,
- .uartclk = 0,
- .fifosize = 16,
- .ops = &s3c24xx_serial_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- }
- }
- #endif
- };
到这个时候,我们就需要分析下,在上文 基于Linux2.6.22和s3c2440的串口驱动简析---(1) 最后说到 s3c2440_serial_probe,那么这个函数的功能是什么呢?对其分析会发现如下的调用关系:
点击(此处)折叠或打开
- s3c2410_serial_probe
- s3c24xx_serial_probe(dev, &s3c2410_uart_inf);
- ourport = &s3c24xx_serial_ports[probe_index]; //此处引用了上面的s3c24xx_serial_ports
- s3c24xx_serial_init_port(ourport, info, dev); //完成硬件寄存器初始化
- s3c24xx_serial_resetport(port, cfg);
- (info->reset_port)(port, cfg); ---> s3c2410_serial_resetport
- uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
- state = drv->state + port->line; //将s3c24xx_serial_ports中的uart_port赋给了uart_driver中的uart_state->port
- state->port = port;
- uart_configure_port(drv, state, port);
- port->ops->config_port(port, flags); //port->flags & UPF_BOOT_AUTOCONF
- tty_register_device(drv->tty_driver, port->line, port->dev); //这个是关键, tty_register_device
- device_create(tty_class, device, dev, name);
- device_register(dev);
- 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都被调用了。整个过程还是比较繁琐的,不知道有没有写清楚~~