/* Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base.h" static OSAL_LIST_HEAD(ot_media_list); static DEFINE_MUTEX(ot_media_sem); /* * Assigned numbers, used for dynamic minors */ #define DYNAMIC_MINORS 64 /* like dynamic majors */ #define DYNAMIC_MINORS_TO_BYTES (DYNAMIC_MINORS / 8) static unsigned char g_ot_media_minors[DYNAMIC_MINORS_TO_BYTES]; /* 8: bitmap 1Byte has 8 bit */ static int ot_media_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); struct ot_media_device *c = NULL; int err = -ENODEV; const struct file_operations *old_fops = NULL; const struct file_operations *new_fops = NULL; mutex_lock(&ot_media_sem); osal_list_for_each_entry(c, &ot_media_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if (new_fops == NULL) { mutex_unlock(&ot_media_sem); request_module("char-major-%d-%u", OT_MEDIA_DEVICE_MAJOR, minor); mutex_lock(&ot_media_sem); osal_list_for_each_entry(c, &ot_media_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if (new_fops == NULL) { goto fail; } } err = 0; old_fops = file->f_op; file->f_op = new_fops; if (file->f_op->open) { file->private_data = c; err = file->f_op->open(inode, file); if (err) { fops_put(file->f_op); file->private_data = NULL; file->f_op = fops_get(old_fops); } } fops_put(old_fops); fail: mutex_unlock(&ot_media_sem); return err; } static struct file_operations g_ot_media_fops = { .owner = THIS_MODULE, .open = ot_media_open, }; int ot_media_search_minors(struct ot_media_device *ot_media) { int i = DYNAMIC_MINORS; int index; while (--i >= 0) { index = (unsigned int)i >> 3; /* 3: left shift 3bit to locate the index of char array */ if (index >= DYNAMIC_MINORS_TO_BYTES) { return -1; } if ((g_ot_media_minors[index] & (1 << ((unsigned int)i & 7))) == 0) { /* 7: locate the bit in bitmap */ break; } } if (i < 0) { return -1; } ot_media->minor = i; return 0; } /* * ot_media_register - register a ot_media device * @ot_media: device structure * * Register a ot_media device with the kernel. If the minor * number is set to %OT_MEDIA_DYNAMIC_MINOR a minor number is assigned * and placed in the minor field of the structure. For other cases * the minor number requested is used. * * The structure passed is linked into the kernel and may not be * destroyed until it has been unregistered. * * A zero is returned on success and a negative errno code for * failure. */ int ot_media_register(struct ot_media_device *ot_media) { struct ot_media_device *ptmp = NULL; struct ot_media_driver *pdrv = NULL; int err; if ((ot_media == NULL) || (ot_media->owner == NULL)) { return -EBUSY; } mutex_lock(&ot_media_sem); /* check if registered */ osal_list_for_each_entry(ptmp, &ot_media_list, list) { if (ptmp->minor == ot_media->minor) { mutex_unlock(&ot_media_sem); return -EBUSY; } } /* check minor */ if (ot_media->minor == OT_MEDIA_DYNAMIC_MINOR) { if (ot_media_search_minors(ot_media) != 0) { mutex_unlock(&ot_media_sem); return -EBUSY; } } if (ot_media->minor < DYNAMIC_MINORS) { g_ot_media_minors[ot_media->minor >> 3] |= 1 << (ot_media->minor & 7); /* 3, 7: bitmap write set bit 1 */ } /* device register */ err = ot_media_device_register(ot_media); if (err < 0) { g_ot_media_minors[ot_media->minor >> 3] &= ~(1 << (ot_media->minor & 7)); /* 3, 7: bitmap write set bit 0 */ goto out; } /* driver register */ pdrv = ot_media_driver_register(ot_media->devfs_name, ot_media->owner); if (IS_ERR(pdrv)) { ot_media_device_unregister(ot_media); g_ot_media_minors[ot_media->minor >> 3] &= ~(1 << (ot_media->minor & 7)); /* 3, 7: bitmap write set bit 0 */ err = PTR_ERR(pdrv); goto out; } ot_media->driver = pdrv; /* * Add it to the front, so that later devices can "override" * earlier defaults */ osal_list_add(&ot_media->list, &ot_media_list); out: mutex_unlock(&ot_media_sem); return err; } EXPORT_SYMBOL(ot_media_register); /* * ot_media_unregister - unregister a ot_media device * @ot_media: device to unregister * * Unregister a ot_media device that was previously * successfully registered with ot_media_register(). Success * is indicated by a zero return, a negative errno code * indicates an error. */ int ot_media_unregister(struct ot_media_device *ot_media) { struct ot_media_device *ptmp = NULL; struct ot_media_device *_ptmp = NULL; if (ot_media == NULL) { return -EINVAL; } if (osal_list_empty(&ot_media->list)) { return -EINVAL; } mutex_lock(&ot_media_sem); osal_list_for_each_entry_safe(ptmp, _ptmp, &ot_media_list, list) { /* if found, unregister device & driver */ if (ptmp->minor == ot_media->minor) { osal_list_del(&ot_media->list); ot_media_driver_unregister(ot_media->driver); ot_media->driver = NULL; ot_media_device_unregister(ot_media); g_ot_media_minors[ot_media->minor >> 3] &= ~(1 << (ot_media->minor & 7)); /* 3, 7: bitmap write set bit 0 */ break; } } mutex_unlock(&ot_media_sem); return 0; } EXPORT_SYMBOL(ot_media_unregister); int ot_media_init(void) { int ret; /* 1 */ ret = ot_media_bus_init(); if (ret) { goto err0; } /* 2 */ ret = -EIO; if (register_chrdev(OT_MEDIA_DEVICE_MAJOR, "ot_media", &g_ot_media_fops)) { goto err1; } printk("Module ot_media: init ok\n"); return 0; /* 3 */ err1: ot_media_bus_exit(); err0: return ret; } void ot_media_exit(void) { /* 0 */ if (!osal_list_empty(&ot_media_list)) { printk("!!! Module ot_media: sub module in list\n"); return; } unregister_chrdev(OT_MEDIA_DEVICE_MAJOR, "ot_media"); ot_media_bus_exit(); printk("!!! Module ot_media: exit ok\n"); }