diff options
Diffstat (limited to 'sys/powerpc/ps3/ps3disk.c')
| -rw-r--r-- | sys/powerpc/ps3/ps3disk.c | 712 | 
1 files changed, 712 insertions, 0 deletions
| diff --git a/sys/powerpc/ps3/ps3disk.c b/sys/powerpc/ps3/ps3disk.c new file mode 100644 index 000000000000..4d9976ecccee --- /dev/null +++ b/sys/powerpc/ps3/ps3disk.c @@ -0,0 +1,712 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * 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. + * 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/param.h> +#include <sys/systm.h> +#include <sys/sysctl.h> +#include <sys/disk.h> +#include <sys/bio.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.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/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <geom/geom_disk.h> + +#include "ps3bus.h" +#include "ps3-hvcall.h" + +#define PS3DISK_LOCK_INIT(_sc)		\ +	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3disk", MTX_DEF) +#define PS3DISK_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx); +#define PS3DISK_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx) +#define	PS3DISK_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx) +#define PS3DISK_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define PS3DISK_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +#define LV1_STORAGE_ATA_HDDOUT 		0x23 + +static SYSCTL_NODE(_hw, OID_AUTO, ps3disk, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, +    "PS3 Disk driver parameters"); + +#ifdef PS3DISK_DEBUG +static int ps3disk_debug = 0; +SYSCTL_INT(_hw_ps3disk, OID_AUTO, debug, CTLFLAG_RW, &ps3disk_debug, +	0, "control debugging printfs"); +TUNABLE_INT("hw.ps3disk.debug", &ps3disk_debug); +enum { +	PS3DISK_DEBUG_INTR	= 0x00000001, +	PS3DISK_DEBUG_TASK	= 0x00000002, +	PS3DISK_DEBUG_READ	= 0x00000004, +	PS3DISK_DEBUG_WRITE	= 0x00000008, +	PS3DISK_DEBUG_FLUSH	= 0x00000010, +	PS3DISK_DEBUG_ANY	= 0xffffffff +}; +#define	DPRINTF(sc, m, fmt, ...)				\ +do {								\ +	if (sc->sc_debug & (m))					\ +		printf(fmt, __VA_ARGS__);			\ +} while (0) +#else +#define	DPRINTF(sc, m, fmt, ...) +#endif + +struct ps3disk_region { +	uint64_t r_id; +	uint64_t r_start; +	uint64_t r_size; +	uint64_t r_flags; +}; + +struct ps3disk_softc { +	device_t sc_dev; + +	struct mtx sc_mtx; + +	uint64_t sc_blksize; +	uint64_t sc_nblocks; + +	uint64_t sc_nregs; +	struct ps3disk_region *sc_reg; + +	int sc_irqid; +	struct resource	*sc_irq; +	void *sc_irqctx; + +	struct disk **sc_disk; + +	struct bio_queue_head sc_bioq; +	struct bio_queue_head sc_deferredq; +	struct proc *sc_task;	 + +	bus_dma_tag_t sc_dmatag; + +	int sc_running; +	int sc_debug; +}; + +static int ps3disk_open(struct disk *dp); +static int ps3disk_close(struct disk *dp); +static void ps3disk_strategy(struct bio *bp); + +static void ps3disk_task(void *arg); +static void ps3disk_intr(void *arg); +static int ps3disk_get_disk_geometry(struct ps3disk_softc *sc); +static int ps3disk_enum_regions(struct ps3disk_softc *sc); +static void ps3disk_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, +    int error); + +static void ps3disk_sysctlattach(struct ps3disk_softc *sc); + +static MALLOC_DEFINE(M_PS3DISK, "ps3disk", "PS3 Disk"); + +static int +ps3disk_probe(device_t dev) +{ +	if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE || +	    ps3bus_get_devtype(dev) != PS3_DEVTYPE_DISK) +		return (ENXIO); + +	device_set_desc(dev, "Playstation 3 Disk"); + +	return (BUS_PROBE_SPECIFIC); +} + +static int +ps3disk_attach(device_t dev) +{ +	struct ps3disk_softc *sc; +	struct disk *d; +	intmax_t mb; +	uint64_t junk; +	char unit; +	int i, err; + +	sc = device_get_softc(dev); +	sc->sc_dev = dev; + +	PS3DISK_LOCK_INIT(sc); + +	err = ps3disk_get_disk_geometry(sc); +	if (err) { +		device_printf(dev, "Could not get disk geometry\n"); +		err = ENXIO; +		goto fail_destroy_lock; +	} + +	device_printf(dev, "block size %lu total blocks %lu\n", +	    sc->sc_blksize, sc->sc_nblocks); + +	err = ps3disk_enum_regions(sc); +	if (err) { +		device_printf(dev, "Could not enumerate disk regions\n"); +		err = ENXIO; +		goto fail_destroy_lock; +	} + +	device_printf(dev, "Found %lu regions\n", sc->sc_nregs); + +	if (!sc->sc_nregs) { +		err = ENXIO; +		goto fail_destroy_lock; +	} + +	/* 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_free_regions; +	} + +	err = bus_setup_intr(dev, sc->sc_irq, +	    INTR_TYPE_BIO | INTR_MPSAFE | INTR_ENTROPY, +	    NULL, ps3disk_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 disks */ + +	sc->sc_disk = malloc(sc->sc_nregs * sizeof(struct disk *), +	    M_PS3DISK, M_ZERO | M_WAITOK); +	if (!sc->sc_disk) { +		device_printf(dev, "Could not allocate disk(s)\n"); +		err = ENOMEM; +		goto fail_teardown_intr; +	} + +	for (i = 0; i < sc->sc_nregs; i++) { +		struct ps3disk_region *rp = &sc->sc_reg[i]; + +		d = sc->sc_disk[i] = disk_alloc(); +		d->d_open = ps3disk_open; +		d->d_close = ps3disk_close; +		d->d_strategy = ps3disk_strategy; +		d->d_name = "ps3disk"; +		d->d_drv1 = sc; +		d->d_maxsize = PAGE_SIZE; +		d->d_sectorsize = sc->sc_blksize; +		d->d_unit = i; +		d->d_mediasize = sc->sc_reg[i].r_size * sc->sc_blksize; +		d->d_flags |= DISKFLAG_CANFLUSHCACHE; + +		mb = d->d_mediasize >> 20; +		unit = 'M'; +		if (mb >= 10240) { +			unit = 'G'; +			mb /= 1024; +		} + +		/* Test to see if we can read this region */ +		err = lv1_storage_read(ps3bus_get_device(dev), d->d_unit, +		    0, 0, rp->r_flags, 0, &junk); +		device_printf(dev, "region %d %ju%cB%s\n", i, mb, unit, +		    (err == LV1_DENIED_BY_POLICY) ?  " (hypervisor protected)" +		    : ""); + +		if (err != LV1_DENIED_BY_POLICY) +			disk_create(d, DISK_VERSION); +	} +	err = 0; + +	bioq_init(&sc->sc_bioq); +	bioq_init(&sc->sc_deferredq); +	kproc_create(&ps3disk_task, sc, &sc->sc_task, 0, 0, "ps3disk"); + +	ps3disk_sysctlattach(sc); +	sc->sc_running = 1; +	return (0); + +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_free_regions: +	free(sc->sc_reg, M_PS3DISK); +fail_destroy_lock: +	PS3DISK_LOCK_DESTROY(sc); +	return (err); +} + +static int +ps3disk_detach(device_t dev) +{ +	struct ps3disk_softc *sc = device_get_softc(dev); +	int i; + +	for (i = 0; i < sc->sc_nregs; i++) +		disk_destroy(sc->sc_disk[i]); + +	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); + +	free(sc->sc_disk, M_PS3DISK); +	free(sc->sc_reg, M_PS3DISK); + +	PS3DISK_LOCK_DESTROY(sc); + +	return (0); +} + +static int +ps3disk_open(struct disk *dp) +{ +	return (0); +} + +static int +ps3disk_close(struct disk *dp) +{ +	return (0); +} + +/* Process deferred blocks */ +static void +ps3disk_task(void *arg) +{ +	struct ps3disk_softc *sc = (struct ps3disk_softc *) arg; +	struct bio *bp; + +	while (1) { +		kproc_suspend_check(sc->sc_task); +		tsleep(&sc->sc_deferredq, PRIBIO, "ps3disk", 10); + +		PS3DISK_LOCK(sc); +		bp = bioq_takefirst(&sc->sc_deferredq); +		PS3DISK_UNLOCK(sc); + +		if (bp == NULL) +			continue; + +		if (bp->bio_driver1 != NULL) { +			bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t) +			    bp->bio_driver1); +			bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t) +			    bp->bio_driver1); +		} + +		ps3disk_strategy(bp); +	} + +	kproc_exit(0); +} + +static void +ps3disk_strategy(struct bio *bp) +{ +	struct ps3disk_softc *sc = (struct ps3disk_softc *)bp->bio_disk->d_drv1; +	int err; + +	if (sc == NULL) { +		bp->bio_flags |= BIO_ERROR; +		bp->bio_error = EINVAL; +		biodone(bp); +		return; +	} + +	PS3DISK_LOCK(sc); +	bp->bio_resid = bp->bio_bcount; +	bioq_insert_tail(&sc->sc_bioq, bp); + +	DPRINTF(sc, PS3DISK_DEBUG_TASK, "%s: bio_cmd 0x%02x\n", +	    __func__, bp->bio_cmd); + +	err = 0; +	if (bp->bio_cmd == BIO_FLUSH) { +		bp->bio_driver1 = 0; +		err = lv1_storage_send_device_command( +		    ps3bus_get_device(sc->sc_dev), LV1_STORAGE_ATA_HDDOUT, +		    0, 0, 0, 0, (uint64_t *)&bp->bio_driver2); +		if (err == LV1_BUSY) +			err = EAGAIN; +	} else if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { +		if (bp->bio_bcount % sc->sc_blksize != 0) { +			err = EINVAL; +		} else { +			bus_dmamap_create(sc->sc_dmatag, BUS_DMA_COHERENT, +			    (bus_dmamap_t *)(&bp->bio_driver1)); +			err = bus_dmamap_load(sc->sc_dmatag, +			    (bus_dmamap_t)(bp->bio_driver1), bp->bio_data, +			    bp->bio_bcount, ps3disk_transfer, bp, 0); +			if (err == EINPROGRESS) +				err = 0; +		} +	} else { +		err = EINVAL; +	} + +	if (err == EAGAIN) { +		bioq_remove(&sc->sc_bioq, bp); +		bioq_insert_tail(&sc->sc_deferredq, bp); +	} else if (err != 0) { +		bp->bio_error = err; +		bp->bio_flags |= BIO_ERROR; +		bioq_remove(&sc->sc_bioq, bp); +		disk_err(bp, "hard error", -1, 1); +		biodone(bp); +	} + +	PS3DISK_UNLOCK(sc); +} + +static void +ps3disk_intr(void *arg) +{ +	struct ps3disk_softc *sc = (struct ps3disk_softc *) arg; +	device_t dev = sc->sc_dev; +	uint64_t devid = ps3bus_get_device(dev); +	struct bio *bp; +	uint64_t tag, status; + +	if (lv1_storage_get_async_status(devid, &tag, &status) != 0) +		return; + +	PS3DISK_LOCK(sc); + +	DPRINTF(sc, PS3DISK_DEBUG_INTR, "%s: tag 0x%016lx " +	    "status 0x%016lx\n", __func__, tag, status); + +	/* Locate the matching request */ +	TAILQ_FOREACH(bp, &sc->sc_bioq.queue, bio_queue) { +		if ((uint64_t)bp->bio_driver2 != tag) +			continue; + +		if (status != 0) { +			device_printf(sc->sc_dev, "%s error (%#lx)\n", +			    (bp->bio_cmd == BIO_READ) ? "Read" : "Write", +			    status); +			bp->bio_error = EIO; +			bp->bio_flags |= BIO_ERROR; +		} else { +			bp->bio_error = 0; +			bp->bio_resid = 0; +			bp->bio_flags |= BIO_DONE; +		} + +		if (bp->bio_driver1 != NULL) { +			if (bp->bio_cmd == BIO_READ) +				bus_dmamap_sync(sc->sc_dmatag, (bus_dmamap_t) +				    bp->bio_driver1, BUS_DMASYNC_POSTREAD); +			bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t) +			    bp->bio_driver1); +			bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t) +			    bp->bio_driver1); +		} + +		bioq_remove(&sc->sc_bioq, bp); +		biodone(bp); +		break; +	} + +	if (bioq_first(&sc->sc_deferredq) != NULL) +		wakeup(&sc->sc_deferredq); + +	PS3DISK_UNLOCK(sc); +} + +static int +ps3disk_get_disk_geometry(struct ps3disk_softc *sc) +{ +	device_t dev = sc->sc_dev; +	uint64_t bus_index = ps3bus_get_busidx(dev); +	uint64_t dev_index = ps3bus_get_devidx(dev); +	uint64_t junk; +	int err; + +	err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, +	    (lv1_repository_string("bus") >> 32) | bus_index, +	    lv1_repository_string("dev") | dev_index, +	    lv1_repository_string("blk_size"), 0, &sc->sc_blksize, &junk); +	if (err) { +		device_printf(dev, "Could not get block size (0x%08x)\n", err); +		return (ENXIO); +	} + +	err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, +	    (lv1_repository_string("bus") >> 32) | bus_index, +	    lv1_repository_string("dev") | dev_index, +	    lv1_repository_string("n_blocks"), 0, &sc->sc_nblocks, &junk); +	if (err) { +		device_printf(dev, "Could not get total number of blocks " +		    "(0x%08x)\n", err); +		err = ENXIO; +	} + +	return (err); +} + +static int +ps3disk_enum_regions(struct ps3disk_softc *sc) +{ +	device_t dev = sc->sc_dev; +	uint64_t bus_index = ps3bus_get_busidx(dev); +	uint64_t dev_index = ps3bus_get_devidx(dev); +	uint64_t junk; +	int i, err; + +	/* Read number of regions */ + +	err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, +	    (lv1_repository_string("bus") >> 32) | bus_index, +	    lv1_repository_string("dev") | dev_index, +	    lv1_repository_string("n_regs"), 0, &sc->sc_nregs, &junk); +	if (err) { +		device_printf(dev, "Could not get number of regions (0x%08x)\n", +		    err); +		err = ENXIO; +		goto fail; +	} + +	if (!sc->sc_nregs) +		return 0; + +	sc->sc_reg = malloc(sc->sc_nregs * sizeof(struct ps3disk_region), +	    M_PS3DISK, M_ZERO | M_WAITOK); +	if (!sc->sc_reg) { +		err = ENOMEM; +		goto fail; +	} + +	/* Setup regions */ + +	for (i = 0; i < sc->sc_nregs; i++) { +		err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, +		    (lv1_repository_string("bus") >> 32) | bus_index, +		    lv1_repository_string("dev") | dev_index, +		    lv1_repository_string("region") | i, +		    lv1_repository_string("id"), &sc->sc_reg[i].r_id, &junk); +		if (err) { +			device_printf(dev, "Could not get region id (0x%08x)\n", +			    err); +			err = ENXIO; +			goto fail; +		} + +		err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, +		    (lv1_repository_string("bus") >> 32) | bus_index, +		    lv1_repository_string("dev") | dev_index, +		    lv1_repository_string("region") | i, +		    lv1_repository_string("start"), &sc->sc_reg[i].r_start, +		    &junk); +		if (err) { +			device_printf(dev, "Could not get region start " +			    "(0x%08x)\n", err); +			err = ENXIO; +			goto fail; +		} + +		err = lv1_get_repository_node_value(PS3_LPAR_ID_PME, +		    (lv1_repository_string("bus") >> 32) | bus_index, +		    lv1_repository_string("dev") | dev_index, +		    lv1_repository_string("region") | i, +		    lv1_repository_string("size"), &sc->sc_reg[i].r_size, +		    &junk); +		if (err) { +			device_printf(dev, "Could not get region size " +			    "(0x%08x)\n", err); +			err = ENXIO; +			goto fail; +		} + +		if (i == 0) +			sc->sc_reg[i].r_flags = 0x2; +		else +			sc->sc_reg[i].r_flags = 0; +	} + +	return (0); + +fail: + +	sc->sc_nregs = 0; +	if (sc->sc_reg) +		free(sc->sc_reg, M_PS3DISK); + +	return (err); +} + +static void +ps3disk_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ +	struct bio *bp = (struct bio *)(arg); +	struct ps3disk_softc *sc = (struct ps3disk_softc *)bp->bio_disk->d_drv1; +	struct ps3disk_region *rp = &sc->sc_reg[bp->bio_disk->d_unit]; +	bus_dma_segment_t *seg = &segs[0]; +	uint64_t devid = ps3bus_get_device(sc->sc_dev); +	uint64_t block, bio_length, sector_op_count; +	int err; + +	/* Locks already held by busdma */ +	PS3DISK_ASSERT_LOCKED(sc); + +	if (error) { +		bp->bio_error = error; +		bp->bio_flags |= BIO_ERROR; +		bioq_remove(&sc->sc_bioq, bp); +		biodone(bp); +		return; +	} + +	/* supports only 1 segment */ + +	KASSERT(nsegs == 1, +		    ("nsegs must be 1!, %d", nsegs)); + +	block = bp->bio_pblkno; +	bio_length = bp->bio_length; + +	/* ds_len always >= bio_length */ + +	KASSERT((seg->ds_len % bio_length) == 0, +		    ("ds_len not bio_length multiples, %lu, %lu", +		    (uint64_t)seg->ds_len, bio_length)); + +	KASSERT((bio_length % sc->sc_blksize) == 0, +		    ("bio_length not blocksize multiples, %lu, %lu", +		    bio_length, (uint64_t)sc->sc_blksize)); + +	sector_op_count = bio_length / sc->sc_blksize; + +	if (bp->bio_cmd == BIO_READ) { +		err = lv1_storage_read(devid, rp->r_id, +				block, sector_op_count, +				rp->r_flags, seg->ds_addr, +				(uint64_t *)&bp->bio_driver2); +	} else { +		bus_dmamap_sync(sc->sc_dmatag, +			(bus_dmamap_t)bp->bio_driver1, +			BUS_DMASYNC_PREWRITE); + +		err = lv1_storage_write(devid, rp->r_id, +			block, sector_op_count, +			rp->r_flags, seg->ds_addr, +			(uint64_t *)&bp->bio_driver2); +	} + +	if (err) { +		if (err == LV1_BUSY) { +			bioq_remove(&sc->sc_bioq, bp); +			bioq_insert_tail(&sc->sc_deferredq, bp); +		} else { +			bus_dmamap_unload(sc->sc_dmatag, (bus_dmamap_t) +			    bp->bio_driver1); +			bus_dmamap_destroy(sc->sc_dmatag, (bus_dmamap_t) +			    bp->bio_driver1); +			device_printf(sc->sc_dev, "Could not read " +			    "sectors (0x%08x)\n", err); +			bp->bio_error = EINVAL; +			bp->bio_flags |= BIO_ERROR; +			bioq_remove(&sc->sc_bioq, bp); +			biodone(bp); +		} +	} + +	DPRINTF(sc, PS3DISK_DEBUG_READ, "%s: tag 0x%016lx\n", +		__func__, sc->sc_bounce_tag); +} + +#ifdef PS3DISK_DEBUG +static int +ps3disk_sysctl_debug(SYSCTL_HANDLER_ARGS) +{ +	struct ps3disk_softc *sc = arg1; +	int debug, error; + +	debug = sc->sc_debug; + +	error = sysctl_handle_int(oidp, &debug, 0, req); +	if (error || !req->newptr) +		return error; + +	sc->sc_debug = debug; + +	return 0; +} +#endif + +static void +ps3disk_sysctlattach(struct ps3disk_softc *sc) +{ +#ifdef PS3DISK_DEBUG +	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); +	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + +	sc->sc_debug = ps3disk_debug; + +	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, +	    "debug", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, +	    ps3disk_sysctl_debug, "I", "control debugging printfs"); +#endif +} + +static device_method_t ps3disk_methods[] = { +	DEVMETHOD(device_probe,		ps3disk_probe), +	DEVMETHOD(device_attach,	ps3disk_attach), +	DEVMETHOD(device_detach,	ps3disk_detach), +	{0, 0}, +}; + +static driver_t ps3disk_driver = { +	"ps3disk", +	ps3disk_methods, +	sizeof(struct ps3disk_softc), +}; + +DRIVER_MODULE(ps3disk, ps3bus, ps3disk_driver, 0, 0); | 
