当前位置: 首页 > news >正文

[linux 驱动]misc设备驱动详解与实战

目录

1 描述

2 结构体

2.1 miscdevice

2.2 file_operations

3 注册和注销

3.1 misc_register

3.2 misc_deregister

4 解析 misc 内核源码

4.1 核心代码

4.2 函数解析

4.2.1 class_create_file

4.2.2 class_destroy

4.2.3 register_chrdev

5 示例

5.1 简单示例

5.2 实战示例


1 描述

        所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号,MISC 设备驱动就用于解决此问题。MISC 设备会自动创建 cdev

        所有的 misc 设备都属于同一个类,/sys/class/misc 目录下就是 misc 这个类的所有设备,每个设备对应一个子目录。

zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:$ ls /sys/class/misc
autofs           fuse       loop-control  rfkill    uinput
cpu_dma_latency  hpet       mcelog        snapshot  vfio
device-mapper    hw_random  microcode     tun       vga_arbiter
ecryptfs         kvm        psaux         udmabuf
zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:$ 

        驱动与设备匹配成功以后在/dev/ 文件夹下生成相应设备驱动文件,如 /dev/kvm 的主设备为 10,次设备号为 232

zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:$ ls /dev/kvm -l
crw------- 1 root root 10, 232 9月  11 10:17 /dev/kvm
zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:$ 

        Linux 系统已经预定义了一些 MISC 设备的子设备号,我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要 这个子设备号没有被其他设备使用接口。

15 #define PSMOUSE_MINOR           116 #define MS_BUSMOUSE_MINOR       2       /* unused */17 #define ATIXL_BUSMOUSE_MINOR    3       /* unused */18 /*#define AMIGAMOUSE_MINOR      4       FIXME OBSOLETE */19 #define ATARIMOUSE_MINOR        5       /* unused */20 #define SUN_MOUSE_MINOR         6       /* unused */21 #define APOLLO_MOUSE_MINOR      7       /* unused */22 #define PC110PAD_MINOR          9       /* unused */23 /*#define ADB_MOUSE_MINOR       10      FIXME OBSOLETE */24 #define WATCHDOG_MINOR          130     /* Watchdog timer     */25 #define TEMP_MINOR              131     /* Temperature Sensor */26 #define APM_MINOR_DEV           13427 #define RTC_MINOR               13528 #define EFI_RTC_MINOR           136     /* EFI Time services */29 #define VHCI_MINOR              13730 #define SUN_OPENPROM_MINOR      13931 #define DMAPI_MINOR             140     /* unused */32 #define NVRAM_MINOR             14433 #define SGI_MMTIMER             15334 #define STORE_QUEUE_MINOR       155     /* unused */35 #define I2O_MINOR               16636 #define HWRNG_MINOR             18337 #define MICROCODE_MINOR         18438 #define IRNET_MINOR             18739 #define D7S_MINOR               19340 #define VFIO_MINOR              19641 #define TUN_MINOR               20042 #define CUSE_MINOR              20343 #define MWAVE_MINOR             219     /* ACP/Mwave Modem */44 #define MPT_MINOR               22045 #define MPT2SAS_MINOR           22146 #define MPT3SAS_MINOR           22247 #define UINPUT_MINOR            22348 #define MISC_MCELOG_MINOR       22749 #define HPET_MINOR              22850 #define FUSE_MINOR              22951 #define KVM_MINOR               23252 #define BTRFS_MINOR             23453 #define AUTOFS_MINOR            23554 #define MAPPER_CTRL_MINOR       23655 #define LOOP_CTRL_MINOR         23756 #define VHOST_NET_MINOR         23857 #define UHID_MINOR              23958 #define USERIO_MINOR            24059 #define VHOST_VSOCK_MINOR       24160 #define RFKILL_MINOR            24261 #define MISC_DYNAMIC_MINOR      255

2 结构体

2.1 miscdevice

        miscdevice 结构体,它在 Linux 内核中用于描述和管理一个“杂项设备”(miscellaneous device)。这个结构体主要用于注册和管理那些不属于主流设备驱动类别的小型设备。

66 struct miscdevice  {67         int minor;68         const char *name;69         const struct file_operations *fops;70         struct list_head list;71         struct device *parent;72         struct device *this_device;73         const struct attribute_group **groups;74         const char *nodename;75         umode_t mode;76 };

int minor:

设备的次设备号(minor number),用来区分同一主设备号下的不同设备。在设备文件中,这通常用于识别不同的设备实例。

const char *name:

设备的名称,通常用于表示设备的标识符。当此设备注册成功以后就会在/dev 目录下生成一个名为 name 的设备文件

const struct file_operations *fops:

