开发环境:Ubuntu 10.04
开发板: Frindly ARM Micro 2440
一、首先查看与led相关的原理图,如下图所示:
从图中可以看出LED1、2、3、4分别连接到S3C2440的GPB5、GPB6、GPB7、GPB8这四个Pin脚,且为共阳极,当IO口输出为低时,对应的LED会点亮。
以LED1为例,其对应的pin脚为GPB5,当GPB5/nXBACK被配置成GPIO,方向为output后,若输出低电平,则LED1会被点亮。
从datasheet中查到GPB5的基地址为
0x56000010,通过ioremap后的虚拟地址为:
0xc4810010,但是驱动代码中使用的却是:0xfb000010,这其中有什么玄机吗?为什么使用的不是虚拟地址
0xc4810010?
二 source code
1. source code
list
- #include <linux/miscdevice.h>
-
#include <linux/delay.h>
-
#include <asm/irq.h>
-
#include <mach/regs-gpio.h>
-
#include <mach/hardware.h>
-
#include <linux/kernel.h>
-
#include <linux/module.h>
-
#include <linux/init.h>
-
#include <linux/mm.h>
-
#include <linux/fs.h>
-
#include <linux/types.h>
-
#include <linux/delay.h>
-
#include <linux/moduleparam.h>
-
#include <linux/slab.h>
-
#include <linux/errno.h>
-
#include <linux/ioctl.h>
-
#include <linux/cdev.h>
-
#include <linux/string.h>
-
#include <linux/list.h>
-
#include <linux/pci.h>
-
#include <linux/gpio.h>
-
#include <asm/uaccess.h>
-
#include <asm/atomic.h>
-
#include <asm/unistd.h>
-
-
-
#define DEVICE_NAME "leds"
-
-
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 dev_fops = {
-
.owner = THIS_MODULE,
-
.ioctl = sbc2440_leds_ioctl, //只注册了ioctl,真省事儿...
-
};
-
-
static struct miscdevice misc = {
-
.minor = MISC_DYNAMIC_MINOR,
-
.name = DEVICE_NAME,
-
.fops = &dev_fops,
-
};
-
-
static int __init dev_init(void) //模块注册函数
-
{
-
int ret;
-
-
int i;
-
-
/* datasheet上的基地址,经过ioremap后为: 0xc4810010,但是在 函数 s3c2410_gpio_cfgpin却使
-
* 用的是0xfb000010,搞不懂为什么这样用? 下面三句是我自己加的打印信息。
-
*/
-
unsigned int __iomem base = (unsigned int)ioremap(0x56000010,1);
-
printk(KERN_INFO"s3c2410_gpio_cfgpin ioremap == 0x%0x\n", base);
-
iounmap(base);
-
-
for (i = 0; i < 4; i++) {
-
printk(KERN_INFO"dev_init\n");
-
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //管脚复用为GPIO
-
s3c2410_gpio_setpin(led_table[i], 0); //配置GPIO的方向为output
-
}
-
-
ret = misc_register(&misc); //注册设备
-
-
printk (DEVICE_NAME"\tinitialized\n");
-
-
return ret;
-
}
-
-
static void __exit dev_exit(void) //注销设备
-
{
-
misc_deregister(&misc);
-
}
-
-
module_init(dev_init);
-
module_exit(dev_exit);
-
MODULE_LICENSE("GPL");
-
MODULE_AUTHOR("FriendlyARM Inc.");
2. source code
浅析
在模块注册函数static
int __init dev_init(void) 中使用for循环来初始化led要用到的GPIO口:
- for (i = 0; i < 4; i++) {
- printk(KERN_INFO"dev_init\n");
- s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //管脚复用为GPIO
- s3c2410_gpio_setpin(led_table[i], 0); //配置GPIO的方向为output
-
}
在函数 s3c2410_gpio_cfgpin()和
s3c2410_gpio_setpin()都出现了一对与中断相关的函数:
- local_irq_save();
-
local_irq_restore();
这两个函数在《Linux内核设计与实现
第二版》中有详细的解释,见下文蓝字部分:
6.7.1
禁止和激活中断
用于禁止当前处理器上的本地中断,随后又激活他们的语句为:
- local_irp_disable();
-
/* 禁止中断 */
-
local_irq_enable();
这两个函数通常以单个汇编指令来实现。
如果在调用local_irq_disable()例程之前已经禁止了中断,那么该例程往往会带来潜在的危险;
同样相应的local_irq_enable()例程也存在潜在危险,因为它将无条件地激活中断,尽管这些中断可能在开始时就是关闭的。所以我们需要一种机制把中断恢复到以前的状态而不是简单地禁止或激活。内核普遍关心这点,因为内核中一个给定的代码路径既可以在中断激活的情况下到达,也可以在中断禁止的情况下到达,这取决于具体的调用链。随着内核的不断增长,要想知道到达这个函数的所有代码路径将变得越来越困难,因此,在禁止中断之前保存中断系统的状态会更加安全一些。相反,在准备激活中断时,只需要把中断恢复到它们原来的状态。
- unsigned long flags;
-
local_irq_save(flags); /* 禁止中断 */
-
/* … */
-
local_irq_restore(flags); /* 中断被恢复到它们原来的状态 */
这些方法至少部分要以宏的形式实现,因此表面上flags参数是以值传递的。该参数包含具体体系结构的数据,也就是包含中断系统的状态。至少有一种体系结构把栈信息与值相结合(SPARC),因此flags不能传递给另一个函数(特别是它必须驻留在同一栈帧中)。基于这个原因,对local_irq_save()的调用和对local_irq_restore()的调用必须在同一个函数中进行。
接下来使用misc_register(&misc)来注册设备。参数&misc的类型为struct
miscdevice,在Linux中,miscdevice是特殊的字符设备,注册驱动程序时采用misc_register函数注册,此函数中会自动创建设备节点,无需使用mknod指令,这是因为misc_register()会调用device_create(),device_creater()函数会以主设备号10跟次设备号来注册设备并创建设备节点。在include/linux/miscdevice.h文件中,要把自己的miscdevice从设备号定义在这里,如果不定义的话,可以使用MISC_DYNAMIC_MINOR参数,这样内核会自动分配一个从设备号。
应用层的ioctl调用,最终会落在函数
sbc2440_leds_ioctl中,在此函数中,实际操GPIO口时是使用
s3c2410_gpio_setpin来写相应的数据寄存器。
代码的其它部分都很明了,不再赘述。
阅读(3588) | 评论(0) | 转发(1) |