对于驱动的学习停歇了几乎一周的时间,期间忙于补习Linux应用编程和搜索驱动、内核相关书籍,以便之后更进一步地学习。在之前友善提供的驱动例程里面,涉及的知识面非常有限,需要研究更多的驱动源码,了解更多的驱动知识,是当务之急。研究别人代码的同时,当然不忘自己也要动手练习。以下贴出串口驱动程序,并在程序里附上简要注释。
-
-
-
-
-
-
-
-
-
-
-
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #include
- #include
-
- #include
- #include
-
- #include "samsung.h"
-
-
- static int s3c2440_serial_setsource(struct uart_port *port,
- struct s3c24xx_uart_clksrc *clk)
- {
- unsigned long ucon = rd_regl(port, S3C2410_UCON);
-
-
-
- ucon &= ~S3C2440_UCON_CLKMASK;
-
- if (strcmp(clk->name, "uclk") == 0)
- ucon |= S3C2440_UCON_UCLK;
- else if (strcmp(clk->name, "pclk") == 0)
- ucon |= S3C2440_UCON_PCLK;
- else if (strcmp(clk->name, "fclk") == 0)
- ucon |= S3C2440_UCON_FCLK;
- else {
- printk(KERN_ERR "unknown clock source %s/n", clk->name);
- return -EINVAL;
- }
-
- wr_regl(port, S3C2410_UCON, ucon);
- return 0;
- }
-
-
- static int s3c2440_serial_getsource(struct uart_port *port,
- struct s3c24xx_uart_clksrc *clk)
- {
- unsigned long ucon = rd_regl(port, S3C2410_UCON);
- unsigned long ucon0, ucon1, ucon2;
-
- switch (ucon & S3C2440_UCON_CLKMASK) {
- case S3C2440_UCON_UCLK:
- clk->divisor = 1;
- clk->name = "uclk";
- break;
-
- case S3C2440_UCON_PCLK:
- case S3C2440_UCON_PCLK2:
- clk->divisor = 1;
- clk->name = "pclk";
- break;
-
- case S3C2440_UCON_FCLK:
-
-
-
- ucon0 = __raw_readl(S3C24XX_VA_UART0 + S3C2410_UCON);
- ucon1 = __raw_readl(S3C24XX_VA_UART1 + S3C2410_UCON);
- ucon2 = __raw_readl(S3C24XX_VA_UART2 + S3C2410_UCON);
-
- printk("ucons: %08lx, %08lx, %08lx/n", ucon0, ucon1, ucon2);
-
- ucon0 &= S3C2440_UCON0_DIVMASK;
- ucon1 &= S3C2440_UCON1_DIVMASK;
- ucon2 &= S3C2440_UCON2_DIVMASK;
-
- if (ucon0 != 0) {
- clk->divisor = ucon0 >> S3C2440_UCON_DIVSHIFT;
- clk->divisor += 6;
- } else if (ucon1 != 0) {
- clk->divisor = ucon1 >> S3C2440_UCON_DIVSHIFT;
- clk->divisor += 21;
- } else if (ucon2 != 0) {
- clk->divisor = ucon2 >> S3C2440_UCON_DIVSHIFT;
- clk->divisor += 36;
- } else {
-
- clk->divisor = 9;
- }
-
- clk->name = "fclk";
- break;
- }
-
- return 0;
- }
-
- static int s3c2440_serial_resetport(struct uart_port *port,
- struct s3c2410_uartcfg *cfg)
- {
- unsigned long ucon = rd_regl(port, S3C2410_UCON);
-
- dbg("s3c2440_serial_resetport: port=%p (%08lx), cfg=%p/n",
- port, port->mapbase, cfg);
-
-
-
- ucon &= (S3C2440_UCON0_DIVMASK | (3<<10));
-
- wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
- wr_regl(port, S3C2410_ULCON, cfg->ulcon);
-
-
-
- wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
- wr_regl(port, S3C2410_UFCON, cfg->ufcon);
-
- return 0;
- }
-
- static struct s3c24xx_uart_info s3c2440_uart_inf = {
- .name = "Samsung S3C2440 UART",
- .type = PORT_S3C2440,
- .fifosize = 64,
- .rx_fifomask = S3C2440_UFSTAT_RXMASK,
- .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
- .rx_fifofull = S3C2440_UFSTAT_RXFULL,
- .tx_fifofull = S3C2440_UFSTAT_TXFULL,
- .tx_fifomask = S3C2440_UFSTAT_TXMASK,
- .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
- .get_clksrc = s3c2440_serial_getsource,
- .set_clksrc = s3c2440_serial_setsource,
- .reset_port = s3c2440_serial_resetport,
- };
-
-
-
- static int s3c2440_serial_probe(struct platform_device *dev)
- {
- dbg("s3c2440_serial_probe: dev=%p/n", dev);
- return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
- }
-
- static struct platform_driver s3c2440_serial_driver = {
- .probe = s3c2440_serial_probe,
- .remove = __devexit_p(s3c24xx_serial_remove),
- .driver = {
- .name = "s3c2440-uart",
- .owner = THIS_MODULE,
- },
- };
-
- s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf);
-
- static int __init s3c2440_serial_init(void)
- {
- return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf);
- }
-
- static void __exit s3c2440_serial_exit(void)
- {
- platform_driver_unregister(&s3c2440_serial_driver);
- }
-
- module_init(s3c2440_serial_init);
- module_exit(s3c2440_serial_exit);
-
- MODULE_DESCRIPTION("Samsung S3C2440,S3C2442 SoC Serial port driver");
- MODULE_AUTHOR("Ben Dooks ");
- MODULE_LICENSE("GPL v2");
- MODULE_ALIAS("platform:s3c2440-uart");
几个问题需要我们注意:
1.设备如何注册、注销
串口驱动被作为一个单独的模块被加载进内核,在模块的加载和卸载函数中,只需注册和注销一个platform_driver结构体。
注册:
- static struct platform_driver s3c2440_serial_driver = {
- .probe = s3c2440_serial_probe,
- .remove = __devexit_p(s3c24xx_serial_remove),
- .driver = {
- .name = "s3c2440-uart",
- .owner = THIS_MODULE,
- },
- };
注销:
- platform_driver_unregister(&s3c2440_serial_driver);
2.几个非常重要的结构体
s3c2410_uartcfg :保存ucon ulcon ufcon三个串口寄存器的值
- struct s3c2410_uartcfg {
- unsigned char hwport;
- unsigned char unused;
- unsigned short flags;
- upf_t uart_flags;
-
- unsigned int has_fracval;
-
- unsigned long ucon;
- unsigned long ulcon;
- unsigned long ufcon;
-
- struct s3c24xx_uart_clksrc *clocks;
- unsigned int clocks_size;
- };
s3c24xx_uart_info :提供串口设备环境信息,并提供三个函数的接口
- struct s3c24xx_uart_info {
- char *name;
- unsigned int type;
- unsigned int fifosize;
- unsigned long rx_fifomask;
- unsigned long rx_fifoshift;
- unsigned long rx_fifofull;
- unsigned long tx_fifomask;
- unsigned long tx_fifoshift;
- unsigned long tx_fifofull;
-
-
-
- unsigned int has_divslot:1;
-
-
-
- int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
- int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
-
-
- int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);
- };
platform_device :设备的信息
- struct platform_device {
- const char * name;
- int id;
- struct device dev;
- u32 num_resources;
- struct resource * resource;
-
- const struct platform_device_id *id_entry;
-
-
- struct pdev_archdata archdata;
- };
platform_driver :设备注册用
- struct platform_driver {
- int (*probe)(struct platform_device *);
- int (*remove)(struct platform_device *);
- void (*shutdown)(struct platform_device *);
- int (*suspend)(struct platform_device *, pm_message_t state);
- int (*resume)(struct platform_device *);
- struct device_driver driver;
- const struct platform_device_id *id_table;
- };
3.读写寄存器的宏定义
(1)读寄存器
unsigned long ucon = rd_regl(port, S3C2410_UCON);
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
}
#define portaddr(port, reg) ((port)->membase + (reg))
(2)写寄存器
wr_regl(port, S3C2410_UCON, ucon);
4.函数的注册方式
细心的朋友可能会发现,我们之前一直使用的是传统的 device driver 机制(通过 driver_register 函数进行注册)本串口所使用的是一个设备用 Platform_device 表示,驱动用 Platform_driver 进行注册的机制。而后者是在内核2.6版本所提出来的新事物,其优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过 platform device 提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。关于这两种机制更深入的分析,请看以下链接:http://blog.csdn.net/jarvis_xian/archive/2011/05/23/6440649.aspx
阅读(965) | 评论(0) | 转发(1) |