您的位置:首页 > 其它

简单的LED驱动

2012-11-12 13:36 197 查看
编译模块的Makefile文件
ifeq ($(KERNELRELEASE),)
# set your object kernel dir
KERNELDIR ?= /source/kernel/linux-2.6.22.6-farsight
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers
.PHONY: modules modules_install clean
else
obj-m := led_drv.o
endif
编译模块的Makefile文件、
注:如果一个模块由多个*.c文件编译而成,那么应该如下编写Makefile文件:
obj-m := led_drv.o
module-objs := file1.o file2.o

驱动程序详细设计:

#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/fcntl.h>
#include <linux/slab.h>
#include <asm/io.h>

MODULE_LICENSE("GPL");

#define rGPFDAT 0x56000054 //实际IO的物理地址
#define rGPFCON 0x56000050

#define LED_ON 0x1
#define LED_OFF 0x2

//static dev_t my_led_id;
static int led_major = 257; //主设备号

struct __my_led
{
struct cdev dev;
unsigned char *vrGPFDAT;
unsigned short int *vrGPFCON;
}my_led;

static int my_led_open(struct inode *inode, struct file *filp)
{
struct __my_led *p = container_of(inode->i_cdev, struct __my_led, dev);
filp->private_data = p;
//实际物理地址映射到内核虚拟地址
p->vrGPFDAT = (unsigned char *)ioremap(rGPFDAT, 0x1);
p->vrGPFCON = (unsigned short int *)ioremap(rGPFCON, 0x2);
*p->vrGPFCON = (*p->vrGPFCON) & 0x00ff |(0x55<<8);
// *p->vrGPFDAT = 0x30;
// printk("open my_led, p->vrGPFDAT = 0x%x, p->vrGPFCON = 0x%x\n", p->vrGPFDAT, p->vrGPFCON);

return 0;
}

static int my_led_release(struct inode *inode, struct file *filp)
{
printk("close my_led\n");
filp->private_data = NULL;
//解除映射
iounmap(my_led.vrGPFDAT);
iounmap(my_led.vrGPFCON);

return 0;
}

static int my_led_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct __my_led *p = filp->private_data;
int ret = 0;
// printk("in ioctl_dri func , cmd = %d, p->vrGPFDAT = 0x%x\n", cmd, p->vrGPFDAT);
switch (cmd)
{
case LED_ON:
*p->vrGPFDAT &= 0x0f; //GPF4,5,6,7 置为低电平
break;
case LED_OFF:
*p->vrGPFDAT |= 0xf0;//GPF4,5,6,7 置为高电平
break;
default:
ret = EINVAL;
break;
}

return ret;
}

static const struct file_operations my_led_fops =
{
.owner = THIS_MODULE,
.open = my_led_open,
.release = my_led_release,
.ioctl = my_led_ioctl,
};

static void my_led_setup_cdev(void)
{
int err;
int devno=MKDEV(led_major, 0);

//是字符设备关联一个my_led_fops
cdev_init(&my_led.dev, &my_led_fops);
my_led.dev.owner = THIS_MODULE;
//添加字符设备
err = cdev_add(&my_led.dev, devno, 1);
if (err)
{
printk("add my_led device error, err = %d \n", err);
return ;
}

return;
}

int __init my_led_init(void)
{
int ret;
dev_t dev = MKDEV(led_major,0);

//注册设备号
memset(&my_led, 0, sizeof(my_led));
ret = alloc_chrdev_region(&dev, 0, 1, "my_led");
if (ret < 0)
{
printk("alloc led device id error \n");
return ret;
}
my_led_setup_cdev();
printk("add my_led device ok!\n");
printk(KERN_NOTICE"[DEBUG] adc device major is %d\n",led_major);

return 0;
}

void __exit my_led_exit(void)
{
//注意顺序不能颠倒,要和加载设备时步骤相反
cdev_del(&my_led);
unregister_chrdev_region(MKDEV(led_major,0) ,1);
printk("my-adc device uninstalled\n");
}
module_init(my_led_init);
module_exit(my_led_exit);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: