关于驱动的学习停歇了简直一周的时刻,期间忙于补习Linux运用编程和查找驱动、内核相关书本,以便之后更进一步地学习。在之前友善供给的驱动例程里边,触及的常识面十分有限,需求研讨更多的驱动源码,了解更多的驱动常识,是燃眉之急。研讨他人代码的一起,当然不忘自己也要着手操练。以下贴出串口驱动程序,并在程序里附上扼要注释。
- // linux/drivers/serial/s3c2440.c
* * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs. * * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - //
- #include
“linux/module.h” - #include
“linux/ioport.h” - #include
“linux/io.h” - #include
“linux/platform_device.h” - #include
“linux/init.h” - #include
“linux/serial_core.h” - #include
“linux/serial.h” - #include
“asm/irq.h” - #include
“mach/hardware.h” - #include
“plat/regs-serial.h” - #include
“mach/regs-gpio.h” - #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 // todo – proper fclk<>nonfclk switch. // ucon &= ~S3C2440_UCON_CLKMASK; //#define S3C2440_UCON_CLKMASK (3<<10) 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); //把设置过的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: // the fun of calculating the uart divisors on * the s3c2440 // 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: lx, lx, lx/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 { // manual calims 44, seems to be 9 // 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 (lx), cfg=%p/n”, port, port->mapbase, cfg); // ensure we dont change the clock settings… // ucon &= (S3C2440_UCON0_DIVMASK | (3<<10)); wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); //从头设置寄存器UCON wr_regl(port, S3C2410_ULCON, cfg->ulcon); //从头设置寄存器ULCON // reset both fifos // wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); //重启fifo wr_regl(port, S3C2410_UFCON, cfg->ufcon); //从头设定寄存器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, - };
- //
device management // - 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; // hardware port number // unsigned char unused; unsigned short flags; upf_t uart_flags; // default uart flags // unsigned int has_fracval; unsigned long ucon; // value of ucon for port // unsigned long ulcon; // value of ulcon for port // unsigned long ufcon; // value of ufcon for port // 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; // uart port features // unsigned int has_divslot:1; // clock source control // int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk); int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk); // uart controls // 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; // arch specific additions // 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)
static unsigned char __raw_readb(unsigned int ptr)
{
}
#define portaddr(port, reg)
(2)写寄存器
wr_regl(port, S3C2410_UCON, ucon);
#define wr_regl(port, reg, val)
#define portaddr(port, reg)
#define __raw_writel(v,p)
4.函数的注册方法