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

[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           1
 16 #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           134
 27 #define RTC_MINOR               135
 28 #define EFI_RTC_MINOR           136     /* EFI Time services */
 29 #define VHCI_MINOR              137
 30 #define SUN_OPENPROM_MINOR      139
 31 #define DMAPI_MINOR             140     /* unused */
 32 #define NVRAM_MINOR             144
 33 #define SGI_MMTIMER             153
 34 #define STORE_QUEUE_MINOR       155     /* unused */
 35 #define I2O_MINOR               166
 36 #define HWRNG_MINOR             183
 37 #define MICROCODE_MINOR         184
 38 #define IRNET_MINOR             187
 39 #define D7S_MINOR               193
 40 #define VFIO_MINOR              196
 41 #define TUN_MINOR               200
 42 #define CUSE_MINOR              203
 43 #define MWAVE_MINOR             219     /* ACP/Mwave Modem */
 44 #define MPT_MINOR               220
 45 #define MPT2SAS_MINOR           221
 46 #define MPT3SAS_MINOR           222
 47 #define UINPUT_MINOR            223
 48 #define MISC_MCELOG_MINOR       227
 49 #define HPET_MINOR              228
 50 #define FUSE_MINOR              229
 51 #define KVM_MINOR               232
 52 #define BTRFS_MINOR             234
 53 #define AUTOFS_MINOR            235
 54 #define MAPPER_CTRL_MINOR       236
 55 #define LOOP_CTRL_MINOR         237
 56 #define VHOST_NET_MINOR         238
 57 #define UHID_MINOR              239
 58 #define USERIO_MINOR            240
 59 #define VHOST_VSOCK_MINOR       241
 60 #define RFKILL_MINOR            242
 61 #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              10

147 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);

http://www.kler.cn/a/304648.html

相关文章:

  • 代码修改材质参数
  • 1111111111待修改--大流量分析(三)-BUUCTF
  • 随手记:简单实现纯前端文件导出(XLSX)
  • 十三、注解配置SpringMVC
  • 什么是数字图像?
  • 用vscode编写verilog时,如何有信号定义提示、信号定义跳转(go to definition)、模块跳转这些功能
  • 【Python小知识 - 2】:在VSCode中切换Python解释器版本
  • 王者荣耀改重复名(java源码)
  • 服务器数据增量迁移方案-—SAAS本地化及未来之窗行业应用跨平台架构
  • Java项目: 基于SpringBoot+mybatis+maven新闻推荐系统(含源码+数据库+毕业论文)
  • 【vue-media-upload】一个好用的上传图片的组件,注意事项
  • 道路检测-目标检测数据集(包括VOC格式、YOLO格式)
  • Jenkins、Ansible 和 Git 的自动化部署教程
  • 使用C++实现一个支持基本消息传递的TCP客户端和服务器
  • 精准学:用一根垂直大模型支柱,撑起教育普惠的未来
  • 私域流量的价值探索:开源链动 2+1 模式、AI 智能名片与 S2B2C 商城小程序的助力
  • Apache POI 学习
  • Linux的luks设备上的分区名字的一个现象
  • Docker镜像下载-使用github action- 解决无法下载docker镜像的问题
  • Apache Spark Streaming技术深度解析
  • IP core 在硬件上实现的流程
  • Linux环境使用Git同步教程
  • 软考中项(第三版) 项目成本管理总结
  • IP-Adapter学习
  • a-table 定时平滑轮播组件
  • 目标检测从入门到精通——数据增强方法总结