指向 file_operations 结构体的指针,该结构体定义了设备的文件操作方法,比如 open, read, write, ioctl 等。这些函数实现了设备的行为。

struct list_head list:

用于在全局设备列表中将 miscdevice 结构体节点连接起来。这是 Linux 内核中常用的链表结构,便于管理和遍历多个设备实例。

struct device *parent:

设备的父设备指针。如果设备有父设备,这个字段指向其父设备。通常用于设备树中表示设备层次结构。

struct device *this_device:

指向该设备本身的结构体,用于进一步的设备管理和操作。

const struct attribute_group **groups:

指向 attribute_group 结构体的指针数组,用于设备的 sysfs 属性管理。sysfs 是一个虚拟文件系统,用于在用户空间和内核之间提供设备的属性接口。

const char *nodename:

设备的节点名称,通常用于设备树的设备节点标识。在设备树中,节点名称用于描述硬件设备的属性和结构。

umode_t mode:

设备的文件权限模式,定义了设备文件的访问权限(例如,读、写权限)。这是文件系统中设备文件的权限设置。

2.2 file_operations

        该结构体定义了设备的文件操作方法,比如 open, read, write, ioctl 等。这些函数实现了设备的行为。

1807 struct file_operations { 
1808         struct module *owner;
1809         loff_t (*llseek) (struct file *, loff_t, int);
1810         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
1811         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
1812         ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
1813         ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
1814         int (*iterate) (struct file *, struct dir_context *);
1815         int (*iterate_shared) (struct file *, struct dir_context *);
1816         __poll_t (*poll) (struct file *, struct poll_table_struct *);
1817         long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
1818         long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
1819         int (*mmap) (struct file *, struct vm_area_struct *);
1820         unsigned long mmap_supported_flags;
1821         int (*open) (struct inode *, struct file *);
1822         int (*flush) (struct file *, fl_owner_t id);
1823         int (*release) (struct inode *, struct file *);
1824         int (*fsync) (struct file *, loff_t, loff_t, int datasync);
1825         int (*fasync) (int, struct file *, int);
1826         int (*lock) (struct file *, int, struct file_lock *);
1827         ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
1828         unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
1829         int (*check_flags)(int); 
1830         int (*flock) (struct file *, int, struct file_lock *);
1831         ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
1832         ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
1833         int (*setlease)(struct file *, long, struct file_lock **, void **);
1834         long (*fallocate)(struct file *file, int mode, loff_t offset,
1835                           loff_t len);
1836         void (*show_fdinfo)(struct seq_file *m, struct file *f);
1837 #ifndef CONFIG_MMU
1838         unsigned (*mmap_capabilities)(struct file *);
1839 #endif
1840         ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
1841                         loff_t, size_t, unsigned int);
1842         int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
1843                         u64);
1844         int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t,
1845                         u64);
1846         int (*fadvise)(struct file *, loff_t, loff_t, int);
1847 
1848         ANDROID_KABI_RESERVE(1);
1849         ANDROID_KABI_RESERVE(2);
1850         ANDROID_KABI_RESERVE(3);
1851         ANDROID_KABI_RESERVE(4);
1852 } __randomize_layout;

3 注册和注销

3.1 misc_register

函数原型

int misc_register(struct miscdevice *misc)

参数

struct miscdevice *misc

struct miscdevice *misc 结构体包含设备的主要信息,如设备号、设备名称和文件操作结构

返回值

int

成功:0 失败:负数

功能

注册一个杂项设备(misc device),并将其添加到内核中

173 int misc_register(struct miscdevice *misc)
174 {
175         dev_t dev;
176         int err = 0;
177         bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
178                                                   
179         INIT_LIST_HEAD(&misc->list);
180         
181         mutex_lock(&misc_mtx);
182 
183         if (is_dynamic) {
184                 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
185 
186                 if (i >= DYNAMIC_MINORS) {
187                         err = -EBUSY;
188                         goto out;
189                 }
190                 misc->minor = DYNAMIC_MINORS - i - 1;
191                 set_bit(i, misc_minors);
192         } else {
193                 struct miscdevice *c;
194 
195                 list_for_each_entry(c, &misc_list, list) {
196                         if (c->minor == misc->minor) {
197                                 err = -EBUSY;
198                                 goto out;
199                         }
200                 }
201         }
202 
203         dev = MKDEV(MISC_MAJOR, misc->minor);
204 
205         misc->this_device =
206                 device_create_with_groups(misc_class, misc->parent, dev,
207                                           misc, misc->groups, "%s", misc->name);
208         if (IS_ERR(misc->this_device)) {
209                 if (is_dynamic) {
210                         int i = DYNAMIC_MINORS - misc->minor - 1;
211 
212                         if (i < DYNAMIC_MINORS && i >= 0)
213                                 clear_bit(i, misc_minors);
214                         misc->minor = MISC_DYNAMIC_MINOR;
215                 }
216                 err = PTR_ERR(misc->this_device);
217                 goto out;
218         }
219 
220         /*
221          * Add it to the front, so that later devices can "override"
222          * earlier defaults
223          */
224         list_add(&misc->list, &misc_list);
225  out:
226         mutex_unlock(&misc_mtx);
227         return err;
228 }
3363 struct device *device_create_with_groups(struct class *class,
3364                                          struct device *parent, dev_t devt,
3365                                          void *drvdata,
3366                                          const struct attribute_group **groups,
3367                                          const char *fmt, ...)
3368 {       
3369         va_list vargs;
3370         struct device *dev;
3371         
3372         va_start(vargs, fmt);
3373         dev = device_create_groups_vargs(class, parent, devt, drvdata, groups,
3374                                          fmt, vargs);
3375         va_end(vargs);
3376         return dev;
3377 }      

