/** 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); } }
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; } }
/** 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); }
#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); } } /** List answerbox contents. *
/** * 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); }
/** Perform a path lookup. * * @param path Path to be resolved; it must be a NULL-terminated * string. * @param lflag Flags to be used during lookup. * @param result Empty structure where the lookup result will be stored. * Can be NULL. * @param altroot If non-empty, will be used instead of rootfs as the root * of the whole VFS tree. * * @return EOK on success or an error code from errno.h. * */ int vfs_lookup_internal(char *path, int lflag, vfs_lookup_res_t *result, vfs_pair_t *altroot, ...) { vfs_pair_t *root; if (altroot) root = altroot; else root = &rootfs; if (!root->fs_handle) return ENOENT; size_t len; path = canonify(path, &len); if (!path) return EINVAL; fs_index_t index = 0; if (lflag & L_LINK) { va_list ap; va_start(ap, altroot); index = va_arg(ap, fs_index_t); va_end(ap); } fibril_mutex_lock(&plb_mutex); plb_entry_t entry; link_initialize(&entry.plb_link); entry.len = len; size_t first; /* the first free index */ size_t last; /* the last free index */ if (list_empty(&plb_entries)) { first = 0; last = PLB_SIZE - 1; } else { plb_entry_t *oldest = list_get_instance( list_first(&plb_entries), plb_entry_t, plb_link); plb_entry_t *newest = list_get_instance( list_last(&plb_entries), plb_entry_t, plb_link); first = (newest->index + newest->len) % PLB_SIZE; last = (oldest->index - 1) % PLB_SIZE; } if (first <= last) { if ((last - first) + 1 < len) { /* * The buffer cannot absorb the path. */ fibril_mutex_unlock(&plb_mutex); return ELIMIT; } } else { if (PLB_SIZE - ((first - last) + 1) < len) { /* * The buffer cannot absorb the path. */ fibril_mutex_unlock(&plb_mutex); return ELIMIT; } } /* * We know the first free index in PLB and we also know that there is * enough space in the buffer to hold our path. */ entry.index = first; entry.len = len; /* * Claim PLB space by inserting the entry into the PLB entry ring * buffer. */ list_append(&entry.plb_link, &plb_entries); fibril_mutex_unlock(&plb_mutex); /* * Copy the path into PLB. */ size_t cnt1 = min(len, (PLB_SIZE - first) + 1); size_t cnt2 = len - cnt1; memcpy(&plb[first], path, cnt1); memcpy(plb, &path[cnt1], cnt2); ipc_call_t answer; async_exch_t *exch = vfs_exchange_grab(root->fs_handle); aid_t req = async_send_5(exch, VFS_OUT_LOOKUP, (sysarg_t) first, (sysarg_t) (first + len - 1) % PLB_SIZE, (sysarg_t) root->service_id, (sysarg_t) lflag, (sysarg_t) index, &answer); sysarg_t rc; async_wait_for(req, &rc); vfs_exchange_release(exch); fibril_mutex_lock(&plb_mutex); list_remove(&entry.plb_link); /* * Erasing the path from PLB will come handy for debugging purposes. */ memset(&plb[first], 0, cnt1); memset(plb, 0, cnt2); fibril_mutex_unlock(&plb_mutex); if ((int) rc < EOK) return (int) rc; if (!result) return EOK; result->triplet.fs_handle = (fs_handle_t) rc; result->triplet.service_id = (service_id_t) IPC_GET_ARG1(answer); result->triplet.index = (fs_index_t) IPC_GET_ARG2(answer); result->size = (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(answer), IPC_GET_ARG4(answer)); result->lnkcnt = (unsigned int) IPC_GET_ARG5(answer); if (lflag & L_FILE) result->type = VFS_NODE_FILE; else if (lflag & L_DIRECTORY) result->type = VFS_NODE_DIRECTORY; else result->type = VFS_NODE_UNKNOWN; return EOK; }