转载请注明出处:http://blog.youkuaiyun.com/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