Linux misc设备驱动理解

power_supply 探究设备 关系 makefile: obj-$(CONFIG_MTK_CHARGER) += charger_class.o mtk_basic_charger.o mtk_charger.o mtk_charger_algorithm_class.o mtk_charger_intf.o adapter_class.o mtk_pd_adapter.o mtk_pd.o mtk_pd_hal.o mtk_pulse_charger.o obj-$(CONFIG_CHARGER_TYPE_MT6357)... 阅读详情

   感兴趣可以加QQ群85486140,大家一起交流相互学习下!

   Linux里面的misc杂项设备是主设备号为10的驱动设备,它的注册跟使用比较的简单,所以比较适用于功能简单的设备。正因为简单,所以它通常嵌套在platform 总线驱动中,配合总线驱动达到更复杂,多功能的效果。下面我们一起来分析它的过程。博客中如果有问题,欢迎大家指正,我们共同努力,进步。

一.杂项设备数据结构分析

杂项设备驱动结构还是很简单的,他可以夹杂到其它结构体当中,以丰富驱动的血肉。一般情况下,我们是将它嵌套在其它结构当中的。

 

struct miscdevice  {
	int minor; //次设备号,主设备号已经敲定是10了,后面我们跟进代码看一下。
	const char *name;  //驱动名字,最终会反映在设备节点名字上。
	const struct file_operations *fops; //设备操作方法集合
	struct list_head list; //链接到所有杂项设备链表当中。
	struct device *parent; //父设备,这个一般为NULL
	struct device *this_device;//当前设备的devices结构。
	const char *nodename;
	umode_t mode;
};

上面:

parent:这个指针决定了在/sys文件系统里面,它是创建在哪个目录下。如果为空就在/sys/class根目录下创建,如果不为空都是在/sys/class/misc 文件下面创建的一些属性文件。
this_device:这个就代表当前设备的设备结构体,这个在查找扩充数据结构时,非常有用。

 

typedef struct led{
	struct miscdevice *led_dev;
	struct mutex led_lock;
	spinlock_t io_lock;
	int flset;
	int flen;
	uint8_t regs[4+1];
}rt8547_dev_t;

上面就是我定义的数据结构,我就是把struct miscdevice 嵌套在我自己定义的头文件中。
 

 

二、杂项设备的注册过程

在介绍注册过程时,我们先来了解一些非常重要的全局变量。

 

static LIST_HEAD(misc_list);
/************************************************
*#define LIST_HEAD_INIT(name) { &(name), &(name) }
*
*#define LIST_HEAD(name) \
*   struct list_head name = LIST_HEAD_INIT(name) //可以看到这里偷偷的定义了一个为name的struct list_head结构
*************************************************/
 static DEFINE_MUTEX(misc_mtx);
/***********************************************************
* #define DEFINE_MUTEX(mutexname) \
*    struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) //同样这里也定义了一个互斥锁。
************************************************************/
 /*
 * Assigned numbers, used for dynamic minors
 */
#define DYNAMIC_MINORS 64 /* like dynamic majors */
static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
/***********************************************
*#define DECLARE_BITMAP(name,bits) \
*    unsigned long name[BITS_TO_LONGS(bits)] //这里这个宏BITS_TO_LONGS(64) 我在机器上验证结果是2
*所以,上面相当与直接定义了 unsigned long misc_minors[2];//而我所验证的平台上unsigned long大小为4个字节,这样的话可以表示64字符设备。
************************************************/

misc_list:这是所有misc设备的头指针,打个比方说,所有misc设备结构体都挂在它上面。我画了一个简图如下所示。在我的内核版本中,我打印log发现,它只定义了64个标志位,来标示次设备号的使用情况。网上有人说次设备号最多有255个,但不管它有多少个吧,原理都是一样的。

注册的过程是用下面这个函数来实现的,具体的注释我都打在代码中了,自己理解吧^_^

 

