1.设备号
Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux有约定俗成的编号,如硬盘的主设备号是3。
一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和次设备号统称为设备号。主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各设备。例如一个嵌入式系统,有两个LED指示灯,LED灯需要独立的打开或者关闭。那么,可以写一个LED灯的字符设备驱动程序,可以将其主设备号注册成5号设备,次设备号分别为1和2。这里,次设备号就分别表示两个LED灯。
2.驱动代码的编写
1.驱动代码编写:pin4test.c pin4driver.c
2.内核驱动编译
cd /home/duyong/SYSTEM/linux-rpi-4.14.y/drivers/char
驱动代码pin4driver.c 拷贝到 /home/duyong/SYSTEM/linux-rpi-4.14.y/drivers/char
修改 目录下的 Makefile
cd ../../
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
生成 /drivers/char/pin4driver.ko
交叉编译:arm-linux-gnueabihf-gcc pin4test.c -o pin4test
拷贝: scp ./drivers/char/pin4driver.ko pi@192.168.1.11:/home/pi/Driver
scp pin4test pi@192.168.1.11:/home/pi/Driver
8.测试驱动:
sudo insmod pin4driver.ko 内核驱动装载 lsmod 查看内核模块
sudo rmmod pin4drive 内核驱动卸载
sudo chmod 666 /dev/pin4 ls /dev/pin4 -l
执行 ./pin4test
手动生成设备:sudo mknod chenlichen c 8 1 (设备类型 主设备号,次设备号)
pin4driver.c:
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <asm/io.h>
volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0 = NULL;
volatile unsigned int* GPCLR0 = NULL;
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";
static ssize_t pin4_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int userCmd;
//获取上层write值
copy_from_user(&userCmd,buf,count);
printk("get value:%d\n",userCmd);
//根据值操作io口
if(userCmd == 1){
*GPSET0 |= 0x1<<4;
printk("set 1\n");
}else if(userCmd == 0){
*GPCLR0 |= 0x1<<4;
printk("set 0\n");
}else{
printk("undo\n");
}
return 0;
}
static ssize_t pin4_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
printk("pin4_read\n");
return 0;
}
static int pin4_open(struct inode * inode, struct file * filp)
{
*GPFSEL0 &= ~(0x6<<12);//配置pin4引脚为输出引脚
*GPFSEL0 |= 0x1<<12;
printk("pin4_open\n");//内核的打印函数
return 0;
}
//在内核源码查找struct file_operations看结构体成员,添加用到的函数
static const struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.write = pin4_write,//函数指针
.open = pin4_open,
.read = pin4_read,
};
static int __init pin4_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");//代码自动生成设备
pin4_class_dev = device_create(pin4_class,NULL,devno,NULL,module_name);//创建设备文件
GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);//物理地址转换为虚拟地址,io口寄存器映射成普通内存单元进行访问
GPSET0 = (volatile unsigned int *)ioremap(0x3f20001C,4);
GPCLR0 = (volatile unsigned int *)ioremap(0x3f200028,4);
return 0;
}
static void __exit pin4_exit(void)
{
iounmap(GPFSEL0);//先解除虚拟地址映射,在销毁设备
iounmap(GPSET0);
iounmap(GPCLR0);
device_destroy(pin4_class,devno);//销毁设备
class_destroy(pin4_class);//销毁类
unregister_chrdev(major,module_name);//卸载设备
}
module_init(pin4_init);//入口,是个宏
module_exit(pin4_exit);
MODULE_LICENSE("GPL v2");
pin4test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd;
int cmd;
fd = open("/dev/pin4",O_RDWR);
if(fd < 0){
printf("open failed\n");
perror("reson:");
exit(-1);
}else{
printf("open success\n");
}
printf("input commnd :1/0 \n1:set pin4 high \n0:set pin4 low\n");
scanf("%d",&cmd);
printf("cmd= %d\n",cmd);
write(fd,&cmd,1);
close(fd);
return 0;
}