/***************************************************************************** * * Filename: * --------- * ccci_fs.c * * Project: * -------- * YuSu * * Description: * ------------ * MT6516 CCCI RPC * * Author: * ------- * * ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /********************************************************************************* * RPC Daemon section *********************************************************************************/ #if defined(CONFIG_MTK_TC1_FEATURE) struct rpc_stream_msg_t { unsigned length; unsigned index; }; static void rpc_daemon_notify(int md_id, unsigned int buff_index); #define RPC_CCCI_TC1_CMD 0x00003000 #define CCCI_RPC_IOC_MAGIC 'R' #define CCCI_RPC_IOCTL_GET_INDEX _IO(CCCI_RPC_IOC_MAGIC, 1) #define CCCI_RPC_IOCTL_SEND _IOR(CCCI_RPC_IOC_MAGIC, 2, unsigned int) #endif /* CONFIG_MTK_TC1_FEATURE */ struct rpc_ctl_block_t { spinlock_t rpc_fifo_lock; struct RPC_BUF *rpc_buf_vir; unsigned int rpc_buf_phy; unsigned int rpc_buf_len; struct kfifo rpc_fifo; struct work_struct rpc_work; int m_md_id; int rpc_smem_instance_size; int rpc_max_buf_size; int rpc_ch_num; #if defined(CONFIG_MTK_TC1_FEATURE) spinlock_t rpc_daemon_fifo_lock; struct kfifo rpc_daemon_fifo; atomic_t rpcd_response_done; wait_queue_head_t rpcd_send_waitq; wait_queue_head_t rpcd_response_waitq; struct cdev rpc_cdev; dev_t rpc_dev_num; atomic_t md_is_ready; struct MD_CALL_BACK_QUEUE md_status_update_call_back; #ifdef _DEBUG_RPCD atomic_t rpcd_resp_indx; #endif #endif }; static struct rpc_ctl_block_t *rpc_ctl_block[MAX_MD_NUM]; static int get_pkt_info(int md_id, unsigned int *pktnum, struct RPC_PKT *pkt_info, char *pdata) { unsigned int pkt_num = *((unsigned int *)pdata); unsigned int idx = 0; unsigned int i = 0; struct rpc_ctl_block_t *ctl_b = rpc_ctl_block[md_id]; CCCI_RPC_MSG(md_id, "package number = 0x%08X\n", pkt_num); if (pkt_num > IPC_RPC_MAX_ARG_NUM) return -1; idx = sizeof(unsigned int); for (i = 0; i < pkt_num; i++) { pkt_info[i].len = *((unsigned int *)(pdata + idx)); idx += sizeof(unsigned int); pkt_info[i].buf = (pdata + idx); CCCI_RPC_MSG(md_id, "pak[%d]: vir = 0x%08X, len = 0x%08X\n", i, (unsigned int)pkt_info[i].buf, pkt_info[i].len); /* 4 byte alignment */ idx += ((pkt_info[i].len + 3) >> 2) << 2; } if (idx > ctl_b->rpc_max_buf_size) { CCCI_MSG_INF(md_id, "rpc", "over flow, pdata = %p, idx = 0x%08X, max = %p\n", pdata, idx, pdata + ctl_b->rpc_max_buf_size); return -1; } *pktnum = pkt_num; return 0; } static int rpc_write(int md_id, int buf_idx, struct RPC_PKT *pkt_src, unsigned int pkt_num) { int ret = 0; struct ccci_msg_t msg; struct RPC_BUF *rpc_buf_tmp = NULL; unsigned char *pdata = NULL; unsigned int data_len = 0; unsigned int i = 0; unsigned int AlignLength = 0; struct rpc_ctl_block_t *ctl_b = rpc_ctl_block[md_id]; /* rpc_buf_tmp = ctl_b->rpc_buf_vir + buf_idx; */ rpc_buf_tmp = (struct RPC_BUF *) ((unsigned int)(ctl_b->rpc_buf_vir) + ctl_b->rpc_smem_instance_size * buf_idx); rpc_buf_tmp->op_id = IPC_RPC_API_RESP_ID | rpc_buf_tmp->op_id; pdata = rpc_buf_tmp->buf; *((unsigned int *)pdata) = pkt_num; pdata += sizeof(unsigned int); data_len += sizeof(unsigned int); for (i = 0; i < pkt_num; i++) { if ((data_len + 2 * sizeof(unsigned int) + pkt_src[i].len) > ctl_b->rpc_max_buf_size) { CCCI_MSG_INF(md_id, "rpc", "Stream buffer full!!\n"); ret = -CCCI_ERR_LARGE_THAN_BUF_SIZE; goto _Exit; } *((unsigned int *)pdata) = pkt_src[i].len; pdata += sizeof(unsigned int); data_len += sizeof(unsigned int); /* 4 byte aligned */ AlignLength = ((pkt_src[i].len + 3) >> 2) << 2; data_len += AlignLength; if (pdata != pkt_src[i].buf) memcpy(pdata, pkt_src[i].buf, pkt_src[i].len); else CCCI_RPC_MSG(md_id, "same addr, no copy\n"); pdata += AlignLength; } /* msg.data0 = ctl_b->rpc_buf_phy + (sizeof(struct RPC_BUF) * buf_idx); */ msg.data0 = (unsigned int)(ctl_b->rpc_buf_phy) - get_md2_ap_phy_addr_fixed() + ctl_b->rpc_smem_instance_size * buf_idx; msg.data1 = data_len + 4; msg.reserved = buf_idx; msg.channel = CCCI_RPC_TX; CCCI_RPC_MSG(md_id, "Write, %08X, %08X, %08X, %08X\n", msg.data0, msg.data1, msg.channel, msg.reserved); /* wait memory updated*/ mb(); ret = ccci_message_send(md_id, &msg, 1); if (ret != sizeof(struct ccci_msg_t)) { CCCI_MSG_INF(md_id, "rpc", "fail send msg <%d>!!!\n", ret); return ret; } ret = 0; _Exit: return ret; } static void ccci_rpc_work(struct work_struct *work) { int pkt_num = 0; int ret_val = 0; unsigned int buf_idx = 0; struct RPC_PKT pkt[IPC_RPC_MAX_ARG_NUM] = { {0}, }; struct RPC_BUF *rpc_buf_tmp = NULL; unsigned int tmp_data[4]; struct rpc_ctl_block_t *ctl_b = container_of(work, struct rpc_ctl_block_t, rpc_work); int md_id = ctl_b->m_md_id; #ifdef _DEBUG_RPCD int resp_inx; #endif CCCI_RPC_MSG(md_id, "ccci_rpc_work++\n"); if (ctl_b->rpc_buf_vir == NULL) { CCCI_MSG_INF(md_id, "rpc", "invalid rpc_buf_vir!!\n"); return; } while (kfifo_out(&ctl_b->rpc_fifo, &buf_idx, sizeof(unsigned int))) { if (buf_idx < 0 || buf_idx > ctl_b->rpc_ch_num) { CCCI_MSG_INF(md_id, "rpc", "invalid idx %d\n", buf_idx); ret_val = FS_PARAM_ERROR; /* !!!!! Make more meaningful */ pkt[pkt_num].len = sizeof(unsigned int); pkt[pkt_num++].buf = (void *)&ret_val; goto _Next; } pkt_num = 0; memset(pkt, 0x00, sizeof(struct RPC_PKT) * IPC_RPC_MAX_ARG_NUM); /* rpc_buf_tmp = ctl_b->rpc_buf_vir + buf_idx; */ rpc_buf_tmp = (struct RPC_BUF *) ((unsigned int)(ctl_b->rpc_buf_vir) + ctl_b->rpc_smem_instance_size * buf_idx); if (get_pkt_info(md_id, &pkt_num, pkt, rpc_buf_tmp->buf) < 0) { CCCI_MSG_INF(md_id, "rpc", "Fail to get packet info\n"); ret_val = FS_PARAM_ERROR; /* !!!!! Make more meaningful */ pkt[pkt_num].len = sizeof(unsigned int); pkt[pkt_num++].buf = (void *)&ret_val; goto _Next; } #if defined(CONFIG_MTK_TC1_FEATURE) if ((rpc_buf_tmp->op_id & 0x0000F000) == RPC_CCCI_TC1_CMD) { #ifdef _DEBUG_RPCD resp_inx = atomic_read(&ctl_b->rpcd_resp_indx); CCCI_DBG_MSG(ctl_b->m_md_id, "rpc", "op_id=0x%X, Wait RPCD [Resp=%d]\n", rpc_buf_tmp->op_id, resp_inx); #endif rpc_daemon_notify(md_id, buf_idx); wait_event_interruptible(ctl_b->rpcd_response_waitq, atomic_read (&ctl_b->rpcd_response_done) == 1); atomic_set(&ctl_b->rpcd_response_done, 0); #ifdef _DEBUG_RPCD CCCI_DBG_MSG(ctl_b->m_md_id, "rpc", "Done RPCD CMD [Resp=%d]\n", resp_inx); #endif continue; } #endif CCCI_RPC_MSG(md_id, "call ccci_rpc_work_helper()\n"); ccci_rpc_work_helper(md_id, &pkt_num, pkt, rpc_buf_tmp, tmp_data); _Next: if (rpc_write(md_id, buf_idx, pkt, pkt_num) != 0) { CCCI_MSG_INF(md_id, "rpc", "fail to write packet!!\r\n"); return; } } CCCI_RPC_MSG(md_id, "ccci_rpc_work--\n"); } static void ccci_rpc_callback(void *private) { struct logic_channel_info_t *ch_info = (struct logic_channel_info_t *) private; struct ccci_msg_t msg; struct rpc_ctl_block_t *ctl_b = (struct rpc_ctl_block_t *) ch_info->m_owner; int md_id = ctl_b->m_md_id; while (get_logic_ch_data(ch_info, &msg)) { CCCI_RPC_MSG(md_id, "ccci_rpc_callback, %08X, %08X, %08X, %08X\n", msg.data0, msg.data1, msg.channel, msg.reserved); spin_lock_bh(&ctl_b->rpc_fifo_lock); kfifo_in(&ctl_b->rpc_fifo, &msg.reserved, sizeof(unsigned int)); spin_unlock_bh(&ctl_b->rpc_fifo_lock); } schedule_work(&ctl_b->rpc_work); CCCI_RPC_MSG(md_id, "ccci_rpc_callback --\n"); } #if defined(CONFIG_MTK_TC1_FEATURE) void rpc_daemon_notify(int md_id, unsigned int buff_index) { struct rpc_ctl_block_t *ctl_b = rpc_ctl_block[md_id]; #ifdef _DEBUG_RPCD CCCI_RPC_MSG(md_id, "[CCCI_RPC] rpc_daemon_notify(%d) [Resp=%d]\n", buff_index, atomic_read(&ctl_b->rpcd_resp_indx)); #endif spin_lock_bh(&ctl_b->rpc_daemon_fifo_lock); kfifo_in(&ctl_b->rpc_daemon_fifo, &buff_index, sizeof(unsigned int)); spin_unlock_bh(&ctl_b->rpc_daemon_fifo_lock); wake_up_interruptible(&ctl_b->rpcd_send_waitq); } static int rpc_get_share_mem_index(struct file *file) { int ret; struct rpc_ctl_block_t *ctl_b = (struct rpc_ctl_block_t *) file->private_data; if (ctl_b == NULL) { CCCI_ERR_MSG(ctl_b->m_md_id, "rpc_get_share_mem_index:ctl_b == NULL\n"); return -EFAULT; } #ifdef _DEBUG_RPCD atomic_inc(&ctl_b->rpcd_resp_indx); CCCI_RPC_MSG(ctl_b->m_md_id, "get index start [Resp=%d]\n", atomic_read(&ctl_b->rpcd_resp_indx)); #endif if (wait_event_interruptible (ctl_b->rpcd_send_waitq, kfifo_len(&ctl_b->rpc_daemon_fifo) != 0) != 0) { CCCI_MSG_INF(ctl_b->m_md_id, "rpc", "return rpc_get_share_mem_index():ERESTARTSYS\n"); return -ERESTARTSYS; } if (kfifo_out (&ctl_b->rpc_daemon_fifo, (unsigned int *)&ret, sizeof(int)) != sizeof(int)) { CCCI_MSG_INF(ctl_b->m_md_id, "rpc", "Unable to get new request from fifo\n"); return -EFAULT; } #ifdef _DEBUG_RPCD CCCI_RPC_MSG(ctl_b->m_md_id, "get index end [Resp=%d]\n", atomic_read(&ctl_b->rpcd_resp_indx)); #endif return ret; } static void wakeup_rpc_work(struct rpc_ctl_block_t *ctl_b) { CCCI_RPC_MSG(ctl_b->m_md_id, "wakeup rpc_work\n"); atomic_set(&ctl_b->rpcd_response_done, 1); wake_up_interruptible(&ctl_b->rpcd_response_waitq); } static void ccci_rpc_resetfifo(struct rpc_ctl_block_t *ctl_b) { CCCI_MSG("(%d) ccci_rpc_resetfifo\n", ctl_b->m_md_id); spin_lock_bh(&ctl_b->rpc_fifo_lock); kfifo_reset(&ctl_b->rpc_fifo); spin_unlock_bh(&ctl_b->rpc_fifo_lock); spin_lock_bh(&ctl_b->rpc_daemon_fifo_lock); kfifo_reset(&ctl_b->rpc_daemon_fifo); spin_unlock_bh(&ctl_b->rpc_daemon_fifo_lock); } static void rpc_md_ev_callback(struct MD_CALL_BACK_QUEUE *queue, unsigned long data) { struct rpc_ctl_block_t *ctl_b = container_of(queue, struct rpc_ctl_block_t, md_status_update_call_back); CCCI_DBG_MSG(ctl_b->m_md_id, "rpc", "rpc_md_ev_callback++\n"); switch (data) { case CCCI_MD_RESET: CCCI_MSG_INF(ctl_b->m_md_id, "rpc", "MD reset call chain !\n"); atomic_set(&ctl_b->md_is_ready, 0); ccci_rpc_resetfifo(ctl_b); wakeup_rpc_work(ctl_b); break; case CCCI_MD_BOOTUP: atomic_set(&ctl_b->rpcd_response_done, 0); atomic_set(&ctl_b->md_is_ready, 1); CCCI_MSG_INF(ctl_b->m_md_id, "rpc", "MD boot up successfully.\n"); break; } CCCI_DBG_MSG(ctl_b->m_md_id, "rpc", "rpc_md_ev_callback--\n"); } static int rpc_daemon_send_helper(struct file *file, unsigned long arg) { void __user *argp; struct ccci_msg_t msg; struct rpc_stream_msg_t message; int md_id; int ret = 0; struct rpc_ctl_block_t *ctl_b = (struct rpc_ctl_block_t *) file->private_data; /* rpc_ctl_block[0]; */ #ifdef _DEBUG_RPCD CCCI_DBG_MSG(ctl_b->m_md_id, "rpc", "Send RPCD -> MD [Resp=%d]\n", atomic_read(&ctl_b->rpcd_resp_indx)); #endif if (ctl_b == NULL) { ret = -EFAULT; goto _send_return; } md_id = ctl_b->m_md_id; argp = (void __user *)arg; if (atomic_read(&ctl_b->md_is_ready) == 0) { CCCI_MSG_INF(ctl_b->m_md_id, "rpc", "rpc_daemon_send_helper-- by MD_RESET!\n"); ret = -EINTR; goto _send_return; } if (copy_from_user((void *)&message, argp, sizeof(struct rpc_stream_msg_t))) { ret = -EFAULT; goto _send_return; } msg.data0 = (unsigned int)(ctl_b->rpc_buf_phy) - get_md2_ap_phy_addr_fixed() + ctl_b->rpc_smem_instance_size * message.index; msg.data1 = message.length + 4; msg.reserved = message.index; msg.channel = CCCI_RPC_TX; CCCI_RPC_MSG(md_id, "Write, %08X, %08X, %08X, %08X\n", msg.data0, msg.data1, msg.channel, msg.reserved); /* wait memory updated*/ mb(); ret = ccci_message_send(md_id, &msg, 1); if (ret != sizeof(struct ccci_msg_t)) { CCCI_MSG_INF(md_id, "rpc", "fail send msg <%d>!!!\n", ret); goto _send_return; } _send_return: CCCI_DBG_MSG(ctl_b->m_md_id, "rpc", "rpc_daemon_send_helper--(%d)\n", ret); wakeup_rpc_work(ctl_b); return ret; } static int rpc_mmap(struct file *file, struct vm_area_struct *vma) { unsigned long off, start, len; struct rpc_ctl_block_t *ctl_b = (struct rpc_ctl_block_t *) file->private_data; /* rpc_ctl_block[0]; */ if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; off = vma->vm_pgoff << PAGE_SHIFT; start = (unsigned long)ctl_b->rpc_buf_phy; len = PAGE_ALIGN((start & ~PAGE_MASK) + ctl_b->rpc_smem_instance_size); if ((vma->vm_end - vma->vm_start + off) > len) return -EINVAL; off += start & PAGE_MASK; vma->vm_pgoff = off >> PAGE_SHIFT; vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP); vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); return remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot); } static long rpc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret; switch (cmd) { case CCCI_RPC_IOCTL_GET_INDEX: ret = rpc_get_share_mem_index(file); break; case CCCI_RPC_IOCTL_SEND: ret = rpc_daemon_send_helper(file, arg); break; default: ret = -ENOIOCTLCMD; break; } return ret; } static int rpc_open(struct inode *inode, struct file *file) { int major = imajor(inode); int md_id; md_id = get_md_id_by_dev_major(major); file->private_data = (void *)rpc_ctl_block[md_id]; return 0; } static int rpc_release(struct inode *inode, struct file *file) { return 0; } static const struct file_operations rpc_fops = { .owner = THIS_MODULE, .unlocked_ioctl = rpc_ioctl, .open = rpc_open, .mmap = rpc_mmap, .release = rpc_release, }; static int rpc_device_init(struct rpc_ctl_block_t *ctl_b) { int ret; int major, minor; char name[16]; int md_id = ctl_b->m_md_id; CCCI_MSG_INF(md_id, "rpc", "rpc_device_init++\n"); ctl_b->md_status_update_call_back.call = rpc_md_ev_callback; ctl_b->md_status_update_call_back.next = NULL, CCCI_MSG_INF(md_id, "rpc", "register md_db rpc_md_ev_callback\n"); md_register_call_chain(md_id, &ctl_b->md_status_update_call_back); #ifdef _DEBUG_RPCD atomic_set(&ctl_b->rpcd_resp_indx, 0); #endif atomic_set(&ctl_b->md_is_ready, 1); atomic_set(&ctl_b->rpcd_response_done, 0); init_waitqueue_head(&ctl_b->rpcd_response_waitq); init_waitqueue_head(&ctl_b->rpcd_send_waitq); ret = get_dev_id_by_md_id(md_id, "rpc", &major, &minor); if (ret < 0) { CCCI_MSG_INF(md_id, "rpc", "get rpc dev id fail: %d\n", ret); goto _ret; } spin_lock_init(&ctl_b->rpc_daemon_fifo_lock); ret = kfifo_alloc(&ctl_b->rpc_daemon_fifo, sizeof(unsigned int) * 8, GFP_KERNEL); if (ret) { CCCI_MSG_INF(md_id, "rpc", "unable to create daemon fifo\n"); goto _ret; } if (md_id) snprintf(name, 16, "ccci%d_rpc", md_id + 1); else strcpy(name, "ccci_rpc"); ctl_b->rpc_dev_num = MKDEV(major, minor); /* Using FS major, sub id is 1, not 0 */ ret = register_chrdev_region(ctl_b->rpc_dev_num, 1, name); if (ret) { CCCI_MSG_INF(md_id, "rpc", "ccci_rpc: Register character device failed\n"); goto _ret_kfifo_free; } cdev_init(&ctl_b->rpc_cdev, &rpc_fops); ctl_b->rpc_cdev.owner = THIS_MODULE; ctl_b->rpc_cdev.ops = &rpc_fops; ret = cdev_add(&ctl_b->rpc_cdev, ctl_b->rpc_dev_num, 1); if (ret) { CCCI_MSG_INF(md_id, "rpc", "ccci_rpc: Char device add failed\n"); unregister_chrdev_region(ctl_b->rpc_dev_num, 1); goto _ret_kfifo_free; } goto _ret; _ret_kfifo_free: kfifo_free(&ctl_b->rpc_daemon_fifo); _ret: CCCI_MSG_INF(md_id, "rpc", "rpc_device_init--\n"); return ret; } void rpc_device_deinit(int md_id) { struct rpc_ctl_block_t *ctl_b = rpc_ctl_block[md_id]; kfifo_free(&ctl_b->rpc_daemon_fifo); cdev_del(&ctl_b->rpc_cdev); unregister_chrdev_region(ctl_b->rpc_dev_num, 1); } #endif /* CONFIG_MTK_TC1_FEATURE */ int __init ccci_rpc_init(int md_id) { int ret; struct rpc_ctl_block_t *ctl_b; struct rpc_cfg_inf_t rpc_cfg; int rpc_buf_vir, rpc_buf_phy, rpc_buf_len; /* Allocate fs ctrl struct memory */ ctl_b = kmalloc(sizeof(struct rpc_ctl_block_t), GFP_KERNEL); if (ctl_b == NULL) return -CCCI_ERR_GET_MEM_FAIL; memset(ctl_b, 0, sizeof(struct rpc_ctl_block_t)); rpc_ctl_block[md_id] = ctl_b; /* Get rpc config information */ ccci_get_sub_module_cfg(md_id, "rpc", (char *)&rpc_cfg, sizeof(struct rpc_cfg_inf_t)); ccci_rpc_base_req(md_id, &rpc_buf_vir, &rpc_buf_phy, &rpc_buf_len); ctl_b->rpc_buf_vir = (struct RPC_BUF *) rpc_buf_vir; ctl_b->rpc_buf_phy = (unsigned int)rpc_buf_phy; ctl_b->rpc_buf_len = rpc_buf_len; ctl_b->rpc_max_buf_size = rpc_cfg.rpc_max_buf_size; ctl_b->rpc_ch_num = rpc_cfg.rpc_ch_num; ctl_b->rpc_smem_instance_size = sizeof(struct RPC_BUF) + ctl_b->rpc_max_buf_size; /* Note!!!!! we should check cofigure mistake */ /* Init ctl_b */ ctl_b->m_md_id = md_id; spin_lock_init(&ctl_b->rpc_fifo_lock); ret = kfifo_alloc(&ctl_b->rpc_fifo, sizeof(unsigned) * ctl_b->rpc_ch_num, GFP_KERNEL); if (ret < 0) { CCCI_MSG_INF(md_id, "rpc", "Unable to create fifo\n"); goto _KFIFO_ALLOC_FAIL; } INIT_WORK(&ctl_b->rpc_work, ccci_rpc_work); /* modem related channel registration. */ CCCI_RPC_MSG(md_id, "rpc_buf_vir=0x%p, rpc_buf_phy=0x%08X, rpc_buf_len=0x%08X\n", ctl_b->rpc_buf_vir, ctl_b->rpc_buf_phy, ctl_b->rpc_buf_len); register_to_logic_ch(md_id, CCCI_RPC_RX, ccci_rpc_callback, ctl_b); #if defined(CONFIG_MTK_TC1_FEATURE) register_to_logic_ch(md_id, CCCI_RPC_TX, ccci_rpc_callback, ctl_b); ret = rpc_device_init(ctl_b); if (0 != ret) goto _KFIFO_ALLOC_FAIL; #endif return ret; _KFIFO_ALLOC_FAIL: kfree(ctl_b); rpc_ctl_block[md_id] = NULL; return ret; } void __exit ccci_rpc_exit(int md_id) { struct rpc_ctl_block_t *ctl_b; CCCI_RPC_MSG(md_id, "ccci_rpc_exit\n"); ctl_b = rpc_ctl_block[md_id]; if (ctl_b == NULL) return; #if defined(CONFIG_MTK_TC1_FEATURE) rpc_device_deinit(md_id); #endif kfifo_free(&ctl_b->rpc_fifo); un_register_to_logic_ch(md_id, CCCI_RPC_RX); #if defined(CONFIG_MTK_TC1_FEATURE) un_register_to_logic_ch(md_id, CCCI_RPC_TX); #endif kfree(ctl_b); rpc_ctl_block[md_id] = NULL; }