diff options
author | nwhitehorn <nwhitehorn@FreeBSD.org> | 2011-08-14 00:20:37 +0000 |
---|---|---|
committer | nwhitehorn <nwhitehorn@FreeBSD.org> | 2011-08-14 00:20:37 +0000 |
commit | ae4052d3f376c0fa7111ebe2cc007fd43435d701 (patch) | |
tree | cd0e784c32101b07218ed9cadb5b59ce85cf50f2 /sys/powerpc/ps3 | |
parent | d7243f9f08159a162ce3b28842403e797017d945 (diff) | |
download | FreeBSD-src-ae4052d3f376c0fa7111ebe2cc007fd43435d701.zip FreeBSD-src-ae4052d3f376c0fa7111ebe2cc007fd43435d701.tar.gz |
Add support for the Blu-Ray drive found in the Sony Playstation 3 and fix
some realted minor bugs in PS3 internal storage support.
Submitted by: glevand <geoffrey.levand@mail.ru>
Approved by: re (bz)
Diffstat (limited to 'sys/powerpc/ps3')
-rw-r--r-- | sys/powerpc/ps3/ps3cdrom.c | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/sys/powerpc/ps3/ps3cdrom.c b/sys/powerpc/ps3/ps3cdrom.c new file mode 100644 index 0000000..319090c --- /dev/null +++ b/sys/powerpc/ps3/ps3cdrom.c @@ -0,0 +1,703 @@ +/*- + * Copyright (C) 2010 Nathan Whitehorn + * Copyright (C) 2011 glevand <geoffrey.levand@mail.ru> + * 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, + * without modification, immediately at the beginning of the file. + * 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. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/ata.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kthread.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/pio.h> +#include <machine/bus.h> +#include <machine/platform.h> +#include <machine/pmap.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> +#include <cam/scsi/scsi_all.h> + +#include "ps3bus.h" +#include "ps3-hvcall.h" + +#define PS3CDROM_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3cdrom", \ + MTX_DEF) +#define PS3CDROM_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define PS3CDROM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define PS3CDROM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define PS3CDROM_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define PS3CDROM_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +#define PS3CDROM_MAX_XFERS 3 + +#define LV1_STORAGE_SEND_ATAPI_COMMAND 0x01 + +struct ps3cdrom_softc; + +struct ps3cdrom_xfer { + TAILQ_ENTRY(ps3cdrom_xfer) x_queue; + struct ps3cdrom_softc *x_sc; + union ccb *x_ccb; + bus_dmamap_t x_dmamap; + uint64_t x_tag; +}; + +TAILQ_HEAD(ps3cdrom_xferq, ps3cdrom_xfer); + +struct ps3cdrom_softc { + device_t sc_dev; + + struct mtx sc_mtx; + + uint64_t sc_blksize; + uint64_t sc_nblocks; + + int sc_irqid; + struct resource *sc_irq; + void *sc_irqctx; + + bus_dma_tag_t sc_dmatag; + + struct cam_sim *sc_sim; + struct cam_path *sc_path; + + struct ps3cdrom_xfer sc_xfer[PS3CDROM_MAX_XFERS]; + struct ps3cdrom_xferq sc_active_xferq; + struct ps3cdrom_xferq sc_free_xferq; +}; + +enum lv1_ata_proto { + NON_DATA_PROTO = 0x00, + PIO_DATA_IN_PROTO = 0x01, + PIO_DATA_OUT_PROTO = 0x02, + DMA_PROTO = 0x03 +}; + +enum lv1_ata_in_out { + DIR_WRITE = 0x00, + DIR_READ = 0x01 +}; + +struct lv1_atapi_cmd { + uint8_t pkt[32]; + uint32_t pktlen; + uint32_t nblocks; + uint32_t blksize; + uint32_t proto; /* enum lv1_ata_proto */ + uint32_t in_out; /* enum lv1_ata_in_out */ + uint64_t buf; + uint32_t arglen; +}; + +static void ps3cdrom_action(struct cam_sim *sim, union ccb *ccb); +static void ps3cdrom_poll(struct cam_sim *sim); +static void ps3cdrom_async(void *callback_arg, u_int32_t code, + struct cam_path* path, void *arg); + +static void ps3cdrom_intr(void *arg); + +static void ps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, + int error); + +static int ps3cdrom_decode_lv1_status(uint64_t status, + u_int8_t *sense_key, u_int8_t *asc, u_int8_t *ascq); + +static int +ps3cdrom_probe(device_t dev) +{ + if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE || + ps3bus_get_devtype(dev) != PS3_DEVTYPE_CDROM) + return (ENXIO); + + device_set_desc(dev, "Playstation 3 CDROM"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +ps3cdrom_attach(device_t dev) +{ + struct ps3cdrom_softc *sc = device_get_softc(dev); + struct cam_devq *devq; + struct ps3cdrom_xfer *xp; + struct ccb_setasync csa; + int i, err; + + sc->sc_dev = dev; + + PS3CDROM_LOCK_INIT(sc); + + /* Setup interrupt handler */ + + sc->sc_irqid = 0; + sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid, + RF_ACTIVE); + if (!sc->sc_irq) { + device_printf(dev, "Could not allocate IRQ\n"); + err = ENXIO; + goto fail_destroy_lock; + } + + err = bus_setup_intr(dev, sc->sc_irq, + INTR_TYPE_CAM | INTR_MPSAFE | INTR_ENTROPY, + NULL, ps3cdrom_intr, sc, &sc->sc_irqctx); + if (err) { + device_printf(dev, "Could not setup IRQ\n"); + err = ENXIO; + goto fail_release_intr; + } + + /* Setup DMA */ + + err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_UNRESTRICTED, 1, PAGE_SIZE, 0, + busdma_lock_mutex, &sc->sc_mtx, &sc->sc_dmatag); + if (err) { + device_printf(dev, "Could not create DMA tag\n"); + err = ENXIO; + goto fail_teardown_intr; + } + + /* Setup transfer queues */ + + TAILQ_INIT(&sc->sc_active_xferq); + TAILQ_INIT(&sc->sc_free_xferq); + + for (i = 0; i < PS3CDROM_MAX_XFERS; i++) { + xp = &sc->sc_xfer[i]; + xp->x_sc = sc; + + err = bus_dmamap_create(sc->sc_dmatag, BUS_DMA_COHERENT, + &xp->x_dmamap); + if (err) { + device_printf(dev, "Could not create DMA map (%d)\n", + err); + goto fail_destroy_dmamap; + } + + TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); + } + + /* Setup CAM */ + + devq = cam_simq_alloc(PS3CDROM_MAX_XFERS - 1); + if (!devq) { + device_printf(dev, "Could not allocate SIM queue\n"); + err = ENOMEM; + goto fail_destroy_dmatag; + } + + sc->sc_sim = cam_sim_alloc(ps3cdrom_action, ps3cdrom_poll, "ps3cdrom", + sc, device_get_unit(dev), &sc->sc_mtx, PS3CDROM_MAX_XFERS - 1, 0, + devq); + if (!sc->sc_sim) { + device_printf(dev, "Could not allocate SIM\n"); + cam_simq_free(devq); + err = ENOMEM; + goto fail_destroy_dmatag; + } + + /* Setup XPT */ + + PS3CDROM_LOCK(sc); + + err = xpt_bus_register(sc->sc_sim, dev, 0); + if (err != CAM_SUCCESS) { + device_printf(dev, "Could not register XPT bus\n"); + err = ENXIO; + PS3CDROM_UNLOCK(sc); + goto fail_free_sim; + } + + err = xpt_create_path(&sc->sc_path, NULL, cam_sim_path(sc->sc_sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); + if (err != CAM_REQ_CMP) { + device_printf(dev, "Could not create XPT path\n"); + err = ENOMEM; + PS3CDROM_UNLOCK(sc); + goto fail_unregister_xpt_bus; + } + + xpt_setup_ccb(&csa.ccb_h, sc->sc_path, 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = AC_LOST_DEVICE; + csa.callback = ps3cdrom_async; + csa.callback_arg = sc->sc_sim; + xpt_action((union ccb *) &csa); + + CAM_DEBUG(sc->sc_path, CAM_DEBUG_TRACE, + ("registered SIM for ps3cdrom%d\n", device_get_unit(dev))); + + PS3CDROM_UNLOCK(sc); + + return (BUS_PROBE_SPECIFIC); + +fail_unregister_xpt_bus: + + xpt_bus_deregister(cam_sim_path(sc->sc_sim)); + +fail_free_sim: + + cam_sim_free(sc->sc_sim, TRUE); + +fail_destroy_dmamap: + + while ((xp = TAILQ_FIRST(&sc->sc_free_xferq))) { + TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue); + bus_dmamap_destroy(sc->sc_dmatag, xp->x_dmamap); + } + +fail_destroy_dmatag: + + bus_dma_tag_destroy(sc->sc_dmatag); + +fail_teardown_intr: + + bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx); + +fail_release_intr: + + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq); + +fail_destroy_lock: + + PS3CDROM_LOCK_DESTROY(sc); + + return (err); +} + +static int +ps3cdrom_detach(device_t dev) +{ + struct ps3cdrom_softc *sc = device_get_softc(dev); + int i; + + xpt_async(AC_LOST_DEVICE, sc->sc_path, NULL); + xpt_free_path(sc->sc_path); + xpt_bus_deregister(cam_sim_path(sc->sc_sim)); + cam_sim_free(sc->sc_sim, TRUE); + + for (i = 0; i < PS3CDROM_MAX_XFERS; i++) + bus_dmamap_destroy(sc->sc_dmatag, sc->sc_xfer[i].x_dmamap); + + bus_dma_tag_destroy(sc->sc_dmatag); + + bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq); + + PS3CDROM_LOCK_DESTROY(sc); + + return (0); +} + +static void +ps3cdrom_action(struct cam_sim *sim, union ccb *ccb) +{ + struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *)cam_sim_softc(sim); + device_t dev = sc->sc_dev; + struct ps3cdrom_xfer *xp; + int err; + + PS3CDROM_ASSERT_LOCKED(sc); + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("function code 0x%02x\n", ccb->ccb_h.func_code)); + + switch (ccb->ccb_h.func_code) { + case XPT_SCSI_IO: + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) + break; + + if(ccb->ccb_h.target_id > 0) { + ccb->ccb_h.status = CAM_TID_INVALID; + break; + } + + if(ccb->ccb_h.target_lun > 0) { + ccb->ccb_h.status = CAM_LUN_INVALID; + break; + } + + xp = TAILQ_FIRST(&sc->sc_free_xferq); + + KASSERT(xp != NULL, ("no free transfers")); + + xp->x_ccb = ccb; + + TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue); + + err = bus_dmamap_load(sc->sc_dmatag, xp->x_dmamap, + ccb->csio.data_ptr, ccb->csio.dxfer_len, ps3cdrom_transfer, + xp, 0); + if (err && err != EINPROGRESS) { + device_printf(dev, "Could not load DMA map (%d)\n", + err); + + xp->x_ccb = NULL; + TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); + ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + break; + } + return; + case XPT_SET_TRAN_SETTINGS: + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + break; + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts = &ccb->cts; + + cts->protocol = PROTO_SCSI; + cts->protocol_version = SCSI_REV_2; + cts->transport = XPORT_SPI; + cts->transport_version = 2; + cts->proto_specific.valid = 0; + cts->xport_specific.valid = 0; + ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_RESET_BUS: + case XPT_RESET_DEV: + ccb->ccb_h.status = CAM_REQ_CMP; + break; + case XPT_CALC_GEOMETRY: + cam_calc_geometry(&ccb->ccg, 1); + break; + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = 0; + cpi->target_sprt = 0; + cpi->hba_inquiry = PI_SDTR_ABLE; + cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN | PIM_NO_6_BYTE; + cpi->hba_eng_cnt = 0; + bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags)); + cpi->max_target = 0; + cpi->max_lun = 0; + cpi->initiator_id = 7; + cpi->bus_id = cam_sim_bus(sim); + cpi->unit_number = cam_sim_unit(sim); + cpi->base_transfer_speed = 150000; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "Sony", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->transport = XPORT_SPI; + cpi->transport_version = 2; + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_2; + cpi->maxio = PAGE_SIZE; + cpi->ccb_h.status = CAM_REQ_CMP; + break; + } + default: + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("unsupported function code 0x%02x\n", + ccb->ccb_h.func_code)); + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + + xpt_done(ccb); +} + +static void +ps3cdrom_poll(struct cam_sim *sim) +{ + ps3cdrom_intr(cam_sim_softc(sim)); +} + +static void +ps3cdrom_async(void *callback_arg, u_int32_t code, + struct cam_path* path, void *arg) +{ + switch (code) { + case AC_LOST_DEVICE: + xpt_print_path(path); + break; + default: + break; + } +} + +static void +ps3cdrom_intr(void *arg) +{ + struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *) arg; + device_t dev = sc->sc_dev; + uint64_t devid = ps3bus_get_device(dev); + struct ps3cdrom_xfer *xp; + union ccb *ccb; + u_int8_t *cdb, sense_key, asc, ascq; + uint64_t tag, status; + + if (lv1_storage_get_async_status(devid, &tag, &status) != 0) + return; + + PS3CDROM_LOCK(sc); + + /* Find transfer with the returned tag */ + + TAILQ_FOREACH(xp, &sc->sc_active_xferq, x_queue) { + if (xp->x_tag == tag) + break; + } + + if (xp) { + ccb = xp->x_ccb; + cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ? + ccb->csio.cdb_io.cdb_ptr : + ccb->csio.cdb_io.cdb_bytes; + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("ATAPI command 0x%02x tag 0x%016lx completed (0x%016lx)\n", + cdb[0], tag, status)); + + if (!status) { + ccb->csio.scsi_status = SCSI_STATUS_OK; + ccb->csio.resid = 0; + ccb->ccb_h.status = CAM_REQ_CMP; + } else { + ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; + ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + + if (!ps3cdrom_decode_lv1_status(status, &sense_key, + &asc, &ascq)) { + struct scsi_sense_data sense_data; + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("sense key 0x%02x asc 0x%02x ascq 0x%02x\n", + sense_key, asc, ascq)); + + bzero(&sense_data, sizeof(sense_data)); + sense_data.error_code = SSD_CURRENT_ERROR; + sense_data.flags |= sense_key; + sense_data.extra_len = 0xa; + sense_data.add_sense_code = asc; + sense_data.add_sense_code_qual = ascq; + ccb->csio.sense_len = sizeof(sense_data); + bcopy(&sense_data, &ccb->csio.sense_data, + ccb->csio.sense_len); + ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | + CAM_AUTOSNS_VALID; + } + + if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) + ccb->csio.resid = ccb->csio.dxfer_len; + } + + if (ccb->ccb_h.flags & CAM_DIR_IN) + bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap, + BUS_DMASYNC_POSTREAD); + + bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap); + + xp->x_ccb = NULL; + TAILQ_REMOVE(&sc->sc_active_xferq, xp, x_queue); + TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); + + xpt_done(ccb); + } else { + device_printf(dev, + "Could not find transfer with tag 0x%016lx\n", tag); + } + + PS3CDROM_UNLOCK(sc); +} + +static void +ps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct ps3cdrom_xfer *xp = (struct ps3cdrom_xfer *) arg; + struct ps3cdrom_softc *sc = xp->x_sc; + device_t dev = sc->sc_dev; + uint64_t devid = ps3bus_get_device(dev); + union ccb *ccb = xp->x_ccb; + u_int8_t *cdb; + uint64_t start_sector, block_count; + int err; + + KASSERT(nsegs == 1, ("invalid number of DMA segments")); + + PS3CDROM_ASSERT_LOCKED(sc); + + if (error) { + device_printf(dev, "Could not load DMA map (%d)\n", error); + + xp->x_ccb = NULL; + TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); + ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; + xpt_done(ccb); + return; + } + + cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ? + ccb->csio.cdb_io.cdb_ptr : + ccb->csio.cdb_io.cdb_bytes; + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("ATAPI command 0x%02x cdb_len %d dxfer_len %d\n ", cdb[0], + ccb->csio.cdb_len, ccb->csio.dxfer_len)); + + switch (cdb[0]) { + case READ_10: + start_sector = (cdb[2] << 24) | (cdb[3] << 16) | + (cdb[4] << 8) | cdb[5]; + block_count = (cdb[7] << 8) | cdb[8]; + + err = lv1_storage_read(devid, 0 /* region id */, + start_sector, block_count, 0 /* flags */, segs[0].ds_addr, + &xp->x_tag); + bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap, + BUS_DMASYNC_POSTREAD); + break; + case WRITE_10: + start_sector = (cdb[2] << 24) | (cdb[3] << 16) | + (cdb[4] << 8) | cdb[5]; + block_count = (cdb[7] << 8) | cdb[8]; + + bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap, + BUS_DMASYNC_PREWRITE); + err = lv1_storage_write(devid, 0 /* region id */, + start_sector, block_count, 0 /* flags */, + segs[0].ds_addr, &xp->x_tag); + break; + default: + { + struct lv1_atapi_cmd atapi_cmd; + + bzero(&atapi_cmd, sizeof(atapi_cmd)); + atapi_cmd.pktlen = 12; + bcopy(cdb, atapi_cmd.pkt, ccb->csio.cdb_len); + + if (ccb->ccb_h.flags & CAM_DIR_IN) { + atapi_cmd.in_out = DIR_READ; + atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ? + DMA_PROTO : PIO_DATA_IN_PROTO; + } else if (ccb->ccb_h.flags & CAM_DIR_OUT) { + atapi_cmd.in_out = DIR_WRITE; + atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ? + DMA_PROTO : PIO_DATA_OUT_PROTO; + } else { + atapi_cmd.proto = NON_DATA_PROTO; + } + + atapi_cmd.nblocks = atapi_cmd.arglen = segs[0].ds_len; + atapi_cmd.blksize = 1; + atapi_cmd.buf = segs[0].ds_addr; + + if (ccb->ccb_h.flags & CAM_DIR_OUT) + bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap, + BUS_DMASYNC_PREWRITE); + + err = lv1_storage_send_device_command(devid, + LV1_STORAGE_SEND_ATAPI_COMMAND, vtophys(&atapi_cmd), + sizeof(atapi_cmd), atapi_cmd.buf, atapi_cmd.arglen, + &xp->x_tag); + + break; + } + } + + if (err) { + struct scsi_sense_data sense_data; + + device_printf(dev, "ATAPI command 0x%02x failed (%d)\n", + cdb[0], err); + + bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap); + + xp->x_ccb = NULL; + TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue); + + bzero(&sense_data, sizeof(sense_data)); + sense_data.error_code = SSD_CURRENT_ERROR; + sense_data.flags |= SSD_KEY_ILLEGAL_REQUEST; + ccb->csio.sense_len = sizeof(sense_data); + bcopy(&sense_data, &ccb->csio.sense_data, ccb->csio.sense_len); + ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID; + xpt_done(ccb); + } else { + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("ATAPI command 0x%02x tag 0x%016lx submitted\n ", cdb[0], + xp->x_tag)); + + TAILQ_INSERT_TAIL(&sc->sc_active_xferq, xp, x_queue); + ccb->ccb_h.status |= CAM_SIM_QUEUED; + } +} + +static int +ps3cdrom_decode_lv1_status(uint64_t status, u_int8_t *sense_key, u_int8_t *asc, + u_int8_t *ascq) +{ + if (((status >> 24) & 0xff) != SCSI_STATUS_CHECK_COND) + return -1; + + *sense_key = (status >> 16) & 0xff; + *asc = (status >> 8) & 0xff; + *ascq = status & 0xff; + + return (0); +} + +static device_method_t ps3cdrom_methods[] = { + DEVMETHOD(device_probe, ps3cdrom_probe), + DEVMETHOD(device_attach, ps3cdrom_attach), + DEVMETHOD(device_detach, ps3cdrom_detach), + {0, 0}, +}; + +static driver_t ps3cdrom_driver = { + "ps3cdrom", + ps3cdrom_methods, + sizeof(struct ps3cdrom_softc), +}; + +static devclass_t ps3cdrom_devclass; + +DRIVER_MODULE(ps3cdrom, ps3bus, ps3cdrom_driver, ps3cdrom_devclass, 0, 0); +MODULE_DEPEND(ps3cdrom, cam, 1, 1, 1); |