| /* | |
| * Copyright 2014, NICTA | |
| * | |
| * This software may be distributed and modified according to the terms of | |
| * the GNU General Public License version 2. Note that NO WARRANTY is provided. | |
| * See "LICENSE_GPLv2.txt" for details. | |
| * | |
| * @TAG(NICTA_GPL) | |
| */ | |
| #include "vmm/vmm.h" | |
| #include "vmm/vmm_manager.h" | |
| #include "vmm/vchan_component.h" | |
| #include "vmm/debug.h" | |
| #include "vmm/vchan_sharemem.h" | |
| static libvchan_t *vchan_init(int domain, int port, int server); | |
| static int libvchan_readwrite_action(libvchan_t *ctrl, void *data, size_t size, int stream, int action); | |
| static camkes_vchan_con_t *vchan_comp_con = NULL; | |
| /* | |
| Set up the vchan connection interface | |
| Currently, the number of vchan connection interfaces allowed is hardcoded to 1 per component | |
| */ | |
| void init_camkes_vchan(camkes_vchan_con_t *c) { | |
| vchan_comp_con = c; | |
| } | |
| /* | |
| Register a callback to be fired whenever a vchan event occurs | |
| */ | |
| int vchan_set_callback(callback_func_t cb, void *data) { | |
| vchan_comp_con->reg_callback(cb, data); | |
| return 0; | |
| } | |
| /* | |
| Create a new vchan server instance | |
| */ | |
| libvchan_t *libvchan_server_init(int domain, int port, size_t read_min, size_t write_min) { | |
| return vchan_init(domain, port, 1); | |
| } | |
| /* | |
| Create a new vchan client instance | |
| */ | |
| libvchan_t *libvchan_client_init(int domain, int port) { | |
| return vchan_init(domain, port, 0); | |
| } | |
| /* | |
| Create a new client/server instance | |
| */ | |
| libvchan_t *vchan_init(int domain, int port, int server) { | |
| if(vchan_comp_con == NULL) { | |
| return NULL; | |
| } | |
| libvchan_t *new_connection = malloc(sizeof(libvchan_t)); | |
| if(new_connection == NULL) { | |
| return NULL; | |
| } | |
| new_connection->is_server = server; | |
| new_connection->server_persists = 1; | |
| new_connection->blocking = 1; | |
| new_connection->domain_num = domain; | |
| new_connection->port_num = port; | |
| new_connection->con = vchan_comp_con; | |
| /* Perform vchan component initialisation */ | |
| vchan_connect_t t = { | |
| .v.domain = vchan_comp_con->component_dom_num, | |
| .v.dest = domain, | |
| .v.port = port, | |
| .server = new_connection->is_server, | |
| }; | |
| if(vchan_comp_con->connect(t) < 0) { | |
| free(new_connection); | |
| return NULL; | |
| } | |
| return new_connection; | |
| } | |
| /* | |
| Reading and writing to the vchan | |
| */ | |
| int libvchan_write(libvchan_t *ctrl, const void *data, size_t size) { | |
| return libvchan_readwrite_action(ctrl, (void *) data, size, 1, VCHAN_SEND); | |
| } | |
| int libvchan_send(libvchan_t *ctrl, const void *data, size_t size) { | |
| return libvchan_readwrite_action(ctrl, (void *) data, size, 1, VCHAN_SEND); | |
| } | |
| int libvchan_read(libvchan_t *ctrl, void *data, size_t size) { | |
| return libvchan_readwrite_action(ctrl, data, size, 1, VCHAN_RECV); | |
| } | |
| int libvchan_recv(libvchan_t *ctrl, void *data, size_t size) { | |
| return libvchan_readwrite_action(ctrl, data, size, 1, VCHAN_RECV); | |
| } | |
| /* | |
| Return correct buffer for given vchan read/write action | |
| */ | |
| vchan_buf_t *get_vchan_buf(vchan_ctrl_t *args, camkes_vchan_con_t *c, int action) { | |
| if(c->data_buf == NULL) { | |
| LOG_ERROR("Mangled vchan connection: null data buffer\n"); | |
| return NULL; | |
| } | |
| int buf_pos = c->get_buf(*args, action); | |
| /* Check that a buffer was retrieved */ | |
| if(buf_pos < 0) { | |
| return NULL; | |
| } | |
| void *addr = c->data_buf; | |
| addr += buf_pos; | |
| vchan_buf_t *b = (vchan_buf_t *) (addr); | |
| return b; | |
| } | |
| /* | |
| Helper function | |
| Allows connected components to get correct vchan buffer for read/write | |
| */ | |
| vchan_buf_t *get_vchan_ctrl_databuf(libvchan_t *ctrl, int action) { | |
| vchan_ctrl_t args = { | |
| .domain = ctrl->con->component_dom_num, | |
| .dest = ctrl->domain_num, | |
| .port = ctrl->port_num, | |
| }; | |
| return get_vchan_buf(&args, ctrl->con, action); | |
| } | |
| /* | |
| Perform a vchan read/write action into a given buffer | |
| This function is intended for non Init components, Init components have a different method | |
| */ | |
| int libvchan_readwrite_action(libvchan_t *ctrl, void *data, size_t size, int stream, int action) { | |
| int *update; | |
| vchan_buf_t *b = get_vchan_ctrl_databuf(ctrl, action); | |
| if(b == NULL) { | |
| return -1; | |
| } | |
| /* | |
| How data is stored in a given vchan buffer | |
| Position of data in buffer is given by | |
| (either b->write_pos or b->read_pos) % VCHAN_BUF_SIZE | |
| read_pos is incremented by x when x bytes are read from the buffer | |
| write_pos is incremented by x when x bytes are read from the buffer | |
| Amount of bytes in buffer is given by the difference between read_pos and write_pos | |
| if write_pos > read_pos, there is data yet to be read | |
| */ | |
| size_t filled = abs(b->write_pos - b->read_pos); | |
| if(action == VCHAN_SEND) { | |
| while(filled == VCHAN_BUF_SIZE) { | |
| ctrl->con->wait(); | |
| filled = abs(b->write_pos - b->read_pos); | |
| } | |
| if(stream) { | |
| size = MIN(VCHAN_BUF_SIZE - filled, size); | |
| } else if(size > VCHAN_BUF_SIZE - filled) { | |
| return -1; | |
| } | |
| update = &b->write_pos; | |
| } else { | |
| while(filled == 0) { | |
| ctrl->con->wait(); | |
| filled = abs(b->write_pos - b->read_pos); | |
| } | |
| if(stream) { | |
| size = MIN(filled, size); | |
| } else if(size > filled) { | |
| return -1; | |
| } | |
| update = &b->read_pos; | |
| } | |
| /* | |
| Because this buffer is circular, | |
| data may have to wrap around to the start of the buffer | |
| This is achieved by doing two copies, one to buffer end | |
| And one at start of buffer for remaining data | |
| E.g if buffer size = 12 | |
| and if write pos = 7 && number of bytes to write = 8 | |
| Start: | |
| write_pos | |
| V | |
| [oooooooooooo] | |
| End: | |
| write_pos | |
| V | |
| [xxxooooxxxxx] | |
| */ | |
| off_t start = (*update % VCHAN_BUF_SIZE); | |
| off_t remain = 0; | |
| if(start + size > VCHAN_BUF_SIZE) { | |
| remain = (start + size) - VCHAN_BUF_SIZE; | |
| size -= remain; | |
| } | |
| void *dbuf = &b->sync_data; | |
| if(action == VCHAN_SEND) { | |
| memcpy(dbuf + start, data, size); | |
| memcpy(dbuf, data + size, remain); | |
| } else { | |
| memcpy(data, ((void *) dbuf) + start, size); | |
| memcpy(data + size, dbuf, remain); | |
| } | |
| __sync_synchronize(); | |
| /* | |
| Update either the read byte counter or the written byte counter | |
| With how much was written or read | |
| */ | |
| *update += (size + remain); | |
| ctrl->con->alert(); | |
| return (size + remain); | |
| } | |
| /* | |
| Wait for data to arrive to a component from a given vchan | |
| */ | |
| int libvchan_wait(libvchan_t *ctrl) { | |
| vchan_buf_t *b = get_vchan_ctrl_databuf(ctrl, VCHAN_RECV); | |
| assert(b != NULL); | |
| size_t filled = abs(b->write_pos - b->read_pos); | |
| while(filled == 0) { | |
| ctrl->con->wait(); | |
| b = get_vchan_ctrl_databuf(ctrl, VCHAN_RECV); | |
| filled = abs(b->write_pos - b->read_pos); | |
| } | |
| return 0; | |
| } | |
| void libvchan_close(libvchan_t *ctrl) { | |
| /* Perform vchan component initialisation */ | |
| vchan_connect_t t = { | |
| .v.domain = ctrl->con->component_dom_num, | |
| .v.dest = ctrl->domain_num, | |
| .v.port = ctrl->port_num, | |
| .server = ctrl->is_server, | |
| }; | |
| ctrl->con->disconnect(t); | |
| free(ctrl); | |
| ctrl = NULL; | |
| } | |
| int libvchan_is_open(libvchan_t *ctrl) { | |
| vchan_ctrl_t args = { | |
| .domain = ctrl->con->component_dom_num, | |
| .dest = ctrl->domain_num, | |
| .port = ctrl->port_num, | |
| }; | |
| return ctrl->con->status(args); | |
| } | |
| /* | |
| How much data can be read from the vchan | |
| */ | |
| int libvchan_data_ready(libvchan_t *ctrl) { | |
| vchan_buf_t *b = get_vchan_ctrl_databuf(ctrl, VCHAN_RECV); | |
| size_t filled = abs(b->write_pos - b->read_pos); | |
| return filled; | |
| } | |
| /* | |
| How much data can be written to the vchan | |
| */ | |
| int libvchan_buffer_space(libvchan_t *ctrl) { | |
| vchan_buf_t *b = get_vchan_ctrl_databuf(ctrl, VCHAN_SEND); | |
| size_t filled = abs(b->write_pos - b->read_pos); | |
| return VCHAN_BUF_SIZE - filled; | |
| } |