在完成这个驱动代码的时候,中间经历了很多的小问题,具体的总结我都放在了这个地方:
http://www.jyguagua.com/?p=723
针对字符驱动,可以参考LDD3去学习研究,LDD3讲的非常详细,本文只是针对kernel 2.6.32.2环境下对mini 2440的开发板的led驱动做演示:
首先看下leddrive.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> //#include <asm/irq.h> //#include <asm/arch/regs-gpio.h> //#include <asm/hardware.h> #include <asm/io.h> #include <mach/irqs.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/device.h> MODULE_LICENSE("GPL"); struct class *led_class; //struct class_device *led_dev; /*led IO寄存器定义*/ volatile unsigned long *gpbcon = NULL; volatile unsigned long *gpbdat = NULL; volatile unsigned long *gpbup = NULL; /*主设备号*/ int major; static int led_open(struct inode *inode, struct file *file) { printk("first_drv_open\n"); /*配置GPBCON, GPBDAT, GPBUP寄存器,根据mini2440原理图可知是GPB_IO口*/ *gpbcon &= ~((0x3 << (5*2)) | (0x3 << (6*2)) | (0x3 << (7*2)) | (0x3 << (8*2))); *gpbcon |= (0x1 << (5*2)) | (0x1 << (6*2)) | (0x1 << (7*2)) | (0x1 << (8*2)); *gpbdat |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8); //上拉电阻关闭 *gpbup |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8); return 0; } static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { printk("first_drv_write1\n"); int val; //将数据从用户空间拷贝至内核空间 copy_from_user(&val, buf, count); if (val == 1) { printk("first_drv_write2\n"); //如果是1的话,就给各端口写入低电平,这样led就点亮,反之则灭,如下一种情况 *gpbdat &= ~((1 << 5) | (1 << 6) | (1 << 7) | (1 << 8)); } else { printk("first_drv_write3\n"); *gpbdat |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8); } return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .write = led_write, }; static int led_init(void) { //获取主设备号 major = register_chrdev(0, "led_drv", &led_fops); //通过class_create以及device_create来在/dev路径下创建设备led led_class = class_create(THIS_MODULE, "leddrv"); device_create(led_class, NULL, MKDEV(major, 0), NULL, "led"); //内存映射,调用ioremap函数,对应的GPBDAT, GPBUP只是增加单位而已 gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16); gpbdat = gpbcon + 1; gpbup = gpbdat + 1; return 0; } static void led_exit(void) { //注销设备号 unregister_chrdev(major, "led_drv"); //删除设备 device_destroy(led_class, MKDEV(major, 0)); class_destroy(led_class); iounmap(gpbcon); } module_init(led_init); module_exit(led_exit);
然后是Makefile:
ifneq ($(KERNELRELEASE),) obj-m := leddrive.o else #KERNELDIR ?= /lib/modules/$(shell uname -r)/build KERNELDIR ?= /usr/src/linux-2.6.32.2 PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif
最后是我们的测试代码,测试代码就是读取dev下我们新创建的设备,然后发送参数,使得led点亮,代码ledtest.c如下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* firstdrvtest on * firstdrvtest off */ int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/led", O_RDWR); if (fd < 0) { printf("can't open!\n"); } if (argc != 2) { printf("Usage :\n"); printf("%s <on|off>\n", argv[0]); return 0; } if (strcmp(argv[1], "on") == 0) { val = 1; } else { val = 0; } write(fd, &val, 4); return 0; }
如上代码编好之后,可以通过如下命令获得leddrive.ko 和测试程序ledtest:
[ypf@localhost linux]#make #获得leddrive.ko [ypf@localhost linux]#arm-linux-gcc -o ledtest ledtest.c #获得测试程序ledtest
运行如下:
[ypf@localhost linux]#insmod leddrive.ko [ypf@localhost linux]#./ledtest on #打开led [ypf@localhost linux]#./ledtest off #关闭led
文章的脚注信息由WordPress的wp-posturl插件自动生成