/* * Copyright (c) 2011-2015, Emulex * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ /** * @file * OCS user space driver IOCTL interface API */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #define MIN(x,y) ((x) < (y) ? (x) : (y)) /** * @brief Generic buffer object */ typedef struct { void *buf; /*>> pointer to buffer */ uint32_t len; /*>> length of buffer */ } buf_t; #define OCS_USPACE_RPC_BUF_COUNT 4 typedef struct { buf_t c2sbufs[OCS_USPACE_RPC_BUF_COUNT]; uint32_t c2sbufcount; buf_t s2cbufs[OCS_USPACE_RPC_BUF_COUNT]; uint32_t s2cbufcount; uint32_t s2cbufs_rcvd; } ocs_uspace_rpc_t; static int ocs_uspace_rpc_init(ocs_uspace_rpc_t *rpc); static int ocs_uspace_rpc_c2sbuf(ocs_uspace_rpc_t *rpc, void *buf, uint32_t len); static int ocs_uspace_rpc_s2cbuf(ocs_uspace_rpc_t *rpc, void *buf, uint32_t len); static int ocs_uspace_rpc(ocs_skt_node_t *node, uint32_t cmd, ocs_uspace_rpc_t *rpc); /** * @brief open user space device * * The instance index in 'path' is extracted and returend. * * @param path pointer to device name * @param flags open flags (ignored) * * @return returns pointer to OCS socket object or NULL */ void *ocsu_open(const char *hostname, int devidx) { ocs_skt_node_t *node = NULL; int32_t rc; node = malloc(sizeof(*node)); if (node != NULL) { rc = ocs_skt_client(node, hostname, devidx); if (rc) { free(node); node = NULL; } } return node; } /** * @brief close user space device * * The user space socket device pointed to by 'device' is closed, then * free'd * * @param device pointer to user space device * * @return none */ void ocsu_close(void *device) { if (device != NULL) { ocs_skt_close(device); free(device); } } /** * @brief initailize RPC * * An RPC context is initialized * * @param rpc pointer to RPC context to initialize * * @return returns 0 for success, a negative error code value for failure. */ static int ocs_uspace_rpc_init(ocs_uspace_rpc_t *rpc) { memset(rpc, 0, sizeof(*rpc)); return 0; } /** * @brief append a client to server buffer * * A buffer is appended to the client to server buffer list for the RPC context * * @param rpc pointer to RPC context * @param buf pointer to data buffer * @param len length of data buffer in bytes * * @return returns 0 for success, a negative error code value for failure. */ static int ocs_uspace_rpc_c2sbuf(ocs_uspace_rpc_t *rpc, void *buf, uint32_t len) { if (rpc->c2sbufcount < OCS_USPACE_RPC_BUF_COUNT) { rpc->c2sbufs[rpc->c2sbufcount].buf = buf; rpc->c2sbufs[rpc->c2sbufcount].len = len; rpc->c2sbufcount ++; return 0; } return -1; } /** * @brief append a server to client buffer * * A data buffer is appended to the server to client buffer list * * @param rpc pointer to RPC context * @param buf pointer to data buffer * @param len length of data buffer in bytes * * @return returns 0 for success, a negative error code value for failure. */ static int ocs_uspace_rpc_s2cbuf(ocs_uspace_rpc_t *rpc, void *buf, uint32_t len) { if (rpc->s2cbufcount < OCS_USPACE_RPC_BUF_COUNT) { rpc->s2cbufs[rpc->s2cbufcount].buf = buf; rpc->s2cbufs[rpc->s2cbufcount].len = len; rpc->s2cbufcount ++; return 0; } return -1; } /** * @brief invoke user space IOCTL * * The user space driver IOCTL is executed by posting an RPC to the IOCTL server * * @param device device object * @param cmd IOCTL command * @param ... IOCTL parameters * * @return returns 0 for success, a negative error code value for failure. */ int ocsu_ioctl(void *device, int cmd, void *arg) { int rc = -1; ocs_uspace_rpc_t rpc; ocs_uspace_rpc_init(&rpc); switch(cmd) { case OCS_IOCTL_CMD_TEST: { ocs_ioctl_test_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); break; } case OCS_IOCTL_CMD_ELXU_MBOX: { ocs_ioctl_elxu_mbox_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_c2sbuf(&rpc, (void*) req->in_addr, req->in_bytes); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, (void*)req->out_addr, req->out_bytes); break; } case OCS_IOCTL_CMD_DRIVER_INFO: { ocs_ioctl_driver_info_t *info = arg; ocs_uspace_rpc_c2sbuf(&rpc, info, sizeof(*info)); ocs_uspace_rpc_s2cbuf(&rpc, info, sizeof(*info)); break; } case OCS_IOCTL_CMD_ECD_HELPER: { ocs_ioctl_ecd_helper_t *req = arg; ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); break; } case OCS_IOCTL_CMD_VPORT: { ocs_ioctl_vport_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); break; } case OCS_IOCTL_CMD_GET_DDUMP: { ocs_ioctl_ddump_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req->user_buffer, req->user_buffer_len); break; } case OCS_IOCTL_CMD_MGMT_LIST: case OCS_IOCTL_CMD_MGMT_GET_ALL: { ocs_ioctl_mgmt_buffer_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req->user_buffer, req->user_buffer_len); break; } case OCS_IOCTL_CMD_MGMT_GET: { ocs_ioctl_cmd_get_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_c2sbuf(&rpc, req->name, strlen((char*)req->name) + 1); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req->value, req->value_length); break; } case OCS_IOCTL_CMD_MGMT_SET: { ocs_ioctl_cmd_set_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_c2sbuf(&rpc, req->name, strlen((char*)req->name) + 1); ocs_uspace_rpc_c2sbuf(&rpc, req->value, strlen((char*)req->value) + 1); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); break; } case OCS_IOCTL_CMD_MGMT_EXEC: { ocs_ioctl_action_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_c2sbuf(&rpc, req->name, strlen((char*)req->name)+1); ocs_uspace_rpc_c2sbuf(&rpc, req->arg_in, req->arg_in_length); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req->arg_out, req->arg_out_length); break; } case OCS_IOCTL_CMD_CONNECTION_INFO: { ocs_ioctl_connections_t *req = arg; ocs_uspace_rpc_c2sbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req, sizeof(*req)); ocs_uspace_rpc_s2cbuf(&rpc, req->connections, req->max_connections * sizeof(ocs_ioctl_connection_info_t)); break; } /* * Commands with no arguments ... */ case OCS_IOCTL_CMD_LINK_ONLINE: case OCS_IOCTL_CMD_GEN_DUMP: case OCS_IOCTL_CMD_UNLOAD: { break; } default: printf("Error: unsupported ioctl command %08X\n", cmd); return -1; } rc = ocs_uspace_rpc(device, cmd, &rpc); return rc; } /** * @brief complete an RPC request * * The RPC context is sent to the server. The code waits for the server * to respond. * * @param devno device number * @param cmd IOCTL command being sent * @param evt rpc pointer to RPC context * * @return returns 0 for success, a negative error code value for failure. */ static int ocs_uspace_rpc(ocs_skt_node_t *node, uint32_t cmd, ocs_uspace_rpc_t *rpc) { int i; char *str; ocs_skt_str_write(node, 0, "ocs:ioctl:0x%08x:%d", cmd, rpc->c2sbufcount); // send the client to server buffers for (i = 0; i < rpc->c2sbufcount; i ++) { ocs_skt_write(node, rpc->c2sbufs[i].buf, rpc->c2sbufs[i].len, 0); } // send terminator ocs_skt_str_write(node, 0, "ocs:ioctl:end"); // expect ocs:ioctl:rsp:status:buffercount str = ocs_skt_str_read(node, NULL); int status; int buffercount; sscanf(str, "ocs:ioctl:resp:%d:%d", &status, &buffercount); free(str); for (i = 0; i < buffercount; i ++) { ocs_skt_frame_t *f = ocs_skt_frame_read(node); if (f == NULL) { printf("expected frame null\n"); } else if (i < rpc->s2cbufcount) { memcpy(rpc->s2cbufs[i].buf, f->buffer, MIN(f->buffer_len, rpc->s2cbufs[i].len)); } else { printf("%s: discard frame\n", __func__); } ocs_skt_frame_free(f); } // expect last string str = ocs_skt_str_read(node, NULL); free(str); return status; }