[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架构(客户端/服务器架构)与B/S架构(浏览器/服务器架构)在适用场景上各有特点,主要取决于应用的具体需求、用户群体、系统维护成本、跨平台需求等因素。 一、C/S架构的适用场景 1、高性能与交互性要求高的应用&…...

AI论文精读笔记-Generative Adversarial Nets(GAN)
1. 论文基本信息 论文标题:Generative Adversarial Nets 作者:Ian J. Goodfellow,∗ Jean Pouget-Abadie,† Mehdi Mirza, Bing Xu, David Warde-Farley, Sherjil Ozair,‡ Aaron Courville, Yoshua Bengio 发表时间和期刊:2014.06…...

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吞吐量控制器
一、吞吐量控制器作用:旨在混合场景中,控制样本数,通常在比例场景中使用 吞吐量控制器提供了两种控制模式: 百分比执行(Percent Executions): 吞吐量控制器会根据配置的百分比来决定其下的作用…...

【GBase 8c V5_3.0.0 分布式数据库常用几个SQL】
1.检查应用连接数 以管理员用户 gbase,登录数据库主节点。 接数据库,并执行如下 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选项表示在结果中显示匹配的行的行号 关键字为必填参数,表示过滤的关键字(可以使用""…...

NLP-文本分类文献阅读-前置基础-词汇解释-通俗易懂-9月份-学习总结
目录 迁移学习 特征选择 特征工程 朴素贝叶斯分类方法 支持向量机 K-最近邻(K-Nearest Neighbors, KNN) 特征向量稀疏 卷积神经网络 循环神经网络 图神经网络 TextCNN 动态 K 最大池化 One-hot BOW Word2vec 池化(Pooling) 全连接…...

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

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

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

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

图论篇--代码随想录算法训练营第六十一天打卡| Floyd 算法,A*算法
Floyd 算法(求多源汇最短路) 题目链接:97. 小明逛公园 题目描述: 小明喜欢去公园散步,公园内布置了许多的景点,相互之间通过小路连接,小明希望在观看景点的同时,能够节省体力&…...

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

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

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

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

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)的参数,你会发现渲染效果是从第一个点开始到最后一个点,依次连成线。 // 线材质对象 const material new THREE.LineBasicMaterial({color: 0xff0000 //线条颜色 }); //…...

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

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

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

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

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

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

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

ERP进销存多仓库管理系统源码 带完整的安装代码包以及搭建部署教程
系统概述 ERP进销存多仓库管理系统是一款专为中小企业量身定制的集成化管理软件,它集成了采购管理、销售管理、库存管理、财务管理以及多仓库协同作业等核心模块。通过统一的平台,企业可以实时掌握商品从入库到出库的全过程,实现库存的自动化…...

数据清洗-缺失值填充-对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(单选按钮) 4.1 QButtonGroup 5、QCheckBox 结语 前言: 按钮类控件是Qt中最重要的控件类型之一,该类型的控件可以通过鼠标的点击…...

union 的定义和基本结构以及用途
在 C 语言中,union(联合体) 是一种数据结构,它允许多个成员共享相同的内存空间。换句话说,联合体中的所有成员都存储在同一块内存区域,不同的成员会占用相同的内存地址,但在同一时刻只能保存一个…...