int misc_register(struct miscdevice * misc)
{
	dev_t dev;
	int err = 0;

	INIT_LIST_HEAD(&misc->list); //struct list_head 初始化,这个当我们有这个结构的话,要记着用这个宏初始化一下。

	mutex_lock(&misc_mtx); //同样互斥锁也要初始化,当然我们如果用到的话,也要这样初始化。

	if (misc->minor == MISC_DYNAMIC_MINOR) { //我们在驱动中配置的就是MISC_DYNAMIC_MINOR(动态分配)
		int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); //其中的原理我们不深究,这里就是找一个没有使用的次设备号给我们用。
		if (i >= DYNAMIC_MINORS) {
			mutex_unlock(&misc_mtx);
			return -EBUSY;
		}
		misc->minor = DYNAMIC_MINORS - i - 1;
		set_bit(i, misc_minors); //请看下面详细代码。
	} else {
		struct miscdevice *c;

		list_for_each_entry(c, &misc_list, list) { //如果上面不是动态注册,就遍历整个misc_list,确定是否已经注册过了。
			if (c->minor == misc->minor) { 
				mutex_unlock(&misc_mtx);
				return -EBUSY;
			}
		}
	}

	dev = MKDEV(MISC_MAJOR, misc->minor);//主设备好和次设备号,加工成真正的设备号。

	misc->this_device = device_create(misc_class, misc->parent, dev, //这个函数是非常重要的。
					  misc, "%s", misc->name);
	if (IS_ERR(misc->this_device)) {
		int i = DYNAMIC_MINORS - misc->minor - 1;
		if (i < DYNAMIC_MINORS && i >= 0)
			clear_bit(i, misc_minors);
		err = PTR_ERR(misc->this_device);
		goto out;
	}

	/*
	 * Add it to the front, so that later devices can "override"
	 * earlier defaults
	 */
	list_add(&misc->list, &misc_list); //最后将该设备加入全局misc设备链表中
 out:
	mutex_unlock(&misc_mtx); //解锁
	return err;
}

//下面一段的函数的意思就是把数组中的对应的标志位置位,表示这个次设备号已经使用了。
static inline void set_bit(int nr, volatile unsigned long *addr)
{
    //#define BIT_MASK(nr)        (1UL << ((nr) % BITS_PER_LONG))
    unsigned long mask = BIT_MASK(nr); //找到次设备号偏移,如上宏定义
    unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);//这里如果nr>=32,这里就会+1,(类型是long,所以实际上这里是+4)标志位就跑到第二行了。
    unsigned long flags;

     //#define BIT_MASK(nr)        (1UL << ((nr) % BITS_PER_LONG)) 即1<<(nr % 32)

     _atomic_spin_lock_irqsave(p, flags); //加了保护
    *p  |= mask; //对应标志为置1.
    _atomic_spin_unlock_irqrestore(p, flags);
}

假如现在我们创建的次设备号是20,那么标志位偏移量就是1<<20,那么就会有上面的misc_minors[0] =misc_minors[0] | 1<<20;同样假如现在我们创建的次设备号是40,那么就会有mask = 1<<(40%32),即mask = 1<<8,就有misc_minors[1] = misc_minors[1] | 1<<8。下面这幅图是根据我所使用的内核版本,我使用的内核里面只定义了64个标志位,这里也就画了8个字节。但是不管有多少个字节吧,原理还是一样的。

 

 

可以看看/dev目录下的设备节点主设备是10,次设备号是42.这样的话,标志位就放在第二排了。

 

三、引人深思的misc_class

misc_class就是一个引子,创建了这样一个class之后,所有的misc设备的class设备文件都会在/sys/class/misc目录下创建。

 

static struct class *misc_class; //全局的变量

static int __init misc_init(void)
{
	int err;

#ifdef CONFIG_PROC_FS
	proc_create("misc", 0, NULL, &misc_proc_fops); //如果允许proc文件系统,这里也会创建proc文件夹。
#endif
	misc_class = class_create(THIS_MODULE, "misc");//这里就看到了,创建了全局的class文件。
	err = PTR_ERR(misc_class);
	if (IS_ERR(misc_class))
		goto fail_remove;

	err = -EIO;
	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) //这里还是调用字符设备的注册接口,只不过这里主设备号始终就是10了。
		goto fail_printk;
	misc_class->devnode = misc_devnode;
	return 0;

