首先贴上代码:
字符设备驱动代码:
/**
*file name: led.c
*/
#include <linux/sched.h> #include <linux/signal.h> #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/random.h> #include <linux/poll.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/wait.h> #include <linux/mutex.h> #include <linux/io.h> #include <linux/fs.h> static struct class *led_class; //创建类 static struct class_device *led_class_devs[4]; //创建类对应的设备 1个总设备文件 3个单个灯的设备文件 volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; int led_open(struct inode *inode, struct file *fp) { int minor = MINOR(inode->i_rdev); //获取打开设备文件的次设备号 switch(minor) { case 0: *gpfcon &= ~((0x3<<(4*2))|(0x3<<(5*2))|(0x3<<(6*2))); *gpfcon |= (0x1<<(4*2))|(0x1<<(5*2))|(0x1<<(6*2)); break; case 1: *gpfcon &= ~(0x3<<(4*2)); *gpfcon |= (0x1<<(4*2)); break; case 2: *gpfcon &= ~(0x3<<(5*2)); *gpfcon |= (0x1<<(5*2)); break; case 3: *gpfcon &= ~(0x3<<(6*2)); *gpfcon |= (0x1<<(6*2)); break; } return 0; } ssize_t led_read(struct file *fp, char __user *c, size_t *t){ int minor = MINOR(fp->f_dentry->d_inode->i_rdev);
char leds_status;
switch(minor)
{
case 0:
leds_status = (~((*gpfdat & ((1<<4)|(1<<5)|(1<<6)))>>4))&0x7;
copy_to_user(buff,(const void *)&leds_status,1); //将数据从用户拷贝到内核空间
break;
case 1:
leds_status = (~(*gpfdat>>4))&0x1;
copy_to_user(buff,(const void *)&leds_status,1);
break;
case 2:
leds_status = (~(*gpfdat>>5))&0x1;
copy_to_user(buff,(const void *)&leds_status,1);
break;
case 3:
leds_status = (~(*gpfdat>>6))&0x1;
copy_to_user(buff,(const void *)&leds_status,1);
break;
} } ssize_t led_write(struct file *fp, const char __user *buf, size_t count, loff_t *ppos){ int minor = MINOR(fp->f_dentry->d_inode->i_rdev); int val; copy_from_user(&val, buf, 1); switch(minor) { case 0: val &= 0x1; *gpfdat &= ~((1<<4)|(1<<5)|(1<<6)); *gpfdat |= (val<<4)|(val<<5)|(val<<6); break; case 1: *gpfdat &= ~(1<<4); *gpfdat |= (val&0x1)<<4; break; case 2: *gpfdat &= ~(1<<5); *gpfdat |= (val&0x1)<<5; break; case 3: *gpfdat &= ~(1<<6); *gpfdat |= (val&0x1)<<6; break; } } struct file_operations led_fops={ .owner = THIS_MODULE, .open = led_open, .write = led_write, }; int major; static int led_init(void) { int minor; major = register_chrdev( 0,"led_drv", &led_fops ); //当指定的设备号为0时,系统会自动生成一个设备号 led_class = class_create(THIS_MODULE, "my_leds"); if(IS_ERR(led_class)) return PTR_ERR(led_class); led_class_devs[0] = class_device_create(led_class,NULL,MKDEV(major,0),NULL,"my_leds"); //创建设备文件 if(unlikely(IS_ERR(led_class_devs))) return PTR_ERR(led_class_devs); for( minor=1; minor<4; minor++ ) { led_class_devs[minor] = class_device_create(led_class,NULL,MKDEV(major,minor),NULL,"my_led%d",minor); if(unlikely(IS_ERR(led_class_devs))) return PTR_ERR(led_class_devs); } gpfcon = (volatile unsigned long *)ioremap(0x56000050,16); //物理地址映射为虚拟地址 gpfdat = gpfcon + 1; printk("led install Module "); return 0; } static void led_exit(void) { unregister_chrdev( major, "led_drv" ); class_device_unregister(led_class_devs[0]); //注销设备文件 class_device_unregister(led_class_devs[1]); class_device_unregister(led_class_devs[2]); class_device_unregister(led_class_devs[3]); class_destroy(led_class); //销毁类 iounmap(gpfcon); //取消物理地址映射 printk("led Module exit "); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
模块的Makefile:
obj-m:=led.o KERNELDIR:=/home/jz2440/linux-2.6.22.6 PWD:=$(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *.mod.c *.mod.o *.ko *.symvers
测试文件:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main( int argc, char **argv ) { int fd; int val = 1; if(argc != 3 ) { printf("please input righ data "); printf("eg:/dev/my_leds <on"off> "); return 0; } fd = open(argv[1], O_RDWR); if(fd<0) { printf("open failed "); return 0; } if(strcmp(argv[2],"on")==0) { val = 0; } else { val = 1; } write(fd, &val, 4);
read(fd, &val,1);
printf("led status =%d ",val); return 0; }
完