基于mini2440的led字符设备驱动程序

2019-07-13 08:24发布

已经大三了开始进行专业课的学习了,一学年的时间要学习完linux应用程序和linux驱动程序,按照进度感觉什么也学习不到。进入大三也有些烦躁,学习也有些急功近利,必须让自己沉下心读读书、敲敲代码了。
刚开始学习嵌入式linux驱动程序,看了些以前国嵌的视频发现一些函数根本找不到,很多的函数已经更新了。linux内核代码更新比较快,很多新的技术会很快取代旧的技术,这也是linux作为开源系统的特点。学习linux不能只记函数,而是要更深层次的了解其原理
在字符驱动中struct file、struct inode、struct cdev、struct file_operations、unsigned long copy_from_user、unsigned long copy_to_user是比较重要的几个结构体和函数,它们是内核空间与用户空间连接的桥梁。
基于mini2440的嵌入式linux的led驱动程序: /* * led_device.c * * Created on: 2016年11月3日 * Author: chy */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct leds { /*led描述*/ struct cdev led_dev; char val[2]; volatile unsigned long *gpio_coun; volatile unsigned long *gpio_data; }; static struct leds led; static ssize_t open_leds(struct inode *led_inode,struct file *file_led) /*打开文件*/ { *led.gpio_coun &= (~(0x3 << 5 | 0x3 << 6 | 0x3 << 7 | 0x3 << 8)); *led.gpio_coun |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8); *led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8)); *led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8); return 0; } static ssize_t write_leds(struct file *file_led,const char __user *bufer,size_t size,loff_t *off) /*点亮所有led*/ { int val,ssize; ssize = copy_from_user(&led.val,bufer,strlen(bufer)); /*将数据由用户区拷贝到内核区*/ if(ssize < 0) return -1; val = led.val[0] - '0'; if(!val) *led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8)); else *led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8); return 0; } static ssize_t read_leds(struct file *file_led,char __user *bufer,size_t size,loff_t *off) /*读取数据*/ { int f; f = copy_to_user((void *)bufer,&led.val,strlen(led.val)); /*将数据由内核区拷贝到用户区*/ if(f < 0) return -1; return strlen(bufer); } static long ioctl_leds(struct file *file_led,unsigned int cmd,unsigned long arg) /*发送文件命令*/ { if((cmd !=0 && cmd != 1) || (arg > 3 || arg < 0)) return -1; switch(cmd){ case 0: *led.gpio_data &= ~(0x1 << (arg + 5)); break; case 1: *led.gpio_data |= (0x1 << (arg + 5)); break; } return 0; } static int close_file(struct inode *inode,struct file *filep) /*关闭文件*/ { *led.gpio_data &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8)); *led.gpio_data |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8); return 0; } struct file_operations file_oper = { .owner = THIS_MODULE, .open = open_leds, .write = write_leds, .read = read_leds, .unlocked_ioctl = ioctl_leds, .release = close_file, }; dev_t dev_num; static struct class *class_file = NULL; static int init_leds_device(void) /*加载驱动模块*/ { alloc_chrdev_region(&dev_num,0,1,"my_leds"); /*动态分配设备号*/ led.led_dev.owner = THIS_MODULE; /*设备模板*/ cdev_init(&led.led_dev,&file_oper); /*初始化cdev*/ cdev_add(&led.led_dev,dev_num,1); /*注册设备*/ led.gpio_coun = (volatile unsigned long *)ioremap(0x56000010, 4); /*led端口映射*/ led.gpio_data = led.gpio_coun + 1; /*自动创建设备文件l*/ class_file = class_create(THIS_MODULE,"my_leds"); device_create(class_file,NULL,dev_num,NULL,"my_leds"); return 0; } static void exit_leds_device(void) /*卸载驱动模块*/ { cdev_del(&led.led_dev); /*注销设备*/ /*删除设备文件*/ device_destroy(class_file, dev_num); class_destroy(class_file); unregister_chrdev_region(dev_num,1); /*注销设备号*/ /*取消端口映射*/ iounmap(led.gpio_coun); return; } MODULE_LICENSE("GPL"); module_init(init_leds_device); /*加载模块*/ module_exit(exit_leds_device); /*卸载模块*/
驱动程序的Makefile: obj-m:=./src/led_device.o all: make -C /home/chy/linux-kernel/ M=$(PWD) modules cp -f ./src/led_device.ko ~/NFS/qu_dong/ clean: make -C /home/chy/linux-kernel/ M=$(PWD) clean rm -f ~/NFS/qu_dong/led_device.ko /home/chy/linux-kernel/  是开发板linux内核的路径,linux内核必须为编译过的且和开发板上跑的内核配置是一样的。不要使用友善之臂提供编译内核的默认方法,自己重新配置内核时去掉友善之臂的led驱动模块。
验证驱动程序: /* * led_test.c * * Created on: 2016年11月3日 * Author: chy */ #include #include #include #include #include #include #include #include #define Max 256 #define NO 0 #define OFF 1 #define ERR(msg,f) { if(f < 0) { fprintf(stderr,"%s",msg); exit(1); } } int main(int argc,char *argv[]) { int file_id,write_num; char buf[Max] = "0"; file_id = open("/dev/my_leds", O_RDWR); /*打开文件*/ ERR("open file failed ",file_id); printf("Led All NO "); ERR("write file failed ",write(file_id,"0",Max)); /*点亮所有led*/ sleep(2); /*进程睡眠2秒*/ close(file_id); /*关闭文件*/ sleep(1); file_id = open("/dev/my_leds", O_RDWR);/*打开文件*/ ERR("open file failed ",file_id); int i; /* 流水点亮、熄灭 */ printf("流水 "); for(i = 0; i < 4; i++){ ERR("ioctl failed ",ioctl(file_id, NO,i)); sleep(1); ERR("ioctl failed ",ioctl(file_id, OFF,i)); } /* 读取数据 */ ERR("read file faile ",read(file_id,&buf,Max)); printf("read data is: %s ",buf); close(file_id); exit(0); }