fail_printk:
	printk("unable to get major %d for misc devices\n", MISC_MAJOR);
	class_destroy(misc_class);
fail_remove:
	remove_proc_entry("misc", NULL);
	return err;
}
subsys_initcall(misc_init); //这里预示系统刚起来时,就会调用这里的初始化函数,确保在所有misc设备初始化之前,misc_class,misc_list都创建好了。

只要有了misc_class对象,所有misc设备就有地方放了。

 

四.杂项设备的注销过程

 

杂项设备的注销过程,其实就是注册过程的逆向过程。只是说这里代码非常简单,其实主要工作都在device_destroy()中。

int misc_deregister(struct miscdevice *misc)
{
	int i = DYNAMIC_MINORS - misc->minor - 1;

	if (WARN_ON(list_empty(&misc->list)))
		return -EINVAL;

	mutex_lock(&misc_mtx);
	list_del(&misc->list);
	device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
	if (i < DYNAMIC_MINORS && i >= 0)
		clear_bit(i, misc_minors);
	mutex_unlock(&misc_mtx);
	return 0;
}

void device_destroy(struct class *class, dev_t devt)
{
	struct device *dev;

	dev = class_find_device(class, NULL, &devt, __match_devt);
	if (dev) {
		put_device(dev);
		device_unregister(dev);
	}
}

上面就是根据misc_class,加上设备的次设备好,就可以找到代表这个设备的struct device结构,然后就可以将它从misc_list中移除。这样就完成了设备注销。当然注销过程也包含了一些属性文件的删除,只是这里没有体现出来。

五.Rt8547闪光灯misc设备驱动的分析

下面的这个例子是我在添加camera 闪光灯rt8547设备时,编写的一个闪光灯驱动的初版文件。初步的功能我已经验证过了,是可以工作的。

1)open函数中的共享精神

下面限于篇幅问题,我不会贴出所有代码,只是加上自己的一些分析,和在编码过程中遇到的问题。

 

rt8547_dev_t *led_dev = NULL; //全局的闪光灯设备结构体

static int led_open(struct inode *node, struct file *file){

	struct my_platfrom_struct *my_data = kmalloc(sizeof(struct my_platfrom_struct), GFP_KERNEL);
	if (!my_data){
		RT8547_ERR("malloc my_data falied!");
	}
	my_data->grade = 1;
	my_data->age = 22;
	my_data->phone = 110;
	file->private_data = my_data;
	RT8547_INFO("open led devices success!!!\n");
	return 0;
}

上面代码中,我最想强调的就是file->private_data = my_data.这行代码鬼使神功的将我们需要共享的变量,放到了其它函数都能看得到的地方。因为在其它的read,write,ioctl,release等函数都可以通过file->private_data拿到这个共享的变量。一定要学会共享,理解共享的精神。

2)ioctl函数中的多元化

 

 

static long led_ioctl(struct file *file, unsigned int cmd,unsigned long arg){
	struct my_platfrom_struct *para = (struct my_platfrom_struct *)arg;
	struct my_platfrom_struct *my_dat = (struct my_platfrom_struct *)file->private_data; //这里我们就拿到了共享变量。

	switch(cmd){
		case LED_IO_GET_MY_DATA:
			copy_to_user(para,my_dat,sizeof(struct my_platfrom_struct));
			break;
		case LED_IO_OPEN_TORCH:
			{
				int level;
				copy_from_user(&level,(void*)arg,sizeof(int));
				rt8547_send_data(led_dev,RT8547_CONTROL3,RT8547_MODE_MASK,1);//Torch Mode
				rt8547_send_data(led_dev,RT8547_CONTROL3,0x0f,level&0x0f);
				RT8547_INFO("Open Torch, level:%d",level);
				rt8547_set_led_on(led_dev);
			}
			break;
		case LED_IO_OPEN_FLASH:
			{
				int level;
				copy_from_user(&level,(void*)arg,sizeof(int));
				rt8547_send_data(led_dev,RT8547_CONTROL3,RT8547_MODE_MASK,0);//flash Mode
				rt8547_send_data(led_dev,RT8547_CONTROL2,0x1f,level&0x1f);
				RT8547_INFO("Open Flash, level:%d",level);
				rt8547_set_led_on(led_dev);
			}
			break;
		case LED_IO_CLOSE_LED:
			rt8547_set_led_off(led_dev);
			RT8547_INFO("rt8547 close device!\n");
			break;
		}
	return 0;
}

