字符设备驱动
- 1.注册字符类设备号
- 1.静态分配一个设备号
- 2.动态分配一个设备号
- 3.注销设备号
- 2.注册字符类设备
- 1.定义一个cdev结构体
- 2. 使用cdev_init函数初始化cdev结构体成员变量
- 3.使用cdev_add函数注册到内核
- 3.创建设备节点
- 1.方法一
- 2.方法二
- 1.使用class_create函数创建一个class类
- 2.使用device_create函数在我们创建的类下面创建一个设备
- 4.chrdev.c
- 5 app.c
- 6 编译运行
- 1.编译chrdev_io.c
- 2.编译app.c
- 7 在树莓派运行
1.注册字符类设备号
1.静态分配一个设备号
int register_chrdev_region(dev_t, unsigned, const char *);
参数:
第一个:设备号的起始值。类型是dev_t类型
第二个:次设备号的个数
第三个:设备的名称 (使用cat /proc/devices 输出的就是设备号,设备的名称)
返回值:成功返回0,失败返回负数
dev_t类型:
dev_t是用来保存设备号的,是一个32位数。
高12位用来保存主设备号,低20位用来保存次设备号。
操作dev的函数
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
根据dev_t获取主设备号
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
根据dev_t获取次设备号
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
将我们的主设备号和次设备号组成一个dev_t类型,第一个参数是主设备号,第二个参数是次设备号。
2.动态分配一个设备号
int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
参数:
第一个:保存生成的设备号
第二个:我们请求的第一个设备号,通常是0
第三个:连续申请的设备号的个数。
第四个:设备的名称
返回值:成功返回0 ,失败返回负数。
使用动态分配优先使用234 --255的设备号。
3.注销设备号
void unregister_chrdev_region(dev_t, unsigned);
参数:
第一个:分配设备号的起始地址
第二个: 申请的连续设备号的个数。
2.注册字符类设备
1.定义一个cdev结构体
2. 使用cdev_init函数初始化cdev结构体成员变量
void cdev_init(struct cdev *, const struct file_operations *);
参数:
第一个:要初始化的cdev
第二个:文件操作集
cdev->ops = fops;//实际就是把文件操作集写给ops
3.使用cdev_add函数注册到内核
int cdev_add(struct cdev *, dev_t, unsigned);
参数:
第一个:cdev的结构体指针
第二个:设备号
第三个:次设备号的数量
3.创建设备节点
1.方法一
使用mknod命令创建一个设备节点
格式:
mknod 名称 类型 主设备号 次设备号
2.方法二
1.使用class_create函数创建一个class类
2.使用device_create函数在我们创建的类下面创建一个设备
4.chrdev.c
在/home/kun/build_new/linux_kernel/drivers/char/(这为我内核目录下的字符设备路径)目录下 mkdir 07_chrdev_io ,编写chrdev.c app.c Makefile。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>
#define DEVICE_NUMBER 1
#define DEVICE_SNAME "schrdev"
#define DEVICE_ANANME "achrdev"
#define DEVICE_MINOR 0
#define DEVICE_CLASS_NAME "chrdev_class"
#define DEVICE_NODE_NAME "chrdev_test"
#define GPIO4_DR 0xfe200000
#define GPIO4_H 0xfe20001c
#define GPIO4_L 0xfe200028
unsigned int *vir_gpio4_dr=NULL;
unsigned int *vir_gpio4_h=NULL;
unsigned int *vir_gpio4_l=NULL;
static int major_num, minor_num;
struct cdev cdev;
static dev_t dev_num;
struct class *class;
struct device *device;
int chrdev_open( struct inode * inode, struct file *file)
{
printk( "hello chrdev_open \n");
return 0;
}
ssize_t chrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
char kbuf[64] = {0};
if (copy_from_user(kbuf, ubuf, size) != 0)
{
printk("copy_from_user error\n ");
return -1;
}
printk("kbuf is %s\n ", kbuf);
*vir_gpio4_dr |=(001<<(3*4));
if(kbuf[0]==1)
{
*vir_gpio4_h |=(1<<4);
}
else if(kbuf[0]==0)
{
*vir_gpio4_l |=(1<<4);
}
return 0;
}
struct file_operations chrdev_ops=
{
.owner = THIS_MODULE,
.open = chrdev_open,
.write = chrdev_write,
};
module_param( major_num, int, S_IRUGO);
module_param( minor_num, int, S_IRUGO);
MODULE_PARM_DESC( major_num, "e.g:a=1");
MODULE_PARM_DESC( minor_num, "e.g:a=1");
static int
chrdev_init( void)
{
int ret;
if( major_num)
{
printk( "mjor_num=%d \n", major_num);
printk( "minor_num=%d \n", minor_num);
dev_num = MKDEV( major_num, minor_num);
ret = register_chrdev_region( dev_num, DEVICE_NUMBER, DEVICE_SNAME);
if( ret<0)
{
printk( "register_chrdev_region error\n");
}
printk( "register_chrdev_region success\n");
}
else
{
ret = alloc_chrdev_region( &dev_num, DEVICE_MINOR, DEVICE_NUMBER, DEVICE_ANANME);
if( ret<0)
{
printk( "alloc_chrdev_region error\n");
}
printk( "alloc_chrdev_region success\n");
major_num = MAJOR( dev_num);
minor_num = MINOR( dev_num);
printk( "mjor_num=%d \n", major_num);
printk( "minor_num=%d \n", minor_num);
}
cdev.owner = THIS_MODULE;
cdev_init( &cdev, &chrdev_ops);
cdev_add( &cdev, dev_num, DEVICE_NUMBER);
class = class_create( THIS_MODULE, DEVICE_CLASS_NAME);
device = device_create( class,NULL,dev_num,NULL,DEVICE_NODE_NAME);
vir_gpio4_dr = ioremap( 0xfe200000,4);
if(vir_gpio4_dr == NULL )
{
printk( "gpio4dr ioremap error\n");
return EBUSY;
}
vir_gpio4_h = ioremap( GPIO4_H,4);
if( vir_gpio4_h == NULL)
{
printk( "gpio4h ioremap error\n");
return EBUSY;
}
vir_gpio4_l = ioremap( GPIO4_L,4);
if(vir_gpio4_l == NULL)
{
printk( "gpio4l ioremap error\n");
return EBUSY;
}
printk( "gpio ioremap success\n");
return 0;
}
static void
chrdev_exit( void)
{
unregister_chrdev_region( MKDEV( major_num, minor_num), DEVICE_NUMBER);
cdev_del( &cdev);
device_destroy( class,dev_num);
class_destroy( class);
printk( "bye bye \n");
}
module_init( chrdev_init);
module_exit( chrdev_exit);
MODULE_LICENSE( "GPL");
5 app.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
int fd;
char buf[64] = "0";
fd = open( "/dev/chrdev_test",O_RDWR);
if(fd < 0)
{
perror( "open error \n");
return fd;
}
buf[0]= atoi( argv[1]);
write( fd,buf,sizeof(buf));
close( fd);
return 0;
}
6 编译运行
1.编译chrdev_io.c
Makefile文件内容
obj-m +=chrdev_io.o
KDIR:=/home/kun/build_new/linux_kernel
PWD?=$(shell pwd)
all:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) M=$(PWD) modules
clean:
rm *.mod.c *.order *.ko *.o *.mod *.symvers
KDIR:为自己在linux上存放的内核目录路径
执行make,生成.ko文件
2.编译app.c
arm-linux-gnueabihf-gcc app.c -o app -static
7 在树莓派运行
将步骤6生成的chrdev.ko app移动至树莓派
执行 sudo insmod chrdev.ko即可加载驱动
执行 ./app 1 即可使GPIO4引脚输出高电平
执行 ./app 0即可使GPIO4引脚输出低电平

