mini2440 LED驱动程序之完全剖析
来源:互联网 发布:linux初学者版本 编辑:程序博客网 时间:2024/05/19 14:52
说明
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_dev0return 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_OUTPUTS3C2410_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;//得到要设置的功能,本例为1function <<= 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);}
- mini2440 LED驱动程序之完全剖析
- mini2440 led驱动程序经典分析
- mini2440 led驱动程序经典分析
- mini2440 led驱动程序经典分析
- mini2440 led驱动程序测试实验
- 完全解析input子系统设备模型之mini2440按键驱动程序
- 完全解析input子系统设备模型之mini2440按键驱动程序
- 完全解析input子系统设备模型之mini2440按键驱动程序
- 完全解析input子系统设备模型之mini2440按键驱动程序
- 完全解析input子系统设备模型之mini2440按键驱动程序
- mini2440的led的Linux驱动程序
- 基于mini2440的led字符设备驱动程序
- linux-2.6.32在mini2440开发板上移植(16)之LED 驱动程序移植
- linux-2.6.32在mini2440开发板上移植(16)之LED 驱动程序移植
- linux-2.6.32在mini2440开发板上移植(16)之LED 驱动程序移植
- mini2440驱动分析之LED
- [2014.3.20]mini2440用ioremap写LED驱动程序
- 字符设备驱动程序之LED驱动程序
- 绿皮书再版!ECMAScript 5th有讲呵!
- 数据结构---栈实现
- C++构造函数
- Decorator Pattern (装饰者模式)
- 第5周实验报告任务1.4
- mini2440 LED驱动程序之完全剖析
- 编码方式转换
- 配置config.bib的注意事项以及错误分析(含OEMAddressTable介绍)
- websphere 中datasource 的配置
- linux下视频采集代码
- POJ1157 LITTLE SHOP OF FLOWERS
- Hibernate锁机制
- android获得图片资源的三种方式
- 取消Windows 2003登录及关机提示