ioctl函数在驱动中用到最多,在实际开发中一般都是通过ioctl和kernel交互的。

 

3)Rt8547 gpio 初始化和释放

gpio 初始化相当重要,不初始化就不能用了。gpio的初始化和释放所有kernel都是一样的过程,大家要熟记这些方法。

 

static void rt8547_gpio_deinit(const rt8547_dev_t *dev)
{
	gpio_direction_input(dev->flen);
	gpio_free(dev->flen);

	gpio_direction_input(dev->flset);
	gpio_free(dev->flset);
}

static int rt8547_gpio_init(const rt8547_dev_t *dev)
{
	int ret;
	gpio_request(dev->flen,NULL); //请求gpio
	if (ret) {
		RT8547_INFO("sensor: flen already request %d.\n",dev->flen);
		goto request_flen_fail;
	}
	gpio_direction_output(dev->flen, 1); //设置gpio的输出输出方向
	gpio_set_value(dev->flen,0); //设置为高电平还是低电平

	gpio_request(dev->flset,NULL); //以下同理
	if (ret) {
		printk("sensor: flset already request %d.\n",dev->flen);
		goto request_flset_fail;
	}
	gpio_direction_output(dev->flset, 1);//out
	gpio_set_value(dev->flset,0);
	return 0;
	
request_flset_fail:
	gpio_free(dev->flset);
request_flen_fail:
	gpio_free(dev->flen);
request_fail:
	return ret;
}

注意:大家有没有注意到上面函数gpio口的释放,有什么异样没有。也许你已经发现,谁先注册的,就放到最后在释放,这也是为了kernel的稳定性才这样写的。当有一个gpio口请求失败,那么之前注册成功的gpio资源都要释放。要不然也许有其它模块在请求这个gpio口资源呢。记住注册过程和释放过程是相对的。如下所示。

4)led_fops和misdevice设备填充

 

static const struct file_operations led_fops = {
	.owner = THIS_MODULE, //一般都是THIS_MODULE
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.unlocked_ioctl = led_ioctl,
	.release = led_release,
};//上面都是填充一些关键的函数指针,只要知道怎么用就行了。

static struct miscdevice led_device = {
	.minor = LED_MINOR, //次设备号
	.name = LED_DEVICE_NAME, //设备名字宏定义
	.fops = &led_fops, //文件操作指针集合,如上面的结构
};

上面两个结构是字符设备必不可少的两个结构体。大家有没有发现这两个结构一般都放在靠近驱动模块末尾的位置。不知道大家有没有考虑过为什么会有这样的情况。这是由于驱动中一般没有声明open,read,write,ioctl等接口函数,如果你不想将file_operations安排在驱动末尾。你也可以声明这些驱动函数,这样就不会受限制了。这个要看自己了,一般不这样来,这也是一种习惯。

5)rt8547驱动初始化函数和注销函数

 

int led_init(){
	int ret;
	RT8547_INFO("led_init!\n");
	led_dev =  kzalloc(sizeof(rt8547_dev_t), GFP_KERNEL);
	if(!led_dev){
		RT8547_ERR("alloc mem failed");
	}

	led_dev->led_dev = &led_device;
	led_dev->flset= GPIO_CAM_FLASH_SET;
	led_dev->flen = GPIO_CAM_FLASH_EN;
	led_dev->regs[RT8547_DUMMY] = 0;
	led_dev->regs[RT8547_CONTROL1] = 0x06; //这里模拟寄存器
	led_dev->regs[RT8547_CONTROL2] = 0x12;
	led_dev->regs[RT8547_CONTROL3] = 0x02;
	led_dev->regs[RT8547_CONTROL4] = 0x0f;
	
	//rt8547_parse_dt(led_dev->led_dev->this_device->of_node,led_dev);
	rt8547_gpio_init(led_dev);
	mutex_init(&led_dev->led_lock);
	ret = misc_register(led_dev->led_dev);
	if (unlikely(ret)) {
		RT8547_ERR("failed to register misc device'%s'!\n",
				led_dev->led_dev->name);
		goto out_free_buffer;
	}

	ret = device_create_file(led_dev->led_dev->this_device, &dev_attr_flash); //创建属性文件
	if (ret < 0) {
		printk(KERN_ALERT"Failed to create attribute flash.");
		goto err_register_flash;
	}

	RT8547_INFO("create device attribute file flash!\n");
	return 0;
err_register_flash:

out_free_buffer:
	kfree(led_dev);
	return ret;
}

