说明
mini2440中提供的内核自带了LED的驱动程序,但该驱动是以杂项设备的形式编写,不太适合初学者,作者根据LDD3第三章内容将代码进行了修改,希望对一些初学者有所帮助
转载请说明出处:
http://blog.csdn.net/alleincao/article/details/7362558
驱动程序:
#include <mach/regs-gpio.h> #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/moduleparam.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/gpio.h> #include <linux/device.h> //用于mdev操作 #define DEVICE_NAME "leds" unsigned int gpio_major_number=0; struct cdev gpio_dev; struct class *gpio_class; dev_t dev_nr; static unsigned long led_table [] = { S3C2410_GPB(5), S3C2410_GPB(6), S3C2410_GPB(7), S3C2410_GPB(8), }; static unsigned int led_cfg_table [] = { S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, }; 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; } } static struct file_operations gpio_fops = { .owner = THIS_MODULE, .ioctl = sbc2440_leds_ioctl, }; static int __init dev_init(void) { int ret,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 = alloc_chrdev_region(&dev_nr,0,1,DEVICE_NAME); //动态分配设备号 gpio_major_number = MAJOR(dev_nr); printk(KERN_INFO "Major Num ->%d\n",gpio_major_number); if (ret<0) { printk(KERN_WARNING "gpio:can't get major number %d/n",gpio_major_number); return ret; } cdev_init(&gpio_dev,&gpio_fops); //初始化并向系统添加字符设备 gpio_dev.owner = THIS_MODULE; // gpio_dev.ops = &gpio_fops; ret = cdev_add(&gpio_dev,dev_nr,1); if (ret) { unregister_chrdev_region(dev_nr,1); printk(KERN_NOTICE "Error %d adding gpio device/n",ret); return ret; } gpio_class = class_create(THIS_MODULE, "gpio_class"); //mdev相关,用于自动创建设备节点 if(IS_ERR(gpio_class)) { printk("Err: failed in creating class./n"); return -1; } /* register your own device in sysfs, and this will cause mdev to create corresponding device node */ device_create(gpio_class,NULL,dev_nr, NULL, "gpio_dev%d" ,0);//设备节点名称gpio_dev0 return 0; } static void __exit dev_exit(void) { cdev_del(&gpio_dev); device_destroy(gpio_class,dev_nr); class_destroy(gpio_class); unregister_chrdev_region(dev_nr,1); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Allein.Cao");
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> int main(int argc, char **argv) { int on; int led_no; int fd; if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 || on < 0 || on > 1 || led_no < 0 || led_no > 3) { fprintf(stderr, "Usage: leds led_no 0|1\n"); exit(1); } fd = open("/dev/gpio_dev0", 0); //打开设备节点,需与驱动程序对应起来 if (fd < 0) { perror("open device leds"); exit(1); } ioctl(fd, on, led_no); close(fd); return 0; }
这里就本程序里比较难理解的几个函数做一下解释:
首先,我们看几个宏:
S3C2410_GPB(_nr) S3C2410_GPIO_OUTPUT S3C2410_GPIO_OFFSET(pin) S3C2410_GPIO_BASE(pin)1、/arch/arm/mach-s3c2410/include/mach/gpio-nrs.h
#define S3C2410_GPIO_NEXT(__gpio) \ ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0) #ifndef __ASSEMBLY__ enum s3c_gpio_number { S3C2410_GPIO_A_START = 0, S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A), S3C2410_GPIO_C_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_B), S3C2410_GPIO_D_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_C), S3C2410_GPIO_E_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_D), S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E), S3C2410_GPIO_G_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_F), S3C2410_GPIO_H_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_G), }; #endif /* __ASSEMBLY__ */ /* S3C2410 GPIO number definitions. */ #define S3C2410_GPA(_nr) (S3C2410_GPIO_A_START + (_nr)) #define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr)) #define S3C2410_GPC(_nr) (S3C2410_GPIO_C_START + (_nr)) #define S3C2410_GPD(_nr) (S3C2410_GPIO_D_START + (_nr)) #define S3C2410_GPE(_nr) (S3C2410_GPIO_E_START + (_nr)) #define S3C2410_GPF(_nr) (S3C2410_GPIO_F_START + (_nr)) #define S3C2410_GPG(_nr) (S3C2410_GPIO_G_START + (_nr)) #define S3C2410_GPH(_nr) (S3C2410_GPIO_H_START + (_nr))
算得:
S3C2410_GPB(5) = 37;2、/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
/* general configuration options */ #define S3C2410_GPIO_LEAVE (0xFFFFFFFF) #define S3C2410_GPIO_INPUT (0xFFFFFFF0) /* not available on A */ #define S3C2410_GPIO_OUTPUT (0xFFFFFFF1) #define S3C2410_GPIO_IRQ (0xFFFFFFF2) /* not available for all */ #define S3C2410_GPIO_SFN2 (0xFFFFFFF2) /* bank A => addr/cs/nand */ #define S3C2410_GPIO_SFN3 (0xFFFFFFF3) /* not available on A */ #ifdef CONFIG_CPU_S3C2400 #define S3C24XX_GPIO_BASE(x) S3C2400_GPIO_BASE(x) #define S3C24XX_MISCCR S3C2400_MISCCR #else #define S3C24XX_GPIO_BASE(x) S3C2410_GPIO_BASE(x) #define S3C24XX_MISCCR S3C24XX_GPIOREG2(0x80) #endif /* CONFIG_CPU_S3C2400 */ ................................................... #define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO) //S3C24XX_VA_GPIO代表GPIO模块首地址0x56000000在内核中的虚拟地址 #define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) //得到某个引脚,见下面分析
下面分析关键的2个函数(以GPB5为例)
配置引脚(以配置为输出为例)
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) { void __iomem *base = S3C24XX_GPIO_BASE(pin);//对GPB5来说,得到GPBCON虚拟地址 unsigned long mask; unsigned long con; unsigned long flags; if (pin < S3C2410_GPIO_BANKB) { mask = 1 << S3C2410_GPIO_OFFSET(pin); } else { mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;//计算掩码,在GPBCON中,每个引脚由2位控制 } switch (function) { case S3C2410_GPIO_LEAVE: mask = 0; function = 0; break; case S3C2410_GPIO_INPUT: case S3C2410_GPIO_OUTPUT: case S3C2410_GPIO_SFN2: case S3C2410_GPIO_SFN3: if (pin < S3C2410_GPIO_BANKB) { function -= 1; function &= 1; function <<= S3C2410_GPIO_OFFSET(pin); } else { function &= 3;//得到要设置的功能,本例为1 function <<= S3C2410_GPIO_OFFSET(pin)*2; } } /* modify the specified register wwith IRQs off */ local_irq_save(flags); //关中断 con = __raw_readl(base + 0x00); //读取寄存器值 con &= ~mask; //修改寄存器值 con |= function; __raw_writel(con, base + 0x00); //回写 local_irq_restore(flags); //打开中断 }
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);//对GPBDAT来说,每个引脚由1个控制位控制 dat &= ~(1 << offs); dat |= to << offs; __raw_writel(dat, base + 0x04); local_irq_restore(flags); }