说明
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);
}