static int __init dev_init(void) { int ret; int i; for (i = 0; i < 4; i++) { s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); s3c2410_gpio_setpin(led_table[i], 0); } ret = misc_register(&misc); printk (DEVICE_NAME"\tinitialized\n"); return ret; }这个函数主要调用了三个函数来完成初始化以及注册。首先循环四次调用s3c2410_gpio_cfgpin和s3c2410_gpio_setpin,前一个函数用来配置引脚的功能,后一个函数用来设置引脚的值,这两个函数后面会详细分析。最后调用misc_register注册一个杂项设备,然后输出调试信息。这个misc_register函数是led驱动程序的关键,在后面的杂项设备中再进行分析。当你insmod的时候这个函数被调用,模块就被注册到内核中了。
static void __exit dev_exit(void) { misc_deregister(&misc); }
下面分析led驱动程序的系统调用方法也就是文件操作。可以看出mini2440的led驱动除了ioctl方法外,其他的方法都没有,就连open都没有。为什么连open这个最基本的必须的系统调用都没有实现,这个和杂项设备有关,因为杂项设备实现了open方法,以后会详细分析。我们看看ioctl都做了什么。
static int sbc2440_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { switch(cmd) { case 0: case 1: if (arg > 4) { return -EINVAL; } s3c2410_gpio_setpin(led_table[arg], !cmd); return 0; default: return -EINVAL; } }这个ioctl的实现和我们在ldd3中看到有所不一样,因为没有什么幻数什么的,命令只是简单的0,1。标准的ioctl命令号的确是像ldd3中说的那样要幻数,序数等的,以防止与其他ioctl命令号冲突。这里是为了简单,就用0,1代替了。这里也调用了s3c2410_gpio_setpin这个函数,用于设置引脚的值。下面分析一下这个函数
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) { void __iomem *base = S3C24XX_GPIO_BASE(pin); unsigned long offs = S3C2410_GPIO_OFFSET(pin); unsigned long flags; unsigned long dat; local_irq_save(flags); dat = __raw_readl(base + 0x04); dat &= ~(1 << offs); dat |= to << offs; __raw_writel(dat, base + 0x04); local_irq_restore(flags); }这个函数在arch/arm/plat-s3c24xx/gpio.c中定义,实现了设置引脚值的功能, S3C24XX_GPIO_BASE(pin)这个宏得到具体引脚基地址,S3C2410_GPIO_OFFSET(pin)得到相应引脚的偏移,这个两个宏的实现与s3c2440的寄存器表示以及操作实现有关系,后面再详细分析。
static unsigned long led_table [] = { S3C2410_GPB(5), S3C2410_GPB(6), S3C2410_GPB(7), S3C2410_GPB(8), };这个是led引脚数组
static unsigned int led_cfg_table [] = { S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, };这个是引脚功能数组,四个引脚都为输出
#define S3C24XX_GPIO_BASE(pin) S3C2410_GPIO_BASE(x) #define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)pin 是 S3C2410_GPn(_nr) 形式的宏
#define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr))而 S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A)
#define S3C2410_GPIO_NEXT(__gpio) ((__gpio##_START) + (__gpio##_NR) +CONFIG_S3C_GPIO_SPACE + 0)因为 S3C2410_GPIO_A_START=0
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
得出来的就是 pin - 32
所以 S3C2410_GPIO_OFFSET(S3C2410_GPB(5)) = 37- 32 = 5