点击上方“嵌入式Linux知识共享”,选择“置顶/星标公众号”
精选文章、福利干货,第一时间送达
一、MISC 设备驱动简介
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
#define APOLLO_MOUSE_MINOR 7 /* unused */
#define PC110PAD_MINOR 9 /* unused */
......
#define VHOST_VSOCK_MINOR 241
#define RFKILL_MINOR 242
#define MISC_DYNAMIC_MINOR 255
int misc_register(struct miscdevice * misc)
函数参数和返回值含义如下:
misc:要注册的 MISC 设备。
返回值: 负数,失败; 0,成功。
/* 传统的创建设备过程 */
alloc_chrdev_region(); /* 申请设备号 */
cdev_init(); /* 初始化 cdev */
cdev_add(); /* 添加 cdev */
class_create(); /* 创建类 */
device_create(); /* 创建设备 */
int misc_deregister(struct miscdevice *misc)
函数参数和返回值含义如下:
misc:要注销的 MISC 设备。
返回值: 负数,失败; 0,成功。
/* 传统的删除设备的过程 */
cdev_del(); /* 删除 cdev */
unregister_chrdev_region(); /* 注销设备号 */
device_destroy(); /* 删除设备 */
class_destroy(); /* 删除类 */
二、修改设备树文件
1、添加 pinctrl 节点
pinctrl_beep: beepgrp {
fsl,pins = <
MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0 /* beep */
>;
};
2、添加 BEEP 设备节点
beep {
#address-cells = <1>;
#size-cells = <1>;
compatible = "imx6ull-beep";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_beep>;
beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;
status = "okay";
};
三、驱动编写
/************************************************************
* Copyright © toto Co., Ltd. 1998-2029. All rights reserved.
* Description:
* Version: 1.0
* Autor:
* Date:
* LastEditors:
* LastEditTime:
************************************************************/
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#define MISC_BEEP_NAME "misc_beep" /* 设备名字 */
#define MISC_BEEP_MINOR 144 /* 子设备号 */
#define BEEP_ON 1 /* 打开蜂鸣器 */
#define BEEP_OFF 0 /* 关闭蜂鸣器 */
/* beep 设备结构体 */
struct misc_beep_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int beep_gpio; /* beep 所使用的GPIO编号*/
};
struct misc_beep_dev misc_beep;
/*
* @Brief beep打开/关闭
* @Call Internal or External
* @Param state: 1-打开 0-关闭
* @Note NOne
* @RetVal 无
*/
void beep_switch(u8 state)
{
if(state == BEEP_ON)
{
gpio_set_value(misc_beep.beep_gpio, 0);
}
else if(state == BEEP_OFF)
{
gpio_set_value(misc_beep.beep_gpio, 1);
}
else
{
printk("%s state:%d invalid\n", __func__, state);
}
}
/*
* @Brief 打开设备
* @Call Internal or External
* @Param inode:
* @Param filp:设备文件
* @Note NOne
* @RetVal 0:成功 其他值:失败
*/
static int misc_beep_open(struct inode *inode, struct file *filp)
{
/* 设置私有数据 */
filp->private_data = &misc_beep;
return 0;
}
/*
* @Brief 写数据到设备
* @Call Internal or External
* @Param filp:要打开的设备文件描述符
* @Param buf:要写入设备的数据地址
* @Param cnt:要写入的数据长度
* @Param offt:相对于文件首地址的偏移
* @Note NOne
* @RetVal 写入的字节数,若为负值,表示写失败
*/
static ssize_t misc_beep_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt)
{
int retval;
unsigned char databuf[1];
unsigned char beepstat;
retval = copy_from_user(databuf, buf, cnt);
if(retval < 0)
{
printk("%s copy_from_user failed\n", __func__);
return -EFAULT;
}
beepstat = databuf[0];
if(beepstat != BEEP_ON && beepstat != BEEP_OFF)
{
printk("%s beepstat:%d invalid\n", __func__, beepstat);
return -1;
}
/*打开、关闭LED*/
beep_switch(beepstat);
return 0;
}
/* 设备操作函数 */
static struct file_operations misc_beep_fops = {
.owner = THIS_MODULE,
.open = misc_beep_open,
.write = misc_beep_write,
};
/* MISC 设备结构体 */
static struct miscdevice beep_miscdev = {
.minor = MISC_BEEP_MINOR,
.name = MISC_BEEP_NAME,
.fops = &misc_beep_fops,
};
/*
* @Brief platform 驱动probe函数,当驱动与
* 设备匹配以后此函数就会执行
* @Call Internal or External
* @Param dev:platform设备
* @Note NOne
* @RetVal NOne
*/
static int misc_beep_probe(struct platform_device *dev)
{
int ret;
/* 设置beep 所使用的GPIO*/
/* 1.获取设备节点:beep */
misc_beep.nd = of_find_node_by_path("/beep");
if(misc_beep.nd == NULL)
{
printk("beep node can not found\n");
return -EINVAL;
}
/* 2.获取 设备树中的 gpio 属性,得到 beep 所使用的 gpio 编号 */
misc_beep.beep_gpio = of_get_named_gpio(misc_beep.nd, "beep-gpio", 0);
if(misc_beep.beep_gpio < 0)
{
printk("can't get beep-gpio\n");
return -EINVAL;
}
/* 3.设置 GPIO5_IO01 为输出,并且输出到电平,默认关闭 beep */
ret = gpio_direction_output(misc_beep.beep_gpio, 1);
if(ret < 0)
{
printk("can't set gpio\n");
return -EINVAL;
}
/* 注册MISC设备驱动 */
ret = misc_register(&beep_miscdev);
if (ret < 0) {
printk("misc device register failed\n");
return -EFAULT;
}
return 0;
}
/*
* @Brief 驱动出口函数
* @Call Internal or External
* @Param None
* @Note NOne
* @RetVal NOne
*/
static int misc_beep_remove(struct platform_device *dev)
{
/* 关闭蜂鸣器 */
gpio_set_value(misc_beep.beep_gpio, 1);
/* 释放gpio */
gpio_free(misc_beep.beep_gpio);
/* 注销 misc 设备驱动 */
misc_deregister(&beep_miscdev);
return 0;
}
/* 匹配列表 */
static const struct of_device_id beep_of_match[] = {
{ .compatible = "imx6ull-beep" },
{ /* Sentinel*/ }
};
/* platform 驱动结构体 */
static struct platform_driver beep_driver = {
.driver = {
.name = "imx6ull-beep", /* 驱动名字 */
.of_match_table = beep_of_match,/* 设备树匹配列表 */
},
.probe = misc_beep_probe,
.remove = misc_beep_remove,
};
/*
* @Brief 驱动入口函数
* @Call Internal or External
* @Param None
* @Note NOne
* @RetVal NOne
*/
static int __init misc_beep_init(void)
{
return platform_driver_register(&beep_driver);
}
/*
* @Brief 驱动出口函数
* @Call Internal or External
* @Param None
* @Note NOne
* @RetVal NOne
*/
static void __exit misc_beep_exit(void)
{
platform_driver_unregister(&beep_driver);
}
module_init(misc_beep_init);
module_exit(misc_beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("toto");
四、编写测试 APP
/********************************************
*Description:
*Version: 1.0
*Autor:
*Date:
*LastEditors:
*LastEditTime:
********************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#define BEEPON 1
#define BEEPOFF 0
/*
* @Brief main 主程序
* @Call Internal or External
* @Param argc:
* @Param argv:
* @Note NOne
* @RetVal 0-成功;其他-失败
*/
int main(int argc, char *argv[])
{
int fd, retval;
char *filename;
unsigned char databuf[1];
if(argc != 3)
{
printf("argc != 3\n");
return -1;
}
filename = argv[1];
/*打开驱动文件*/
fd = open(filename, O_RDWR);
if(fd < 0)
{
printf("open filename:%d failed\n", filename);
return -1;
}
/* 要执行的操作:打开或关闭 */
databuf[0] = atoi(argv[2]);
retval = write(fd, databuf, sizeof(databuf));
if(retval < 0)
{
printf("write file:%s failed\n", filename);
}
/*关闭文件*/
close(fd);
return 0;
}
五、运行测试
cd /lib/modules/5.19.0-g794a2f7be62d-dirty
insmod misc_beep.ko
/ # ls /sys/class/misc/
autofs hw_random rfkill watchdog
cpu_dma_latency loop-control ubi_ctrl
fuse misc_beep vga_arbiter
ls -l /dev/misc_beep
结果如下所示:
/ # ls /dev/misc_beep -al
crw------- 1 0 0 10, 144 Jan 5 18:43 /dev/misc_beep
./misc_beep_app /dev/misc_beep 1
输入如下命令关闭 BEEP:
./misc_beep_app /dev/misc_beep 0--- END ---



