1.最简单的杂项设备
/*
* @Descripttion: 最简单的杂项设备驱动
* @version:
* @Author: topeet
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
struct file_operations misc_fops={
.owner = THIS_MODULE
};
//初始化头文件
//最基本的文件,支持动态添加和卸载模块。
/*注册杂项设备头文件*/
/*注册设备节点的文件结构体*/
//文件操作集
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hello_misc",
.fops = &misc_fops, //杂项设备结构体
//动态申请的次设备号
//杂项设备名字是 hello_misc
//文件操作集
};
static int misc_init(void){
//在初始化函数中注册杂项设备
int ret;
ret = misc_register(&misc_dev);
if(ret<0)
{
printk("misc registe is error \n");
}
printk("misc registe is succeed \n");
return 0;
}
static void misc_exit(void) {
//在卸载函数中注销杂项设备
misc_deregister(&misc_dev);
printk(" misc gooodbye! \n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");
将device.c和driver.c编写完成后,开始进行probe函数的编写
probe函数是平台设备驱动主要功能的实现,在该函数中可以实现以下几个重要的功能:
1.设备和驱动文件匹配成功后,实现驱动文件对设备的资源的获取:
直接获取:直接使用传入的形参resource,不安全;
间接获取:platform_get_resource();获取转存,安全;
2.实现设备在内核中资源占用的登记(request_mem_region();函数);
3.设备节点的实现:
上层AP与kernel层的通信都是基于设备节点实现的,
在probe函数中注册设备节点,然后再实现设备节点的结构体和文件操作函数(open,release,read,write等)
2.实现文件操作函数后,就可以在对应的文件操作中,将ap端的数据接受到kernel层进行使用,实现对底层硬件的操作
平台设备驱动代码实例:
【device.c】
/*************************************************************************
> File Name: beep_device.c
> Author: amoscykl
> Mail: amoscykl@163.com
> Created Time: 2022年04月07日 星期四 21时28分28秒
************************************************************************/
#include<linux/init.h> //初始化头文件
#include<linux/module.h> //模块注册注销包含
#include<linux/fs.h>
#include<linux/platform_device.h> //平台设备注册头文件
void beep_release(struct device *dev){
printk("beep release\n");
}
struct resource beep_res[] = {
//第一个资源的参数填充
[0] = {
.start = 0xff790000, //资源开始地址
.end = 0xff790003, //资源结束地址
.flags = IORESOURCE_MEM,//表示资源类型为一段内存
.name = "GPIO5_DR" //资源的名称
}
};
struct platform_device beep_device = {
.name = "beep_test",//平台设备名称,用匹配device和driver的字符串
.id = -1, //设备id,一般写-1表示设备为1个,为相同类型设备进行编号
.resource = beep_res,//设备资源结构体集合数组
.num_resources = ARRAY_SIZE(beep_res),//设备资源数量
.dev={
.release = beep_release
}
};
static int beep_device_init(void)
{
printk("beep_device_init\n");
platform_device_register(&beep_device);
return 0;
}
static void beep_device_exit(void)
{
platform_device_unregister(&beep_device);
printk("beep_device removed\n");
}
module_init(beep_device_init);
module_exit(beep_device_exit);
MODULE_LICENSE("GPL");
【driver.c】
/*************************************************************************
> File Name: beep_driver.c
> Author: amoscykl
> Mail: amoscykl@163.com
> Created Time: 2022年04月07日 星期四 21时36分34秒
************************************************************************/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/platform_device.h>
#include<linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
//request_mem_region(start,n,name);
/*struct resource * platform_get_resource(struct platform_device *pdv,unsigned int,unsigned int);
函数功能:用于获取device.c中的资源结构体
形参:struct platform_device *pdv 平台设备结构体
unsigned int 资源类型
unsigned int 该资源在资源数组中的在同类型中的排序(从0开始)
返回值:struct resource *
备注:
*/
/*struct resource * request_mem_region(start,n,name);
函数功能:平台设备登记函数,主要是在使用硬件的之前对硬件资源进行检测有没有被其他设备占用,没有则登记到内核,占用资源
形参:start 资源起始地址
n 资源占用空间大小
name 资源名称
返回值:struct resource *
备注:
*/
/*release_mem_region(start,n);
函数功能:平台设备取消登记函数
形参:start 资源起始地址
n 资源占用空间大小
返回值:
备注:
*/
unsigned int * beep_gpio_addr;
struct resource *beep_mem; //用于probe函数中接收platform_get_resource();函数返回的device中的资源结构体指针
struct resource *beep_mem_temp;//用于接收request_mem_region();函数的返回值,登记成功返回struct resource *指针,登记失败返回NULL
int beep_misc_open(struct inode *inode,struct file *file)
{
printk("beep_misc open success!\n");
return 0;
}
int beep_misc_release(struct inode *inode,struct file *file)
{
printk("beep_misc released!!!!\n");
return 0;
}
ssize_t beep_misc_read(struct file *file, char __user * ubuf, size_t size, loff_t *loff_t)
{
if((*(beep_gpio_addr)&(1<<1)>>1) == 1){
if(copy_to_user(ubuf,"beep_gpio state is set\n",strlen("beep_gpio state is set\n"))!=0){
printk("copy_to_user error\n");
return -1;
};
}else if((*(beep_gpio_addr)&(1<<1)>>1) == 0){
if(copy_to_user(ubuf,"beep_gpio state is reset\n",strlen("beep_gpio state is reset\n"))!=0){
printk("copy_to_user error\n");
return -1;
};
}else{
printk("beep_gpio state is error!\n");
return -1;
}
return 0;
}
ssize_t beep_misc_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("copy_form_user success\n");
printk("kbuf[0] = %d\n",kbuf[0]);
if(kbuf[0]==1){
*(beep_gpio_addr)=0x4a400000;
//*(beep_gpio_addr) |= (1<<1);
}else if(kbuf[0] == 0){
*(beep_gpio_addr)=0x4a000000;
//*(beep_gpio_addr) &= ~(1<<1);
}else{
printk("CMD ERROR\n");
}
return 0;
}
struct file_operations beep_fops = {
.owner = THIS_MODULE,
.open = beep_misc_open,
.release = beep_misc_release,
.read = beep_misc_read,
.write = beep_misc_write,
};
struct miscdevice beep_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "beep_misc",
.fops = &beep_fops,
};
int beep_probe(struct platform_device *pdv)
{
int ret =0;
printk("beep probe successed!\n");
beep_mem = platform_get_resource(pdv,IORESOURCE_MEM,0);
if(beep_mem == NULL)
{
printk("platform get resource error\n");
return -EBUSY;
}
printk("platform get resource ok\n");
printk("beep_mem->start = 0x%x\n",beep_mem->start);
printk("beep_mem->end = 0x%x\n",beep_mem->end);
#if 0
//设备资源登记占用
beep_mem_temp = request_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1,"beep");
if(beep_mem_temp == NULL)
{
printk("request_mem_region failed!\n");
goto region_error;
}
printk("request_mem_region successed!\n");
#endif
//占用后进行ioremap内存映射
beep_gpio_addr = ioremap(beep_mem->start,4);
if(beep_gpio_addr == NULL)
{
printk("beep_gpio_addr ioremap error!\n");
return -EBUSY;
}
printk("beep_gpio_addr ioremap ok!\n");
//注册杂项设备
ret = misc_register(&beep_misc);
if(ret < 0)
{
printk("beep_misc register error\n");
return -EBUSY;
}
printk("beep_misc register ok\n");
return 0;
region_error:
release_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1);
return -EBUSY;
}
int beep_remove(struct platform_device *pdv)
{
printk("beep all removed!\n");
return 0;
}
struct platform_driver beep_platform_driver ={
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.name = "beep_test",
.owner = THIS_MODULE,
},
//const struct platform_device_id *id_table;
};
static int beep_driver_init(void)
{
int ret =0;
ret = platform_driver_register(&beep_platform_driver);
//printk("%s",beep_platform_driver.name);
if(ret<0)
printk("driver register failed!\n");
printk("platform_driver_register success!\n");
return 0;
}
static void beep_driver_exit(void)
{
platform_driver_unregister(&beep_platform_driver);
misc_deregister(&beep_misc);
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
【app.c】
#include <stdio.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
char buf[64] = {0};
buf[0] = atoi(argv[1]);
printf("***************CMD= %d\n",buf[0]);
fd = open("/dev/beep_misc",O_RDWR);
if(fd<0){
perror("open error!\n");
return fd;
}
write(fd,buf,sizeof(buf));
close(fd);
printf("***************The end\n");
return 0;
}
备注:代码实现基于北京迅为RK3399开发板,实现功能为蜂鸣器控制
该设备对应时钟功能没有使能,需要进行使能才可以实现控制功能
时钟使能指令:
使用命令 cat /sys/kernel/debug/clk/clk_summary |grep gpio 查看 GPIO4 的时钟是否被使能
使用命令 echo 1 > /sys/kernel/debug/clk/pclk_gpio4/clk_enable_count打开时钟
使能时钟后即可通过app程序来控制蜂鸣器了