3.2 misc_deregister

函数原型

void misc_deregister(struct miscdevice *misc)

参数

struct miscdevice *misc

struct miscdevice *misc 结构体包含设备的主要信息,如设备号、设备名称和文件操作结构

返回值

功能

注销一个杂项设备(misc device)

238 void misc_deregister(struct miscdevice *misc)
239 {
240         int i = DYNAMIC_MINORS - misc->minor - 1;
241 
242         if (WARN_ON(list_empty(&misc->list)))
243                 return;
244 
245         mutex_lock(&misc_mtx);
246         list_del(&misc->list);
247         device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
248         if (i < DYNAMIC_MINORS && i >= 0)
249                 clear_bit(i, misc_minors);
250         mutex_unlock(&misc_mtx);
251 }
3395 void device_destroy(struct class *class, dev_t devt)
3396 {
3397         struct device *dev;
3398 
3399         dev = class_find_device(class, NULL, &devt, __match_devt);
3400         if (dev) {
3401                 put_device(dev);
3402                 device_unregister(dev);
3403         }
3404 }

4 解析 misc 内核源码

4.1 核心代码


#define MISC_MAJOR              10147 static struct class *misc_class;
148 
149 static const struct file_operations misc_fops = {
150         .owner          = THIS_MODULE,
151         .open           = misc_open,
152         .llseek         = noop_llseek,
153 };267 static int __init misc_init(void)
268 {
269         int err;
270         struct proc_dir_entry *ret;
271 
272         ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
273         misc_class = class_create(THIS_MODULE, "misc");
274         err = PTR_ERR(misc_class);
275         if (IS_ERR(misc_class))
276                 goto fail_remove;
277 
278         err = -EIO;
279         if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))
280                 goto fail_printk;
281         misc_class->devnode = misc_devnode;
282         return 0;
283 
284 fail_printk:
285         pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
286         class_destroy(misc_class);
287 fail_remove:
288         if (ret)
289                 remove_proc_entry("misc", NULL);
290         return err;
291 }
292 #ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
293 arch_initcall_sync(misc_init);
294 #else
295 subsys_initcall(misc_init);
296 #endif

        通过函数class_create(THIS_MODULE, "misc");创建了 misc 类,生成/sys/class/misc 文件

zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:~/sdb1/android11/kernel$ ls /sys/class/
ata_device  block        devfreq-event  gpio         i2c-dev   mem       nvme            powercap      printer       remoteproc   scsi_generic  thermal  vc            watchdog
ata_link    bsg          dma            graphics     input     misc      nvme-subsystem  power_supply  ptp           rfkill       scsi_host     tpm      vfio          wmi_bus
ata_port    dax          dmi            hidraw       iommu     mmc_host  pci_bus         ppdev         pwm           rtc          sound         tpmrm    virtio-ports
backlight   devcoredump  extcon         hwmon        leds      nd        pci_epc         ppp           rapidio_port  scsi_device  spi_master    tty      vtconsole
bdi         devfreq      firmware       i2c-adapter  mdio_bus  net       phy             pps           regulator     scsi_disk    spi_slave     usbmisc  wakeup
zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:~/sdb1/android11/kernel$ 

4.2 函数解析

4.2.1 class_create_file

函数原型

#define class_create(owner, name) \

({ \

static struct lock_class_key __key; \

__class_create(owner, name, &__key); \

})

struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key)

参数

struct module *owner

指向该设备类所属模块的指针。模块是内核中可加载和卸载的代码块,这个参数允许内核跟踪哪个模块创建了设备类。如果设备类是由内核核心代码(而非可加载模块)创建的,则此参数可能为 NULL

const char *name

设备类的名称

struct lock_class_key *key

指向锁类键的指针,用于锁调试。锁类键是内核中用于调试锁竞争和死锁问题的一种机制。如果不需要锁调试,此参数可以传递 NULL

返回值

struct class *

成功:class结构体指针 失败:NULL

功能

用于创建新的设备类。设备类的创建允许内核将具有相似功能的设备组织在一起,并提供了通过 /sys/class/ 目录与用户空间交互的接口

219 struct class *__class_create(struct module *owner, const char *name,
220                              struct lock_class_key *key)
221 {
222         struct class *cls;
223         int retval;
224 
225         cls = kzalloc(sizeof(*cls), GFP_KERNEL);
226         if (!cls) {
227                 retval = -ENOMEM;
228                 goto error;
229         }       
230 
231         cls->name = name;
232         cls->owner = owner;
233         cls->class_release = class_create_release;
234 
235         retval = __class_register(cls, key);
236         if (retval)
237                 goto error;
238 
239         return cls; 
240 
241 error:   
242         kfree(cls);
243         return ERR_PTR(retval);
244 }        

4.2.2 class_destroy

函数原型

void class_destroy(struct class *cls)

参数

struct class *cls

要摧毁的设备类的指针

返回值

功能

销毁一个设备类

254 void class_destroy(struct class *cls)
255 {
256         if ((cls == NULL) || (IS_ERR(cls)))
257                 return;
258 
259         class_unregister(cls);
260 }   192 void class_unregister(struct class *cls)
193 {
194         pr_debug("device class '%s': unregistering\n", cls->name);
195         class_remove_groups(cls, cls->class_groups);
196         kset_unregister(&cls->p->subsys);
197 }      

4.2.3 register_chrdev

函数原型

int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

参数

unsigned int major

设备的主设备号

const char *name

设备的名称

const struct file_operations *fops

指向 file_operations 结构体的指针,用于定义设备的操作函数

返回值

int

成功:设备的主设备号 失败:负数

功能

注册一个字符设备

2703 static inline int register_chrdev(unsigned int major, const char *name,
2704                                   const struct file_operations *fops)
2705 {
2706         return __register_chrdev(major, 0, 256, name, fops);
2707 }
277 int __register_chrdev(unsigned int major, unsigned int baseminor,
278                       unsigned int count, const char *name,
279                       const struct file_operations *fops)
280 {       
281         struct char_device_struct *cd;
282         struct cdev *cdev;
283         int err = -ENOMEM;
284 
285         cd = __register_chrdev_region(major, baseminor, count, name);
286         if (IS_ERR(cd))
287                 return PTR_ERR(cd);
288         
289         cdev = cdev_alloc();
290         if (!cdev)
291                 goto out2;
292 
293         cdev->owner = fops->owner;
294         cdev->ops = fops;
295         kobject_set_name(&cdev->kobj, "%s", name);
296 
297         err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
298         if (err)
299                 goto out;
300 
301         cd->cdev = cdev;
302 
303         return major ? 0 : cd->major;
304 out:
305         kobject_put(&cdev->kobj);
306 out2:
307         kfree(__unregister_chrdev_region(cd->major, baseminor, count));
308         return err;
309 }

5 示例

5.1 简单示例

        示例代码如下,.minor = MISC_DYNAMIC_MINOR表示此设备号随系统自动分配,misc 设备名称为misc_test。

#include "linux/miscdevice.h"
#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/fs.h>static int spirit_mcu_open(struct inode *inode, struct file *file)
{return 0;
}static int spirit_mcu_release(struct inode *inode, struct file *file)
{ return 0;
}const struct file_operations misc_fops = {.owner		= THIS_MODULE,.open		= spirit_mcu_open,.release	= spirit_mcu_release,
};static struct miscdevice misc_device = {.minor = MISC_DYNAMIC_MINOR,.name = "misc_test",.fops = &misc_fops
};static int misc_test_init(void){int ret;ret = misc_register(&misc_device);if(ret){printk(KERN_ERR "register misc device error\n");goto failed;}printk("register misc device ok\r\n");		return 0;
failed:return ret;}static void misc_test_exit(void){printk("misc_deregister\r\n");misc_deregister(&misc_device);}module_init(misc_test_init);
module_exit(misc_test_exit);
MODULE_LICENSE("GPL");

        insmod misc_test.ko 之后,生成了/sys/class/misc/misc_test/文件夹和/dev/misc_test 文件节点,使用“ls /dev/misc_test -l”命令可以看到,misc_test 的主设备号为 10,此设备号自动分配的,为 49

console:/data # insmod misc_test.ko                                            
[80439.091818] register misc device ok
console:/data # ls /sys/class/misc/                                            
ashmem           iep               network_throughput  uinput
cpu_dma_latency  ion               opteearmtz00        usb_accessory
crypto           loop-control      rfkill              vendor_storage
device-mapper    mali0             rga                 watchdog
fuse             memory_bandwidth  sw_sync
hdmi_hdcp1x      misc_test         tun
hw_random        network_latency   uhid
console:/data # ls /sys/class/misc/misc_test                                   
dev  power  subsystem  uevent
console:/data #
console:/data # ls /dev/misc_test -l                                           
crw------- 1 root root 10,  49 2024-09-14 14:12 /dev/misc_test
console:/data # 

/sys/class/misc/misc_test 和 /dev/misc_test 的区别

/sys/class/misc/misc_test

路径: 这是一个在 sysfs 文件系统中的节点。sysfs 是一个虚拟文件系统,用于提供关于内核对象和系统状态的信息。

作用: /sys/class/misc/misc_test 主要用于展示设备的属性、状态和其他信息。它是一个设备类的虚拟目录,通过 sysfs 提供设备的相关数据。

功能: 在这个路径下,你可以找到与设备相关的属性文件,它们用于读取设备的状态或控制设备的行为。这些文件是内核提供的,通常不直接用于设备的 I/O 操作,而是用于查看或修改设备的配置参数。

/dev/misc_test

路径: 这是一个在 dev 文件系统中的节点。dev 是一个虚拟文件系统,用于访问设备文件。

作用: /dev/misc_test 是实际的设备节点,用于与设备进行 I/O 操作。通过这个设备节点,用户空间程序可以进行读写操作来与设备进行交互。

功能: 这是用户空间程序与设备进行交互的接口。设备驱动程序通过将字符设备注册到这个路径,使得用户空间程序能够使用标准的文件操作系统调用(如 read、write、ioctl)来操作设备。

5.2 实战示例

        实战代码如下所示。

static long spirit_mcu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int ret = 0;unsigned char value;struct mcu_req req;struct DeviceInfomation deviceInfo;struct spirit_mcu *spirit_mcu = i2c_get_clientdata(mcu_i2c_client);void __user *argp = (void __user *)arg;printk("spirit_mcu_ioctl cmd[%d]\n",cmd);switch(cmd) {case MCU_CMD_SYNC:if (copy_from_user(&req, argp, sizeof(struct mcu_req))) {printk(KERN_ERR "copy_from_user failed.\n");ret = -EFAULT;break;}ret = mcu_process(spirit_mcu,&req,&value);if((req.opcode & OP_READ_BIT) != 0){req.mode = value;if (unlikely(copy_to_user(argp, &req, sizeof (struct mcu_req)))) {printk(KERN_ERR "copy_to_user failed.\n");ret = -EFAULT;}}break;case MCU_CMD_DEVICE_INFO:if (copy_from_user(&deviceInfo, argp, sizeof(struct DeviceInfomation))) {printk(KERN_ERR "copy_from_user failed.\n");ret = -EFAULT;break;}deviceInfo = spirit_mcu->deviceInfo;if (unlikely(copy_to_user(argp, &deviceInfo, sizeof(struct DeviceInfomation)))) {printk(KERN_ERR "copy_to_user failed.\n");ret = -EFAULT;break;}break;case MCU_WDT_FEED_CONTROL:if (copy_from_user(&req, argp, sizeof(struct mcu_req))) {printk(KERN_ERR "copy_from_user failed.\n");ret = -EFAULT;break;}if(req.mode == 1){spirit_mcu->userfeed = 0;schedule_delayed_work(&spirit_mcu->work, msecs_to_jiffies(WATCHDOG_FEED_COUNT));}else {spirit_mcu->userfeed = 1;mcu_watchdog_feed(spirit_mcu);cancel_delayed_work_sync(&spirit_mcu->work);}break;case MCU_WDT_USER_FEED:if(spirit_mcu->userfeed == 1){mcu_watchdog_feed(spirit_mcu);}break;default:break;}mutex_unlock(&spirit_mcu->m_lock);printk("mcu ioctrl end\n");return ret;
}static int spirit_mcu_open(struct inode *inode, struct file *file)
{return 0;
}static int spirit_mcu_release(struct inode *inode, struct file *file)
{ return 0;
}const struct file_operations spirit_mcu_operations = {.owner		= THIS_MODULE,.open		= spirit_mcu_open,.release	= spirit_mcu_release,.unlocked_ioctl = spirit_mcu_ioctl,
};static struct miscdevice spirit_mcu_misc_driver = {.minor  = MISC_DYNAMIC_MINOR,.name		= "spirit_mcu",.fops		= &spirit_mcu_operations
};static int  spirit_mcu_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret = 0;struct spirit_mcu *spirit_mcu;struct device_node *np = client->dev.of_node;printk("%s: probe\n", __FUNCTION__);spirit_mcu = devm_kzalloc(&client->dev, sizeof(struct spirit_mcu), GFP_KERNEL);if (!spirit_mcu)return -ENOMEM;spirit_mcu->regmap = devm_regmap_init_i2c(client, &mcu_regmap_config);if (IS_ERR(spirit_mcu->regmap)) {dev_err(&client->dev, "regmap initialization failed\n");return PTR_ERR(spirit_mcu->regmap);}spirit_mcu->userfeed = 0;i2c_set_clientdata(client, spirit_mcu);spirit_mcu->i2c = client;spirit_mcu->np = np;mcu_i2c_client = client;ret = misc_register(&spirit_mcu_misc_driver);if(ret){printk(KERN_ERR "register mcu misc device error\n");goto failed;}		printk("%s: probe ok!!\n", __FUNCTION__);return 0;
failed:return ret;
}static const struct i2c_device_id spirit_mcu_id[] = {{ "spirit_mcu", 0 },{ }
};static struct i2c_driver spirit_mcu_driver = {.driver		= {.name	= "spirit_mcu",.owner	= THIS_MODULE,},.probe		= spirit_mcu_probe,.id_table	= spirit_mcu_id,
};static int __init spirit_mcu_init(void)
{return i2c_add_driver(&spirit_mcu_driver);
}static void __exit spirit_mcu_exit(void)
{i2c_del_driver(&spirit_mcu_driver);
}MODULE_AUTHOR("neilnee@jwele.com.cn");
MODULE_DESCRIPTION("spirit mcu driver");
MODULE_LICENSE("GPL");late_initcall(spirit_mcu_init);
module_exit(spirit_mcu_exit);

相关文章:

[linux 驱动]misc设备驱动详解与实战

目录 1 描述 2 结构体 2.1 miscdevice 2.2 file_operations 3 注册和注销 3.1 misc_register 3.2 misc_deregister 4 解析 misc 内核源码 4.1 核心代码 4.2 函数解析 4.2.1 class_create_file 4.2.2 class_destroy 4.2.3 register_chrdev 5 示例 5.1 简单示例 5…...

C/S架构与B/S架构的适用场景分析

C/S架构&#xff08;客户端/服务器架构&#xff09;与B/S架构&#xff08;浏览器/服务器架构&#xff09;在适用场景上各有特点&#xff0c;主要取决于应用的具体需求、用户群体、系统维护成本、跨平台需求等因素。 一、C/S架构的适用场景 1、高性能与交互性要求高的应用&…...

AI论文精读笔记-Generative Adversarial Nets(GAN)

1. 论文基本信息 论文标题&#xff1a;Generative Adversarial Nets 作者&#xff1a;Ian J. Goodfellow,∗ Jean Pouget-Abadie,† Mehdi Mirza, Bing Xu, David Warde-Farley, Sherjil Ozair,‡ Aaron Courville, Yoshua Bengio 发表时间和期刊&#xff1a;2014.06&#xf…...

Redis(主从复制、哨兵模式、集群)概述及部署测试

目录 一、Redis 主从复制 1.1、Redis 主从复制概念 1.2、主从复制的作用 1.3、主从复制流程 1.4、搭建Redis 主从复制 二、Redis 哨兵模式 2.1、Redis 哨兵模式概念 2.2、哨兵模式原理 2.3、哨兵模式的作用 2.4、哨兵模式的结构 2.5、故障转移机制 2.6、主节点的选…...

jmeter吞吐量控制器

一、吞吐量控制器作用&#xff1a;旨在混合场景中&#xff0c;控制样本数&#xff0c;通常在比例场景中使用 吞吐量控制器提供了两种控制模式&#xff1a; 百分比执行&#xff08;Percent Executions&#xff09;&#xff1a; 吞吐量控制器会根据配置的百分比来决定其下的作用…...

【GBase 8c V5_3.0.0 分布式数据库常用几个SQL】

1.检查应用连接数 以管理员用户 gbase&#xff0c;登录数据库主节点。 接数据库&#xff0c;并执行如下 SQL 语句查看连接数。 SELECT count(*) FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s;2.查看空闲连接 查看空闲(state 字段为”idle”)且长时间没有更…...

grep,wc命令

一.grep命令 1.grep命令的作用 我们通过grep命令从文件中通过关键字过滤文件行 2.grep命令的语法 grep [-n] 关键字 文件路径 其中grep作为命令主体 -n选项表示在结果中显示匹配的行的行号 关键字为必填参数&#xff0c;表示过滤的关键字&#xff08;可以使用""…...

NLP-文本分类文献阅读-前置基础-词汇解释-通俗易懂-9月份-学习总结

目录 迁移学习 特征选择 特征工程 朴素贝叶斯分类方法 支持向量机 K-最近邻&#xff08;K-Nearest Neighbors, KNN&#xff09; 特征向量稀疏 卷积神经网络 循环神经网络 图神经网络 TextCNN 动态 K 最大池化 One-hot BOW Word2vec 池化&#xff08;Pooling&#xff09; 全连接…...

Conda安装和使用(ubuntu)

以下是关于如何使用 Conda 的详细指南。这将涵盖从安装到基本操作的各个方面&#xff0c;帮助您高效地管理Python环境和依赖项。 Conda 简介 Conda 是一个跨平台的开源包管理器和环境管理器&#xff0c;最初由 Anaconda 开发&#xff0c;广泛用于数据科学、机器学习和科学计算…...

JavaEE:文件操作

文章目录 文件操作和IO文件系统操作File介绍属性构造方法方法 代码演示前四个listmkdirrenameTo 文件操作和IO 文件系统操作 创建文件,删除文件,创建目录,重命名… Java中有一个类,可以帮我们完成上述操作. 这个类叫做File类. File介绍 属性 这个表格描述了文件路径的分隔符…...

Python | 练习作业 2

为学生登录系统新增搜索功能。 第二天作业的解题思路&#xff1a; # 1.创建一个空列表保存搜索结果 # 2.让用户输入要搜索的内容 # 3.遍历学生信息&#xff0c;检查学生的id name age gender score # 中的属性值 是否跟用户搜索的内容一致 # 4.如果有一致的属性 那么就将该学生…...

C语言-整数和浮点数在内存中的存储-详解-上

C语言-整数和浮点数在内存中的存储-详解-上 1.前言2.整数2.1无符号整数2.2原码、反码、补码符号位最大值转换过程补码的意义简化算术运算易于转换方便溢出处理 1.前言 在C语言的使用中&#xff0c;需要时刻关注数据的类型&#xff0c;不同类型交替使用可能会发生错误&#xff…...

图论篇--代码随想录算法训练营第六十一天打卡| Floyd 算法,A*算法

Floyd 算法&#xff08;求多源汇最短路&#xff09; 题目链接&#xff1a;97. 小明逛公园 题目描述&#xff1a; 小明喜欢去公园散步&#xff0c;公园内布置了许多的景点&#xff0c;相互之间通过小路连接&#xff0c;小明希望在观看景点的同时&#xff0c;能够节省体力&…...

CMake构建学习笔记16-使用VS进行CMake项目的开发

文章目录 1. 概论2. 详论2.1 创建工程2.2 加载工程2.3 配置文件2.4 工程配置2.5 调试执行 3. 项目案例4. 总结 1. 概论 在之前的系列博文中&#xff0c;我们学习了如何构建第三方的依赖库&#xff0c;也学习了如何去组建自己的CMake项目&#xff0c;尤其是学习了CMake的核心配…...

数据结构中线性表的定义和特点

线性表&#xff1a;有n个数据特征相同的元素构成的有限序列。 特点&#xff1a; 除了第一个元素&#xff0c;最后一个元素&#xff0c;其余的元素都有唯一的前驱和唯一的后继。 案例引入&#xff1a; 一元多项式的运算&#xff1a; 可以将一元多项式p(x)抽象为一个有n1个系…...

【PyTorch单点知识】PyTorch中的自动混合精度(AMP)模块详解

文章目录 0. 前言1. 什么是自动混合精度&#xff1f;2. PyTorch AMP 模块3. 如何使用 PyTorch AMP3.1 环境准备3.2 代码实例3.3 代码解析 4. 结论 0. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我自己学习的理解&#xff0c;虽然参考了他人的宝贵见解及成果&a…...

数据结构 --- 哈希表

哈希表&#xff08;Hash Table&#xff09;&#xff0c;也叫散列表&#xff0c;是一种根据关键码值&#xff08;Key value&#xff09;而直接进行访问的数据结构。 一、基本原理 哈希函数 哈希表通过一个特定的哈希函数&#xff0c;将关键码映射到表中的一个位置。这个位置通常…...

Linux相关:在阿里云下载centos系统镜像

文章目录 1、镜像站2、下载方式一2.1、第一步打开镜像站地址2.2 下载地址: https://mirrors.aliyun.com/centos/2.3、选择7版本2.4、镜像文件在isos文件夹中2.5、选择合适的版本 3、下载镜像快捷方式 1、镜像站 阿里云镜像站地址 2、下载方式一 2.1、第一步打开镜像站地址 2…...

