2-驱动_加载函数_卸载函数_注册设备号_led驱动示例代码
2017-02-03 15:17
441 查看
字符设备框架:
字符设备类包含了同种的字符设备。/sys/class
每种设备都有struct cdev来描述的:
用户空间
应用程序(用到设备文件名——mknod “/dev/设备文件名” c 主设备号 次设备号)
内核空间
加载函数
1、申请设备号——dev_t devno = mar_num << 20 | min_num 或者调用 MKDEV(主设备号,次设备号)
2、注册设备号
* 静态注册:register_chrdev_region(devno,需要注册的设备个数,设备文件名);
* 动态注册:alloc_chrdev_region(devno,起始次设备号,次设备号个数,设备文件名);
* 静态注册的优点是设备启动需要的时间短,但是容易造成和已存在设备号冲突
* cdev = kzalloc(sizeof(struct cdev),GFP_KERNEL);
4、初始化字符设备 cdev_init(&cdev,&op);
5、添加字符设备到内核中 cdev_add(&cdev,devno,1);
卸载函数
6、cdev_del(&cdev);
7、注销设备号
unregister_chrdev_region(devno,注销的字符设备个数);
规避版权
自动创建设备节点 描述时设备节点就是设备文件
在讲自动创建设备节点前,必须先了解另一部分知识点:sysfs这种文件系统默认被挂载到了/sys目录下.
它的作用是给用户空间和内核空间提供交互的接口——会将内核中的信息(例如设备号)导出到用户空间,
并且将信息存放到/sys/module/目录名(以模块名称命名的)/uevent文件中.
在用户空间中有一个小程序叫做udev,每次开机的时候会去遍历所有的uevent文件,提取出设备号然后在
/dev/目录下创建设备文件,一旦创建完成后udev阻塞,当我们新加载一个驱动时,sysfs会将新加载的驱动
信息导出到用户空间同时会产生新的uevent文件,一旦产生uevent文件,udev会唤醒。
创建设备文件接口
功能:在/dev目录下自动创建设备文件
参数1:类结构体指针cls
参数2:父设备的device结构体指针,如果没有要写NULL
参数3:设备号
参数4:一些私有数据,如果不用写NULL
参数5:设备文件名,但不一定是完整的设备文件名,如果不使用第六个参数,那么第五个参数就是完整的设备文件名
如果使用第六个参数,那么设备文件名则是由第五和第六两个参数构成的。
参数6:不需要可以省略
用户空间
系统调用接口open 上层的open通过系统调用号来匹配系统调用源码
内核空间
系统调用
内核中:
1、
2、
3、
无论应用层还是底层所谓的读写,都是站在应用层的角度来看待的。
读:数据从内核空间流向用户空间
写:相反
读 :
功能:将内核空间的数据拷贝到用户空间
参数1:第一个参数内核创建。
参数2:用户空间地址
参数3:从内核空间传递给用户空间的数据大小
参数4:偏移量
返回值:成功返回0,失败返回错误码
参数1:用户空间的某个地址
参数2: 内核空间的某个地址
参数3: 需要给用户空间拷贝的字节数
返回值:成功返回0,失败返回错误码
参数1:内核空间的某个地址
参数2:用户空间某个地址
参数3:用户空间给内核空间传递的数据的字节数
应用层:
参数1:文件描述符
参数2: 命令
参数3:如果不需要则省略,如果需要可能是一个普通变量也可能是一个地址
如果参数3需要传递一个结构,这个时候需要传递这个结构的首地址,同时驱动层要想获取数据必须调用copy_from_user或者copy_to_user
如果参数3只是传递一个基本类型的数据,驱动层直接通过第三个参数来接收应用层的数据。
驱动层:
参数2:命令
参数3:接收应用程序传过来的数据
虚拟地址 = ioremap(物理地址,物理地址占用字节数)
head.h
led.c
led.c
字符设备类包含了同种的字符设备。/sys/class
每种设备都有struct cdev来描述的:
struct file_operations *ops = &hello_op cdev_init(&cdev,&hello_op) dev_t *dev = &devno cdev_add(&cdev,devno,1)
用户空间
应用程序(用到设备文件名——mknod “/dev/设备文件名” c 主设备号 次设备号)
内核空间
系统调用 | vfs (struct cdev struct file_operations) | 驱动 设备号 = MKDEV(主设备号,次设备号);
//struct cdev cdev; struct cdev *cdev; 3、初始化file_operations结构体 struct file_operations op = { .owner = THIS_MODULE,//代表了当前模块的意思,不写也没错 .open = hello_open };
加载函数
1、申请设备号——dev_t devno = mar_num << 20 | min_num 或者调用 MKDEV(主设备号,次设备号)
2、注册设备号
* 静态注册:register_chrdev_region(devno,需要注册的设备个数,设备文件名);
* 动态注册:alloc_chrdev_region(devno,起始次设备号,次设备号个数,设备文件名);
* 静态注册的优点是设备启动需要的时间短,但是容易造成和已存在设备号冲突
* cdev = kzalloc(sizeof(struct cdev),GFP_KERNEL);
4、初始化字符设备 cdev_init(&cdev,&op);
5、添加字符设备到内核中 cdev_add(&cdev,devno,1);
卸载函数
6、cdev_del(&cdev);
7、注销设备号
unregister_chrdev_region(devno,注销的字符设备个数);
规避版权
自动创建设备节点 描述时设备节点就是设备文件
在讲自动创建设备节点前,必须先了解另一部分知识点:sysfs这种文件系统默认被挂载到了/sys目录下.
它的作用是给用户空间和内核空间提供交互的接口——会将内核中的信息(例如设备号)导出到用户空间,
并且将信息存放到/sys/module/目录名(以模块名称命名的)/uevent文件中.
在用户空间中有一个小程序叫做udev,每次开机的时候会去遍历所有的uevent文件,提取出设备号然后在
/dev/目录下创建设备文件,一旦创建完成后udev阻塞,当我们新加载一个驱动时,sysfs会将新加载的驱动
信息导出到用户空间同时会产生新的uevent文件,一旦产生uevent文件,udev会唤醒。
struct class *cls; cls = class_create(THIS_MODULE,"hello"); 这里的hello代表了类的名字,会在/sys/class目录下出现一个文件夹叫做hello,而hello的下面可能出现很多代表子设备的软连接
创建设备文件接口
struct device *devs; devs = device_create(类结构体指针,父设备的结构体指针,); struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
功能:在/dev目录下自动创建设备文件
参数1:类结构体指针cls
参数2:父设备的device结构体指针,如果没有要写NULL
参数3:设备号
参数4:一些私有数据,如果不用写NULL
参数5:设备文件名,但不一定是完整的设备文件名,如果不使用第六个参数,那么第五个参数就是完整的设备文件名
如果使用第六个参数,那么设备文件名则是由第五和第六两个参数构成的。
参数6:不需要可以省略
sturct inode { umode_t i_mode 判断设备类型 struct cdev *i_cdev 存放的是驱动层中cdev结构体的首地址 } inode结构体是静态的,最初是存放到磁盘上的,第一次打开文件时会被加载到内核中。inode结构体对于一个文件来讲只有一个。 struct file { const struct file_operations *f_op; 存放了驱动中的file_operations结构体的首地址 } 用来描述文件的动态信息的,只要打开一次文件就会出现一个新的struct file结构体 int (*open) (struct inode *, struct file *); 驱动层中的open完成的功能:打开文件,申请资源,识别次设备号,存放私有数据
用户空间
系统调用接口open 上层的open通过系统调用号来匹配系统调用源码
内核空间
系统调用
内核中:
1、
vi arch/arm/include/uapi/asm/unistd.h 33 #define __NR_open (__NR_SYSCALL_BASE+ 5) 5是系统调用号
2、
跟进 __Nr_open 713 __SYSCALL(__NR_open, sys_open) 通过系统调用找到了sys_open 内核中一个函数
3、
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) return do_sys_open(AT_FDCWD, filename, flags, mode); || \/ struct file *f = do_filp_open(dfd, tmp, &op); || \/ filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU); || \/ 3181 error = do_last(nd, &path, file, op, &opened, pathname); || \/ 2882 goto finish_open; || \/ 794 error = do_dentry_open(file, open, current_cred()); || \/ 727 open = f->f_op->open; 其中f->f_op->open; 我们在驱动中自己实现的函数接口,比如.open = hello_open
无论应用层还是底层所谓的读写,都是站在应用层的角度来看待的。
读:数据从内核空间流向用户空间
写:相反
读 :
功能:将内核空间的数据拷贝到用户空间
参数1:第一个参数内核创建。
参数2:用户空间地址
参数3:从内核空间传递给用户空间的数据大小
参数4:偏移量
size_t read(struct file *,char __user *,size_t ,loff_t *) { 将内核空间的数据拷贝到用户空间 copy_to_user(); }
static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n);
返回值:成功返回0,失败返回错误码
参数1:用户空间的某个地址
参数2: 内核空间的某个地址
参数3: 需要给用户空间拷贝的字节数
size_t write(struct file *,const char __user *,size_t ,loff_t *) { 将用户空间的数据拷贝到内核空间 copy_from_user(); }
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
返回值:成功返回0,失败返回错误码
参数1:内核空间的某个地址
参数2:用户空间某个地址
参数3:用户空间给内核空间传递的数据的字节数
应用层:
int ioctl(int fd,int cmd,...);
参数1:文件描述符
参数2: 命令
参数3:如果不需要则省略,如果需要可能是一个普通变量也可能是一个地址
如果参数3需要传递一个结构,这个时候需要传递这个结构的首地址,同时驱动层要想获取数据必须调用copy_from_user或者copy_to_user
如果参数3只是传递一个基本类型的数据,驱动层直接通过第三个参数来接收应用层的数据。
驱动层:
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
参数2:命令
参数3:接收应用程序传过来的数据
cmd是一个32位的无符号整数,这个整数分成4个部分 8位 8位 2位 14位 幻数(代表某个设备,通常用字符) 代表了序号 方向 传递的参数的类型大小
虚拟地址 = ioremap(物理地址,物理地址占用字节数)
练习 :
Makefileifeq ($(KERNELRELEASE),) #KERNELDIR ?= /lib/modules/$(shell uname -r)/build KERNELDIR ?= /home/linux/linux-3.14/ PWD ?= $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules cp *.ko /rootfs app: arm-none-linux-gnueabi-gcc test.c -o test cp test /rootfs clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean .PHONY: modules clean else obj-m += led.o endif
head.h
#ifndef __HEAD_H_ #define __HEAD_H_ #define MAGIC 'l' #define LED2_ON _IO(MAGIC,0) #define LED2_OFF _IO(MAGIC,1) #define LED3_ON _IO(MAGIC,2) #define LED3_OFF _IO(MAGIC,3) #define LED4_ON _IO(MAGIC,4) #define LED4_OFF _IO(MAGIC,5) #define LED5_ON _IO(MAGIC,6) #define LED5_OFF _IO(MAGIC,7) #define LEDALL_ON _IO(MAGIC,8) #define LEDALL_OFF _IO(MAGIC,9) #endif
led.c
#include <linux/kernel.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include <asm/io.h> #include "head.h" #define LED_MAJOR 505 #define LED_MINOR 0 #define LED_NUM 1 #define LED_NAME "ledx" #define CLS_NAME "led_cls" #define DEV_NAME "led" #define GPX2CON 0x11000c40 #define GPX1CON 0x11000c20 #define GPF3CON 0x114001e0 volatile unsigned int * gpx2con; volatile unsigned int * gpx1con; volatile unsigned int * gpf3con; volatile unsigned int * gpx2dat; volatile unsigned int * gpx1dat; volatile unsigned int * gpf3dat; dev_t devno; struct cdev led_cdev; struct class * cls; int led_open (struct inode *inode, struct file *file) { printk(" led_open!!!\n"); return 0; } int led_release (struct inode *inode, struct file *file) { printk(" led_release!!!\n"); return 0; } long led_ioctl (struct file *file, unsigned int cmd, unsigned long arg) { printk(" led_ioctl !!!\n"); switch (cmd) { case LED2_ON: *gpx2dat = (*gpx2dat)|(0x1 << 7); *gpx1dat = (*gpx1dat)&~(0x1 << 0); *gpf3dat = (*gpf3dat)&~(0x3 << 4); break; case LED2_OFF: *gpx2dat = (*gpx2dat)&~(0x1 << 7); *gpx1dat = (*gpx1dat)&~(0x1 << 0); *gpf3dat = (*gpf3dat)&~(0x3 << 4); break; case LED3_ON: *gpx2dat = (*gpx2dat)&~(0x1 << 7); *gpx1dat = (*gpx1dat)|(0x1 << 0); *gpf3dat = (*gpf3dat)&~(0x3 << 4); break; case LED3_OFF: *gpx2dat = (*gpx2dat)&~(0x1 << 7); *gpx1dat = (*gpx1dat)&~(0x1 << 0); *gpf3dat = (*gpf3dat)&~(0x3 << 4); break; case LED4_ON: *gpx2dat = (*gpx2dat)&~(0x1 << 7); *gpx1dat = (*gpx1dat)&~(0x1 << 0); *gpf3dat = (*gpf3dat)|(0x1 << 4); *gpf3dat = (*gpf3dat)&~(0x1 << 5); break; case LED4_OFF: *gpx2dat = (*gpx2dat)&~(0x1 << 7); *gpx1dat = (*gpx1dat)&~(0x1 << 0); *gpf3dat = (*gpf3dat)&~(0x3 << 4); break; case LED5_ON: *gpx2dat = (*gpx2dat)&~(0x1 << 7); *gpx1dat = (*gpx1dat)&~(0x1 << 0); *gpf3dat = (*gpf3dat)|(0x1 << 5); *gpf3dat = (*gpf3dat)&~(0x1 << 4); break; case LED5_OFF: *gpx2dat = (*gpx2dat)&~(0x1 << 7); *gpx1dat = (*gpx1dat)&~(0x1 << 0); *gpf3dat = (*gpf3dat)&~(0x3 << 4); break; case LEDALL_ON: *gpx2dat = (*gpx2dat)|(0x1 << 7); *gpx1dat = (*gpx1dat)|(0x1 << 0); *gpf3dat = (*gpf3dat)|(0x3 << 4); break; case LEDALL_OFF: *gpx2dat = (*gpx2dat)&~(0x1 << 7); *gpx1dat = (*gpx1dat)&~(0x1 << 0); *gpf3dat = (*gpf3dat)&~(0x3 << 4); break; default: printk(" fault cmd!!!\n"); return -EFAULT; break; } return 0; } struct file_operations led_fops={ .owner = THIS_MOD b87e ULE, .open = led_open, .release = led_release, .unlocked_ioctl = led_ioctl, }; int __init led_init(void) { int ret; printk(" led_init!!!\n"); devno = MKDEV(LED_MAJOR,LED_MINOR); ret = register_chrdev_region(devno,LED_NUM,LED_NAME); if (ret < 0) { printk(" register_chrdev_region fail!!!\n"); return -EFAULT; } printk(" register_chrdev_region success!!!\n"); printk(" major=%d,minor=%d\n",MAJOR(devno),MINOR(devno)); cdev_init(&led_cdev,&led_fops); led_cdev.owner = THIS_MODULE; cdev_add(&led_cdev,devno,LED_NUM); cls = class_create(THIS_MODULE,CLS_NAME); if (IS_ERR(cls)) { printk(" class_create fail!!!\n"); return -EFAULT; } device_create(cls,NULL,devno,NULL,DEV_NAME); gpx2con = ioremap(GPX2CON,0x4); if (NULL == gpx2con) { printk(" gpx2con ioremap fail!!!\n"); return -EFAULT; } gpx1con = ioremap(GPX1CON,0x4); if (NULL == gpx1con ) { printk(" gpx1con ioremap fail!!!\n"); return -EFAULT; } gpf3con = ioremap(GPF3CON,0x4); if (NULL == gpf3con) { printk(" gpf3con ioremap fail!!!\n"); return -EFAULT; } gpx2dat = gpx2con + 1; //gpx2dat = ioremap(0x1100c44,0x4); gpx1dat = gpx1con + 1; gpf3dat = gpf3con + 1; *gpx2con = ((*gpx2con)&~(0xf << 28))|(0x1 << 28); *gpx1con = ((*gpx1con)&~(0xf << 0))|(0x1 << 0); *gpf3con = ((*gpf3con)&~(0xff <<16))|(0x11 << 16); *gpx2dat =(*gpx2dat)|(0x1 << 7); *gpx1dat =(*gpx1dat)|(0x1 << 0); *gpf3dat =(*gpf3dat)|(0x3 << 4); return 0; } module_init(led_init); void __exit led_exit(void) { printk(" led_exit!!!\n"); iounmap(gpf3con); iounmap(gpx1con); iounmap(gpx2con); device_destroy(cls,devno); class_destroy(cls); cdev_del(&led_cdev); unregister_chrdev_region(devno,LED_NUM); } module_exit(led_exit); MODULE_LICENSE("GPL");
led.c
#include <stdio.h> #include <fcntl.h> #include <sys/ioctl.h> #include "head.h" int main(int argc, char *argv[]) { int fd; fd = open("/dev/led",O_RDWR); if (fd < 0) { printf(" open fail!!!\n"); return -1; } for(;;) { ioctl(fd,LED2_ON); sleep(1); ioctl(fd,LED3_ON); sleep(1); ioctl(fd,LED4_ON); sleep(1); ioctl(fd,LED5_ON); sleep(1); ioctl(fd,LEDALL_ON); sleep(1); } close(fd); return 0; }
相关文章推荐
- 如何在 Linux 上安装设备驱动程序
- 解决Vista系统OpenGL驱动问题的方法整理
- 处理驱动器和文件夹
- Windows Vista手动安装SATA硬盘驱动全过程
- Nodejs学习笔记之测试驱动
- C#中使用1.7版本驱动操作MongoDB简单例子
- C#判断指定驱动器是否已经准备就绪的方法
- MongoDB入门教程之C#驱动操作实例
- JDBC数据库连接过程及驱动加载与设计模式详解
- C#返回当前系统所有可用驱动器符号的方法
- Linux 字符设备驱动框架详细介绍
- linux 驱动之Kconfig文件和Makefile文件实例
- SQL SERVER使用ODBC 驱动建立的链接服务器调用存储过程时参数不能为NULL值
- js事件驱动机制 浏览器兼容处理方法
- monkeyrunner之电脑安装驱动(5)
- 使用java web 在jsp文件及Class中连接MySQL和SQLsever 的驱动方法
- MongoDB快速入门笔记(八)之MongoDB的java驱动操作代码讲解
- python测试驱动开发实例
- 解决ubuntu 15.04 无法开启wifi问题
- LDD3阅读笔记-概述