void led_exit(void)
{
	RT8547_INFO("rt8547 exit!\n");
	misc_deregister(led_dev->led_dev);
	kfree(led_dev);
}

驱动初始化函数中一般进行全局设备结构题的初始化,资源的请求,设备树的解析。当然如果是platform设备,这些也可以在probe函数中做。大家有没有发现我在上面的初始化函数中使用device_create_file()创建了一个设备属性文件。后面我会专门写一篇博文专门来分析这些和创建设备文件相关的函数。

 

6)应用程序

 

#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include "led_rt8547.h"

#define FILE_PATH "/dev/rt8547_led"

int main(){
	int ret;
	int mode,level;
	my_data my_dat; 

	int fd = open(FILE_PATH,O_RDWR);
	if ( -1 == fd){
		printf("open led_flash node failed!\n");
		return -1;
	}
	printf("unsigned size:%d\n",sizeof(unsigned long));

	printf("\n1:GET_MY_DATA\n \
		2:LED_OPEN_TORCH\n \
		3:LED_OPEN_FLASH\n \
		4:LED_CLOSE\n");
	printf("mode:");
	scanf("%d",&mode);
	if(2 == mode || 3 == mode){
		printf("level:");
		scanf("%d",&level);
	}
	printf("Rt8547 mode:%d,level:%d\n",mode,level);
	switch(mode){
		case 1:
			ret = ioctl(fd,LED_IO_GET_MY_DATA, &my_dat);
			printf("Rt8547 Get my data success!\n");
			printf("Rt8547 grade:%d,age:%d,phone:%d",my_dat.grade,my_dat.age,my_dat.phone);
			if(0 != ret){
				printf("Rt8547 Get my data failed!\n");
				ret = -1;
			}
			break;
		case 2:
			ret = ioctl(fd,LED_IO_OPEN_TORCH, &level);
			printf("Rt8547 open torch success!\n");
			if(0 != ret){
				printf("Rt8547 open torch failed!\n");
				ret = -1;
			}
			break;
		case 3:
			ret = ioctl(fd,LED_IO_OPEN_FLASH, &level);
			printf("Rt8547 open flash success!\n");
			if(0 != ret){
				printf("Rt8547 open flash failed!\n");
				ret = -1;
			}
			break;
		case 4:
			ret = ioctl(fd,LED_IO_CLOSE_LED, &level);
			printf("Rt8547 close led success!\n");
			if(0 != ret){
				printf("Rt8547 close led failed!\n");
				ret = -1;
			}
			break;
	}
	return ret;
}

上面调用系统调用,一切都在代码中,read the fucking code!!!!!!

 

五.调试过程中遇到的问题及解决办法和思路

1.Android.mk编写问题

在Anrdroid大环境下,最主要的问题是找不到头文件的问题。一开始的编译的时候,找不到头文件,后来参考其它工程的Android.mk发现他们一般都包含了$(TARGET_OUT_INTERMEDIATES)/KERNEL/source/include/video(如下所示),大家一看都明白,这里就是存放头文件的地方。其实在编译的过程中,kernel的头文件都会拷贝到out目录对应工程的/KERNEL/source/include目录下,这些头文件和kernel源码中是一样的。大家一定要牢记。我在编译led_flash测试程序时,用的就是下面这个Android.mk

其中LOCAL_MODULE_TAGS :=  optional意思就是所有版本都编译该模块。