24. 线模型对象

线模型Line渲染顶点数据 下面代码是把几何体作为线模型Line (opens new window)的参数&#xff0c;你会发现渲染效果是从第一个点开始到最后一个点&#xff0c;依次连成线。 // 线材质对象 const material new THREE.LineBasicMaterial({color: 0xff0000 //线条颜色 }); //…...

EasyExcel 快速入门

目录 一、 EasyExcel简介 官网链接&#xff1a; 代码链接&#xff1a; 二、 EasyExcel快速上手 引入依赖&#xff1a; 设置Excel相关注解 编写对应的监听类&#xff1a; 简单写入数据&#xff1a; 简单读取数据&#xff1a; 不需要使用监听器&#xff1a; 需要使…...

Sparse4D v1

Sparse4D: Multi-view 3D Object Detection with Sparse Spatial-Temporal Fusion Abstract 基于鸟瞰图 (BEV) 的方法最近在多视图 3D 检测任务方面取得了重大进展。与基于 BEV 的方法相比&#xff0c;基于稀疏的方法在性能上落后&#xff0c;但仍然有很多不可忽略的优点。为了…...

速盾:你知道高防 IP 和高防 CDN 的区别吗?

在当今网络安全形势日益严峻的情况下&#xff0c;网站的安全防护成为了企业和个人关注的焦点。高防 IP 和高防 CDN 作为两种常见的网络安全防护手段&#xff0c;被广泛应用于网站的安全防护中。那么&#xff0c;高防 IP 和高防 CDN 有什么区别呢&#xff1f;防护网站哪个更好呢…...

HTML和CSS网页制作成品

HTML和CSS网页制作成品 一、引言 1. 背景介绍 在当今数字化时代&#xff0c;网页已成为信息传递和交流的重要媒介。HTML和CSS作为网页制作的基石&#xff0c;对于构建美观、功能丰富的网站至关重要。本文将详细介绍如何使用HTML和CSS来制作一个网页成品。 2. 目的和重要性 …...

Ai+若依(集成easyexcel实现excel表格增强)

EasyExcel 介绍 官方地址:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网 Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一…...

钻机、塔吊等大型工程设备,如何远程维护、实时采集运行数据?

在建筑和工程领域&#xff0c;重型设备的应用不可或缺&#xff0c;无论是在道路与桥梁建设、高层建筑施工&#xff0c;还是在风电、石油等能源项目的开发中&#xff0c;都会用到塔吊、钻机等大型机械工程设备。 随着数字化升级、工业4.0成为行业发展趋势&#xff0c;为了进一步…...

【AutoX.js】选择器 UiSelector - 查找包名

文章目录 原文&#xff1a;https://blog.c12th.cn/archives/38.html选择器 UiSelector - 查找包名笔记直接查找包名双层判断(推荐)查找最外层控件的子控件 最后 原文&#xff1a;https://blog.c12th.cn/archives/38.html 选择器 UiSelector - 查找包名 笔记 AutoX.js UiSelec…...

ERP进销存多仓库管理系统源码 带完整的安装代码包以及搭建部署教程

系统概述 ERP进销存多仓库管理系统是一款专为中小企业量身定制的集成化管理软件&#xff0c;它集成了采购管理、销售管理、库存管理、财务管理以及多仓库协同作业等核心模块。通过统一的平台&#xff0c;企业可以实时掌握商品从入库到出库的全过程&#xff0c;实现库存的自动化…...

数据清洗-缺失值填充-对XGBoost参数优化填充

目录 一、安装所需的python包二、采用XGboost算法进行缺失值填充2.1可直接运行代码2.2以某个缺失值数据进行实战2.2.1 代码运行过程截屏:2.2.2 填充后的数据截屏:三、网格搜索(Grid Search)对 XGBoost 模型的超参数进行优化原理介绍3.1 说明3.2 参数优化的原理1. 网格搜索(…...

Qt_按钮类控件

目录 1、QAbstractButton 2、设置带图标的按钮 3、设置带有快捷键的按钮 4、QRadioButtion&#xff08;单选按钮&#xff09; 4.1 QButtonGroup 5、QCheckBox 结语 前言&#xff1a; 按钮类控件是Qt中最重要的控件类型之一&#xff0c;该类型的控件可以通过鼠标的点击…...

union 的定义和基本结构以及用途

在 C 语言中&#xff0c;union&#xff08;联合体&#xff09; 是一种数据结构&#xff0c;它允许多个成员共享相同的内存空间。换句话说&#xff0c;联合体中的所有成员都存储在同一块内存区域&#xff0c;不同的成员会占用相同的内存地址&#xff0c;但在同一时刻只能保存一个…...