当前位置: 首页 > 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);

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Bean】BeanPostProcessor的前置方法和后置方法的作用和使用
  • clip论文阅读(Learning Transferable Visual Models From Natural Language Supervision)
  • 探索Go语言中的随机数生成、矩阵运算与数独验证
  • 代理IP池纯净度对数据抓取有影响吗?
  • flink on k8s
  • Windows 的 docker 删除容器后 WSL2 磁盘空间不释放的问题
  • MongoDB高可用和分片集群知识
  • 【C++】标准库IO查漏补缺
  • CORS漏洞及其防御措施:保护Web应用免受攻击
  • 看Threejs好玩示例,学习创新与技术(三)
  • `character_set_server` 和 `collation_server`
  • Nuxt Kit 组件管理:注册与自动导入
  • 一. Unity实现虚拟摇杆及屏幕自适应功能
  • GPS/LBS/Wi-Fi定位,全安排!—合宙Air201资产定位模组LuatOS快速入门04
  • Rust Web开发框架对比:Warp与Actix-web
  • 【Linux系统编程】快速查找errno错误码信息
  • 2017年终总结、随想
  • 77. Combinations
  • Angular 2 DI - IoC DI - 1
  • Docker: 容器互访的三种方式
  • express + mock 让前后台并行开发
  • JS学习笔记——闭包
  • mysql中InnoDB引擎中页的概念
  • Nacos系列:Nacos的Java SDK使用
  • Python利用正则抓取网页内容保存到本地
  • supervisor 永不挂掉的进程 安装以及使用
  • vue-loader 源码解析系列之 selector
  • vue学习系列(二)vue-cli
  • vue中实现单选
  • webpack+react项目初体验——记录我的webpack环境配置
  • 对象管理器(defineProperty)学习笔记
  • 设计模式走一遍---观察者模式
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • #{}和${}的区别是什么 -- java面试
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (c语言)strcpy函数用法
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (生成器)yield与(迭代器)generator
  • (四)模仿学习-完成后台管理页面查询
  • (未解决)macOS matplotlib 中文是方框
  • (一)十分简易快速 自己训练样本 opencv级联haar分类器 车牌识别
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转) Android中ViewStub组件使用
  • (转)VC++中ondraw在什么时候调用的
  • (转载)Linux 多线程条件变量同步
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库