summaryrefslogtreecommitdiffstats
path: root/sys/powerpc/ps3
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2011-08-14 00:20:37 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2011-08-14 00:20:37 +0000
commitae4052d3f376c0fa7111ebe2cc007fd43435d701 (patch)
treecd0e784c32101b07218ed9cadb5b59ce85cf50f2 /sys/powerpc/ps3
parentd7243f9f08159a162ce3b28842403e797017d945 (diff)
downloadFreeBSD-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.c703
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);
OpenPOWER on IntegriCloud