diff options
author | Joel Sherrill <joel.sherrill@oarcorp.com> | 2012-03-07 09:52:04 -0600 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@oarcorp.com> | 2012-03-07 09:52:04 -0600 |
commit | a9153ec3040f54fa52b68e14dafed2aba7b780ae (patch) | |
tree | fda80e3380dfebf7d97868507aa185757852e882 /rtems/freebsd/rtems/rtems-bsd-cam.c | |
download | rtems-libbsd-a9153ec3040f54fa52b68e14dafed2aba7b780ae.tar.bz2 |
Initial import
Code is based on FreeBSD 8.2 with USB support from Sebastian Huber
and Thomas Doerfler. Initial TCP/IP stack work is from Kevel Kirspel.
Diffstat (limited to 'rtems/freebsd/rtems/rtems-bsd-cam.c')
-rw-r--r-- | rtems/freebsd/rtems/rtems-bsd-cam.c | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/rtems/freebsd/rtems/rtems-bsd-cam.c b/rtems/freebsd/rtems/rtems-bsd-cam.c new file mode 100644 index 00000000..36d4d67c --- /dev/null +++ b/rtems/freebsd/rtems/rtems-bsd-cam.c @@ -0,0 +1,495 @@ +/** + * @file + * + * @ingroup rtems_bsd_rtems + * + * @brief TODO. + */ + +/* + * Copyright (c) 2009, 2010 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include <rtems/freebsd/machine/rtems-bsd-config.h> + +#include <rtems/freebsd/sys/param.h> +#include <rtems/freebsd/sys/systm.h> +#include <rtems/freebsd/sys/malloc.h> +#include <rtems/freebsd/sys/kernel.h> +#include <rtems/freebsd/sys/lock.h> +#include <rtems/freebsd/sys/mutex.h> +#include <rtems/freebsd/sys/condvar.h> + +#include <rtems/freebsd/cam/cam.h> +#include <rtems/freebsd/cam/cam_ccb.h> +#include <rtems/freebsd/cam/cam_sim.h> +#include <rtems/freebsd/cam/cam_xpt.h> +#include <rtems/freebsd/cam/cam_xpt_sim.h> +#include <rtems/freebsd/cam/cam_debug.h> + +#include <rtems/freebsd/cam/scsi/scsi_all.h> + +#include <rtems/media.h> +#include <rtems/libio.h> +#include <rtems/diskdevs.h> + +#define BSD_CAM_DEVQ_DUMMY ((struct cam_devq *) 0xdeadbeef) + +#define BSD_SCSI_TAG 0 + +#define BSD_SCSI_RETRIES 4 + +#define BSD_SCSI_TIMEOUT (60 * 1000) + +#define BSD_SCSI_MIN_COMMAND_SIZE 10 + +MALLOC_DEFINE(M_CAMSIM, "CAM SIM", "CAM SIM buffers"); + +static void +rtems_bsd_sim_set_state(struct cam_sim *sim, enum bsd_sim_state state) +{ + sim->state = state; +} + +static void +rtems_bsd_sim_set_state_and_notify(struct cam_sim *sim, enum bsd_sim_state state) +{ + sim->state = state; + cv_broadcast(&sim->state_changed); +} + +static void +rtems_bsd_sim_wait_for_state(struct cam_sim *sim, enum bsd_sim_state state) +{ + while (sim->state != state) { + cv_wait(&sim->state_changed, sim->mtx); + } +} + +static void +rtems_bsd_sim_wait_for_state_and_cancel_ccb(struct cam_sim *sim, enum bsd_sim_state state) +{ + while (sim->state != state) { + if (sim->state != BSD_SIM_BUSY) { + cv_wait(&sim->state_changed, sim->mtx); + } else { + sim->ccb.ccb_h.status = CAM_SEL_TIMEOUT; + (*sim->ccb.ccb_h.cbfcnp)(NULL, &sim->ccb); + } + } +} + +static void +rtems_bsd_ccb_callback(struct cam_periph *periph, union ccb *ccb) +{ + struct cam_sim *sim = ccb->ccb_h.sim; + + BSD_ASSERT(periph == NULL && sim->state == BSD_SIM_INIT_BUSY); + + rtems_bsd_sim_set_state_and_notify(sim, BSD_SIM_INIT_READY); +} + +static rtems_status_code +rtems_bsd_ccb_action(union ccb *ccb) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + struct cam_sim *sim = ccb->ccb_h.sim; + + mtx_lock(sim->mtx); + + BSD_ASSERT(sim->state == BSD_SIM_INIT); + rtems_bsd_sim_set_state(sim, BSD_SIM_INIT_BUSY); + (*sim->sim_action)(sim, ccb); + rtems_bsd_sim_wait_for_state(sim, BSD_SIM_INIT_READY); + if (ccb->ccb_h.status != CAM_REQ_CMP) { + sc = RTEMS_IO_ERROR; + } + rtems_bsd_sim_set_state(sim, BSD_SIM_INIT); + + mtx_unlock(sim->mtx); + + return sc; +} + +static rtems_status_code +rtems_bsd_scsi_inquiry(union ccb *ccb, struct scsi_inquiry_data *inq_data) +{ + memset(inq_data, 0, sizeof(*inq_data)); + + scsi_inquiry( + &ccb->csio, + BSD_SCSI_RETRIES, + rtems_bsd_ccb_callback, + BSD_SCSI_TAG, + (u_int8_t *) inq_data, + sizeof(*inq_data) - 1, + FALSE, + 0, + SSD_MIN_SIZE, + BSD_SCSI_TIMEOUT + ); + + return rtems_bsd_ccb_action(ccb); +} + +static rtems_status_code +rtems_bsd_scsi_test_unit_ready(union ccb *ccb) +{ + scsi_test_unit_ready( + &ccb->csio, + BSD_SCSI_RETRIES, + rtems_bsd_ccb_callback, + BSD_SCSI_TAG, + SSD_FULL_SIZE, + BSD_SCSI_TIMEOUT + ); + + return rtems_bsd_ccb_action(ccb); +} + +static rtems_status_code +rtems_bsd_scsi_read_capacity(union ccb *ccb, uint32_t *block_count, uint32_t *block_size) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + struct scsi_read_capacity_data rdcap; + + memset(&rdcap, 0, sizeof(rdcap)); + + scsi_read_capacity( + &ccb->csio, + BSD_SCSI_RETRIES, + rtems_bsd_ccb_callback, + BSD_SCSI_TAG, + &rdcap, + SSD_FULL_SIZE, + BSD_SCSI_TIMEOUT + ); + + sc = rtems_bsd_ccb_action(ccb); + if (sc != RTEMS_SUCCESSFUL) { + return RTEMS_IO_ERROR; + } + + *block_size = scsi_4btoul(rdcap.length); + *block_count = scsi_4btoul(rdcap.addr) + 1; + + return RTEMS_SUCCESSFUL; +} + +static void +rtems_bsd_csio_callback(struct cam_periph *periph, union ccb *ccb) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + bool done = false; + struct cam_sim *sim = ccb->ccb_h.sim; + + BSD_ASSERT(periph == NULL && sim->state == BSD_SIM_BUSY); + + if (ccb->ccb_h.status == CAM_REQ_CMP) { + rtems_blkdev_sg_buffer *sg = ccb->csio.sg_current; + + if (sg != ccb->csio.sg_end) { + scsi_read_write( + &ccb->csio, + BSD_SCSI_RETRIES, + rtems_bsd_csio_callback, + BSD_SCSI_TAG, + ccb->csio.readop, + 0, + BSD_SCSI_MIN_COMMAND_SIZE, + sg->block, + sg->length / 512, /* FIXME */ + sg->buffer, + sg->length, + SSD_FULL_SIZE, + BSD_SCSI_TIMEOUT + ); + ccb->csio.sg_current = sg + 1; + (*sim->sim_action)(sim, ccb); + } else { + done = true; + } + } else if (ccb->ccb_h.status == CAM_SEL_TIMEOUT) { + sc = RTEMS_UNSATISFIED; + done = true; + } else { + sc = RTEMS_IO_ERROR; + done = true; + } + + if (done) { + ccb->csio.req->req_done(ccb->csio.req->done_arg, sc); + rtems_bsd_sim_set_state_and_notify(sim, BSD_SIM_IDLE); + } +} + +static int rtems_bsd_sim_disk_read_write(struct cam_sim *sim, rtems_blkdev_request *req) +{ + mtx_lock(sim->mtx); + + rtems_bsd_sim_wait_for_state(sim, BSD_SIM_IDLE); + rtems_bsd_sim_set_state(sim, BSD_SIM_BUSY); + + switch (req->req) { + case RTEMS_BLKDEV_REQ_READ: + sim->ccb.csio.readop = TRUE; + break; + case RTEMS_BLKDEV_REQ_WRITE: + sim->ccb.csio.readop = FALSE; + break; + default: + mtx_unlock(sim->mtx); + return -1; + } + + sim->ccb.csio.sg_current = req->bufs; + sim->ccb.csio.sg_end = req->bufs + req->bufnum; + sim->ccb.csio.req = req; + + sim->ccb.ccb_h.status = CAM_REQ_CMP; + + rtems_bsd_csio_callback(NULL, &sim->ccb); + + mtx_unlock(sim->mtx); + + return 0; +} + +static int rtems_bsd_sim_disk_ioctl(rtems_disk_device *dd, uint32_t req, void *arg) +{ + struct cam_sim *sim = rtems_disk_get_driver_data(dd); + + if (req == RTEMS_BLKIO_REQUEST) { + rtems_blkdev_request *r = arg; + + return rtems_bsd_sim_disk_read_write(sim, r); + } else if (req == RTEMS_BLKIO_DELETED) { + mtx_lock(sim->mtx); + + free(sim->disk, M_RTEMS_HEAP); + sim->disk = NULL; + rtems_bsd_sim_set_state_and_notify(sim, BSD_SIM_DELETED); + + mtx_unlock(sim->mtx); + + return 0; + } else { + return -1; + } +} + +static void +rtems_bsd_sim_disk_initialized(struct cam_sim *sim, char *disk) +{ + mtx_lock(sim->mtx); + + sim->disk = disk; + rtems_bsd_sim_set_state_and_notify(sim, BSD_SIM_IDLE); + + mtx_unlock(sim->mtx); +} + +static rtems_status_code +rtems_bsd_sim_attach_worker(rtems_media_state state, const char *src, char **dest, void *arg) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_device_major_number major = UINT32_MAX; + struct cam_sim *sim = arg; + char *disk = NULL; + + if (state == RTEMS_MEDIA_STATE_READY) { + dev_t dev = 0; + unsigned retries = 0; + + struct scsi_inquiry_data inq_data; + uint32_t block_count = 0; + uint32_t block_size = 0; + + sc = rtems_io_register_driver(0, &rtems_blkdev_generic_ops, &major); + if (sc != RTEMS_SUCCESSFUL) { + BSD_PRINTF("OOPS: register driver failed\n"); + goto error; + } + + disk = rtems_media_create_path("/dev", src, major); + if (disk == NULL) { + BSD_PRINTF("OOPS: create path failed\n"); + goto unregister_and_error; + } + + sc = rtems_bsd_scsi_inquiry(&sim->ccb, &inq_data); + if (sc != RTEMS_SUCCESSFUL) { + BSD_PRINTF("OOPS: inquiry failed\n"); + goto unregister_and_error; + } + scsi_print_inquiry(&inq_data); + + for (retries = 0; retries <= 3; ++retries) { + sc = rtems_bsd_scsi_test_unit_ready(&sim->ccb); + if (sc == RTEMS_SUCCESSFUL) { + break; + } + } + if (sc != RTEMS_SUCCESSFUL) { + BSD_PRINTF("OOPS: test unit ready failed\n"); + goto unregister_and_error; + } + + sc = rtems_bsd_scsi_read_capacity(&sim->ccb, &block_count, &block_size); + if (sc != RTEMS_SUCCESSFUL) { + BSD_PRINTF("OOPS: read capacity failed\n"); + goto unregister_and_error; + } + + BSD_PRINTF("read capacity: block count %u, block size %u\n", block_count, block_size); + + dev = rtems_filesystem_make_dev_t(major, 0); + + sc = rtems_disk_create_phys(dev, block_size, block_count, rtems_bsd_sim_disk_ioctl, sim, disk); + if (sc != RTEMS_SUCCESSFUL) { + goto unregister_and_error; + } + + /* FIXME */ +#if 0 + rtems_disk_device *dd = rtems_disk_obtain(dev); + dd->block_size *= 64; + rtems_disk_release(dd); +#endif + + rtems_bsd_sim_disk_initialized(sim, disk); + + *dest = strdup(disk, M_RTEMS_HEAP); + } + + return RTEMS_SUCCESSFUL; + +unregister_and_error: + + rtems_io_unregister_driver(major); + +error: + + free(disk, M_RTEMS_HEAP); + + rtems_bsd_sim_disk_initialized(sim, NULL); + + return RTEMS_IO_ERROR; +} + +struct cam_sim * +cam_sim_alloc( + sim_action_func sim_action, + sim_poll_func sim_poll, + const char *sim_name, + void *softc, + u_int32_t unit, + struct mtx *mtx, + int max_dev_transactions, + int max_tagged_dev_transactions, + struct cam_devq *queue +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + struct cam_sim *sim = NULL; + + if (mtx == NULL) { + return NULL; + } + + sim = malloc(sizeof(*sim), M_CAMSIM, M_NOWAIT | M_ZERO); + if (sim == NULL) { + return NULL; + } + + sim->sim_action = sim_action; + sim->sim_poll = sim_poll; + sim->sim_name = sim_name; + sim->softc = softc; + sim->mtx = mtx; + sim->unit_number = unit; + sim->ccb.ccb_h.sim = sim; + + cv_init(&sim->state_changed, "SIM state changed"); + + sc = rtems_media_server_disk_attach(sim_name, rtems_bsd_sim_attach_worker, sim); + BSD_ASSERT_SC(sc); + + return sim; +} + +void +cam_sim_free(struct cam_sim *sim, int free_devq) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + /* + * The umass_detach() cancels all transfers via + * usbd_transfer_unsetup(). This prevents also the start of new + * transfers since the transfer descriptors will be removed. Started + * transfers that are not in the transferring state will be canceled + * and the callbacks will be not called. Thus it is necessary to do + * this here if we are in the BUSY state. + */ + rtems_bsd_sim_wait_for_state_and_cancel_ccb(sim, BSD_SIM_IDLE); + + if (sim->disk != NULL) { + sc = rtems_media_server_disk_detach(sim->disk); + BSD_ASSERT_SC(sc); + + rtems_bsd_sim_wait_for_state(sim, BSD_SIM_DELETED); + } + + cv_destroy(&sim->state_changed); + free(sim, M_CAMSIM); +} + +struct cam_devq * +cam_simq_alloc(u_int32_t max_sim_transactions) +{ + return BSD_CAM_DEVQ_DUMMY; +} + +void +cam_simq_free(struct cam_devq *devq) +{ + BSD_ASSERT(devq == BSD_CAM_DEVQ_DUMMY); +} + +void +xpt_done(union ccb *done_ccb) +{ + (*done_ccb->ccb_h.cbfcnp)(NULL, done_ccb); +} + +int32_t +xpt_bus_register(struct cam_sim *sim, device_t parent, u_int32_t bus) +{ + /* + * We ignore this bus stuff completely. This is easier than removing + * the calls from "umass.c". + */ + + return CAM_SUCCESS; +} + +int32_t +xpt_bus_deregister(path_id_t pathid) +{ + /* + * We ignore this bus stuff completely. This is easier than removing + * the calls from "umass.c". + */ + + return CAM_REQ_CMP; +} |