远程视频监控之驱动篇(串口)

       转载请注明出处: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                                   

你可能感兴趣的:(linux,C语言,三星,ARM9,linux串口驱动)