/* Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include "securec.h" #include "ot_media.h" #include "ot_osal.h" #define DRVAL_DEBUG 0 #define get_file_return(file) \ do { \ if (__get_file(file) < 0) \ return (-1); \ } while (0) #define put_file_return(file) \ do { \ if (__put_file(file) < 0) \ return (-1); \ } while (0) typedef struct osal_coat_dev { struct osal_dev osal_dev; struct ot_media_device ot_media_dev; } osal_coat_dev_t; struct osal_private_data { struct osal_dev *dev; void *data; struct osal_poll table; int f_ref_cnt; }; spinlock_t g_f_lock; void osal_device_init(void) { spin_lock_init(&g_f_lock); } void osal_device_exit(void) { } static int __get_file(struct file *file) { struct osal_private_data *pdata = NULL; spin_lock(&g_f_lock); pdata = file->private_data; if (pdata == NULL) { spin_unlock(&g_f_lock); return -1; } pdata->f_ref_cnt++; spin_unlock(&g_f_lock); return 0; } static int __put_file(struct file *file) { struct osal_private_data *pdata = NULL; spin_lock(&g_f_lock); pdata = file->private_data; if (pdata == NULL) { spin_unlock(&g_f_lock); return -1; } pdata->f_ref_cnt--; spin_unlock(&g_f_lock); return 0; } static int osal_open(struct inode *inode, struct file *file) { struct ot_media_device *ot_media = NULL; osal_coat_dev_t *coat_dev = NULL; struct osal_private_data *pdata = NULL; osal_unused(inode); if (!capable(CAP_SYS_RAWIO) || !capable(CAP_SYS_ADMIN)) { return -EPERM; } ot_media = (struct ot_media_device *)file->private_data; if (ot_media == NULL) { osal_trace("%s - get ot_media device error!\n", __FUNCTION__); return -1; } coat_dev = osal_container_of(ot_media, struct osal_coat_dev, ot_media_dev); pdata = (struct osal_private_data *)kmalloc(sizeof(struct osal_private_data), GFP_KERNEL); if (pdata == NULL) { osal_trace("%s - kmalloc error!\n", __FUNCTION__); return -1; } if (DRVAL_DEBUG) { osal_trace("%s - file->private_data=%pK!\n", __FUNCTION__, pdata); } (void)memset_s(pdata, sizeof(struct osal_private_data), 0, sizeof(struct osal_private_data)); file->private_data = pdata; pdata->dev = &(coat_dev->osal_dev); if (coat_dev->osal_dev.fops->open != NULL) { return coat_dev->osal_dev.fops->open((void *)&(pdata->data)); } return 0; } static ssize_t osal_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { struct osal_private_data *pdata = file->private_data; int ret = 0; get_file_return(file); if (pdata->dev->fops->read != NULL) { ret = pdata->dev->fops->read(buf, (int)size, (long *)offset, (void *)&(pdata->data)); } put_file_return(file); return ret; } static ssize_t osal_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { struct osal_private_data *pdata = file->private_data; int ret = -1; get_file_return(file); if (pdata->dev->fops->write != NULL) { ret = pdata->dev->fops->write(buf, (int)size, (long *)offset, (void *)&(pdata->data)); } put_file_return(file); return ret; } static loff_t osal_llseek(struct file *file, loff_t offset, int whence) { struct osal_private_data *pdata = file->private_data; int ret = 0; get_file_return(file); if (DRVAL_DEBUG) { osal_trace("%s - file->private_data=%pK!\n", __FUNCTION__, pdata); } if (whence == SEEK_SET) { if (pdata->dev->fops->llseek != NULL) { ret = pdata->dev->fops->llseek((long)offset, OSAL_SEEK_SET, (void *)&(pdata->data)); } } else if (whence == SEEK_CUR) { if (pdata->dev->fops->llseek != NULL) { ret = pdata->dev->fops->llseek((long)offset, OSAL_SEEK_CUR, (void *)&(pdata->data)); } } else if (whence == SEEK_END) { if (pdata->dev->fops->llseek != NULL) { ret = pdata->dev->fops->llseek((long)offset, OSAL_SEEK_END, (void *)&(pdata->data)); } } put_file_return(file); return (loff_t)ret; } static int osal_release(struct inode *inode, struct file *file) { int ret = 0; struct osal_private_data *pdata = file->private_data; osal_unused(inode); get_file_return(file); if (DRVAL_DEBUG) { osal_trace("%s - file->private_data=%pK!\n", __FUNCTION__, pdata); } if (pdata->dev->fops->release != NULL) { ret = pdata->dev->fops->release((void *)&(pdata->data)); } put_file_return(file); if (ret != 0) { osal_trace("%s - release failed!\n", __FUNCTION__); return ret; } spin_lock(&g_f_lock); if (pdata->f_ref_cnt != 0) { osal_trace("%s - release failed!\n", __FUNCTION__); spin_unlock(&g_f_lock); return -1; } kfree(file->private_data); file->private_data = NULL; spin_unlock(&g_f_lock); return 0; } static int osal_unlocked_ioctl_cmd_none(unsigned int cmd, unsigned long arg, const struct osal_private_data *pdata) { if (pdata->dev->fops->unlocked_ioctl == NULL) { return -1; } return pdata->dev->fops->unlocked_ioctl(cmd, arg, (void *)&(pdata->data)); } static int osal_unlocked_ioctl_cmd_write(unsigned int cmd, unsigned long arg, const struct osal_private_data *pdata) { int ret = -1; char *kbuf = NULL; if ((char *)(uintptr_t)arg == NULL) { return -1; } kbuf = (char *)vmalloc(_IOC_SIZE(cmd)); if (kbuf == NULL) { osal_trace("%s - vmalloc failed!\n", __FUNCTION__); return -1; } if (copy_from_user(kbuf, (char *)(uintptr_t)arg, _IOC_SIZE(cmd))) { vfree(kbuf); kbuf = NULL; return -1; } if (pdata->dev->fops->unlocked_ioctl == NULL) { vfree(kbuf); kbuf = NULL; return -1; } else { ret = pdata->dev->fops->unlocked_ioctl(cmd, (unsigned long)(uintptr_t)kbuf, (void *)&(pdata->data)); } vfree(kbuf); kbuf = NULL; return ret; } static int osal_unlocked_ioctl_cmd_read(unsigned int cmd, unsigned long arg, const struct osal_private_data *pdata) { int ret = -1; char *kbuf = NULL; if ((char *)(uintptr_t)arg == NULL) { return -1; } kbuf = vmalloc(_IOC_SIZE(cmd)); if (kbuf == NULL) { osal_trace("%s - vmalloc failed!\n", __FUNCTION__); return -1; } if (pdata->dev->fops->unlocked_ioctl == NULL) { vfree(kbuf); kbuf = NULL; return -1; } else { ret = pdata->dev->fops->unlocked_ioctl(cmd, (unsigned long)(uintptr_t)kbuf, (void *)&(pdata->data)); if (ret == 0) { if (copy_to_user((char *)(uintptr_t)arg, kbuf, _IOC_SIZE(cmd))) { vfree(kbuf); kbuf = NULL; return -1; } } } vfree(kbuf); kbuf = NULL; return ret; } static int osal_unlocked_ioctl_cmd_read_write(unsigned int cmd, unsigned long arg, const struct osal_private_data *pdata) { int ret = -1; char *kbuf = NULL; if ((char *)(uintptr_t)arg == NULL) { return -1; } kbuf = vmalloc(_IOC_SIZE(cmd)); if (kbuf == NULL) { osal_trace("%s - vmalloc failed!\n", __FUNCTION__); return -1; } if (copy_from_user(kbuf, (char *)(uintptr_t)arg, _IOC_SIZE(cmd))) { vfree(kbuf); kbuf = NULL; return -1; } if (pdata->dev->fops->unlocked_ioctl == NULL) { vfree(kbuf); kbuf = NULL; return -1; } else { ret = pdata->dev->fops->unlocked_ioctl(cmd, (unsigned long)(uintptr_t)kbuf, (void *)&(pdata->data)); if (ret == 0) { if (copy_to_user((char *)(uintptr_t)arg, kbuf, _IOC_SIZE(cmd))) { vfree(kbuf); kbuf = NULL; return -1; } } } vfree(kbuf); kbuf = NULL; return ret; } static long __osal_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = -1; struct osal_private_data *pdata = file->private_data; if (DRVAL_DEBUG) { osal_trace("%s - file->private_data=%pK!\n", __FUNCTION__, pdata); } if (((_IOC_SIZE(cmd) == 0) && (_IOC_DIR(cmd) != _IOC_NONE))) { return -1; } if ((_IOC_DIR(cmd) != _IOC_NONE) && (((char *)(uintptr_t)arg) == NULL)) { osal_trace("%s - Input param err,it is null!\n", __FUNCTION__); return -1; } if (_IOC_DIR(cmd) == _IOC_NONE) { ret = osal_unlocked_ioctl_cmd_none(cmd, arg, pdata); } else if (_IOC_DIR(cmd) == _IOC_WRITE) { ret = osal_unlocked_ioctl_cmd_write(cmd, arg, pdata); } else if (_IOC_DIR(cmd) == _IOC_READ) { ret = osal_unlocked_ioctl_cmd_read(cmd, arg, pdata); } else if (_IOC_DIR(cmd) == (_IOC_READ + _IOC_WRITE)) { ret = osal_unlocked_ioctl_cmd_read_write(cmd, arg, pdata); } return ret; } static long osal_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; get_file_return(file); ret = __osal_unlocked_ioctl(file, cmd, arg); put_file_return(file); return ret; } #ifdef CONFIG_COMPAT static int osal_compat_ioctl_check_none(unsigned int cmd, unsigned long arg, const struct osal_private_data *pdata) { if (pdata->dev->fops->compat_ioctl == NULL) { return -1; } else { return pdata->dev->fops->compat_ioctl(cmd, arg, (void *)&(pdata->data)); } } static int osal_compat_ioctl_check_write(unsigned int cmd, unsigned long arg, const struct osal_private_data *pdata) { int ret = -1; char *kbuf = NULL; if ((char *)(uintptr_t)arg == NULL) { return -1; } kbuf = (char *)vmalloc(_IOC_SIZE(cmd)); if (kbuf == NULL) { osal_trace("%s - vmalloc failed!\n", __FUNCTION__); return -1; } if (copy_from_user(kbuf, (char *)(uintptr_t)arg, _IOC_SIZE(cmd))) { vfree(kbuf); return -1; } if (pdata->dev->fops->compat_ioctl == NULL) { vfree(kbuf); return -1; } else { ret = pdata->dev->fops->compat_ioctl(cmd, (unsigned long)(uintptr_t)kbuf, (void *)&(pdata->data)); } vfree(kbuf); return ret; } static int osal_compat_ioctl_check_read(unsigned int cmd, unsigned long arg, const struct osal_private_data *pdata) { int ret = -1; char *kbuf = NULL; if ((char *)(uintptr_t)arg == NULL) { return -1; } kbuf = (char *)vmalloc(_IOC_SIZE(cmd)); if (kbuf == NULL) { osal_trace("%s - vmalloc failed!\n", __FUNCTION__); return -1; } if (pdata->dev->fops->compat_ioctl == NULL) { vfree(kbuf); return -1; } else { ret = pdata->dev->fops->compat_ioctl(cmd, (unsigned long)(uintptr_t)kbuf, (void *)&(pdata->data)); if (ret == 0) { if (copy_to_user((char *)(uintptr_t)arg, kbuf, _IOC_SIZE(cmd))) { vfree(kbuf); return -1; } } } vfree(kbuf); return ret; } static int osal_compat_ioctl_check_read_write(unsigned int cmd, unsigned long arg, const struct osal_private_data *pdata) { int ret = -1; char *kbuf = NULL; if ((char *)(uintptr_t)arg == NULL) { return -1; } kbuf = (char *)vmalloc(_IOC_SIZE(cmd)); if (kbuf == NULL) { osal_trace("%s - vmalloc failed!\n", __FUNCTION__); return -1; } if (copy_from_user(kbuf, (char *)(uintptr_t)arg, _IOC_SIZE(cmd))) { vfree(kbuf); return -1; } if (pdata->dev->fops->compat_ioctl == NULL) { vfree(kbuf); return -1; } else { ret = pdata->dev->fops->compat_ioctl(cmd, (unsigned long)(uintptr_t)kbuf, (void *)&(pdata->data)); if (ret == 0) { if (copy_to_user((char *)(uintptr_t)arg, kbuf, _IOC_SIZE(cmd))) { vfree(kbuf); return -1; } } } vfree(kbuf); return ret; } static long __osal_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = -1; struct osal_private_data *pdata = file->private_data; if (DRVAL_DEBUG) { osal_trace("%s - file->private_data=%pK!\n", __FUNCTION__, pdata); } if (((_IOC_SIZE(cmd) == 0) && (_IOC_DIR(cmd) != _IOC_NONE))) { return -1; } if ((_IOC_DIR(cmd) != _IOC_NONE) && (((char *)(uintptr_t)arg) == NULL)) { osal_trace("%s - Input param err,it is null!\n", __FUNCTION__); return -1; } if (_IOC_DIR(cmd) == _IOC_NONE) { ret = osal_compat_ioctl_check_none(cmd, arg, pdata); } else if (_IOC_DIR(cmd) == _IOC_WRITE) { ret = osal_compat_ioctl_check_write(cmd, arg, pdata); } else if (_IOC_DIR(cmd) == _IOC_READ) { ret = osal_compat_ioctl_check_read(cmd, arg, pdata); } else if (_IOC_DIR(cmd) == (_IOC_READ + _IOC_WRITE)) { ret = osal_compat_ioctl_check_read_write(cmd, arg, pdata); } return ret; } static long osal_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; get_file_return(file); ret = __osal_compat_ioctl(file, cmd, arg); put_file_return(file); return ret; } #endif static unsigned int osal_poll(struct file *file, struct poll_table_struct *table) { struct osal_private_data *pdata = file->private_data; struct osal_poll t; unsigned int ret = 0; get_file_return(file); if (DRVAL_DEBUG) { osal_trace("%s - table=%pK, file=%pK!\n", __FUNCTION__, table, file); } t.poll_table = table; t.data = file; if (pdata->dev->fops->poll != NULL) { ret = pdata->dev->fops->poll(&t, (void *)&(pdata->data)); } put_file_return(file); return ret; } int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) { /* check physical addr greater than the max addr supported by the system */ if ((pfn + (size >> PAGE_SHIFT)) > (1 + ((~0UL) >> PAGE_SHIFT))) { osal_trace("--%s - %d--!\n", __FUNCTION__, __LINE__); return 0; } /* It's necessary for the variable "size" to align 4k(page_size). */ #define PAGE_SIZE_MASK 0xfffffffffffff000ULL if ((unsigned long)size & (~PAGE_SIZE_MASK)) { osal_trace("--%s - %d--!\n", __FUNCTION__, __LINE__); return 0; } #undef PAGE_SIZE_MASK return 1; } static int osal_mmap(struct file *file, struct vm_area_struct *vm) { struct osal_vm osal_vm; struct osal_private_data *pdata = file->private_data; osal_vm.vm = vm; if (!valid_mmap_phys_addr_range(vm->vm_pgoff, vm->vm_end - vm->vm_start)) { osal_trace("\n%s - invalid argument size=%ld!!!\n", __FUNCTION__, vm->vm_end - vm->vm_start); return -EINVAL; } if (DRVAL_DEBUG) { osal_trace("%s - start=%lx, end=%lx!, off=%lx\n", __FUNCTION__, vm->vm_start, vm->vm_end, vm->vm_pgoff); } if (pdata->dev->fops->mmap != NULL) { return pdata->dev->fops->mmap(&osal_vm, vm->vm_start, vm->vm_end, vm->vm_pgoff, (void *)&(pdata->data)); } return 0; } static struct file_operations g_osal_fops = { .owner = THIS_MODULE, .open = osal_open, .read = osal_read, .write = osal_write, .llseek = osal_llseek, .unlocked_ioctl = osal_unlocked_ioctl, .release = osal_release, .poll = osal_poll, .mmap = osal_mmap, #ifdef CONFIG_COMPAT .compat_ioctl = osal_compat_ioctl, #endif }; osal_dev_t *osal_createdev(const char *name) { osal_coat_dev_t *pdev = NULL; errno_t err_value; if (name == NULL) { osal_trace("%s - parameter invalid!\n", __FUNCTION__); return NULL; } pdev = (osal_coat_dev_t *)kmalloc(sizeof(osal_coat_dev_t), GFP_KERNEL); if (pdev == NULL) { osal_trace("%s - kmalloc error!\n", __FUNCTION__); return NULL; } (void)memset_s(pdev, sizeof(osal_coat_dev_t), 0, sizeof(osal_coat_dev_t)); err_value = strncpy_s(pdev->osal_dev.name, OSAL_MAX_DEV_NAME_LEN, name, sizeof(pdev->osal_dev.name) - 1); if (err_value != EOK) { kfree(pdev); osal_trace("%s - strncpy_s failed!\n", __FUNCTION__); return NULL; } pdev->osal_dev.dev = pdev; return &(pdev->osal_dev); } EXPORT_SYMBOL(osal_createdev); int osal_destroydev(osal_dev_t *osal_dev) { osal_coat_dev_t *pdev = NULL; if (osal_dev == NULL) { osal_trace("%s - parameter invalid!\n", __FUNCTION__); return -1; } pdev = osal_dev->dev; if (pdev == NULL) { osal_trace("%s - parameter invalid!\n", __FUNCTION__); return -1; } kfree(pdev); return 0; } EXPORT_SYMBOL(osal_destroydev); int osal_registerdevice(const osal_dev_t *osal_dev) { struct ot_media_device *ot_media = NULL; errno_t err_value; if ((osal_dev == NULL) || (osal_dev->fops == NULL) || (osal_dev->dev == NULL)) { osal_trace("%s - parameter invalid!\n", __FUNCTION__); return -1; } ot_media = &(((osal_coat_dev_t *)(osal_dev->dev))->ot_media_dev); if (osal_dev->minor != 0) { ot_media->minor = osal_dev->minor; } else { ot_media->minor = OT_MEDIA_DYNAMIC_MINOR; } ot_media->owner = THIS_MODULE; ot_media->fops = &g_osal_fops; err_value = strncpy_s(ot_media->devfs_name, OT_MEDIA_MAX_DEV_NAME_LEN, osal_dev->name, sizeof(ot_media->devfs_name) - 1); if (err_value != EOK) { osal_trace("%s - strncpy_s failed!\n", __FUNCTION__); return -1; } return ot_media_register(ot_media); } EXPORT_SYMBOL(osal_registerdevice); void osal_deregisterdevice(const osal_dev_t *pdev) { if ((pdev == NULL) || (pdev->dev == NULL)) { osal_trace("%s - parameter invalid!\n", __FUNCTION__); return; } ot_media_unregister((struct ot_media_device *)&(((osal_coat_dev_t *)(pdev->dev))->ot_media_dev)); } EXPORT_SYMBOL(osal_deregisterdevice); void osal_poll_wait(osal_poll_t *table, const osal_wait_t *wait) { if ((table == NULL) || (table->data == NULL) || (table->poll_table == NULL) || (wait == NULL) || (wait->wait == NULL)) { osal_trace("%s - input param is null-!\n", __FUNCTION__); return; } if (DRVAL_DEBUG) { osal_trace("%s - call poll_wait +!, table=%pK, file=%pK\n", __FUNCTION__, table->poll_table, table->data); } poll_wait((struct file *)table->data, (wait_queue_head_t *)(wait->wait), table->poll_table); if (DRVAL_DEBUG) { osal_trace("%s - call poll_wait -!\n", __FUNCTION__); } } EXPORT_SYMBOL(osal_poll_wait); void osal_pgprot_noncached(osal_vm_t *vm) { struct vm_area_struct *v = NULL; if ((vm == NULL) || (vm->vm == NULL)) { osal_trace("%s - input error -!\n", __FUNCTION__); return; } v = (struct vm_area_struct *)(vm->vm); v->vm_page_prot = pgprot_writecombine(v->vm_page_prot); } EXPORT_SYMBOL(osal_pgprot_noncached); void osal_pgprot_cached(osal_vm_t *vm) { struct vm_area_struct *v = NULL; if ((vm == NULL) || (vm->vm == NULL)) { osal_trace("%s - input error -!\n", __FUNCTION__); return; } v = (struct vm_area_struct *)(vm->vm); #ifdef CONFIG_64BIT v->vm_page_prot = __pgprot(pgprot_val(v->vm_page_prot) | PTE_VALID | PTE_DIRTY | PTE_AF); #else v->vm_page_prot = __pgprot(pgprot_val(v->vm_page_prot) | L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_MT_DEV_CACHED); #endif } EXPORT_SYMBOL(osal_pgprot_cached); int osal_remap_pfn_range(osal_vm_t *vm, unsigned long addr, unsigned long pfn, unsigned long size) { struct vm_area_struct *v = NULL; if ((vm == NULL) || (vm->vm == NULL)) { osal_trace("%s - input error -!\n", __FUNCTION__); return -EPERM; } if (size == 0) { return -EPERM; } v = (struct vm_area_struct *)(vm->vm); return remap_pfn_range(v, addr, pfn, size, v->vm_page_prot); } EXPORT_SYMBOL(osal_remap_pfn_range);