/* * Copyright (c) 2008 Jakub Jermar * Copyright (c) 2008 Martin Decky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - 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. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ /** @addtogroup libblock * @{ */ /** * @file * @brief */ #include "libblock.h" #include "../../srv/vfs/vfs.h" #include "../../srv/rd/rd.h" #include #include #include #include #include #include #include #include #include #include /** Lock protecting the device connection list */ static futex_t dcl_lock = FUTEX_INITIALIZER; /** Device connection list head. */ static LIST_INITIALIZE(dcl_head); typedef struct { link_t link; int dev_handle; int dev_phone; void *com_area; size_t com_size; void *bb_buf; off_t bb_off; size_t bb_size; } devcon_t; static devcon_t *devcon_search(dev_handle_t dev_handle) { link_t *cur; futex_down(&dcl_lock); for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) { devcon_t *devcon = list_get_instance(cur, devcon_t, link); if (devcon->dev_handle == dev_handle) { futex_up(&dcl_lock); return devcon; } } futex_up(&dcl_lock); return NULL; } static int devcon_add(dev_handle_t dev_handle, int dev_phone, void *com_area, size_t com_size) { link_t *cur; devcon_t *devcon; devcon = malloc(sizeof(devcon_t)); if (!devcon) return ENOMEM; link_initialize(&devcon->link); devcon->dev_handle = dev_handle; devcon->dev_phone = dev_phone; devcon->com_area = com_area; devcon->com_size = com_size; devcon->bb_buf = NULL; devcon->bb_off = 0; devcon->bb_size = 0; futex_down(&dcl_lock); for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) { devcon_t *d = list_get_instance(cur, devcon_t, link); if (d->dev_handle == dev_handle) { futex_up(&dcl_lock); free(devcon); return EEXIST; } } list_append(&devcon->link, &dcl_head); futex_up(&dcl_lock); return EOK; } static void devcon_remove(devcon_t *devcon) { futex_down(&dcl_lock); list_remove(&devcon->link); futex_up(&dcl_lock); } int block_init(dev_handle_t dev_handle, size_t com_size) { int rc; int dev_phone; void *com_area; com_area = mmap(NULL, com_size, PROTO_READ | PROTO_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); if (!com_area) { return ENOMEM; } dev_phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP, DEVMAP_CONNECT_TO_DEVICE, dev_handle); if (dev_phone < 0) { munmap(com_area, com_size); return dev_phone; } rc = ipc_share_out_start(dev_phone, com_area, AS_AREA_READ | AS_AREA_WRITE); if (rc != EOK) { munmap(com_area, com_size); ipc_hangup(dev_phone); return rc; } rc = devcon_add(dev_handle, dev_phone, com_area, com_size); if (rc != EOK) { munmap(com_area, com_size); ipc_hangup(dev_phone); return rc; } return EOK; } void block_fini(dev_handle_t dev_handle) { devcon_t *devcon = devcon_search(dev_handle); assert(devcon); devcon_remove(devcon); if (devcon->bb_buf) free(devcon->bb_buf); munmap(devcon->com_area, devcon->com_size); ipc_hangup(devcon->dev_phone); free(devcon); } int block_bb_read(dev_handle_t dev_handle, off_t off, size_t size) { void *bb_buf; devcon_t *devcon = devcon_search(dev_handle); if (!devcon) return ENOENT; if (devcon->bb_buf) return EEXIST; bb_buf = malloc(size); if (!bb_buf) return ENOMEM; off_t bufpos = 0; size_t buflen = 0; if (!block_read(dev_handle, &bufpos, &buflen, &off, bb_buf, size, size)) { free(bb_buf); return EIO; /* XXX real error code */ } devcon->bb_buf = bb_buf; devcon->bb_off = off; devcon->bb_size = size; return EOK; } void *block_bb_get(dev_handle_t dev_handle) { devcon_t *devcon = devcon_search(dev_handle); assert(devcon); return devcon->bb_buf; } /** Read data from a block device. * * @param dev_handle Device handle of the block device. * @param bufpos Pointer to the first unread valid offset within the * communication buffer. * @param buflen Pointer to the number of unread bytes that are ready in * the communication buffer. * @param pos Device position to be read. * @param dst Destination buffer. * @param size Size of the destination buffer. * @param block_size Block size to be used for the transfer. * * @return True on success, false on failure. */ bool block_read(int dev_handle, off_t *bufpos, size_t *buflen, off_t *pos, void *dst, size_t size, size_t block_size) { off_t offset = 0; size_t left = size; devcon_t *devcon = devcon_search(dev_handle); assert(devcon); while (left > 0) { size_t rd; if (*bufpos + left < *buflen) rd = left; else rd = *buflen - *bufpos; if (rd > 0) { /* * Copy the contents of the communication buffer to the * destination buffer. */ memcpy(dst + offset, devcon->com_area + *bufpos, rd); offset += rd; *bufpos += rd; *pos += rd; left -= rd; } if (*bufpos == *buflen) { /* Refill the communication buffer with a new block. */ ipcarg_t retval; int rc = async_req_2_1(devcon->dev_phone, RD_READ_BLOCK, *pos / block_size, block_size, &retval); if ((rc != EOK) || (retval != EOK)) return false; *bufpos = 0; *buflen = block_size; } } return true; } block_t *block_get(dev_handle_t dev_handle, off_t offset, size_t bs) { /* FIXME */ block_t *b; off_t bufpos = 0; size_t buflen = 0; off_t pos = offset * bs; b = malloc(sizeof(block_t)); if (!b) return NULL; b->data = malloc(bs); if (!b->data) { free(b); return NULL; } b->size = bs; if (!block_read(dev_handle, &bufpos, &buflen, &pos, b->data, bs, bs)) { free(b->data); free(b); return NULL; } return b; } void block_put(block_t *block) { /* FIXME */ free(block->data); free(block); } /** @} */