Linux驱动开发笔记(十五)——MISC驱动实验

视频:第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
相关推荐
丁劲犇2 小时前
使用Manjaro制作SDR业余软件无线电LiveDVD发行版
linux·archlinux·sdr·manjaro·livecd·业余软件无线电·livedvd
m0_689618282 小时前
纳米工程重构生物材料:从实验室到临床的革命性突破
人工智能·笔记·学习·计算机视觉
YJlio2 小时前
Contig 学习笔记(13.6):分析现有文件碎片化程度——报告、日志与“碎片基线”
笔记·学习·ffmpeg
大聪明-PLUS2 小时前
深入 initrd
linux·嵌入式·arm·smarc
AI+程序员在路上2 小时前
linux下网络IP、网关及路由设置详解
linux·网络·tcp/ip
iconball2 小时前
个人用云计算学习笔记 --33 Containerd
运维·笔记·学习·云计算
wabil2 小时前
VSCode远程调试Linux的GUI程序
linux·ide·vscode
丝斯20112 小时前
AI学习笔记整理(40)——自然语言处理算法之Seq2Seq
人工智能·笔记·学习
lbb 小魔仙2 小时前
【Linux】Linux 安全实战:防火墙配置 + 漏洞修复,符合企业合规标准
linux·运维·安全