知识扩展:
 user: 指该模块只在user版本下才编译
 eng: 指该模块只在eng版本下才编译
 tests: 指该模块只在tests版本下才编译
 optional:指该模块在 所有版本下都编译,默认是optional

 

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)//清除环境变量
#LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)

#头文件路径
LOCAL_C_INCLUDES := \
		$(TARGET_OUT_INTERMEDIATES)/KERNEL/source/include/misc \ 
		$(TARGET_OUT_INTERMEDIATES)/KERNEL/source/include/video 

LOCAL_SRC_FILES := led_flash.c  #源文件
LOCAL_MODULE := led_flash #应用程序,编译成可执行程序时的名字
LOCAL_MODULE_TAGS := optional 

include $(BUILD_EXECUTABLE)

 

2)Kernel头文件编写问题

在上面Android.mk中包含了头文件路径时,有时候还是会提示找不到一些头文件。比如我在led_rt8547.h头文件中添加了一个#include <linux/mutex.h>。编译的时候找不到mutex.h这个头文件。这是因为我们在Android.mk中根本就没有包含mutex.h头文件所在的路径。为此我有以下几点意见:

1.应用程序一般都是使用IOCTL命令和一些宏信息,所以在驱动模块的头文件中,不要添加一些和kernel相关的结构体,尽量使用我们自己定义的数据类型。

2.头文件尽量少包含与kernel紧密联系的头文件(有联系的就要将头文件路径加到android.mk中),要不然也找不到头文件(除非已经包含了头文件路径),我们自己定义的其它头文件是可以添加进来。

3.模块中和内核紧密联系的结构体,尽量放在驱动源码中,避免编译报错。应用层其实也用不到这些结构体,那么就不要放到头文件中了。

 

 

 

[RK-Linux] misc分区详解 misc 其实是英文 miscellaneous 的前四个字母,杂项、混合体、大杂烩的意思。misc 分区的概念来源于 Android 系统,Linux 系统中常用来作为系统升级时或者恢复出厂设置时使用。misc 分区的读写:misc 分区在以下情况下会被读写。Uboot:设备加电启动时,首先启动 Uboot,在 Uboot 中会读取 misc 分区的内容。根据 misc分区中 command 命令内容决定是进入正常系统还是 recovery 模式。 阅读详情

相关推荐

2.简单misc杂项设备驱动1

2.1misc使用的结构体和函数misc设备其实也是字符设备,只不过misc设备驱动在字符设备的基础上又进行了一次封装,使用户可以更方便的使用 2.2为什么要有

Linux Kernel设备驱动模型之class 查找设备

