2011-03-25 15:51:25| 分类: Linux + ARM9 +LP | 标签:linux下一个led驱动程序的详细分析 |字号 订阅
/* led.c
By Genghao
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "led.h"
#define DEV_NAME "led"
#define GPIO_IOBASE io_p2v(GPIO_BASE)
/*
io_p2v(x)和 这个函数的功能是物理地址(physical)向虚拟地址(virtual)的转换,对于有MMU的ARM9来说这个很重要,有p to v那么就一定有v to p,根据不同的映射关系,映射函数自然也就不同,在linux中该公式是以宏的方式给出的。
*/
static struct semaphore led_sem;
static int tp_led_Istruct inode *inode, struct file *I)
{
__raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05
try_module_get(THIS_MODULE);
/*
try_module_get(THIS_MODULE);函数的功能是使得该模块的引用计数加1,相反既然有加一,那么自然也就有减一,下面就会看到。
*/
printk( KERN_INFO DEV_NAME " opened!\n");
return 0;
}
static int tp_led_release(struct inode *inode, struct file *filp)
{
__raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05
module_put(THIS_MODULE);
/*
模块的引用计数减一
*/
printk(KERN_INFO DEV_NAME " released!\n");
return 0;
}
static ssize_t tp_led_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
{
int i;
unsigned char ctrl=0;
if (count > 1) {
return -EFBIG;
}
if (down_interruptible(&led_sem))
return -ERESTARTSYS;
get_user(ctrl, (u8 *)buff);
/*
get_user
Name
get_user -- Get a simple variable from user space.
Synopsis
get_user ( x, ptr);
Arguments
x
Variable to store result.
ptr
Source address, in user space.
Context
User context only. This function may sleep.
Description
This macro copies a single simple variable from user space to kernel space. It supports simple types like char and int, but not larger data types like structures or arrays.
ptr must have pointer-to-simple-variable type, and the result of dereferencing ptr must be assignable to x without a cast.
Returns zero on success, or -EFAULT on error. On error, the variable x is set to zero.
意思是将一个简单的变量从用户空间传送到内核空间,毕竟驱动是运行在内核中。如果该函数(这是个宏)返回-EFAULT,表示失败,失败后,会将x付为0;函数成功返回0标识成功。
*/
//printk("write date ctrl=0x%0x\n", ctrl);
i = (ctrl-0x30)&0x03;
if(i==0) {
__raw_writel(_BIT(5), GPIO_P3_OUTP_CLR(GPIO_IOBASE));//CLR GPIO_05
} else {
__raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05
}
/*
由于LED的电路属于上拉型,所以只要AMR9 的IO口输出低电平,那么LED就将被点亮。
*/
up(&led_sem);
return count;
}
static int tp_led_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
{
int level;
if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) {
return -ENOTTY;
}
/*
上面的这段代码很有意思,说的通俗点,是为了防止命令冲突。今天先不讲这个,因为这个讲起来很费时间,但是很重要,下次我会详细说明!
*/
if (_IOC_NR(cmd) >= LED_IOC_MAXNR) {
return -ENOTTY;
}
/*
LED_IOC_MAXNR是定义在.h中的一个常量,它的意识是该模块的最大引用计数。
*/
printk("arg=0x%x\n", arg);
switch (cmd) {
case SET_LED_ON:
//printk("LED_ON\n");
__raw_writel(_BIT(5), GPIO_P3_OUTP_CLR(GPIO_IOBASE));//CLR GPIO_05
udelay(10);
break;
case SET_LED_OFF:
//printk("LED_OFF\n");
__raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05
udelay(10);
break;
default:
__raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05
break;
}
return 0;
}
static struct file_operations tp_led_fops = {
.owner = THIS_MODULE,
.write = tp_led_write,
.ioctl = tp_led_ioctl,
.open = tp_led_open,
.release = tp_led_release,
};
/*
file_operation就是把系统调用和驱动程序关联起来的关键数据结构 。
*/
static struct miscdevice tp_led_miscdev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &tp_led_fops,
};
/*
先看下面的层次结构图
(基类)
kobject --------------------
/ \ \
/ \ \
device cdev driver
/ \ (设备驱动操作方法) \
/ \ \
miscdevice platform_device platform_driver
(设备驱动操作方法) (设备的资源) (设备驱动)
杂项设备(miscdevice)
杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。因为有的设备不是标准的,这个“标准的”可以这样理解,例如有的设备需要总线,而有压根就不需要。在 Linux 内核的include\linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主编号10 ,一起归于miscdevice, 也就是说,misc设备其实也就是特殊的字符设备。
对于想LED,beep这些不属于标准的设备,使用miscdevice 模型驱动比使用字符设备驱动要简单的多,复杂度大大降低,这就是为什么有这类设备的原因。
*/
static int tp_led_probe(struct device *dev)
{
int ret;
printk(KERN_INFO DEV_NAME " probing...\n");
ret = misc_register(&tp_led_miscdev);
if (ret)
printk(KERN_ERR "Failed to register miscdev.\n");
return ret;
}
static int tp_led_remove(struct device *dev)
{
misc_deregister(&tp_led_miscdev);
printk(KERN_INFO DEV_NAME " removed!\n");
return 0;
}
struct platform_device *tp_led_device;
static struct device_driver tp_led_driver = {
.name = DEV_NAME,
.owner = THIS_MODULE,
.bus = &platform_bus_type,
.probe = tp_led_probe,
.remove = tp_led_remove,
};
/*通过上面的结构体 建立总线、驱动程序、设备三者之间的联系*/
static int __init tp_led_init(void)
{
int rc = 0;
int i;
printk(KERN_INFO DEV_NAME " init...\n");
__raw_writel(_BIT(5), GPIO_P3_MUX_CLR(GPIO_IOBASE));//pin as GPO_05
__raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05
tp_led_device = platform_device_register_simple(DEV_NAME, -1, NULL, 0);
if(IS_ERR(tp_led_device)) {
goto out;
}
rc = driver_register(&tp_led_driver);
if (rc < 0) {
platform_device_unregister(tp_led_device);
}
sema_init(&led_sem, 1);
out:
return rc;
}
static void __exit tp_led_exit(void)
{
__raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE));//SET GPIO_05
printk(KERN_INFO DEV_NAME " init...\n");
driver_unregister(&tp_led_driver);
platform_device_unregister(tp_led_device);
printk(KERN_INFO "tp_led exit!\n");
}
module_init(tp_led_init);
/*模块的初始化*/
module_exit(tp_led_exit);
/*模块的卸载*/
MODULE_AUTHOR("Abing
/*作者*/
MODULE_DESCRIPTION("ARM9-3250 led Driver");
/*描述*/
MODULE_LICENSE("GPL");