转载请注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/38638831
由于串口驱动略显复杂,且调试比较困难,我并没有进行重新改写,这里主要是分析一下三星的串口驱动。GSM是通过串口通信的,GSM模块的内容我将在应用篇中讲解。在阅读下面内容时我强烈建议你打开内核的驱动代码,而且为了方便建议你使用Source Insight进行阅读代码。驱动位置:\linux-3.4.91\drivers\tty\serial\samsung.c
由于串口属于平台总线模型,所以自然有平台设备和平台驱动两个分支,下面我将分别去讨论:
一.平台设备
1.平台设备初始化
start_kernel()
setup_arch() // /arch/arm/kernal/Setup.c
paging_init(mdesc);
devicemaps_init(mdesc);
mdesc->map_io();
.map_io = smdk2440_map_io, // /arch/arm/mach-s3c2440/mach-smdk2440.c
smdk2440_map_io(void)
s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
(cpu->init_uarts)(cfg, no); // /arch/arm/plat-samsung/Init.c
到这里突然发现分析不下去了,接着仔细查看cpu是个什么东东,找到这个定义static struct cpu_table *cpu;
到这里我们还不知道cpu是什么,仔细看代码发现这样一段代码:大致意思就是通过比较返回注册的平台
static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode, struct cpu_table *tab, unsigned int count) { for (; count != 0; count--, tab++) { if ((idcode & tab->idmask) == (tab->idcode & tab->idmask)) return tab; } return NULL; }
紧接着在s3c_init_cpu()中发现cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);所以cpu现在就是注册的平台。接下来就好办了去/arch/arm/plat-s3c24XX/cpu.c中你会发现它的赋值
static struct cpu_table cpu_ids[] __initdata = { <pre name="code" class="cpp"> //省略...... { .idcode = 0x32440000, .idmask = 0xffffffff, .map_io = s3c2440_map_io, .init_clocks = s3c244x_init_clocks, .init_uarts = s3c244x_init_uarts, .init = s3c2440_init, .name = name_s3c2440 }, //省略...... };
s3c244x_init_uarts()
s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no); //平台设备名字,资源,配置
/*s3c2410_uart_resources这个东东改了地方,找了好久才找到:/arch/arm/plat-s3c24xx/dev-uart.c*/ static struct resource s3c2410_uart0_resource[] = { [0] = { .start = S3C2410_PA_UART0, .end = S3C2410_PA_UART0 + 0x3fff, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_S3CUART_RX0, .end = IRQ_S3CUART_ERR0, .flags = IORESOURCE_IRQ, } }; static struct resource s3c2410_uart1_resource[] = { [0] = { .start = S3C2410_PA_UART1, .end = S3C2410_PA_UART1 + 0x3fff, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_S3CUART_RX1, .end = IRQ_S3CUART_ERR1, .flags = IORESOURCE_IRQ, } }; static struct resource s3c2410_uart2_resource[] = { [0] = { .start = S3C2410_PA_UART2, .end = S3C2410_PA_UART2 + 0x3fff, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_S3CUART_RX2, .end = IRQ_S3CUART_ERR2, .flags = IORESOURCE_IRQ, } }; static struct resource s3c2410_uart3_resource[] = { [0] = { .start = S3C2443_PA_UART3, .end = S3C2443_PA_UART3 + 0x3fff, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_S3CUART_RX3, .end = IRQ_S3CUART_ERR3, .flags = IORESOURCE_IRQ, }, }; struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = { [0] = { .resources = s3c2410_uart0_resource, .nr_resources = ARRAY_SIZE(s3c2410_uart0_resource), }, [1] = { .resources = s3c2410_uart1_resource, .nr_resources = ARRAY_SIZE(s3c2410_uart1_resource), }, [2] = { .resources = s3c2410_uart2_resource, .nr_resources = ARRAY_SIZE(s3c2410_uart2_resource), }, [3] = { .resources = s3c2410_uart3_resource, .nr_resources = ARRAY_SIZE(s3c2410_uart3_resource), }, };
/* cfg对应 /arch/arm/mach-s3c24xx/mach-smdk2440.c</span></span> */ static struct s3c2410_uartcfg smdk2440_uartcfgs[] __initdata = { [0] = { .hwport = 0, .flags = 0, .ucon = 0x3c5, //rx,tx采取中断方式 .ulcon = 0x03, //数据长度8bit .ufcon = 0x51, //开机fifo,并且设置tx,rx触发字节数,怎么设置要根据芯片手册了,这个不是很难,我就不写了 }, [1] = { .hwport = 1, .flags = 0, .ucon = 0x3c5, .ulcon = 0x03, .ufcon = 0x51, }, /* IR port */ [2] = { .hwport = 2, .flags = 0, .ucon = 0x3c5, .ulcon = 0x43, .ufcon = 0x51, } };
参数都弄清了然后让我们看看怎么初始化的吧:终于到这步了,上面只是说明了 (cpu->init_uarts)(cfg, no); 后执行的是s3c24xx_init_uartdevs(),下面接着来分析
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);//s3c2410_uartcfg拷贝到cfgptr /*将串口的资源参数等都传给平台设备*/ for (uart = 0; uart < no; uart++, cfg++, cfgptr++) { platdev = s3c24xx_uart_src[cfgptr->hwport]; resp = res + cfgptr->hwport; s3c24xx_uart_devs[uart] = platdev; platdev->name = name; platdev->resource = resp->resources; platdev->num_resources = resp->nr_resources; platdev->dev.platform_data = cfgptr; } nr_uarts = no; }
2.平台设备注册
start_kernel -> rest_init -> kernel_thread -> init -> do_basic_setup -> do_initcalls
do_initcalls()
static void __init do_initcalls(void) { int level; for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) do_initcall_level(level); }
arch_initcall(s3c_arch_init);
它的定义:#define arch_initcall(fn) __define_initcall("3",fn,3) // \include\linux\init.h
s3c_arch_init(void)
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; }
二.平台驱动的数据结构
为了下面能更好的理解平台驱动,在此先列出平台驱动的三大数据结构(include/linux/serial_core.h中定义):
1.UART特定的驱动程序结构定义:struct uart_driver s3c24xx_uart_drv;
static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .driver_name = "s3c2410_serial", .nr = CONFIG_SERIAL_SAMSUNG_UARTS, .cons = S3C24XX_SERIAL_CONSOLE, .dev_name = S3C24XX_SERIAL_NAME, .major = S3C24XX_SERIAL_MAJOR, .minor = S3C24XX_SERIAL_MINOR, };
2.UART端口结构定义:s3c24xx_uart_port s3c24xx_serial_ports
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { [0] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), .iotype = UPIO_MEM, .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, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 1, } }, #if CONFIG_SERIAL_SAMSUNG_UARTS > 2 [2] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), .iotype = UPIO_MEM, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 2, } }, #endif #if CONFIG_SERIAL_SAMSUNG_UARTS > 3 [3] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), .iotype = UPIO_MEM, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 3, } } #endif };
3.UART相关操作函数结构定义: struct uart_ops s3c24xx_serial_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, };
三.平台驱动
UART驱动要想和内核联系起来,必须完成两件事:
a.uart_register_driver(&s3c24xx_uart_drv);向上层注册自己(它的上层是串口核心)
b.调用uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);注册支持的每个端口
下面我们就来从头分析整个过程:
驱动的入口函数:static int __init s3c24xx_serial_modinit(void)
uart_register_driver(&s3c24xx_uart_drv); //向上层注册自己
platform_driver_register(&samsung_serial_driver); //注册平台驱动的入口点(prob().remove().等)
drv->driver.bus = &platform_bus_type;
.match = platform_match,
platform_match //设备和驱动进行匹配 (平台驱动和设备进行绑定) s3c24xx_serial_probe
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
uart_configure_port(drv, state, uport);
port->ops->config_port(port, flags);
uart_report_port(drv, port);
printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", port->dev ? dev_name(port->dev) : "", port->dev ? ": " : "", drv->dev_name, drv->tty_driver->name_base + port->line, address, port->irq, uart_type(port));
这就是我们内核启动时候看到的打印信息,到此我们的驱动就注册成功了。
参考:http://blog.chinaunix.net/uid-27041925-id-3999817.html
http://www.linuxidc.com/Linux/2012-02/54970p3.htm