static void kbd_port_events(ipc_callid_t iid, ipc_call_t *icall, void *arg) { /* Ignore parameters, the connection is already opened */ while (true) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); int retval = EOK; if (!IPC_GET_IMETHOD(call)) { /* TODO: Handle hangup */ return; } switch (IPC_GET_IMETHOD(call)) { case ADB_REG_NOTIF: adb_kbd_reg0_data(IPC_GET_ARG1(call)); break; default: retval = ENOENT; } async_answer_0(callid, retval); } }
/** Handle one connection to APIC. * * @param iid Hash of the request that opened the connection. * @param icall Call data of the request that opened the connection. * @param arg Local argument. */ static void apic_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { ipc_callid_t callid; ipc_call_t call; /* * Answer the first IPC_M_CONNECT_ME_TO call. */ async_answer_0(iid, EOK); while (true) { callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) { /* The other side has hung up. */ async_answer_0(callid, EOK); return; } switch (IPC_GET_IMETHOD(call)) { case IRC_ENABLE_INTERRUPT: async_answer_0(callid, apic_enable_irq(IPC_GET_ARG1(call))); break; case IRC_CLEAR_INTERRUPT: /* Noop */ async_answer_0(callid, EOK); break; default: async_answer_0(callid, EINVAL); break; } } }
static void loc_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg) { while (true) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) { /* TODO: Handle hangup */ return; } switch (IPC_GET_IMETHOD(call)) { case LOC_EVENT_CAT_CHANGE: fibril_mutex_lock(&loc_callback_mutex); loc_cat_change_cb_t cb_fun = cat_change_cb; fibril_mutex_unlock(&loc_callback_mutex); async_answer_0(callid, EOK); if (cb_fun != NULL) (*cb_fun)(); break; default: async_answer_0(callid, ENOTSUP); } } }
static void kbdev_callback_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg) { kbdev_t *kbdev; int retval; int type, key; /* Kbdev device structure */ kbdev = arg; while (true) { ipc_call_t call; ipc_callid_t callid; callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) { kbdev_destroy(kbdev); return; } switch (IPC_GET_IMETHOD(call)) { case KBDEV_EVENT: /* Got event from keyboard device */ retval = 0; type = IPC_GET_ARG1(call); key = IPC_GET_ARG2(call); kbd_push_event(kbdev->kbd_dev, type, key); break; default: retval = ENOTSUP; break; } async_answer_0(callid, retval); } }
/** Handle loader connection. * * Receive and carry out commands (of which the last one should be * to execute the loaded program). */ static void ldr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { /* Already have a connection? */ if (connected) { async_answer_0(iid, ELIMIT); return; } connected = true; /* Accept the connection */ async_answer_0(iid, EOK); /* Ignore parameters, the connection is already open */ (void) icall; while (true) { int retval; ipc_call_t call; ipc_callid_t callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) exit(0); switch (IPC_GET_IMETHOD(call)) { case LOADER_GET_TASKID: ldr_get_taskid(callid, &call); continue; case LOADER_SET_CWD: ldr_set_cwd(callid, &call); continue; case LOADER_SET_PATHNAME: ldr_set_pathname(callid, &call); continue; case LOADER_SET_ARGS: ldr_set_args(callid, &call); continue; case LOADER_SET_FILES: ldr_set_files(callid, &call); continue; case LOADER_LOAD: ldr_load(callid, &call); continue; case LOADER_RUN: ldr_run(callid, &call); /* Not reached */ default: retval = EINVAL; break; } async_answer_0(callid, retval); } }
/** Handle data requests. * * @param fun ddf_fun_t function. * @param id callid * @param call IPC request. * */ void default_handler(ddf_fun_t *fun, ipc_callid_t id, ipc_call_t *call) { const sysarg_t method = IPC_GET_IMETHOD(*call); const size_t size = IPC_GET_ARG1(*call); switch (method) { case IPC_CHAR_READ: if (size <= 4 * sizeof(sysarg_t)) { sysarg_t message[4] = {}; i8042_read(fun, (char *) message, size); async_answer_4(id, size, message[0], message[1], message[2], message[3]); } else async_answer_0(id, ELIMIT); break; case IPC_CHAR_WRITE: if (size <= 3 * sizeof(sysarg_t)) { const sysarg_t message[3] = { IPC_GET_ARG2(*call), IPC_GET_ARG3(*call), IPC_GET_ARG4(*call) }; i8042_write(fun, (char *) message, size); async_answer_0(id, size); } else async_answer_0(id, ELIMIT); default: async_answer_0(id, EINVAL); } }
/** Default handler for IPC methods not handled by DDF. * * @param fun Device function handling the call. * @param icallid Call id. * @param icall Call data. * */ static void default_connection_handler(ddf_fun_t *fun, ipc_callid_t icallid, ipc_call_t *icall) { const sysarg_t method = IPC_GET_IMETHOD(*icall); xt_kbd_t *kbd = ddf_dev_data_get(ddf_fun_get_dev(fun)); switch (method) { case KBDEV_SET_IND: { /* * XT keyboards do not support setting mods, * assume AT keyboard with Scan Code Set 1. */ const unsigned mods = IPC_GET_ARG1(*icall); const uint8_t status = 0 | ((mods & KM_CAPS_LOCK) ? LI_CAPS : 0) | ((mods & KM_NUM_LOCK) ? LI_NUM : 0) | ((mods & KM_SCROLL_LOCK) ? LI_SCROLL : 0); uint8_t cmds[] = { KBD_CMD_SET_LEDS, status }; async_exch_t *exch = async_exchange_begin(kbd->parent_sess); const ssize_t size = chardev_write(exch, cmds, sizeof(cmds)); async_exchange_end(exch); async_answer_0(icallid, size < 0 ? size : EOK); break; } /* * This might be ugly but async_callback_receive_start makes no * difference for incorrect call and malloc failure. */ case IPC_M_CONNECT_TO_ME: { async_sess_t *sess = async_callback_receive_start(EXCHANGE_SERIALIZE, icall); /* Probably ENOMEM error, try again. */ if (sess == NULL) { ddf_msg(LVL_WARN, "Failed creating callback session"); async_answer_0(icallid, EAGAIN); break; } if (kbd->client_sess == NULL) { kbd->client_sess = sess; ddf_msg(LVL_DEBUG, "Set client session"); async_answer_0(icallid, EOK); } else { ddf_msg(LVL_ERROR, "Client session already set"); async_answer_0(icallid, ELIMIT); } break; } default: ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method); async_answer_0(icallid, EINVAL); break; } }
/** Character device connection handler */ static void cuda_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { ipc_callid_t callid; ipc_call_t call; sysarg_t method; service_id_t dsid; int dev_addr, i; /* Get the device handle. */ dsid = IPC_GET_ARG1(*icall); /* Determine which disk device is the client connecting to. */ dev_addr = -1; for (i = 0; i < ADB_MAX_ADDR; i++) { if (adb_dev[i].service_id == dsid) dev_addr = i; } if (dev_addr < 0) { async_answer_0(iid, EINVAL); return; } /* Answer the IPC_M_CONNECT_ME_TO call. */ async_answer_0(iid, EOK); while (true) { callid = async_get_call(&call); method = IPC_GET_IMETHOD(call); if (!method) { /* The other side has hung up. */ async_answer_0(callid, EOK); return; } async_sess_t *sess = async_callback_receive_start(EXCHANGE_SERIALIZE, &call); if (sess != NULL) { if (adb_dev[dev_addr].client_sess == NULL) { adb_dev[dev_addr].client_sess = sess; /* * A hack so that we send the data to the session * regardless of which address the device is on. */ for (i = 0; i < ADB_MAX_ADDR; ++i) { if (adb_dev[i].service_id == dsid) adb_dev[i].client_sess = sess; } async_answer_0(callid, EOK); } else async_answer_0(callid, ELIMIT); } else async_answer_0(callid, EINVAL); } }
static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { async_answer_0(iid, EOK); while (true) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) { if (client_sess != NULL) { async_hangup(client_sess); client_sess = NULL; } async_answer_0(callid, EOK); return; } async_sess_t *sess = async_callback_receive_start(EXCHANGE_SERIALIZE, &call); if (sess != NULL) { if (client_sess == NULL) { client_sess = sess; async_answer_0(callid, EOK); } else async_answer_0(callid, ELIMIT); } else { switch (IPC_GET_IMETHOD(call)) { case INPUT_YIELD: kbd_devs_yield(); async_answer_0(callid, EOK); break; case INPUT_RECLAIM: kbd_devs_reclaim(); async_answer_0(callid, EOK); break; default: async_answer_0(callid, EINVAL); } } } }
static void inet_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg) { while (true) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) { /* TODO: Handle hangup */ return; } switch (IPC_GET_IMETHOD(call)) { case INET_EV_RECV: inet_ev_recv(callid, &call); break; default: async_answer_0(callid, ENOTSUP); } } }
static void bd_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg) { bd_t *bd = (bd_t *)arg; (void)bd; while (true) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) { /* TODO: Handle hangup */ return; } switch (IPC_GET_IMETHOD(call)) { default: async_answer_0(callid, ENOTSUP); } } }
/** * Recording callback. Writes recorded data. * @param iid IPC call id. * @param icall Poitner to IPC call structure. * @param arg Argument. Poitner to recording helper structure. */ static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void* arg) { async_answer_0(iid, EOK); record_t *rec = arg; const size_t buffer_part = rec->buffer.size / BUFFER_PARTS; bool record = true; while (record) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); switch(IPC_GET_IMETHOD(call)) { case PCM_EVENT_CAPTURE_TERMINATED: printf("Recording terminated\n"); record = false; break; case PCM_EVENT_FRAMES_CAPTURED: printf("%" PRIun " frames\n", IPC_GET_ARG1(call)); break; default: printf("Unknown event %" PRIun ".\n", IPC_GET_IMETHOD(call)); async_answer_0(callid, ENOTSUP); continue; } if (!record) { async_answer_0(callid, EOK); break; } /* Write directly from device buffer to file */ const size_t bytes = fwrite(rec->buffer.position, sizeof(uint8_t), buffer_part, rec->file); printf("%zu ", bytes); rec->buffer.position += buffer_part; if (rec->buffer.position >= (rec->buffer.base + rec->buffer.size)) rec->buffer.position = rec->buffer.base; async_answer_0(callid, EOK); } }
/** * Accept reads and pull data from the stream. * @param stream target stream, will pull data from there. */ static void hound_server_write_data(void *stream) { ipc_callid_t callid; ipc_call_t call; size_t size = 0; int ret_answer = EOK; /* accept data read and drain */ while (async_data_read_receive_call(&callid, &call, &size) || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) { /* drain does not make much sense but it is allowed */ if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) { int ret = ENOTSUP; if (server_iface->drain_stream) ret = server_iface->drain_stream(stream); async_answer_0(callid, ret); continue; } /* there was an error last time */ if (ret_answer != EOK) { async_answer_0(callid, ret_answer); continue; } char *buffer = malloc(size); if (!buffer) { async_answer_0(callid, ENOMEM); continue; } int ret = server_iface->stream_data_read(stream, buffer, size); if (ret == EOK) { ret_answer = async_data_read_finalize(callid, buffer, size); } } const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT ? EOK : EINVAL; async_answer_0(callid, ret); }
/** Forward a received call to another destination - slow version. * * This function is the slow verision of the sys_ipc_forward_fast interface. * It can copy all five new arguments and the new interface and method from * the userspace. It naturally extends the functionality of the fast version. * For system methods, it additionally stores the new value of arg3 to ARG4. * For non-system methods, it additionally stores the new value of arg3, arg4 * and arg5, respectively, to ARG3, ARG4 and ARG5, respectively. * * @param callid Hash of the call to forward. * @param phoneid Phone handle to use for forwarding. * @param data Userspace address of the new IPC data. * @param mode Flags that specify mode of the forward operation. * * @return 0 on succes, otherwise an error code. * */ sysarg_t sys_ipc_forward_slow(sysarg_t callid, sysarg_t phoneid, ipc_data_t *data, unsigned int mode) { ipc_data_t newdata; int rc = copy_from_uspace(&newdata.args, &data->args, sizeof(newdata.args)); if (rc != 0) return (sysarg_t) rc; return sys_ipc_forward_common(callid, phoneid, IPC_GET_IMETHOD(newdata), IPC_GET_ARG1(newdata), IPC_GET_ARG2(newdata), IPC_GET_ARG3(newdata), IPC_GET_ARG4(newdata), IPC_GET_ARG5(newdata), mode, true); }
static void notification_received(ipc_callid_t callid, ipc_call_t *call) { switch (IPC_GET_IMETHOD(*call)) { case VFS_TASK_STATE_CHANGE: if (IPC_GET_ARG1(*call) == VFS_PASS_HANDLE) vfs_pass_handle( (task_id_t) MERGE_LOUP32(IPC_GET_ARG4(*call), IPC_GET_ARG5(*call)), call->in_task_id, (int) IPC_GET_ARG2(*call)); break; default: break; } }
/** Decide if the caller (e.g. ipc_answer()) should save the old call contents * for answer_preprocess(). * * @param call Call structure to be decided. * * @return true if the old call contents should be saved. * */ static inline bool answer_need_old(call_t *call) { switch (IPC_GET_IMETHOD(call->data)) { case IPC_M_CONNECTION_CLONE: case IPC_M_CLONE_ESTABLISH: case IPC_M_CONNECT_TO_ME: case IPC_M_CONNECT_ME_TO: case IPC_M_SHARE_OUT: case IPC_M_SHARE_IN: case IPC_M_DATA_WRITE: case IPC_M_DATA_READ: case IPC_M_STATE_CHANGE_AUTHORIZE: return true; default: return false; } }
/** Default handler for IPC methods not handled by DDF. * * @param fun Device function handling the call. * @param icallid Call id. * @param icall Call data. * */ static void default_connection_handler(ddf_fun_t *fun, ipc_callid_t icallid, ipc_call_t *icall) { const sysarg_t method = IPC_GET_IMETHOD(*icall); at_kbd_t *kbd = ddf_dev_data_get(ddf_fun_get_dev(fun)); switch (method) { case KBDEV_SET_IND: { async_answer_0(icallid, ENOTSUP); break; } /* * This might be ugly but async_callback_receive_start makes no * difference for incorrect call and malloc failure. */ case IPC_M_CONNECT_TO_ME: { async_sess_t *sess = async_callback_receive_start(EXCHANGE_SERIALIZE, icall); /* Probably ENOMEM error, try again. */ if (sess == NULL) { ddf_msg(LVL_WARN, "Failed creating callback session"); async_answer_0(icallid, EAGAIN); break; } if (kbd->client_sess == NULL) { kbd->client_sess = sess; ddf_msg(LVL_DEBUG, "Set client session"); async_answer_0(icallid, EOK); } else { ddf_msg(LVL_ERROR, "Client session already set"); async_answer_0(icallid, ELIMIT); } break; } default: ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method); async_answer_0(icallid, EINVAL); break; } }
/** Main IPC call handling from virtual host controller. * * @param iid Caller identification * @param icall Initial incoming call * @param arg Local argument */ static void callback_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { assert(DEV != NULL); async_answer_0(iid, EOK); while (true) { ipc_callid_t callid; ipc_call_t call; callid = async_get_call(&call); bool processed = usbvirt_ipc_handle_call(DEV, callid, &call); if (!processed) { if (!IPC_GET_IMETHOD(call)) { async_answer_0(callid, EOK); return; } else async_answer_0(callid, EINVAL); } } }
/** Handle incoming IPC call for virtual USB device. * * @param dev Target USB device. * @param callid Caller id. * @param call Incoming call. * @return Whether the call was handled. */ bool usbvirt_ipc_handle_call(usbvirt_device_t *dev, ipc_callid_t callid, ipc_call_t *call) { switch (IPC_GET_IMETHOD(*call)) { case IPC_M_USBVIRT_GET_NAME: ipc_get_name(dev, callid, call); break; case IPC_M_USBVIRT_CONTROL_READ: ipc_control_read(dev, callid, call); break; case IPC_M_USBVIRT_CONTROL_WRITE: ipc_control_write(dev, callid, call); break; case IPC_M_USBVIRT_INTERRUPT_IN: ipc_data_in(dev, USB_TRANSFER_INTERRUPT, callid, call); break; case IPC_M_USBVIRT_BULK_IN: ipc_data_in(dev, USB_TRANSFER_BULK, callid, call); break; case IPC_M_USBVIRT_INTERRUPT_OUT: ipc_data_out(dev, USB_TRANSFER_INTERRUPT, callid, call); break; case IPC_M_USBVIRT_BULK_OUT: ipc_data_out(dev, USB_TRANSFER_BULK, callid, call); break; default: return false; } return true; }
static void mouse_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { async_answer_0(iid, EOK); async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE); fibril_mutex_lock(&client_mutex); if (client_sess == NULL) { client_sess = sess; } fibril_mutex_unlock(&client_mutex); while (true) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) break; async_answer_0(callid, ENOTSUP); } }
/** Forward a received call to another destination * * Common code for both the fast and the slow version. * * @param callid Hash of the call to forward. * @param phoneid Phone handle to use for forwarding. * @param imethod New interface and method to use for the forwarded call. * @param arg1 New value of the first argument for the forwarded call. * @param arg2 New value of the second argument for the forwarded call. * @param arg3 New value of the third argument for the forwarded call. * @param arg4 New value of the fourth argument for the forwarded call. * @param arg5 New value of the fifth argument for the forwarded call. * @param mode Flags that specify mode of the forward operation. * @param slow If true, arg3, arg4 and arg5 are considered. Otherwise * the function considers only the fast version arguments: * i.e. arg1 and arg2. * * @return 0 on succes, otherwise an error code. * * Warning: Make sure that ARG5 is not rewritten for certain system IPC * */ static sysarg_t sys_ipc_forward_common(sysarg_t callid, sysarg_t phoneid, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, unsigned int mode, bool slow) { call_t *call = get_call(callid); phone_t *phone; bool need_old = answer_need_old(call); bool after_forward = false; ipc_data_t old; int rc; if (!call) return ENOENT; if (need_old) old = call->data; if (phone_get(phoneid, &phone) != EOK) { rc = ENOENT; goto error; } if (!method_is_forwardable(IPC_GET_IMETHOD(call->data))) { rc = EPERM; goto error; } call->flags |= IPC_CALL_FORWARDED; /* * User space is not allowed to change interface and method of system * methods on forward, allow changing ARG1, ARG2, ARG3 and ARG4 by * means of imethod, arg1, arg2 and arg3. * If the interface and method is immutable, don't change anything. */ if (!method_is_immutable(IPC_GET_IMETHOD(call->data))) { if (method_is_system(IPC_GET_IMETHOD(call->data))) { if (IPC_GET_IMETHOD(call->data) == IPC_M_CONNECT_TO_ME) phone_dealloc(IPC_GET_ARG5(call->data)); IPC_SET_ARG1(call->data, imethod); IPC_SET_ARG2(call->data, arg1); IPC_SET_ARG3(call->data, arg2); if (slow) IPC_SET_ARG4(call->data, arg3); /* * For system methods we deliberately don't * overwrite ARG5. */ } else { IPC_SET_IMETHOD(call->data, imethod); IPC_SET_ARG1(call->data, arg1); IPC_SET_ARG2(call->data, arg2); if (slow) { IPC_SET_ARG3(call->data, arg3); IPC_SET_ARG4(call->data, arg4); IPC_SET_ARG5(call->data, arg5); } } } rc = ipc_forward(call, phone, &TASK->answerbox, mode); if (rc != EOK) { after_forward = true; goto error; } return EOK; error: IPC_SET_RETVAL(call->data, EFORWARD); (void) answer_preprocess(call, need_old ? &old : NULL); if (after_forward) _ipc_answer_free_call(call, false); else ipc_answer(&TASK->answerbox, call); return rc; }
/** VFS_REGISTER protocol function. * * @param rid Hash of the call with the request. * @param request Call structure with the request. * */ void vfs_register(ipc_callid_t rid, ipc_call_t *request) { dprintf("Processing VFS_REGISTER request received from %p.\n", request->in_phone_hash); vfs_info_t *vfs_info; int rc = async_data_write_accept((void **) &vfs_info, false, sizeof(vfs_info_t), sizeof(vfs_info_t), 0, NULL); if (rc != EOK) { dprintf("Failed to deliver the VFS info into our AS, rc=%d.\n", rc); async_answer_0(rid, rc); return; } /* * Allocate and initialize a buffer for the fs_info structure. */ fs_info_t *fs_info = (fs_info_t *) malloc(sizeof(fs_info_t)); if (!fs_info) { dprintf("Could not allocate memory for FS info.\n"); async_answer_0(rid, ENOMEM); return; } link_initialize(&fs_info->fs_link); fs_info->vfs_info = *vfs_info; free(vfs_info); dprintf("VFS info delivered.\n"); if (!vfs_info_sane(&fs_info->vfs_info)) { free(fs_info); async_answer_0(rid, EINVAL); return; } fibril_mutex_lock(&fs_list_lock); /* * Check for duplicit registrations. */ if (fs_name_to_handle(fs_info->vfs_info.instance, fs_info->vfs_info.name, false)) { /* * We already register a fs like this. */ dprintf("FS is already registered.\n"); fibril_mutex_unlock(&fs_list_lock); free(fs_info); async_answer_0(rid, EEXISTS); return; } /* * Add fs_info to the list of registered FS's. */ dprintf("Inserting FS into the list of registered file systems.\n"); list_append(&fs_info->fs_link, &fs_list); /* * Now we want the client to send us the IPC_M_CONNECT_TO_ME call so * that a callback connection is created and we have a phone through * which to forward VFS requests to it. */ fs_info->sess = async_callback_receive(EXCHANGE_PARALLEL); if (!fs_info->sess) { dprintf("Callback connection expected\n"); list_remove(&fs_info->fs_link); fibril_mutex_unlock(&fs_list_lock); free(fs_info); async_answer_0(rid, EINVAL); return; } dprintf("Callback connection to FS created.\n"); /* * The client will want us to send him the address space area with PLB. */ size_t size; ipc_callid_t callid; if (!async_share_in_receive(&callid, &size)) { dprintf("Unexpected call, method = %d\n", IPC_GET_IMETHOD(call)); list_remove(&fs_info->fs_link); fibril_mutex_unlock(&fs_list_lock); async_hangup(fs_info->sess); free(fs_info); async_answer_0(callid, EINVAL); async_answer_0(rid, EINVAL); return; } /* * We can only send the client address space area PLB_SIZE bytes long. */ if (size != PLB_SIZE) { dprintf("Client suggests wrong size of PFB, size = %d\n", size); list_remove(&fs_info->fs_link); fibril_mutex_unlock(&fs_list_lock); async_hangup(fs_info->sess); free(fs_info); async_answer_0(callid, EINVAL); async_answer_0(rid, EINVAL); return; } /* * Commit to read-only sharing the PLB with the client. */ (void) async_share_in_finalize(callid, plb, AS_AREA_READ | AS_AREA_CACHEABLE); dprintf("Sharing PLB.\n"); /* * That was it. The FS has been registered. * In reply to the VFS_REGISTER request, we assign the client file * system a global file system handle. */ fs_info->fs_handle = (fs_handle_t) atomic_postinc(&fs_handle_next); async_answer_1(rid, EOK, (sysarg_t) fs_info->fs_handle); fibril_condvar_broadcast(&fs_list_cv); fibril_mutex_unlock(&fs_list_lock); dprintf("\"%.*s\" filesystem successfully registered, handle=%d.\n", FS_NAME_MAXLEN, fs_info->vfs_info.name, fs_info->fs_handle); }
static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { bool cont = true; /* * The connection was opened via the IPC_CONNECT_ME_TO call. * This call needs to be answered. */ async_answer_0(iid, EOK); while (cont) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) break; switch (IPC_GET_IMETHOD(call)) { case VFS_IN_REGISTER: vfs_register(callid, &call); cont = false; break; case VFS_IN_MOUNT: vfs_mount(callid, &call); break; case VFS_IN_UNMOUNT: vfs_unmount(callid, &call); break; case VFS_IN_OPEN: vfs_open(callid, &call); break; case VFS_IN_CLOSE: vfs_close(callid, &call); break; case VFS_IN_READ: vfs_read(callid, &call); break; case VFS_IN_WRITE: vfs_write(callid, &call); break; case VFS_IN_SEEK: vfs_seek(callid, &call); break; case VFS_IN_TRUNCATE: vfs_truncate(callid, &call); break; case VFS_IN_FSTAT: vfs_fstat(callid, &call); break; case VFS_IN_STAT: vfs_stat(callid, &call); break; case VFS_IN_MKDIR: vfs_mkdir(callid, &call); break; case VFS_IN_UNLINK: vfs_unlink(callid, &call); break; case VFS_IN_RENAME: vfs_rename(callid, &call); break; case VFS_IN_SYNC: vfs_sync(callid, &call); break; case VFS_IN_DUP: vfs_dup(callid, &call); break; case VFS_IN_WAIT_HANDLE: vfs_wait_handle(callid, &call); break; case VFS_IN_MTAB_GET: vfs_get_mtab(callid, &call); break; default: async_answer_0(callid, ENOTSUP); break; } } /* * Open files for this client will be cleaned up when its last * connection fibril terminates. */ }
/** * Server side implementation of the hound protocol. IPC connection handler. * @param iid initial call id * @param icall pointer to initial call structure. * @param arg (unused) */ void hound_connection_handler(ipc_callid_t iid, ipc_call_t *icall, void *arg) { /* Accept connection if there is a valid iface*/ if (server_iface) { async_answer_0(iid, EOK); } else { async_answer_0(iid, ENOTSUP); return; } while (1) { ipc_call_t call; ipc_callid_t callid = async_get_call(&call); switch (IPC_GET_IMETHOD(call)) { case IPC_M_HOUND_CONTEXT_REGISTER: { /* check interface functions */ if (!server_iface || !server_iface->add_context) { async_answer_0(callid, ENOTSUP); break; } bool record = IPC_GET_ARG1(call); void *name; /* Get context name */ int ret = async_data_write_accept(&name, true, 0, 0, 0, 0); if (ret != EOK) { async_answer_0(callid, ret); break; } hound_context_id_t id = 0; ret = server_iface->add_context(server_iface->server, &id, name, record); /** new context should create a copy */ free(name); if (ret != EOK) { async_answer_0(callid, ret); } else { async_answer_1(callid, EOK, id); } break; } case IPC_M_HOUND_CONTEXT_UNREGISTER: { /* check interface functions */ if (!server_iface || !server_iface->rem_context) { async_answer_0(callid, ENOTSUP); break; } /* get id, 1st param */ hound_context_id_t id = IPC_GET_ARG1(call); const int ret = server_iface->rem_context(server_iface->server, id); async_answer_0(callid, ret); break; } case IPC_M_HOUND_GET_LIST: { /* check interface functions */ if (!server_iface || !server_iface->get_list) { async_answer_0(callid, ENOTSUP); break; } const char **list = NULL; const int flags = IPC_GET_ARG1(call); size_t count = IPC_GET_ARG2(call); const bool conn = IPC_GET_ARG3(call); char *conn_name = NULL; int ret = EOK; /* get connected actor name if provided */ if (conn) ret = async_data_write_accept( (void**)&conn_name, true, 0, 0, 0, 0); if (ret == EOK) ret = server_iface->get_list( server_iface->server, &list, &count, conn_name, flags); free(conn_name); /* Alloc string sizes array */ size_t *sizes = NULL; if (count) sizes = calloc(count, sizeof(size_t)); if (count && !sizes) ret = ENOMEM; async_answer_1(callid, ret, count); /* We are done */ if (count == 0 || ret != EOK) break; /* Prepare sizes table */ for (unsigned i = 0; i < count; ++i) sizes[i] = str_size(list[i]); /* Send sizes table */ ipc_callid_t id; if (async_data_read_receive(&id, NULL)) { ret = async_data_read_finalize(id, sizes, count * sizeof(size_t)); } free(sizes); /* Proceed to send names */ for (unsigned i = 0; i < count; ++i) { size_t size = str_size(list[i]); ipc_callid_t id; if (ret == EOK && async_data_read_receive(&id, NULL)) { ret = async_data_read_finalize(id, list[i], size); } free(list[i]); } free(list); break; } case IPC_M_HOUND_CONNECT: { /* check interface functions */ if (!server_iface || !server_iface->connect) { async_answer_0(callid, ENOTSUP); break; } void *source = NULL; void *sink = NULL; /* read source name */ int ret = async_data_write_accept(&source, true, 0, 0, 0, 0); /* read sink name */ if (ret == EOK) ret = async_data_write_accept(&sink, true, 0, 0, 0, 0); if (ret == EOK) ret = server_iface->connect( server_iface->server, source, sink); free(source); free(sink); async_answer_0(callid, ret); break; } case IPC_M_HOUND_DISCONNECT: { /* check interface functions */ if (!server_iface || !server_iface->disconnect) { async_answer_0(callid, ENOTSUP); break; } void *source = NULL; void *sink = NULL; /* read source name */ int ret = async_data_write_accept(&source, true, 0, 0, 0, 0); /*read sink name */ if (ret == EOK) ret = async_data_write_accept(&sink, true, 0, 0, 0, 0); if (ret == EOK) ret = server_iface->connect( server_iface->server, source, sink); free(source); free(sink); async_answer_0(callid, ret); break; } case IPC_M_HOUND_STREAM_ENTER: { /* check interface functions */ if (!server_iface || !server_iface->is_record_context || !server_iface->add_stream || !server_iface->rem_stream) { async_answer_0(callid, ENOTSUP); break; } /* get parameters */ hound_context_id_t id = IPC_GET_ARG1(call); const int flags = IPC_GET_ARG2(call); const format_convert_t c = {.arg = IPC_GET_ARG3(call)}; const pcm_format_t f = { .sampling_rate = c.f.rate * 100, .channels = c.f.channels, .sample_format = c.f.format, }; size_t bsize = IPC_GET_ARG4(call); void *stream; int ret = server_iface->add_stream(server_iface->server, id, flags, f, bsize, &stream); if (ret != EOK) { async_answer_0(callid, ret); break; } const bool rec = server_iface->is_record_context( server_iface->server, id); if (rec) { if(server_iface->stream_data_read) { async_answer_0(callid, EOK); /* start answering read calls */ hound_server_write_data(stream); server_iface->rem_stream( server_iface->server, stream); } else { async_answer_0(callid, ENOTSUP); } } else { if (server_iface->stream_data_write) { async_answer_0(callid, EOK); /* accept write calls */ hound_server_read_data(stream); server_iface->rem_stream( server_iface->server, stream); } else { async_answer_0(callid, ENOTSUP); } } break; } case IPC_M_HOUND_STREAM_EXIT: case IPC_M_HOUND_STREAM_DRAIN: /* Stream exit/drain is only allowed in stream context*/ async_answer_0(callid, EINVAL); break; default: async_answer_0(callid, ENOTSUP); return; } } } /** * Read data and push it to the stream. * @param stream target stream, will push data there. */ static void hound_server_read_data(void *stream) { ipc_callid_t callid; ipc_call_t call; size_t size = 0; int ret_answer = EOK; /* accept data write or drain */ while (async_data_write_receive_call(&callid, &call, &size) || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) { /* check drain first */ if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) { int ret = ENOTSUP; if (server_iface->drain_stream) ret = server_iface->drain_stream(stream); async_answer_0(callid, ret); continue; } /* there was an error last time */ if (ret_answer != EOK) { async_answer_0(callid, ret_answer); continue; } char *buffer = malloc(size); if (!buffer) { async_answer_0(callid, ENOMEM); continue; } const int ret = async_data_write_finalize(callid, buffer, size); if (ret == EOK) { /* push data to stream */ ret_answer = server_iface->stream_data_write( stream, buffer, size); } } const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT ? EOK : EINVAL; async_answer_0(callid, ret); }
static void tcp_sock_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { ipc_callid_t callid; ipc_call_t call; tcp_client_t client; /* Accept the connection */ async_answer_0(iid, EOK); client.sess = async_callback_receive(EXCHANGE_SERIALIZE); socket_cores_initialize(&client.sockets); while (true) { callid = async_get_call(&call); if (!IPC_GET_IMETHOD(call)) break; log_msg(LVL_DEBUG, "tcp_sock_connection: METHOD=%d\n", (int)IPC_GET_IMETHOD(call)); switch (IPC_GET_IMETHOD(call)) { case NET_SOCKET: tcp_sock_socket(&client, callid, call); break; case NET_SOCKET_BIND: tcp_sock_bind(&client, callid, call); break; case NET_SOCKET_LISTEN: tcp_sock_listen(&client, callid, call); break; case NET_SOCKET_CONNECT: tcp_sock_connect(&client, callid, call); break; case NET_SOCKET_ACCEPT: tcp_sock_accept(&client, callid, call); break; case NET_SOCKET_SEND: tcp_sock_send(&client, callid, call); break; case NET_SOCKET_SENDTO: tcp_sock_sendto(&client, callid, call); break; case NET_SOCKET_RECV: case NET_SOCKET_RECVFROM: tcp_sock_recvfrom(&client, callid, call); break; case NET_SOCKET_CLOSE: tcp_sock_close(&client, callid, call); break; case NET_SOCKET_GETSOCKOPT: tcp_sock_getsockopt(&client, callid, call); break; case NET_SOCKET_SETSOCKOPT: tcp_sock_setsockopt(&client, callid, call); break; default: async_answer_0(callid, ENOTSUP); break; } } /* Clean up */ log_msg(LVL_DEBUG, "tcp_sock_connection: Clean up"); async_hangup(client.sess); socket_cores_release(NULL, &client.sockets, &gsock, tcp_free_sock_data); }
static void tcp_sock_recvfrom(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call) { int socket_id; int flags; size_t addr_length, length; socket_core_t *sock_core; tcp_sockdata_t *socket; ipc_call_t answer; ipc_callid_t rcallid; size_t data_len; struct sockaddr_in addr; tcp_sock_t *rsock; int rc; log_msg(LVL_DEBUG, "%p: tcp_sock_recv[from]()", client); socket_id = SOCKET_GET_SOCKET_ID(call); flags = SOCKET_GET_FLAGS(call); sock_core = socket_cores_find(&client->sockets, socket_id); if (sock_core == NULL) { async_answer_0(callid, ENOTSOCK); return; } socket = (tcp_sockdata_t *)sock_core->specific_data; fibril_mutex_lock(&socket->lock); if (socket->conn == NULL) { fibril_mutex_unlock(&socket->lock); async_answer_0(callid, ENOTCONN); return; } (void)flags; log_msg(LVL_DEBUG, "tcp_sock_recvfrom(): lock recv_buffer_lock"); fibril_mutex_lock(&socket->recv_buffer_lock); while (socket->recv_buffer_used == 0 && socket->recv_error == TCP_EOK) { log_msg(LVL_DEBUG, "wait for recv_buffer_cv + recv_buffer_used != 0"); fibril_condvar_wait(&socket->recv_buffer_cv, &socket->recv_buffer_lock); } log_msg(LVL_DEBUG, "Got data in sock recv_buffer"); data_len = socket->recv_buffer_used; rc = socket->recv_error; switch (socket->recv_error) { case TCP_EOK: rc = EOK; break; case TCP_ENOTEXIST: case TCP_ECLOSING: rc = ENOTCONN; break; case TCP_ERESET: rc = ECONNABORTED; break; default: assert(false); } log_msg(LVL_DEBUG, "**** recv result -> %d", rc); if (rc != EOK) { fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); async_answer_0(callid, rc); return; } if (IPC_GET_IMETHOD(call) == NET_SOCKET_RECVFROM) { /* Fill addr */ rsock = &socket->conn->ident.foreign; addr.sin_family = AF_INET; addr.sin_addr.s_addr = host2uint32_t_be(rsock->addr.ipv4); addr.sin_port = host2uint16_t_be(rsock->port); log_msg(LVL_DEBUG, "addr read receive"); if (!async_data_read_receive(&rcallid, &addr_length)) { fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); async_answer_0(callid, EINVAL); return; } if (addr_length > sizeof(addr)) addr_length = sizeof(addr); log_msg(LVL_DEBUG, "addr read finalize"); rc = async_data_read_finalize(rcallid, &addr, addr_length); if (rc != EOK) { fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); async_answer_0(callid, EINVAL); return; } } log_msg(LVL_DEBUG, "data read receive"); if (!async_data_read_receive(&rcallid, &length)) { fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); async_answer_0(callid, EINVAL); return; } if (length > data_len) length = data_len; log_msg(LVL_DEBUG, "data read finalize"); rc = async_data_read_finalize(rcallid, socket->recv_buffer, length); socket->recv_buffer_used -= length; log_msg(LVL_DEBUG, "tcp_sock_recvfrom: %zu left in buffer", socket->recv_buffer_used); if (socket->recv_buffer_used > 0) { memmove(socket->recv_buffer, socket->recv_buffer + length, socket->recv_buffer_used); tcp_sock_notify_data(socket->sock_core); } fibril_condvar_broadcast(&socket->recv_buffer_cv); if (length < data_len && rc == EOK) rc = EOVERFLOW; SOCKET_SET_READ_DATA_LENGTH(answer, length); async_answer_1(callid, EOK, IPC_GET_ARG1(answer)); fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); }
list_foreach(*list, cur) { call_t *call = list_get_instance(cur, call_t, ab_link); #ifdef __32_BITS__ printf("%10p ", call); #endif #ifdef __64_BITS__ printf("%18p ", call); #endif spinlock_lock(&call->forget_lock); printf("%-8" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun " %-6" PRIun " %-7x", IPC_GET_IMETHOD(call->data), IPC_GET_ARG1(call->data), IPC_GET_ARG2(call->data), IPC_GET_ARG3(call->data), IPC_GET_ARG4(call->data), IPC_GET_ARG5(call->data), call->flags); if (call->forget) { printf(" ? (call forgotten)\n"); } else { printf(" %" PRIu64 " (%s)\n", call->sender->taskid, call->sender->name); } spinlock_unlock(&call->forget_lock); } }
static void udp_sock_recvfrom(udp_client_t *client, ipc_callid_t callid, ipc_call_t call) { int socket_id; int flags; size_t addr_length, length; socket_core_t *sock_core; udp_sockdata_t *socket; ipc_call_t answer; ipc_callid_t rcallid; size_t data_len; udp_error_t urc; udp_sock_t rsock; struct sockaddr_in addr; int rc; log_msg(LVL_DEBUG, "%p: udp_sock_recv[from]()", client); socket_id = SOCKET_GET_SOCKET_ID(call); flags = SOCKET_GET_FLAGS(call); sock_core = socket_cores_find(&client->sockets, socket_id); if (sock_core == NULL) { async_answer_0(callid, ENOTSOCK); return; } socket = (udp_sockdata_t *)sock_core->specific_data; fibril_mutex_lock(&socket->lock); if (socket->assoc == NULL) { fibril_mutex_unlock(&socket->lock); async_answer_0(callid, ENOTCONN); return; } (void)flags; log_msg(LVL_DEBUG, "udp_sock_recvfrom(): lock recv_buffer lock"); fibril_mutex_lock(&socket->recv_buffer_lock); while (socket->recv_buffer_used == 0 && socket->recv_error == UDP_EOK) { log_msg(LVL_DEBUG, "udp_sock_recvfrom(): wait for cv"); fibril_condvar_wait(&socket->recv_buffer_cv, &socket->recv_buffer_lock); } log_msg(LVL_DEBUG, "Got data in sock recv_buffer"); rsock = socket->recv_fsock; data_len = socket->recv_buffer_used; urc = socket->recv_error; log_msg(LVL_DEBUG, "**** recv data_len=%zu", data_len); switch (urc) { case UDP_EOK: rc = EOK; break; /* case TCP_ENOTEXIST: case TCP_ECLOSING: rc = ENOTCONN; break; case TCP_ERESET: rc = ECONNABORTED; break;*/ default: assert(false); } log_msg(LVL_DEBUG, "**** udp_uc_receive -> %d", rc); if (rc != EOK) { fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); async_answer_0(callid, rc); return; } if (IPC_GET_IMETHOD(call) == NET_SOCKET_RECVFROM) { /* Fill addr */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = host2uint32_t_be(rsock.addr.ipv4); addr.sin_port = host2uint16_t_be(rsock.port); log_msg(LVL_DEBUG, "addr read receive"); if (!async_data_read_receive(&rcallid, &addr_length)) { fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); async_answer_0(callid, EINVAL); return; } if (addr_length > sizeof(addr)) addr_length = sizeof(addr); log_msg(LVL_DEBUG, "addr read finalize"); rc = async_data_read_finalize(rcallid, &addr, addr_length); if (rc != EOK) { fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); async_answer_0(callid, EINVAL); return; } } log_msg(LVL_DEBUG, "data read receive"); if (!async_data_read_receive(&rcallid, &length)) { fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); async_answer_0(callid, EINVAL); return; } if (length > data_len) length = data_len; log_msg(LVL_DEBUG, "data read finalize"); rc = async_data_read_finalize(rcallid, socket->recv_buffer, length); if (length < data_len && rc == EOK) rc = EOVERFLOW; log_msg(LVL_DEBUG, "read_data_length <- %zu", length); IPC_SET_ARG2(answer, 0); SOCKET_SET_READ_DATA_LENGTH(answer, length); SOCKET_SET_ADDRESS_LENGTH(answer, sizeof(addr)); async_answer_3(callid, EOK, IPC_GET_ARG1(answer), IPC_GET_ARG2(answer), IPC_GET_ARG3(answer)); socket->recv_buffer_used = 0; fibril_condvar_broadcast(&socket->recv_buffer_cv); fibril_mutex_unlock(&socket->recv_buffer_lock); fibril_mutex_unlock(&socket->lock); }
static void udp_sock_sendto(udp_client_t *client, ipc_callid_t callid, ipc_call_t call) { int socket_id; int fragments; int index; struct sockaddr_in *addr; size_t addr_size; socket_core_t *sock_core; udp_sockdata_t *socket; udp_sock_t fsock, *fsockp; ipc_call_t answer; ipc_callid_t wcallid; size_t length; uint8_t buffer[UDP_FRAGMENT_SIZE]; udp_error_t urc; int rc; log_msg(LVL_DEBUG, "udp_sock_send()"); addr = NULL; if (IPC_GET_IMETHOD(call) == NET_SOCKET_SENDTO) { rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &addr_size); if (rc != EOK) { async_answer_0(callid, rc); goto out; } if (addr_size != sizeof(struct sockaddr_in)) { async_answer_0(callid, EINVAL); goto out; } fsock.addr.ipv4 = uint32_t_be2host(addr->sin_addr.s_addr); fsock.port = uint16_t_be2host(addr->sin_port); fsockp = &fsock; } else { fsockp = NULL; } socket_id = SOCKET_GET_SOCKET_ID(call); fragments = SOCKET_GET_DATA_FRAGMENTS(call); SOCKET_GET_FLAGS(call); sock_core = socket_cores_find(&client->sockets, socket_id); if (sock_core == NULL) { async_answer_0(callid, ENOTSOCK); goto out; } if (sock_core->port == 0) { /* Implicitly bind socket to port */ rc = socket_bind(&client->sockets, &gsock, SOCKET_GET_SOCKET_ID(call), addr, addr_size, UDP_FREE_PORTS_START, UDP_FREE_PORTS_END, last_used_port); if (rc != EOK) { async_answer_0(callid, rc); goto out; } } socket = (udp_sockdata_t *)sock_core->specific_data; fibril_mutex_lock(&socket->lock); if (socket->assoc->ident.local.addr.ipv4 == UDP_IPV4_ANY) { /* Determine local IP address */ inet_addr_t loc_addr, rem_addr; rem_addr.ipv4 = fsockp ? fsock.addr.ipv4 : socket->assoc->ident.foreign.addr.ipv4; rc = inet_get_srcaddr(&rem_addr, 0, &loc_addr); if (rc != EOK) { fibril_mutex_unlock(&socket->lock); async_answer_0(callid, rc); log_msg(LVL_DEBUG, "udp_sock_sendto: Failed to " "determine local address."); return; } socket->assoc->ident.local.addr.ipv4 = loc_addr.ipv4; log_msg(LVL_DEBUG, "Local IP address is %x", socket->assoc->ident.local.addr.ipv4); } assert(socket->assoc != NULL); for (index = 0; index < fragments; index++) { if (!async_data_write_receive(&wcallid, &length)) { fibril_mutex_unlock(&socket->lock); async_answer_0(callid, EINVAL); goto out; } if (length > UDP_FRAGMENT_SIZE) length = UDP_FRAGMENT_SIZE; rc = async_data_write_finalize(wcallid, buffer, length); if (rc != EOK) { fibril_mutex_unlock(&socket->lock); async_answer_0(callid, rc); goto out; } urc = udp_uc_send(socket->assoc, fsockp, buffer, length, 0); switch (urc) { case UDP_EOK: rc = EOK; break; /* case TCP_ENOTEXIST: rc = ENOTCONN; break; case TCP_ECLOSING: rc = ENOTCONN; break; case TCP_ERESET: rc = ECONNABORTED; break;*/ default: assert(false); } if (rc != EOK) { fibril_mutex_unlock(&socket->lock); async_answer_0(callid, rc); goto out; } } IPC_SET_ARG1(answer, 0); SOCKET_SET_DATA_FRAGMENT_SIZE(answer, UDP_FRAGMENT_SIZE); async_answer_2(callid, EOK, IPC_GET_ARG1(answer), IPC_GET_ARG2(answer)); fibril_mutex_unlock(&socket->lock); out: if (addr != NULL) free(addr); }
static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { void *fs_va = NULL; ipc_callid_t callid; ipc_call_t call; sysarg_t method; size_t comm_size; unsigned int flags; int retval; uint64_t ba; size_t cnt; /* Answer the IPC_M_CONNECT_ME_TO call. */ async_answer_0(iid, EOK); if (!async_share_out_receive(&callid, &comm_size, &flags)) { async_answer_0(callid, EHANGUP); return; } (void) async_share_out_finalize(callid, &fs_va); if (fs_va == AS_MAP_FAILED) { async_answer_0(callid, EHANGUP); return; } while (true) { callid = async_get_call(&call); method = IPC_GET_IMETHOD(call); if (!method) { /* The other side has hung up. */ async_answer_0(callid, EOK); return; } switch (method) { case BD_READ_BLOCKS: ba = MERGE_LOUP32(IPC_GET_ARG1(call), IPC_GET_ARG2(call)); cnt = IPC_GET_ARG3(call); if (cnt * block_size > comm_size) { retval = ELIMIT; break; } retval = file_bd_read_blocks(ba, cnt, fs_va); break; case BD_WRITE_BLOCKS: ba = MERGE_LOUP32(IPC_GET_ARG1(call), IPC_GET_ARG2(call)); cnt = IPC_GET_ARG3(call); if (cnt * block_size > comm_size) { retval = ELIMIT; break; } retval = file_bd_write_blocks(ba, cnt, fs_va); break; case BD_GET_BLOCK_SIZE: async_answer_1(callid, EOK, block_size); continue; case BD_GET_NUM_BLOCKS: async_answer_2(callid, EOK, LOWER32(num_blocks), UPPER32(num_blocks)); continue; default: retval = EINVAL; break; } async_answer_0(callid, retval); } }