linux应用编程其实都是通过调用c库,c库给上层应用提供支配内核(进程、网络等)干活的接口
驱动:
在内核中,有个驱动链表管理类所有的设备驱动,驱动插入链表的顺序由设备号检索;
设备号分为主设备号和次设备号,主设备号被系统用来确定要调用哪个驱动程序(设备类型:如USB设备,硬盘设备),主设备号相同的设备是同类设备,使用同一个驱动程序,次设备号被驱动程序用来确定具体哪一个设备。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。
驱动的开发大致分为两个步骤:
1. 添加驱动:编写驱动程序,添加到内核驱动链表
如何编写驱动程序?按照驱动框架,分为如下几步:
a. 设备名
b. 设备号
c. 向驱动框架中添加驱动函数(即操作相关的寄存器)
2. 调用驱动:调用open、reead、write函数,操作硬件
内核是如何知道调用哪个驱动呢?
因为open时由设备名,通过设备名就能知道设备号,然后在驱动链表中就能找到对应的驱动
所以驱动的开发大致流程如下:
当向内核添加驱动后,用户在用户空间调用open,会触发系统调用sys_call,sys_call会调用到sys_open,
然后sys_open会在驱动链表里面通过设备名找到设备号,从而确定对应的驱动,然后再调用驱动函数里的open,在驱动函数里的open就操作硬件寄存器
驱动框架:
#include <linux/fs.h> //file_operations声明 #include <linux/module.h> //module_init module_exit声明 #include <linux/init.h> //__init __exit 宏定义声明 #include <linux/device.h> //class devise声明 #include <linux/uaccess.h> //copy_from_user 的头文件 #include <linux/types.h> //设备号 dev_t 类型声明 #include <asm/io.h> //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno; //设备号 static int major =231; //主设备号 static int minor =0; //次设备号 static char *module_name="pin4"; //模块名 //pin4_read函数 //static int pin4_read(struct file *file,char __user *buf,size_t count, loff_t *ppos) //{ // printk("pin4_write\n"); // return 0; //} //pin4_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_open\n"); //内核的打印函数,和printf类似 return 0; } //pin4_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_write\n"); return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open = pin4_open, .write = pin4_write, }; int __init pin4_drv_init(void) //驱动的真正入口 { int ret; devno = MKDEV(major,minor); //创建设备号 ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE,"myfirstdemo"); //由代码在/dev下自动生成设备 pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class,devno); class_destroy(pin4_class); unregister_chrdev(major, module_name); //卸载驱动 } module_init(pin4_drv_init); //入口:内核加载驱动的时候(insmod),这个宏会被调用,而真正的驱动入口是它调用的函数 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
这样通过驱动框架编写好一个驱动程序,前面提到驱动可以直接编译进内核,也可以通过模块的方式加载进内核,这里我们以模块的方式。
1. 将这个驱动代码放到内核源码下的linux-rpi-4.14.y/drivers/char
,然后仿照该目录下的makefile,添加将这个驱动代码编译成模块的指令,如
2. 然后再执行编译驱动的命令(将其编译成模块):
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 modules
3. 然后在树莓派中装驱动
sudo insmod pin4driver.ko
4. 查看添加的驱动文件是否成功/查看内核中挂载的驱动
ls -l /dev/pin4 或 lsmod
5. 修改/dev/pin4权限
sudo chmod 666 /dev/pin4
demsg:查看内核打印信息,pintk是内核的输出函数。
删除驱动:sudo rmmod pin4drive
GPIO驱动;
#include <linux/fs.h> //file_operations声明 #include <linux/module.h> //module_init module_exit声明 #include <linux/init.h> //__init __exit 宏定义声明 #include <linux/device.h> //class devise声明 #include <linux/uaccess.h> //copy_from_user 的头文件 #include <linux/types.h> //设备号 dev_t 类型声明 #include <asm/io.h> //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno; //设备号 static int major =231; //主设备号 static int minor =0; //次设备号 static char *module_name="pin4"; //模块名 volatile unsigned int* GPFSEL0 = NULL; volatile unsigned int* GPSET0 = NULL; volatile unsigned int* GPCLR0 = NULL; //pin4_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_open\n"); //内核的打印函数,和printf类似 //open的时候配置pin4为输出引脚 *GPFSEL0 &= ~(0x6 << 12); *GPFSEL0 |= (0x1 << 12); return 0; } //pin4_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { int userCmd;//上层写的是整型数1,底层就要对应起来用int.如果是字符则用char printk("pin4_write\n"); //获取上层write的值 copy_from_user(&userCmd,buf,count);//用户空间向内核空间传输数据 //根据值来执行操作 if(userCmd == 1){ printk("set 1\n"); *GPSET0 |= 0x1 << 4; }else if(userCmd == 0){ printk("set 0\n"); *GPCLR0 |= 0x1 << 4; }else{ printk("cmd error\n"); } return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open = pin4_open, .write = pin4_write, }; int __init pin4_drv_init(void) //驱动的真正入口 { int ret; printk("insmod driver pin4 success\n"); devno = MKDEV(major,minor); //创建设备号 ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE,"myfirstdemo"); //由代码在/dev下自动生成设备 pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件 GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);//0x3f200000是物理地址,代码操作的是虚拟地址,所以要将物理地址映射成虚拟地址 GPSET0 = (volatile unsigned int *)ioremap(0x3f20001C,4); GPCLR0 = (volatile unsigned int *)ioremap(0x3f200028,4); return 0; } void __exit pin4_drv_exit(void)//可以发现和init刚好是相反的执行顺序。 { iounmap(GPFSEL0); iounmap(GPSET0); iounmap(GPCLR0); device_destroy(pin4_class,devno); class_destroy(pin4_class); unregister_chrdev(major, module_name); //卸载驱动 } module_init(pin4_drv_init); //入口:内核加载驱动的时候,这个宏会被调用,而真正的驱动入口是它调用的函数 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
标签:static,驱动,pin4,open,class,设备 From: https://www.cnblogs.com/zj-studyrecoding/p/17323198.html