/* IOCTL */ static ssize_t esif_file_ipc( struct file *fp, char __user *buf, size_t count, loff_t *ppos ) { u32 len = 0; struct esif_ipc *ipc_req_ptr = esif_ipc_user_to_kernel( (struct esif_ipc *)buf); struct esif_ipc *ipc_rsp_ptr = NULL; if (count < sizeof(struct esif_ipc)) return -EINVAL; if (NULL == ipc_req_ptr) return -ENOMEM; ESIF_TRACE_DYN_IPC("linux_%s: user %p kernel %p count %d\n", __func__, buf, ipc_req_ptr, (int)count); ipc_rsp_ptr = esif_ipc_process(ipc_req_ptr); if (NULL != ipc_rsp_ptr) { len = esif_ipc_kernel_to_user((struct esif_ipc *)buf, ipc_rsp_ptr); ESIF_TRACE_DYN_IPC("linux_%s: user %p kernel %p count = %d\n", __func__, buf, ipc_rsp_ptr, (int)count); if (ipc_req_ptr != ipc_rsp_ptr) esif_ipc_free(ipc_rsp_ptr); } esif_ccb_free(ipc_req_ptr); return len; }
/* IOCTL Operation */ static long esif_ioctl( struct file *filp_ptr, u_int cmd, u_long arg ) { int rc = -EINVAL; ESIF_TRACE_DYN_IPC("linux_%s: ioctl cmd %x\n", __func__, cmd); switch (cmd) { case ESIF_IOCTL_IPC_NOOP: ESIF_TRACE_DYN_IPC("linux_%s: NOOP cmd=%x\n", __func__, cmd); break; case ESIF_IOCTL_IPC: ESIF_TRACE_DYN_IPC("linux_%s: ESIF_IOCTL_IPC\n", __func__); rc = esif_ipc_ioctl((struct esif_ipc *)arg); break; default: ESIF_TRACE_DYN_IPC("linux_%s: NOT IMPLEMENTED %x\n", __func__, cmd); break; } return rc; }
/* IOCTL */ static int esif_ipc_ioctl(struct esif_ipc *ipc_user_ptr) { int rc = 0; struct esif_ipc *ipc_req_ptr = esif_ipc_user_to_kernel(ipc_user_ptr); struct esif_ipc *ipc_rsp_ptr = NULL; if (NULL == ipc_req_ptr) return -ENOMEM; ESIF_TRACE_DYN_IPC("linux_%s: user %p kernel %p\n", __func__, ipc_user_ptr, ipc_req_ptr); ipc_rsp_ptr = esif_ipc_process(ipc_req_ptr); if (NULL != ipc_rsp_ptr) { esif_ipc_kernel_to_user((struct esif_ipc *)ipc_user_ptr, ipc_rsp_ptr); ESIF_TRACE_DYN_IPC("linux_%s: user %p kernel %p\n", __func__, ipc_user_ptr, ipc_rsp_ptr); if (ipc_req_ptr != ipc_rsp_ptr) esif_ipc_free(ipc_rsp_ptr); } else { rc = -EINVAL; } esif_ccb_free(ipc_req_ptr); return rc; }
/* Translate Kernel IPC To User Space */ static int esif_ipc_kernel_to_user( struct esif_ipc *ipc_user_ptr, struct esif_ipc *ipc_kernel_ptr ) { u32 len = 0; if (NULL == ipc_user_ptr || NULL == ipc_kernel_ptr) return -EINVAL; len = ipc_kernel_ptr->data_len + sizeof(struct esif_ipc); ESIF_TRACE_DYN_IPC( "linux_%s: ipc version=%d, type=%d, len = %d, " "data_len=%d, rc=%d\n", __func__, ipc_kernel_ptr->version, ipc_kernel_ptr->type, (int)len, (int)ipc_kernel_ptr->data_len, ipc_kernel_ptr->return_code); /* TODO Check For Fit When Possible */ if (copy_to_user(ipc_user_ptr, ipc_kernel_ptr, len) > 0) return -ENOMEM; return len; }
/* Translate User Space IPC to Kernel */ static struct esif_ipc *esif_ipc_user_to_kernel(struct esif_ipc *ipc_user_ptr) { int ipc_len = 0; struct esif_ipc ipc_hdr = {0}; struct esif_ipc *ipc_ptr = NULL; if (NULL == ipc_user_ptr) return NULL; /* Copy from user an error indicates how many bytes copied. */ if (copy_from_user(&ipc_hdr, ipc_user_ptr, sizeof(struct esif_ipc)) > 0) { return NULL; } /* Sanity Check */ if (ESIF_IPC_VERSION != ipc_hdr.version) return NULL; ESIF_TRACE_DYN_IPC( "linux_%s: ipc version=%d, type=%d, data_len=%d, rc=%d\n", __func__, ipc_hdr.version, ipc_hdr.type, (int)ipc_hdr.data_len, (int)ipc_hdr.return_code); ipc_len = ipc_hdr.data_len + sizeof(struct esif_ipc); ipc_ptr = esif_ccb_malloc(ipc_len); if (NULL == ipc_ptr) return NULL; /* Copy from user an error indicates how many bytes copied. */ if (copy_from_user(ipc_ptr, ipc_user_ptr, ipc_len) > 0) { esif_ccb_free(ipc_ptr); return NULL; } return ipc_ptr; }
/* Process IPC */ struct esif_ipc *esif_ipc_process( struct esif_ipc *ipc_ptr ) { struct esif_ipc *ipc_ret_ptr = ipc_ptr; struct esif_ipc_command *cmd_ptr = NULL; struct esif_ipc_primitive *prim_ptr = NULL; ESIF_TRACE_DYN_IPC("START ipc %p\n", ipc_ptr); if (NULL == ipc_ptr) goto exit; /* * If we got this far we are guaranteed to have a valid IPC * header now we need to check to see if we hav enough data * for the type specified if so process it if not return to * avoid what would surely result in undesired behavior. */ switch (ipc_ptr->type) { /* Command e.g. Get Participants, Etc. */ case ESIF_IPC_TYPE_COMMAND: ESIF_TRACE_DYN_IPC("COMMAND Received\n"); cmd_ptr = (struct esif_ipc_command *)(ipc_ptr + 1); if ((ipc_ptr->data_len < sizeof(*cmd_ptr)) || (ipc_ptr->data_len < (esif_ipc_command_get_data_len(cmd_ptr) + sizeof(*cmd_ptr)))) ipc_ptr->return_code = ESIF_E_IPC_DATA_INVALID; else esif_execute_ipc_command(cmd_ptr); break; /* Retrieve A Signaled Event Or Check Event Queue */ case ESIF_IPC_TYPE_EVENT: ESIF_TRACE_DYN_IPC("EVENT Received\n"); if (ipc_ptr->data_len < sizeof(struct esif_ipc_event)) ipc_ptr->return_code = ESIF_E_IPC_DATA_INVALID; else ipc_ret_ptr = esif_event_queue_pull(); break; /* Execute Primitive e.g. GET_TEMPERATURE */ case ESIF_IPC_TYPE_PRIMITIVE: ESIF_TRACE_DYN_IPC("PRIMITIVE Received\n"); prim_ptr = (struct esif_ipc_primitive *)(ipc_ptr + 1); if ((ipc_ptr->data_len < sizeof(*prim_ptr)) || (ipc_ptr->data_len < (esif_ipc_primitive_get_data_len(prim_ptr) + sizeof(*prim_ptr)))) ipc_ptr->return_code = ESIF_E_IPC_DATA_INVALID; else esif_execute_ipc_primitive(prim_ptr); break; /* NOOP For Testing */ case ESIF_IPC_TYPE_NOOP: ESIF_TRACE_DYN_IPC("NOOP Received\n"); ipc_ret_ptr = NULL; break; /* Unsupported or Unknown IPC Type Received */ default: ESIF_TRACE_DYN_IPC("Unknown IPC Type Received type=%u\n", ipc_ptr->type); ipc_ptr->return_code = ESIF_E_IPC_DATA_INVALID; break; } ESIF_TRACE_DYN_IPC("FINISH return result: %s(%u)\n", esif_rc_str(ipc_ptr->return_code), ipc_ptr->return_code); exit: return ipc_ret_ptr; }
/* Release */ static int esif_release(struct inode *inode, struct file *filp) { ESIF_TRACE_DYN_IPC("linux_%s: %s Device Closed\n", __func__, IPC_DEVICE); return 0; }