#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtos_cmdqu.h" #include "cvi_mailbox.h" #include "cvi_spinlock.h" #include #include "cmdqu_cb.h" #include "rtos_cmdqu_cb.h" //#define pr_debug //#define __CV182X__ // used to debug on 182x evb struct cvi_rtos_cmdqu_device { struct device *dev; struct miscdevice miscdev; }; spinlock_t mailbox_queue_lock; spinlock_t send_queue_lock; #ifdef _LP64 static __u64 reg_base; #else static __u32 reg_base; #endif static int mailbox_irq; static int cvi_rtos_cmdqu_open(struct inode *inode, struct file *file) { return 0; } static int cvi_rtos_cmdqu_release(struct inode *inode, struct file *file) { return 0; } struct rtos_cmdqu_wait_list_t { struct list_head list; cmdqu_t cmdq; wait_queue_head_t wq; int condition; }; struct rtos_irqaction { void *handler; void *dev_id; unsigned char ip_id; const char *name; }; static struct rtos_irqaction rtos_irqaction[NR_RTOS_IP]; static struct rtos_cmdqu_wait_list_t rtos_cmdqu_wait_head; int request_rtos_irq(unsigned char ip_id, void (*handler), const char *devname, void *dev_id) { pr_debug("ip_id =%d handler=%p devname=%s\n", ip_id, handler, devname); if (ip_id < NR_RTOS_IP && handler && devname) { rtos_irqaction[ip_id].handler = handler; rtos_irqaction[ip_id].name = devname; rtos_irqaction[ip_id].dev_id = dev_id; rtos_irqaction[ip_id].ip_id = ip_id; return 0; } else { pr_err("%s fail ip_id =%d handler=%p devname=%s\n", __func__, ip_id, handler, devname); return -EINVAL; } } EXPORT_SYMBOL(request_rtos_irq); void init_sqirq(void) { int ip_id; for (ip_id = 0; ip_id < NR_RTOS_IP; ip_id++) { rtos_irqaction[ip_id].handler = NULL; rtos_irqaction[ip_id].name = NULL; rtos_irqaction[ip_id].dev_id = NULL; rtos_irqaction[ip_id].ip_id = 0xFF; } } int free_rtos_irq(unsigned char ip_id) { if (ip_id < NR_RTOS_IP) { rtos_irqaction[ip_id].handler = NULL; rtos_irqaction[ip_id].name = NULL; rtos_irqaction[ip_id].dev_id = NULL; rtos_irqaction[ip_id].ip_id = 0xFF; return 0; } return -EINVAL; } EXPORT_SYMBOL(free_rtos_irq); typedef void (*irq_request_func) (unsigned int, unsigned int, void *); /* used for callback test*/ static void callback_rtos_irq_handler(int cmd_id, unsigned int ptr, void *dev_id) { pr_info("callback_rtos_hander cmd_id = %x ptr=%x dev_id=%p\n", cmd_id, ptr, dev_id); } DEFINE_CVI_SPINLOCK(mailbox_lock, SPIN_MBOX); irqreturn_t rtos_irq_handler(int irq, void *dev_id) { char set_val, done_val; int i; int flags; cmdqu_t *cmdq; struct rtos_cmdqu_wait_list_t *wait_list; struct list_head *pos; drv_spin_lock_irqsave(&mailbox_lock, flags); if (flags == MAILBOX_LOCK_FAILED) { pr_err("drv_spin_lock_irqsave failed!\n"); //must clear irq? return IRQ_HANDLED; } pr_debug("rtos_irq_handler irq=%d\n", irq); set_val = mbox_reg->cpu_mbox_set[RECEIVE_CPU].cpu_mbox_int_int.mbox_int; done_val = mbox_done_reg->cpu_mbox_done[RECEIVE_CPU].cpu_mbox_int_int.mbox_int; pr_debug("set_val=%x\n", set_val); pr_debug("done_val=%x\n", done_val); for (i = 0; i < MAILBOX_MAX_NUM && set_val > 0; i++) { /* valid_val uses unsigned char because of mailbox register table * ~valid_val will be 0xFF */ unsigned char valid_val = set_val & (1 << i); pr_debug("MAILBOX_MAX_NUM = %d\n", MAILBOX_MAX_NUM); pr_debug("valid_val = %d set_val=%d i = %d\n", valid_val, set_val, i); if (valid_val) { cmdqu_t linux_cmdq; cmdq = (cmdqu_t *)(mailbox_context) + i; /* mailbox buffer context is send from rtos, clear mailbox interrupt */ mbox_reg->cpu_mbox_set[RECEIVE_CPU].cpu_mbox_int_clr.mbox_int_clr = valid_val; // need to disable enable bit mbox_reg->cpu_mbox_en[RECEIVE_CPU].mbox_info &= ~valid_val; // copy cmdq context (8 bytes) to buffer ASAP ?? *((unsigned long long *) &linux_cmdq) = *((unsigned long long *)cmdq); /* need to clear mailbox interrupt before clear mailbox buffer ??*/ *((unsigned long long *) cmdq) = 0; /* mailbox buffer context is send from rtos */ pr_debug("cmdq=%p\n", cmdq); pr_debug("cmdq->ip_id =%d\n", linux_cmdq.ip_id); pr_debug("cmdq->cmd_id =%d\n", linux_cmdq.cmd_id); pr_debug("cmdq->param_ptr =%x\n", linux_cmdq.param_ptr); pr_debug("cmdq->block =%d\n", linux_cmdq.block); pr_debug("cmdq->linux_valid =%d\n", linux_cmdq.resv.valid.linux_valid); pr_debug("cmdq->rtos_valid =%x", linux_cmdq.resv.valid.rtos_valid); if (linux_cmdq.resv.valid.rtos_valid == 1 && linux_cmdq.cmd_id <= NR_RTOS_CMD && linux_cmdq.ip_id <= NR_RTOS_IP && linux_cmdq.block == 1) { // dewait list_for_each(pos, &rtos_cmdqu_wait_head.list) { wait_list = list_entry(pos, struct rtos_cmdqu_wait_list_t, list); pr_debug("s wait_list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id); pr_debug("s wait_list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id); if (wait_list->cmdq.ip_id == linux_cmdq.ip_id && wait_list->cmdq.cmd_id == linux_cmdq.cmd_id) { /* copy data to wait_list and return to user space */ *((unsigned long long *) &wait_list->cmdq) = *((unsigned long long *) &linux_cmdq); pr_debug("wait_list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id); pr_debug("wait_list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id); pr_debug("wait_list->cmdq.param_ptr=%x\n", wait_list->cmdq.param_ptr); wait_list->condition = 1; wake_up_interruptible(&wait_list->wq); break; } } } else if (linux_cmdq.resv.valid.rtos_valid == 1 && rtos_irqaction[linux_cmdq.ip_id].handler && rtos_irqaction[linux_cmdq.ip_id].ip_id <= NR_RTOS_IP) { irq_request_func rtos_irq_func; pr_debug("handler =%p\n", rtos_irqaction[linux_cmdq.ip_id].handler); pr_debug("name =%s\n", rtos_irqaction[linux_cmdq.ip_id].name); rtos_irq_func = rtos_irqaction[linux_cmdq.ip_id].handler; rtos_irq_func(linux_cmdq.cmd_id, linux_cmdq.param_ptr, rtos_irqaction[linux_cmdq.ip_id].dev_id); } else pr_err("error ip=%d , cmd=%d\n", linux_cmdq.ip_id, linux_cmdq.cmd_id); } } drv_spin_unlock_irqrestore(&mailbox_lock, flags); return IRQ_HANDLED; } long rtos_cmdqu_init(void) { long ret = 0; int i; pr_debug("RTOS_CMDQU_INIT\n"); spin_lock_init(&mailbox_queue_lock); spin_lock_init(&send_queue_lock); mbox_reg = (struct mailbox_set_register *) reg_base; mbox_done_reg = (struct mailbox_done_register *) (reg_base + MAILBOX_DONE_OFFSET); mailbox_context = (unsigned long *) (reg_base + MAILBOX_CONTEXT_OFFSET);//MAILBOX_CONTEXT; pr_debug("mbox_reg=%p\n", mbox_reg); pr_debug("mbox_done_reg=%p\n", mbox_done_reg); pr_debug("mailbox_context=%p\n", mailbox_context); // init mailbox_context for ( i=0;i < MAILBOX_MAX_NUM;i++) mailbox_context[i] = 0; /* init sqirq parameters*/ init_sqirq(); return ret; } long rtos_cmdqu_deinit(void) { long ret = 0; pr_debug("RTOS_CMDQU_DEINIT\n"); //mailbox deinit return ret; } int rtos_cmdqu_send(cmdqu_t *cmdq) { int ret = 0; int valid; unsigned long flags; int mb_flags; cmdqu_t *linux_cmdqu_t; pr_debug("RTOS_CMDQU_SEND\n"); pr_debug("ip_id=%d cmd_id=%d param_ptr=%x\n", cmdq->ip_id, cmdq->cmd_id, (unsigned int)cmdq->param_ptr); spin_lock_irqsave(&mailbox_queue_lock, flags); // when linux and rtos send command at the same time, it might cause a problem. // might need to spinlock with rtos, do it later drv_spin_lock_irqsave(&mailbox_lock, mb_flags); if (mb_flags == MAILBOX_LOCK_FAILED) { pr_err("ip_id=%d cmd_id=%d param_ptr=%x\n", cmdq->ip_id, cmdq->cmd_id, (unsigned int)cmdq->param_ptr); spin_unlock_irqrestore(&mailbox_queue_lock, flags); return -ENOBUFS; } linux_cmdqu_t = (cmdqu_t *) mailbox_context; pr_debug("mailbox_context = %p\n", mailbox_context); pr_debug("linux_cmdqu_t = %p\n", linux_cmdqu_t); pr_debug("cmdq->ip_id = %d\n", cmdq->ip_id); pr_debug("cmdq->cmd_id = %d\n", cmdq->cmd_id); pr_debug("cmdq->block = %d\n", cmdq->block); pr_debug("cmdq->para_ptr = %d\n", cmdq->param_ptr); if (cmdq->ip_id >= IP_LIMIT || cmdq->cmd_id >= SYS_CMD_INFO_LIMIT) { pr_err("invalid-id : ip_id = %d cmd_id = %d\n", cmdq->ip_id, cmdq->cmd_id); drv_spin_unlock_irqrestore(&mailbox_lock, mb_flags); spin_unlock_irqrestore(&mailbox_queue_lock, flags); return -ENOBUFS; } for (valid = 0; valid < MAILBOX_MAX_NUM; valid++) { if (linux_cmdqu_t->resv.valid.linux_valid == 0 && linux_cmdqu_t->resv.valid.rtos_valid == 0) { // mailbox buffer context is int (4 bytes) access int *ptr = (int *)linux_cmdqu_t; linux_cmdqu_t->resv.valid.linux_valid = 1; *ptr = ((cmdq->ip_id << 0) | (cmdq->cmd_id << 8) | (cmdq->block << 15) | (linux_cmdqu_t->resv.valid.linux_valid << 16) | (linux_cmdqu_t->resv.valid.rtos_valid << 24)); linux_cmdqu_t->param_ptr = cmdq->param_ptr; pr_debug("linux_valid = %d\n", linux_cmdqu_t->resv.valid.linux_valid); pr_debug("rtos_valid = %d\n", linux_cmdqu_t->resv.valid.rtos_valid); pr_debug("ip_id = %d\n", linux_cmdqu_t->ip_id); pr_debug("cmd_id = %d\n", linux_cmdqu_t->cmd_id); pr_debug("block = %d\n", linux_cmdqu_t->block); pr_debug("param_ptr = %x\n", linux_cmdqu_t->param_ptr); pr_debug("*ptr = %x\n", *ptr); // clear mailbox mbox_reg->cpu_mbox_set[SEND_TO_CPU].cpu_mbox_int_clr.mbox_int_clr = (1 << valid); // trigger mailbox valid to rtos mbox_reg->cpu_mbox_en[SEND_TO_CPU].mbox_info |= (1 << valid); mbox_reg->mbox_set.mbox_set = (1 << valid); break; } linux_cmdqu_t++; } if (valid >= MAILBOX_MAX_NUM) { pr_err("No valid mailbox is available\n"); drv_spin_unlock_irqrestore(&mailbox_lock, mb_flags); spin_unlock_irqrestore(&mailbox_queue_lock, flags); return -ENOBUFS; } drv_spin_unlock_irqrestore(&mailbox_lock, mb_flags); spin_unlock_irqrestore(&mailbox_queue_lock, flags); return ret; } EXPORT_SYMBOL(rtos_cmdqu_send); int rtos_cmdqu_send_wait(cmdqu_t *cmdq, int wait_cmd_id) { unsigned long flags; struct rtos_cmdqu_wait_list_t *wait_list; struct list_head *pos; int delaytime; int ret = 0; pr_debug("%s %d\n", __func__, __LINE__); spin_lock_irqsave(&send_queue_lock, flags); /* check list with same commands? if yes, ignore it */ list_for_each(pos, &rtos_cmdqu_wait_head.list) { wait_list = list_entry(pos, struct rtos_cmdqu_wait_list_t, list); pr_debug("list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id); pr_debug("list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id); if (cmdq->ip_id == wait_list->cmdq.ip_id && wait_cmd_id == wait_list->cmdq.cmd_id && cmdq->param_ptr == wait_list->cmdq.param_ptr) { pr_debug("exist : wait_list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id); pr_debug("exist : wait_list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id); pr_debug("exist : wait_list->cmdq.param_ptr=%d\n", wait_list->cmdq.param_ptr); spin_unlock_irqrestore(&send_queue_lock, flags); return -EEXIST; } } cmdq->block = 1; wait_list = kzalloc(sizeof(struct rtos_cmdqu_wait_list_t), GFP_KERNEL); if (!wait_list) { spin_unlock_irqrestore(&send_queue_lock, flags); return -ENOMEM; } *((unsigned long long *) &wait_list->cmdq) = *((unsigned long long *) cmdq); wait_list->cmdq.cmd_id = wait_cmd_id; init_waitqueue_head(&wait_list->wq); list_add_tail(&wait_list->list, &rtos_cmdqu_wait_head.list); spin_unlock_irqrestore(&send_queue_lock, flags); /* check the delay ms * if mstime is 65535 (-1), it will be blocked infinite (MAX_JIFFY_OFFSET) */ delaytime = wait_list->cmdq.resv.mstime; if (delaytime == 0xFFFF) delaytime = -1; #ifndef __CV182X__ ret = rtos_cmdqu_send(cmdq); #endif ret = wait_event_interruptible_timeout(wait_list->wq, wait_list->condition != 0, msecs_to_jiffies(delaytime)); spin_lock_irqsave(&send_queue_lock, flags); list_del_init(&wait_list->list); spin_unlock_irqrestore(&send_queue_lock, flags); if (!ret) { ret = -ETIME; kfree(wait_list); pr_err("RTOS_CMDQU_SEND_WAIT timeout\n"); return ret; } pr_debug("RTOS_CMDQU_SEND_WAIT done\n"); pr_debug("list wait_list->cmdq.ip_id=%d wait_list->cmdq.cmd_id=%d wait_list->cmdq.param_ptr=%x\n", wait_list->cmdq.ip_id, wait_list->cmdq.cmd_id, wait_list->cmdq.param_ptr); /* get context from interrupt and return to userspace */ *((unsigned long long *) cmdq) = *((unsigned long long *) &wait_list->cmdq); kfree(wait_list); return 0; } EXPORT_SYMBOL(rtos_cmdqu_send_wait); static long cvi_rtos_cmdqu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct cvi_rtos_cmdqu_device *dev = filp->private_data; struct rtos_cmdqu_wait_list_t *wait_list; struct list_head *pos; long ret = 0; unsigned long flags; cmdqu_t cmdq; pr_debug("%s dev=%p\n", __func__, dev); switch (cmd) { case RTOS_CMDQU_SEND: pr_debug("RTOS_CMDQU_SEND\n"); copy_from_user(&cmdq, (struct cmdqu_t __user *)arg, sizeof(struct cmdqu_t)); ret = rtos_cmdqu_send(&cmdq); break; case RTOS_CMDQU_SEND_WAKEUP: pr_debug("RTOS_CMDQU_SEND_WAKEUP\n"); copy_from_user(&cmdq, (struct cmdqu_t __user *)arg, sizeof(struct cmdqu_t)); pr_debug("cmdq.ip_id=%d cmdq.cmd_id=%d\n", cmdq.ip_id, cmdq.cmd_id); spin_lock_irqsave(&send_queue_lock, flags); list_for_each(pos, &rtos_cmdqu_wait_head.list) { wait_list = list_entry(pos, struct rtos_cmdqu_wait_list_t, list); pr_debug("list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id); pr_debug("list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id); if (cmdq.ip_id == wait_list->cmdq.ip_id && cmdq.cmd_id == wait_list->cmdq.cmd_id && wait_list->cmdq.block == 1) { /* copy data to wait_list and return to user space */ *((unsigned long long *) &wait_list->cmdq) = *((unsigned long long *) &cmdq); pr_debug("wait_list->cmdq.ip_id=%d\n", wait_list->cmdq.ip_id); pr_debug("wait_list->cmdq.cmd_id=%d\n", wait_list->cmdq.cmd_id); pr_debug("wait_list->cmdq.param_ptr=%d\n", wait_list->cmdq.param_ptr); wait_list->condition = 1; wake_up_interruptible(&wait_list->wq); break; } } spin_unlock_irqrestore(&send_queue_lock, flags); pr_debug("RTOS_CMDQU_SEND_WAKEUP done\n"); break; case RTOS_CMDQU_SEND_WAIT: pr_debug("RTOS_CMDQU_SEND_WAIT\n"); ret = copy_from_user(&cmdq, (struct cmdqu_t __user *)arg, sizeof(struct cmdqu_t)); if (ret) { kfree(wait_list); return -EFAULT; } rtos_cmdqu_send_wait(&cmdq, cmdq.cmd_id); ret = copy_to_user((struct cmdqu_t __user *)arg, &cmdq, sizeof(struct cmdqu_t)); kfree(wait_list); break; case RTOS_CMDQU_REQUEST: copy_from_user(&cmdq, (struct cmdqu_t __user *)arg, sizeof(struct cmdqu_t)); ret = request_rtos_irq(cmdq.ip_id, callback_rtos_irq_handler, "RTOS_CMDQU_REQUEST", (void *)((unsigned long)cmdq.param_ptr)); break; case RTOS_CMDQU_REQUEST_FREE: copy_from_user(&cmdq, (struct cmdqu_t __user *)arg, sizeof(struct cmdqu_t)); free_rtos_irq(cmdq.ip_id); break; default: ret = -EFAULT; break; } return ret; } static const struct file_operations rtos_cmdqu_fops = { .owner = THIS_MODULE, .open = cvi_rtos_cmdqu_open, .release = cvi_rtos_cmdqu_release, .unlocked_ioctl = cvi_rtos_cmdqu_ioctl, }; static int _register_dev(struct cvi_rtos_cmdqu_device *ndev) { int rc; ndev->miscdev.minor = MISC_DYNAMIC_MINOR; ndev->miscdev.name = RTOS_CMDQU_DEV_NAME; ndev->miscdev.fops = &rtos_cmdqu_fops; rc = misc_register(&ndev->miscdev); if (rc) { dev_err(ndev->dev, "cvi_rtos_cmdqu: failed to register misc device.\n"); return rc; } return 0; } static int cvi_rtos_cmdqu_register_cb(struct cvi_rtos_cmdqu_device *ndev) { struct base_m_cb_info reg_cb; reg_cb.module_id = E_MODULE_RTOS_CMDQU; reg_cb.dev = (void *)ndev; reg_cb.cb = rtos_cmdqu_cb; return base_reg_module_cb(®_cb); } static int cvi_rtos_cmdqu_rm_cb(void) { return base_rm_module_cb(E_MODULE_RTOS_CMDQU); } static int cvi_rtos_cmdqu_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cvi_rtos_cmdqu_device *ndev; struct resource *res; int ret = 0; int err = -1; pr_info("%s start ---\n", __func__); pr_info("name=%s\n", pdev->name); ndev = devm_kzalloc(&pdev->dev, sizeof(*ndev), GFP_KERNEL); if (!ndev) return -ENOMEM; ndev->dev = dev; ret = _register_dev(ndev); if (ret < 0) { pr_err("regsiter cvi_rtos_cmdqu chrdev error\n"); return ret; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); // void __iomem *regs; // regs = devm_ioremap_resource(dev, res); // if (IS_ERR(regs)) { // ret = PTR_ERR(regs); // goto ERROR_PROVE_DEVICE; // } // printk("regs=%x\n", regs); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) #ifdef _LP64 reg_base = (__u64)devm_ioremap(&pdev->dev, res->start, res->end - res->start); #else reg_base = (__u32)devm_ioremap(&pdev->dev, res->start, res->end - res->start); #endif #else #ifdef _LP64 reg_base = (__u64)ioremap_nocache((unsigned long)res->start, (unsigned long)(res->end - res->start + 1)); #else reg_base = (__u32)ioremap_nocache((unsigned long)res->start, (unsigned long)(res->end - res->start + 1)); #endif #endif pr_info("res-reg: start: 0x%llx, end: 0x%llx, virt-addr(%llx).\n", res->start, res->end, le64_to_cpu(reg_base)); // spinlock ip offset address (0xc0) spinlock_base(reg_base + 0xc0); mailbox_irq = platform_get_irq_byname(pdev, "mailbox"); INIT_LIST_HEAD(&rtos_cmdqu_wait_head.list); /* init cmdqu*/ #ifndef __CV182X__ rtos_cmdqu_init(); #endif platform_set_drvdata(pdev, ndev); err = request_irq(mailbox_irq, rtos_irq_handler, 0, "mailbox", (void *)ndev); if (err) { pr_err("fail to register interrupt handler\n"); return -1; } /* rtos cmdqu register cb */ if (cvi_rtos_cmdqu_register_cb(ndev)) { pr_err("fail to register rtos_cmdqu cb\n"); return -EINVAL; } pr_info("%s DONE\n", __func__); return 0; //ERROR_PROVE_DEVICE: // return err; } static int cvi_rtos_cmdqu_remove(struct platform_device *pdev) { struct cvi_rtos_cmdqu_device *ndev = platform_get_drvdata(pdev); /* rtos cmdqu rm cb */ if (cvi_rtos_cmdqu_rm_cb()) { pr_err("Failed to rm rtos cmdqu cb\n"); } misc_deregister(&ndev->miscdev); platform_set_drvdata(pdev, NULL); /* remove irq handler*/ free_irq(mailbox_irq, ndev); rtos_cmdqu_deinit(); pr_debug("%s DONE\n", __func__); return 0; } static const struct of_device_id cvi_rtos_cmdqu_match[] = { { .compatible = "cvitek,rtos_cmdqu" }, {}, }; static struct platform_driver cvi_rtos_cmdqu_driver = { .probe = cvi_rtos_cmdqu_probe, .remove = cvi_rtos_cmdqu_remove, .driver = { .owner = THIS_MODULE, .name = RTOS_CMDQU_DEV_NAME, .of_match_table = cvi_rtos_cmdqu_match, }, }; struct class *pbase_class; static void cvi_rtos_cmdqu_exit(void) { platform_driver_unregister(&cvi_rtos_cmdqu_driver); class_destroy(pbase_class); cvi_spinlock_uninit(); pr_debug("%s DONE\n", __func__); } static int cvi_rtos_cmdqu_init(void) { int rc; pr_debug("cvi_rtos_cmdqu_init"); pbase_class = class_create(THIS_MODULE, RTOS_CMDQU_DEV_NAME); if (IS_ERR(pbase_class)) { pr_err("create class failed\n"); rc = PTR_ERR(pbase_class); goto cleanup; } platform_driver_register(&cvi_rtos_cmdqu_driver); pr_debug("%s done\n", __func__); cvi_spinlock_init(); return 0; cleanup: cvi_rtos_cmdqu_exit(); return rc; } MODULE_DESCRIPTION("RTOS_CMD_QUEUE"); MODULE_LICENSE("GPL"); module_init(cvi_rtos_cmdqu_init); module_exit(cvi_rtos_cmdqu_exit);