/**  * class_find_device - device iterator for locating a particular device  * @class: the class we're iterating  * @start: Device to begin with  * @data: data for the match function  * @match: f

星空探索 2585

【驱动适配】高通sa522平台tac5x1x驱动适配全指南

本手册详细介绍了TAC5301-Q1音频编解码器在高通sa525平台Linux系统下的驱动适配流程。主要内容包括:1)TAC5301-Q1核心特性(车载级音频Codec、关键接口和功能);2)驱动适配依赖(高通声卡驱动、ALSA SoC框架等);3)I2S协议原理及主从模式说明;4)硬件准备与检查要点;5)完整的驱动移植步骤(代码移植、Kconfig/Makefile修改、设备树配置)。该手册适用于嵌入式音频开发工程师快速完成TAC5301-Q1芯片的驱动移植与验证工作。

嵌入式博哥的博客 303

Linux驱动之MISC设备驱动

每日一个简单的驱动,日久方长,对Linux驱动就越来越熟悉,也越来容易学会写驱动程序。今日进行第七个Linux MISC 设备驱动。

Wang_XB_3434的博客 2169

PIC单片机应用开发实践教程(二): 新建工程

File---New ProjectMicrochip Embedded --- Standalone Project 选择所用MCU对应型号 点击 Finish 后,工程就建好了 至止,我们的一个新工程就建完成了!

qq_57798018的博客 2888

爬虫实战:抖音视频搜索数据爬虫开发实战(附完整Python源码)

【代码】抖音视频搜索数据爬虫开发实战(附完整Python源码)

weixin_61226361的博客 3597

2.1Linux driver misc设备驱动理解

目录 1.misc_init函数分析 一些需要注意的细节部分 2.misc_register函数分析 3.流程图 Linux里面的misc杂项设备是主设备号为10的驱动设备,它的注册跟使用比较的简单,所以比较适用于功能简单的设备。正因为简单,所以它通常嵌套在platform 总线驱动中,配合总线驱动达到更复杂,多功能的效果。 基于linux2.6.22 1.misc_init函数分析...

qq_34738528的博客 765

私有化大模型部署:企业AI应用的核心架构

私有化大模型部署不是“一次性项目”,而是企业AI的“长期主义”投资——它需要企业投入时间、金钱和精力,但回报是数据安全、定制化能力、核心竞争力的提升。就像“自家厨房”一样,私有化大模型可能初期麻烦,但一旦搭建起来,就能为企业“做”出符合自己口味的“特色菜”,而这些“特色菜”,正是企业在激烈竞争中的“护城河”。愿你能构建属于自己的“AI大脑”,让技术真正赋能业务,让复杂变简单,让深奥变通透。附录:私有化大模型部署架构图(注:可根据企业需求调整,此为通用架构)

AI天才研究院 1021

Linux下的misc类型设备驱动学习笔记

一、Linux下的设备分类 Linux根据IO端口的特点和数据的处理方式将外围设备分为三类: 1、字符设备:以字节流的形式处理的低速设备,单位byte; 2、块设备:以区块的形式处理存储设备,单位K; 3、网络设备:以分散、聚合形式处理的高速网卡设备,单位M; 二、Linux下的设备管理方法 1、内核态:采用主设备号和次设备号标识单个独立的设备,注册编写设备驱动时需要提供主、次设备号;...

Leo_ZN_0的博客 943

手把手教你配置SAP PP供应区域(Supply Area):实现线边仓精准发料与物料流优化

本文详细解析了SAP PP供应区域(Supply Area)的配置方法,帮助企业实现线边仓精准发料与物料流优化。通过三级优先级逻辑和四步配置法,系统可自动识别最优组件来源位置,显著提升生产订单处理效率和库存准确率。文章还提供了典型问题解决方案和性能优化技巧,适用于离散制造和重复制造场景。

weixin_29275257的博客 433

Win10 开机突然蓝屏错误代码0xc0000001安全模式也进不了,不用重装系统怎么修复?(已解决)九个解决参考方案

Win 10专业版HP480G7。

本博客,博文仅代表个人操作经验,不能完全解决你的问题,仅供参考,佛系回复。 1万+

misc设备驱动模型及实例解析

1、misc设备驱动模型       本节我们来看一下misc设备驱动模型的有关内容,首先是看看它的设备结构体,定义在include/linux/miscdevice.h中: [cpp] view plaincopy struct miscdevice  {      int minor;                  //次设备号,若为 MISC_DYNAMIC

liwentongliunian的专栏 419

ER图从入门到精通:数据库建模全攻略

本文为数据库建模领域的 ER 图全面指南,系统解析从基础概念到实战应用的核心要点。首先介绍 ER 图的定义、三大组成元素(实体、属性、关系)及三种基础关系类型(一对一、一对多、多对多);其次详解绘制流程,涵盖工具选择(如 Visio、PowerDesigner)、需求分析方法及实体 - 属性 - 关系的分步建模技巧;进一步探讨弱实体、复合属性等高级概念,结合范式理论讲解模型优化策略;针对实体属性混淆、关系类型误判等常见问题提供解决方案;最后通过电商系统、图书馆管理系统等实际案例展示 ER 图设计全流程。

邓邓子的博客 4213

linux MISC设备驱动

misc 的意思是混合、杂项的,因此 MISC 驱动也叫做杂项驱动,也就是当我们板子上的某 些外设无法进行分类的时候就可以使用 MISC 驱动。 MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中,实现复杂的驱动 ...

645

【车载以太网】【架构】整体架构与协议标准

车载以太网架构图 应用层:DHCP /SOMEIP/DoIP/XCP/UDPNM 传输层:TCP/IP 网络层:IPv4/IPv6/ARP/ICMP 链路层:IEEE Ethernet MAC+VLAN 物理层:Ethernet PHY: 100BASE-T1/1000BASE-T1/100BASE-Tx

专注汽车软件开发、AutoSAR、车载以太网、SOA、EE架构。 8354

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Linux misc 驱动详解

misc 驱动是 Linux 内核中一种特殊的字符设备驱动,用于那些不适合用其他更特定类型驱动(如块设备驱动、网络设备驱动等)来表示的设备。这些设备通常具有一些特殊的特性或者功能,不能简单地归为常见的设备类型。

千千道的博客 1573

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

misc_register

以下是网上的解释: ——————————————————————————————————————————————杂项设备(misc device) 杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。在 Linux 内核的include/linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,

ICode联盟 1095

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

linux主设备号和次设备号

1 杂项设备 linux里面的misc杂项设备是主设备号为10的驱动设备 定义头文件 杂项设备的结构体: struct miscdevice{ int minor; //杂项设备的此设备号(如果设置为MISC_DYNAMIC_MINOR,表示系统自动分配未使用的minor) const char *name; const stuct file_operations *fops;/

u012590688的专栏 1000

misc类设备与蜂鸣器驱动-linux驱动开发第6部分-朱有鹏-专题视频课程

本课程是linux驱动开发的第6个课程,主要内容是linux的misc类设备驱动框架讲解,及蜂鸣器驱动源码的分析。在前面2个课程的铺垫下本部分内容显得很容易,目的是使大家进一步理解驱动框架的用法。...

astonqa的专栏 258

miscdevice、platform_device区别?(转)

最近研究Linux设备驱动程序遇到混乱,请大侠过来理理头绪。Linux设备模型中:bus_type、device、device_driver《Linux设备驱动程序》的linux设备模型章中说到设备模型中,所有设备都通过总线相连。添加设备devA,必须指定其device结构体的bus_type域,初始化其他域,然后调用device_register(&devA),将设备devA注册到指定总线。添加

stonye的专栏 3258

linux内核驱动ldd3_【原创】【专栏】《Linux设备驱动程序》--- LDD3源码目录结构和源码分析经典链接...

.|-- LICENSE|-- Makefile|-- include| `-- lddbus.h 示例代码中,#include "lddbus.h"。|-- lddbus| |-- Makefile| `-- lddbus.c CHAPTER_14.4:虚拟总线实现|-- misc-modules| |-- Makefile| |-- complete.c ...

weixin_39951930的博客 289

混杂设备的实现

http://blog.youkuaiyun.com/tianxiawuzhei/article/details/7588504 总结:在 misc_register()中调用 device_create() ,所以将自动创建设备文件/dev/dev_name 转载于:https://www.cnblogs.com/youngvoice/p/5434061.html...

aotasu7207的博客 100

内核驱动中常见的miscdevice、platform_device、platform_driver

最近在看驱动模型,是越看越糊涂,以前接触比较多的都是一些字符驱动,对字符驱动的框架有一定的了解。后来因为想在驱动中实现设备文件的创建,又了解了一下,sysfs文件系统和udev设备文件系统,必然就涉及到了驱动模型。可是发现驱动模型和以前接触的字符驱动没什么联系。 比如,以前写字符驱动,主要的内容就是实现file_operations结构体里的函数,然后就是申请设备号,注册字符设备,根本就没有涉及

林学森的技术专栏 2123

Linux MISC(杂项设备)驱动框架 简析

系统解析 Linux MISC(杂项设备)驱动框架。先从应用与驱动两个视角给出整体架构;后深入核心机制;逐字段剖析 `struct miscdevice`,并给主要 API 的原型、示例与注意事项;讲解 misc 与 **platform 设备驱动框架**及**设备树**的结合方式;覆盖从设备树时代(3.x)到 6.x LTS 的版本演进关键节点;提供“设备树 + platform 驱动 + misc 设备”的完整驱动与应用示例。

smallerxuan的博客 9666
上一篇: android camera动态库加载过程
下一篇: 详解YUV数据格式
armwind
博客等级 码龄12年 406粉丝 152原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值