视频:第18.1讲 Linux杂项(MISC)驱动实验-MISC驱动框架简介_哔哩哔哩_bilibili
资料:《【正点原子】I.MX6U开发指南V1.81》五十七章
本章内容基本与platform一致。新的内容只有第一部分的一个结构体和两个函数。
因为MISC底层依然使用platform,只是对其进行了封装,使用更为简便。
一、相关代码
1.1 设备结构体
cpp
// 定义在include/linux/miscdevice.h
struct miscdevice {
int minor; // 主设备号固定为10
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;
};
1.2 注册 / 注销
cpp
extern int misc_register(struct miscdevice *misc);
extern int misc_deregister(struct miscdevice *misc);
// misc:要注册/注销的MISC设备
// return:0成功;负值失败
MISC已经经过封装,misc_register 覆盖了alloc_chrdev_region(或register)、cdev_init、cdev_add、class_create(实际上是使用现有的 misc_class)以及 device_create的功能;而misc_deregister则覆盖了cdev_del、unregister_chrdev_region、device_destroy、class_destroy的功能。
1.3 子设备号
Linux系统已经预定义了一些MISC设备的子设备号。使用时可以从这些预定义的子设备号中挑选,也可以自己定义(只要没有被使用过)
cpp
// 定义在include/linux/miscdevice.h
# 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 ADB_MOUSE_MINOR 10 FIXME OBSOLETE */
#define WATCHDOG_MINOR 130 /* Watchdog timer */
#define TEMP_MINOR 131 /* Temperature Sensor */
#define RTC_MINOR 135
#define EFI_RTC_MINOR 136 /* EFI Time services */
#define VHCI_MINOR 137
#define SUN_OPENPROM_MINOR 139
#define DMAPI_MINOR 140 /* unused */
#define NVRAM_MINOR 144
#define SGI_MMTIMER 153
#define STORE_QUEUE_MINOR 155 /* unused */
#define I2O_MINOR 166
#define MICROCODE_MINOR 184
#define VFIO_MINOR 196
#define TUN_MINOR 200
#define CUSE_MINOR 203
#define MWAVE_MINOR 219 /* ACP/Mwave Modem */
#define MPT_MINOR 220
#define MPT2SAS_MINOR 221
#define MPT3SAS_MINOR 222
#define UINPUT_MINOR 223
#define MISC_MCELOG_MINOR 227
#define HPET_MINOR 228
#define FUSE_MINOR 229
#define KVM_MINOR 232
#define BTRFS_MINOR 234
#define AUTOFS_MINOR 235
#define MAPPER_CTRL_MINOR 236
#define LOOP_CTRL_MINOR 237
#define VHOST_NET_MINOR 238
#define UHID_MINOR 239
#define MISC_DYNAMIC_MINOR 255
二、代码编写
这么快就开始写代码了,还不太习惯
2.1 设备树
视频中使用的是beep节点,但我这里使用led0来测试。直接使用之前led实现写的节点即可(免得在工位把不可名状之物招来)
使用led0,将其对应的gpio1_io03复用为gpio:

把下面这段代码放到imx6ull-alientek-emmc.dts文件中根节点/里面:
cpp
gpioled{
compatible = "alientek,gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpioled>;
led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; // GPIO1_pin3 低电平有效
status = "okay";
};

cpp
pinctrl_gpioled: ledgrp{
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0
>;
};

