diff --git a/sys/boot/powerpc/ps3/Makefile b/sys/boot/powerpc/ps3/Makefile index 0f0b7829a709..0dee5f4c7a8d 100644 --- a/sys/boot/powerpc/ps3/Makefile +++ b/sys/boot/powerpc/ps3/Makefile @@ -10,7 +10,8 @@ INSTALLFLAGS= -b # Architecture-specific loader code SRCS= start.S conf.c metadata.c vers.c main.c devicename.c ppc64_elf_freebsd.c -SRCS+= lv1call.S ps3cons.c font.h ps3mmu.c ps3net.c ps3repo.c ps3stor.c ps3disk.c +SRCS+= lv1call.S ps3cons.c font.h ps3mmu.c ps3net.c ps3repo.c \ + ps3stor.c ps3disk.c ps3cdrom.c SRCS+= ucmpdi2.c LOADER_DISK_SUPPORT?= yes diff --git a/sys/boot/powerpc/ps3/conf.c b/sys/boot/powerpc/ps3/conf.c index 200fc7f29c69..3a5ae4c41f02 100644 --- a/sys/boot/powerpc/ps3/conf.c +++ b/sys/boot/powerpc/ps3/conf.c @@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$"); #endif extern struct devsw ps3disk; +extern struct devsw ps3cdrom; /* * We could use linker sets for some or all of these, but @@ -47,7 +48,10 @@ extern struct devsw ps3disk; /* Exported for libstand */ struct devsw *devsw[] = { -#if defined(LOADER_DISK_SUPPORT) || defined(LOADER_CD9660_SUPPORT) +#if defined(LOADER_CD9660_SUPPORT) + &ps3cdrom, +#endif +#if defined(LOADER_DISK_SUPPORT) &ps3disk, #endif #if defined(LOADER_NET_SUPPORT) diff --git a/sys/boot/powerpc/ps3/devicename.c b/sys/boot/powerpc/ps3/devicename.c index c46bc8928047..041f853986d5 100644 --- a/sys/boot/powerpc/ps3/devicename.c +++ b/sys/boot/powerpc/ps3/devicename.c @@ -157,6 +157,7 @@ ps3_parsedev(struct ps3_devdesc **dev, const char *devspec, const char **path) break; case DEVT_NET: + case DEVT_CD: /* * PS3 only has one network interface (well, two, but * netbooting over wireless is not something I'm going @@ -213,6 +214,7 @@ ps3_fmtdev(void *vdev) break; case DEVT_NET: + case DEVT_CD: sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); break; } diff --git a/sys/boot/powerpc/ps3/lv1call.S b/sys/boot/powerpc/ps3/lv1call.S index 1c1e28ee0bde..a399a9c3bbf6 100644 --- a/sys/boot/powerpc/ps3/lv1call.S +++ b/sys/boot/powerpc/ps3/lv1call.S @@ -307,14 +307,8 @@ lv1_storage_read: sldi %r6,%r9,32 clrldi %r7,%r10,32 or %r6,%r6,%r7 - lwz %r7,8(%r1) - lwz %r8,12(%r1) - sldi %r7,%r7,32 - or %r7,%r7,%r8 - lwz %r8,16(%r1) - lwz %r9,20(%r1) - sldi %r8,%r8,32 - or %r8,%r8,%r9 + ld %r7,8(%r1) + ld %r8,16(%r1) li %r11,245 hc diff --git a/sys/boot/powerpc/ps3/lv1call.h b/sys/boot/powerpc/ps3/lv1call.h index da47afb4dad8..fb8044825bd0 100644 --- a/sys/boot/powerpc/ps3/lv1call.h +++ b/sys/boot/powerpc/ps3/lv1call.h @@ -69,12 +69,12 @@ int lv1_net_stop_tx_dma(int bus, int dev, int); int lv1_net_stop_rx_dma(int bus, int dev, int); int lv1_get_repository_node_value(uint64_t lpar_id, uint64_t n1, uint64_t n2, - uint64_t n3, uint64_t n4, uint64_t *v1, uint64_t *v2); + uint64_t n3, uint64_t n4, uint64_t *v1, uint64_t *v2); -int lv1_storage_read(uint64_t dev_id, uint64_t region_id, - uint64_t start_sector, uint64_t sector_count, - uint64_t flags, uint64_t buf, uint64_t *tag); -int lv1_storage_check_async_status(uint64_t dev_id, uint64_t tag, uint64_t *status); +int lv1_storage_read(uint64_t dev_id, uint64_t region_id, uint64_t start_sector, + uint64_t sector_count, uint64_t flags, uint64_t buf, uint64_t *tag); +int lv1_storage_check_async_status(uint64_t dev_id, uint64_t tag, + uint64_t *status); #endif diff --git a/sys/boot/powerpc/ps3/main.c b/sys/boot/powerpc/ps3/main.c index db808adba289..64bd7e9f9a24 100644 --- a/sys/boot/powerpc/ps3/main.c +++ b/sys/boot/powerpc/ps3/main.c @@ -92,11 +92,17 @@ main(void) } } - printf("\nDevice: %s\n", devsw[i]->dv_name); - currdev.d_dev = devsw[i]; currdev.d_type = currdev.d_dev->dv_type; + if (strcmp(devsw[i]->dv_name, "cd") == 0) { + f.f_devdata = &currdev; + currdev.d_unit = 0; + + if (devsw[i]->dv_open(&f, &currdev) == 0) + break; + } + if (strcmp(devsw[i]->dv_name, "disk") == 0) { f.f_devdata = &currdev; currdev.d_unit = 3; @@ -113,6 +119,8 @@ main(void) if (devsw[i] == NULL) panic("No boot device found!"); + else + printf("Boot device: %s\n", devsw[i]->dv_name); /* * Get timebase at boot. diff --git a/sys/boot/powerpc/ps3/ps3cdrom.c b/sys/boot/powerpc/ps3/ps3cdrom.c new file mode 100644 index 000000000000..843ecd56052f --- /dev/null +++ b/sys/boot/powerpc/ps3/ps3cdrom.c @@ -0,0 +1,154 @@ +/*- + * Copyright (C) 2011 glevand + * 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. + * 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 TOOLS GMBH 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include + +#include "bootstrap.h" +#include "ps3bus.h" +#include "ps3devdesc.h" +#include "ps3stor.h" + +#define dev_printf(dev, fmt, args...) \ + printf("%s%d: " fmt "\n", dev->d_dev->dv_name, dev->d_unit, ##args) + +#ifdef CD_DEBUG +#define DEBUG(fmt, args...) printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args) +#else +#define DEBUG(fmt, args...) +#endif + +static int ps3cdrom_init(void); +static int ps3cdrom_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int ps3cdrom_open(struct open_file *f, ...); +static int ps3cdrom_close(struct open_file *f); +static void ps3cdrom_print(int verbose); + +struct devsw ps3cdrom = { + "cd", + DEVT_CD, + ps3cdrom_init, + ps3cdrom_strategy, + ps3cdrom_open, + ps3cdrom_close, + noioctl, + ps3cdrom_print, +}; + +static struct ps3_stordev stor_dev; + +static int ps3cdrom_init(void) +{ + int err; + + err = ps3stor_setup(&stor_dev, PS3_DEV_TYPE_STOR_CDROM); + if (err) + return err; + + return 0; +} + +static int ps3cdrom_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize) +{ + struct ps3_devdesc *dev = (struct ps3_devdesc *) devdata; + int err; + + DEBUG("d_unit=%u dblk=%llu size=%u", dev->d_unit, dblk, size); + + if (flag != F_READ) { + dev_printf(dev, "write operation is not supported!"); + return EROFS; + } + + if (dblk % (stor_dev.sd_blksize / DEV_BSIZE) != 0) + return EINVAL; + + dblk /= (stor_dev.sd_blksize / DEV_BSIZE); + + if (size % stor_dev.sd_blksize) { + dev_printf(dev, + "size=%u is not multiple of device block size=%llu", size, + stor_dev.sd_blksize); + return EINVAL; + } + + if (rsize) + *rsize = 0; + + err = ps3stor_read_sectors(&stor_dev, dev->d_unit, dblk, + size / stor_dev.sd_blksize, 0, buf); + + if (!err && rsize) + *rsize = size; + + if (err) + dev_printf(dev, + "read operation failed dblk=%llu size=%d err=%d", dblk, + size, err); + + return err; +} + +static int ps3cdrom_open(struct open_file *f, ...) +{ + char buf[2048]; + va_list ap; + struct ps3_devdesc *dev; + int err; + + va_start(ap, f); + dev = va_arg(ap, struct ps3_devdesc *); + va_end(ap); + + if (dev->d_unit > 0) { + dev_printf(dev, "attempt to open nonexistent disk"); + return ENXIO; + } + + err = ps3stor_read_sectors(&stor_dev, dev->d_unit, 16, 1, 0, buf); + if (err) + return EIO; + + /* Do not attach if not ISO9660 (workaround for buggy firmware) */ + if (memcmp(buf, "\001CD001", 6) != 0) + return EIO; + + return 0; +} + +static int ps3cdrom_close(struct open_file *f) +{ + return 0; +} + +static void ps3cdrom_print(int verbose) +{ +} diff --git a/sys/boot/powerpc/ps3/ps3stor.c b/sys/boot/powerpc/ps3/ps3stor.c index 667b39ca5e98..bbfc56a7ae2a 100644 --- a/sys/boot/powerpc/ps3/ps3stor.c +++ b/sys/boot/powerpc/ps3/ps3stor.c @@ -52,35 +52,39 @@ int ps3stor_setup(struct ps3_stordev *sd, int type) if (err) goto out; - err = ps3repo_read_bus_dev_id(sd->sd_busidx, sd->sd_devidx, &sd->sd_devid); + err = ps3repo_read_bus_dev_id(sd->sd_busidx, sd->sd_devidx, + &sd->sd_devid); if (err) goto out; - err = ps3repo_read_bus_dev_blk_size(sd->sd_busidx, sd->sd_devidx, &sd->sd_blksize); + err = ps3repo_read_bus_dev_blk_size(sd->sd_busidx, sd->sd_devidx, + &sd->sd_blksize); if (err) goto out; - err = ps3repo_read_bus_dev_nblocks(sd->sd_busidx, sd->sd_devidx, &sd->sd_nblocks); + err = ps3repo_read_bus_dev_nblocks(sd->sd_busidx, sd->sd_devidx, + &sd->sd_nblocks); if (err) goto out; - err = ps3repo_read_bus_dev_nregs(sd->sd_busidx, sd->sd_devidx, &sd->sd_nregs); + err = ps3repo_read_bus_dev_nregs(sd->sd_busidx, sd->sd_devidx, + &sd->sd_nregs); if (err) goto out; for (i = 0; i < sd->sd_nregs; i++) { - err = ps3repo_read_bus_dev_reg_id(sd->sd_busidx, sd->sd_devidx, i, - &sd->sd_regs[i].sr_id); + err = ps3repo_read_bus_dev_reg_id(sd->sd_busidx, sd->sd_devidx, + i, &sd->sd_regs[i].sr_id); if (err) goto out; - err = ps3repo_read_bus_dev_reg_start(sd->sd_busidx, sd->sd_devidx, i, - &sd->sd_regs[i].sr_start); + err = ps3repo_read_bus_dev_reg_start(sd->sd_busidx, + sd->sd_devidx, i, &sd->sd_regs[i].sr_start); if (err) goto out; - err = ps3repo_read_bus_dev_reg_size(sd->sd_busidx, sd->sd_devidx, i, - &sd->sd_regs[i].sr_size); + err = ps3repo_read_bus_dev_reg_size(sd->sd_busidx, + sd->sd_devidx, i, &sd->sd_regs[i].sr_size); if (err) goto out; } @@ -109,19 +113,20 @@ int ps3stor_setup(struct ps3_stordev *sd, int type) return err; } +static char dma_buf[2048] __aligned(2048); + int ps3stor_read_sectors(struct ps3_stordev *sd, int regidx, uint64_t start_sector, uint64_t sector_count, uint64_t flags, char *buf) { #define MIN(a, b) ((a) <= (b) ? (a) : (b)) -#define BOUNCE_SECTORS 4 +#define BOUNCE_SECTORS (sizeof(dma_buf) / sd->sd_blksize) #define ASYNC_STATUS_POLL_PERIOD 100 /* microseconds */ struct ps3_storreg *reg = &sd->sd_regs[regidx]; - char dma_buf[sd->sd_blksize * BOUNCE_SECTORS]; uint64_t nleft, nread, nsectors; uint64_t tag, status; unsigned int timeout; - int err; + int err = 0; nleft = sector_count; nread = 0; @@ -129,8 +134,9 @@ int ps3stor_read_sectors(struct ps3_stordev *sd, int regidx, while (nleft) { nsectors = MIN(nleft, BOUNCE_SECTORS); - err = lv1_storage_read(sd->sd_devid, reg->sr_id, start_sector + nread, nsectors, - flags, (uint32_t) dma_buf, &tag); + err = lv1_storage_read(sd->sd_devid, reg->sr_id, + start_sector + nread, nsectors, flags, (uint32_t)dma_buf, + &tag); if (err) return err; @@ -140,7 +146,8 @@ int ps3stor_read_sectors(struct ps3_stordev *sd, int regidx, if (timeout < ASYNC_STATUS_POLL_PERIOD) return ETIMEDOUT; - err = lv1_storage_check_async_status(sd->sd_devid, tag, &status); + err = lv1_storage_check_async_status(sd->sd_devid, tag, + &status); if (!err && !status) break; @@ -148,12 +155,16 @@ int ps3stor_read_sectors(struct ps3_stordev *sd, int regidx, timeout -= ASYNC_STATUS_POLL_PERIOD; } - memcpy(buf + nread * sd->sd_blksize, (u_char *) dma_buf, nsectors * sd->sd_blksize); + if (status != 0) + return EIO; + + memcpy(buf + nread * sd->sd_blksize, (u_char *)dma_buf, + nsectors * sd->sd_blksize); nread += nsectors; nleft -= nsectors; } - return 0; + return err; #undef MIN #undef BOUNCE_SECTORS diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 9f5a35701fe0..031a1848f800 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -207,8 +207,8 @@ powerpc/ps3/ohci_ps3.c optional ps3 ohci powerpc/ps3/if_glc.c optional ps3 glc powerpc/ps3/mmu_ps3.c optional ps3 powerpc/ps3/platform_ps3.c optional ps3 -powerpc/ps3/ps3ata.c optional ps3 ps3ata powerpc/ps3/ps3bus.c optional ps3 +powerpc/ps3/ps3cdrom.c optional ps3 scbus powerpc/ps3/ps3disk.c optional ps3 powerpc/ps3/ps3pic.c optional ps3 powerpc/ps3/ps3_syscons.c optional ps3 sc diff --git a/sys/powerpc/ps3/ps3cdrom.c b/sys/powerpc/ps3/ps3cdrom.c new file mode 100644 index 000000000000..319090cacb56 --- /dev/null +++ b/sys/powerpc/ps3/ps3cdrom.c @@ -0,0 +1,703 @@ +/*- + * Copyright (C) 2010 Nathan Whitehorn + * Copyright (C) 2011 glevand + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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);