然后检查gpio1 io03有没有其他地方被使用过,有则注释掉。
编译、复制:
bash
make dtbs
sudo cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb /.../tftpboot/
2.2 文件结构
cpp
20_MISC(工作区)
└── 20_misc
├── .vscode
│ ├── c_cpp_properties.json
│ └── settings.json
├── miscled.c
├── 20_misc.code-workspace
├── miscledAPP.c
└── Makefile
2.3 Makefile
bash
CFLAGS_MODULE += -w
KERNELDIR := /home/改成自己的linux内核路径/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek # 内核路径
CURRENT_PATH := $(shell pwd) # 当前路径
obj-m := miscled.o # 编译文件
build: kernel_modules # 编译模块
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
2.4 驱动框架
大致结构如下:
cpp
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/stat.h>
#include <linux/device.h>
// 匹配表
static const struct of_device_id led_of_match[] = {
{.compatible = "alientek,gpioled"}, // 与设备树的compatible保持一致
{ /*sentinel*/},
};
// probe & remove 函数
static int misc_led_probe(struct platform_device *dev){
return 0;
}
static void misc_led_remove(struct platform_device *dev){
}
//platform
static struct platform_driver misc_driver = {
.driver = {
.name = "alientek,gpioled", // 与设备树的compatible保持一致
.owner = THIS_MODULE,
.of_match_table = led_of_match, // 匹配表
},
.probe = misc_led_probe,
.remove = misc_led_remove,
};
//驱动入口
static int __init miscled_init(void){
return platform_device_register(&misc_driver);
}
// 驱动出口
static void __exit miscled_exit(void){
platform_driver_unregister(&misc_driver);
}
module_init(miscled_init);
module_exit(miscled_exit);
MODULE_LICENSE("GPL");
2.5 完整驱动代码
在驱动框架的基础上完善。
cpp
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/stat.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/of_gpio.h>
#define MISCLED_NAME "miscled"
#define MISCLED_NUM 1
#define MISCLED_MINOR 144
#define LED_ON 1
#define LED_OFF 0
// misc led设备结构体
struct miscled_dev{
struct device_node *nd;
int gpio;
};
struct miscled_dev miscled;
// 操作集函数
static int miscled_open(struct inode *inode, struct file *filp){
filp->private_data = &miscled;
return 0;
}
static int miscled_release(struct inode *inode, struct file *filp){
return 0;
}
static ssize_t miscled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos){
int ret;
unsigned char databuf[1];
struct miscled_dev *dev = filp->private_data;
ret = copy_from_user(databuf, buf, count);
if(ret < 0){
return -EINVAL;
}
if(databuf[0] == LED_ON){ //开灯
gpio_set_value(dev->gpio, 0); // 低电平开灯
} else if(databuf[0] == LED_OFF){
gpio_set_value(dev->gpio, 1); // 高电平关灯
}
return 0;
}
// 操作集
struct file_operations miscled_fops = {
.owner = THIS_MODULE,
.open = miscled_open,
.release = miscled_release,
// .read = ,
.write = miscled_write,
};
// misc device结构体
static struct miscdevice led_miscdev = {
.minor = MISCLED_MINOR,
.name = MISCLED_NAME,
.fops = &miscled_fops,
};
// 匹配表
static const struct of_device_id led_of_match[] = {
{.compatible = "alientek,gpioled"}, // 与设备树的compatible保持一致
{ /*sentinel*/},
};
// probe & remove 函数
static int misc_led_probe(struct platform_device *dev){
int ret = 0;
// 申请gpio
miscled.nd = dev->dev.of_node;
miscled.gpio = of_get_named_gpio(miscled.nd, "led-gpios", 0);
if(miscled.gpio<0){
printk("can't get gpio!\r\n",miscled.gpio);
ret = -EINVAL;
goto fail_get_gpio;
}
ret = gpio_request(miscled.gpio, "led-gpios");
if(ret){
printk("can't request %d gpio!\r\n",miscled.gpio);
ret = -EINVAL;
goto fail_request_gpio;
}
ret = gpio_direction_output(miscled.gpio, 1); // 关灯
if(ret < 0){
printk("can't set %d gpio direction!\r\n",miscled.gpio);
goto fail_set_gpio;
}
// misc注册
ret = misc_register(&led_miscdev);
if(ret < 0){
goto fail_misc_register;
}
return 0;
fail_misc_register:
fail_set_gpio:
gpio_free(miscled.gpio);
fail_request_gpio:
fail_get_gpio:
return ret;
}
static void misc_led_remove(struct platform_device *dev){
gpio_set_value(miscled.gpio, 1); // 关灯
gpio_free(miscled.gpio);
misc_deregister(&led_miscdev);
}
//platform
static struct platform_driver misc_driver = {
.driver = {
.name = "alientek,gpioled", // 与设备树的compatible保持一致
.owner = THIS_MODULE,
.of_match_table = led_of_match, // 匹配表
},
.probe = misc_led_probe,
.remove = misc_led_remove,
};
//驱动入口
static int __init miscled_init(void){
return platform_driver_register(&misc_driver);
}
// 驱动出口
static void __exit miscled_exit(void){
platform_driver_unregister(&misc_driver);
}
module_init(miscled_init);
module_exit(miscled_exit);
MODULE_LICENSE("GPL");
2.6 应用代码
应用代码与platform设备驱动4.1.5部分的代码完全一致。将以下代码写入到miscledAPP.c文件中:
cpp
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功; else失败
* 调用 ./platformApp <filename> <0:1> 0关灯,1开灯
* ./platformApp /dev/platled 0 关灯
* ./platformApp /dev/platled 1 开灯
*/
#define LEDOFF 0
#define LEDON 1
int main(int argc, char *argv[]){
if(argc != 3){ // 判断用法是否错误
printf("Error Usage!\r\n");
return -1;
}
char *filename;
int fd = 0;
unsigned char databuf[1];
int retvalue = 0;
filename = argv[1];
fd = open(filename, O_RDWR); // 读写模式打开驱动文件filename
if(fd <0){
printf("file %s open failed!\r\n");
return -1;
}
databuf[0] = atoi(argv[2]); // char 2 int
retvalue = write(fd, databuf, sizeof(databuf)); // 缓冲区数据写入fd
if(retvalue <0){
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
close(fd);
return 0;
}
2.7 测试
bash
# VSCODE
make
arm-linux-gnueabihf-gcc miscledAPP.c -o miscledAPP
sudo cp miscled.ko miscledAPP /home/for/linux/nfs/rootfs/lib/modules/4.1.15/
# 控制台
cd /lib/modules/4.1.15
depmod
modprobe miscled.ko
ls /dev/miscled -l # 应能看到对应设备

bash
./miscledAPP /dev/miscled 1 # 红灯亮起
./miscledAPP /dev/miscled 0 # 红灯熄灭
rmmod miscled.ko