1224857Snwhitehorn/*-
2224857Snwhitehorn * Copyright (C) 2010 Nathan Whitehorn
3224857Snwhitehorn * Copyright (C) 2011 glevand <geoffrey.levand@mail.ru>
4224857Snwhitehorn * All rights reserved.
5224857Snwhitehorn *
6224857Snwhitehorn * Redistribution and use in source and binary forms, with or without
7224857Snwhitehorn * modification, are permitted provided that the following conditions
8224857Snwhitehorn * are met:
9224857Snwhitehorn * 1. Redistributions of source code must retain the above copyright
10224857Snwhitehorn *    notice, this list of conditions and the following disclaimer,
11224857Snwhitehorn *    without modification, immediately at the beginning of the file.
12224857Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
13224857Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
14224857Snwhitehorn *    documentation and/or other materials provided with the distribution.
15224857Snwhitehorn *
16224857Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17224857Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18224857Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19224857Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20224857Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21224857Snwhitehorn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22224857Snwhitehorn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23224857Snwhitehorn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24224857Snwhitehorn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25224857Snwhitehorn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26224857Snwhitehorn */
27224857Snwhitehorn
28224857Snwhitehorn#include <sys/cdefs.h>
29224857Snwhitehorn__FBSDID("$FreeBSD$");
30224857Snwhitehorn
31224857Snwhitehorn#include <sys/param.h>
32224857Snwhitehorn#include <sys/module.h>
33224857Snwhitehorn#include <sys/systm.h>
34224857Snwhitehorn#include <sys/kernel.h>
35224857Snwhitehorn#include <sys/ata.h>
36224857Snwhitehorn#include <sys/bus.h>
37224857Snwhitehorn#include <sys/conf.h>
38224857Snwhitehorn#include <sys/kthread.h>
39224857Snwhitehorn#include <sys/lock.h>
40224857Snwhitehorn#include <sys/malloc.h>
41224857Snwhitehorn#include <sys/mutex.h>
42224857Snwhitehorn
43224857Snwhitehorn#include <vm/vm.h>
44224857Snwhitehorn#include <vm/pmap.h>
45224857Snwhitehorn
46224857Snwhitehorn#include <machine/pio.h>
47224857Snwhitehorn#include <machine/bus.h>
48224857Snwhitehorn#include <machine/platform.h>
49224857Snwhitehorn#include <machine/pmap.h>
50224857Snwhitehorn#include <machine/resource.h>
51224857Snwhitehorn#include <sys/bus.h>
52224857Snwhitehorn#include <sys/rman.h>
53224857Snwhitehorn
54224857Snwhitehorn#include <cam/cam.h>
55224857Snwhitehorn#include <cam/cam_ccb.h>
56224857Snwhitehorn#include <cam/cam_sim.h>
57224857Snwhitehorn#include <cam/cam_xpt_sim.h>
58224857Snwhitehorn#include <cam/cam_debug.h>
59224857Snwhitehorn#include <cam/scsi/scsi_all.h>
60224857Snwhitehorn
61224857Snwhitehorn#include "ps3bus.h"
62224857Snwhitehorn#include "ps3-hvcall.h"
63224857Snwhitehorn
64224857Snwhitehorn#define PS3CDROM_LOCK_INIT(_sc)		\
65224857Snwhitehorn	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3cdrom", \
66224857Snwhitehorn	    MTX_DEF)
67224857Snwhitehorn#define PS3CDROM_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
68224857Snwhitehorn#define PS3CDROM_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
69224857Snwhitehorn#define	PS3CDROM_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
70224857Snwhitehorn#define PS3CDROM_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
71224857Snwhitehorn#define PS3CDROM_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
72224857Snwhitehorn
73224857Snwhitehorn#define PS3CDROM_MAX_XFERS		3
74224857Snwhitehorn
75224857Snwhitehorn#define	LV1_STORAGE_SEND_ATAPI_COMMAND	0x01
76224857Snwhitehorn
77224857Snwhitehornstruct ps3cdrom_softc;
78224857Snwhitehorn
79224857Snwhitehornstruct ps3cdrom_xfer {
80224857Snwhitehorn	TAILQ_ENTRY(ps3cdrom_xfer) x_queue;
81224857Snwhitehorn	struct ps3cdrom_softc *x_sc;
82224857Snwhitehorn	union ccb *x_ccb;
83224857Snwhitehorn	bus_dmamap_t x_dmamap;
84224857Snwhitehorn	uint64_t x_tag;
85224857Snwhitehorn};
86224857Snwhitehorn
87224857SnwhitehornTAILQ_HEAD(ps3cdrom_xferq, ps3cdrom_xfer);
88224857Snwhitehorn
89224857Snwhitehornstruct ps3cdrom_softc {
90224857Snwhitehorn	device_t sc_dev;
91224857Snwhitehorn
92224857Snwhitehorn	struct mtx sc_mtx;
93224857Snwhitehorn
94224857Snwhitehorn	uint64_t sc_blksize;
95224857Snwhitehorn	uint64_t sc_nblocks;
96224857Snwhitehorn
97224857Snwhitehorn	int sc_irqid;
98224857Snwhitehorn	struct resource	*sc_irq;
99224857Snwhitehorn	void *sc_irqctx;
100224857Snwhitehorn
101224857Snwhitehorn	bus_dma_tag_t sc_dmatag;
102224857Snwhitehorn
103224857Snwhitehorn	struct cam_sim *sc_sim;
104224857Snwhitehorn	struct cam_path *sc_path;
105224857Snwhitehorn
106224857Snwhitehorn	struct ps3cdrom_xfer sc_xfer[PS3CDROM_MAX_XFERS];
107224857Snwhitehorn	struct ps3cdrom_xferq sc_active_xferq;
108224857Snwhitehorn	struct ps3cdrom_xferq sc_free_xferq;
109224857Snwhitehorn};
110224857Snwhitehorn
111224857Snwhitehornenum lv1_ata_proto {
112224857Snwhitehorn	NON_DATA_PROTO		= 0x00,
113224857Snwhitehorn	PIO_DATA_IN_PROTO	= 0x01,
114224857Snwhitehorn	PIO_DATA_OUT_PROTO	= 0x02,
115224857Snwhitehorn	DMA_PROTO		= 0x03
116224857Snwhitehorn};
117224857Snwhitehorn
118224857Snwhitehornenum lv1_ata_in_out {
119224857Snwhitehorn	DIR_WRITE		= 0x00,
120224857Snwhitehorn	DIR_READ		= 0x01
121224857Snwhitehorn};
122224857Snwhitehorn
123224857Snwhitehornstruct lv1_atapi_cmd {
124224857Snwhitehorn	uint8_t pkt[32];
125224857Snwhitehorn	uint32_t pktlen;
126224857Snwhitehorn	uint32_t nblocks;
127224857Snwhitehorn	uint32_t blksize;
128224857Snwhitehorn	uint32_t proto;		/* enum lv1_ata_proto */
129224857Snwhitehorn	uint32_t in_out;	/* enum lv1_ata_in_out */
130224857Snwhitehorn	uint64_t buf;
131224857Snwhitehorn	uint32_t arglen;
132224857Snwhitehorn};
133224857Snwhitehorn
134224857Snwhitehornstatic void ps3cdrom_action(struct cam_sim *sim, union ccb *ccb);
135224857Snwhitehornstatic void ps3cdrom_poll(struct cam_sim *sim);
136224857Snwhitehornstatic void ps3cdrom_async(void *callback_arg, u_int32_t code,
137224857Snwhitehorn    struct cam_path* path, void *arg);
138224857Snwhitehorn
139224857Snwhitehornstatic void ps3cdrom_intr(void *arg);
140224857Snwhitehorn
141224857Snwhitehornstatic void ps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs,
142224857Snwhitehorn    int error);
143224857Snwhitehorn
144224857Snwhitehornstatic int ps3cdrom_decode_lv1_status(uint64_t status,
145224857Snwhitehorn	u_int8_t *sense_key, u_int8_t *asc, u_int8_t *ascq);
146224857Snwhitehorn
147224857Snwhitehornstatic int
148224857Snwhitehornps3cdrom_probe(device_t dev)
149224857Snwhitehorn{
150224857Snwhitehorn	if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE ||
151224857Snwhitehorn	    ps3bus_get_devtype(dev) != PS3_DEVTYPE_CDROM)
152224857Snwhitehorn		return (ENXIO);
153224857Snwhitehorn
154224857Snwhitehorn	device_set_desc(dev, "Playstation 3 CDROM");
155224857Snwhitehorn
156224857Snwhitehorn	return (BUS_PROBE_SPECIFIC);
157224857Snwhitehorn}
158224857Snwhitehorn
159224857Snwhitehornstatic int
160224857Snwhitehornps3cdrom_attach(device_t dev)
161224857Snwhitehorn{
162224857Snwhitehorn	struct ps3cdrom_softc *sc = device_get_softc(dev);
163224857Snwhitehorn	struct cam_devq *devq;
164224857Snwhitehorn	struct ps3cdrom_xfer *xp;
165224857Snwhitehorn	struct ccb_setasync csa;
166224857Snwhitehorn	int i, err;
167224857Snwhitehorn
168224857Snwhitehorn	sc->sc_dev = dev;
169224857Snwhitehorn
170224857Snwhitehorn	PS3CDROM_LOCK_INIT(sc);
171224857Snwhitehorn
172224857Snwhitehorn	/* Setup interrupt handler */
173224857Snwhitehorn
174224857Snwhitehorn	sc->sc_irqid = 0;
175224857Snwhitehorn	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid,
176224857Snwhitehorn	    RF_ACTIVE);
177224857Snwhitehorn	if (!sc->sc_irq) {
178224857Snwhitehorn		device_printf(dev, "Could not allocate IRQ\n");
179224857Snwhitehorn		err = ENXIO;
180224857Snwhitehorn		goto fail_destroy_lock;
181224857Snwhitehorn	}
182224857Snwhitehorn
183224857Snwhitehorn	err = bus_setup_intr(dev, sc->sc_irq,
184224857Snwhitehorn	    INTR_TYPE_CAM | INTR_MPSAFE | INTR_ENTROPY,
185224857Snwhitehorn	    NULL, ps3cdrom_intr, sc, &sc->sc_irqctx);
186224857Snwhitehorn	if (err) {
187224857Snwhitehorn		device_printf(dev, "Could not setup IRQ\n");
188224857Snwhitehorn		err = ENXIO;
189224857Snwhitehorn		goto fail_release_intr;
190224857Snwhitehorn	}
191224857Snwhitehorn
192224857Snwhitehorn	/* Setup DMA */
193224857Snwhitehorn
194224857Snwhitehorn	err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0,
195224857Snwhitehorn	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
196224857Snwhitehorn	    BUS_SPACE_UNRESTRICTED, 1, PAGE_SIZE, 0,
197224857Snwhitehorn	    busdma_lock_mutex, &sc->sc_mtx, &sc->sc_dmatag);
198224857Snwhitehorn	if (err) {
199224857Snwhitehorn		device_printf(dev, "Could not create DMA tag\n");
200224857Snwhitehorn		err = ENXIO;
201224857Snwhitehorn		goto fail_teardown_intr;
202224857Snwhitehorn	}
203224857Snwhitehorn
204224857Snwhitehorn	/* Setup transfer queues */
205224857Snwhitehorn
206224857Snwhitehorn	TAILQ_INIT(&sc->sc_active_xferq);
207224857Snwhitehorn	TAILQ_INIT(&sc->sc_free_xferq);
208224857Snwhitehorn
209224857Snwhitehorn	for (i = 0; i < PS3CDROM_MAX_XFERS; i++) {
210224857Snwhitehorn		xp = &sc->sc_xfer[i];
211224857Snwhitehorn		xp->x_sc = sc;
212224857Snwhitehorn
213224857Snwhitehorn		err = bus_dmamap_create(sc->sc_dmatag, BUS_DMA_COHERENT,
214224857Snwhitehorn		    &xp->x_dmamap);
215224857Snwhitehorn		if (err) {
216224857Snwhitehorn			device_printf(dev, "Could not create DMA map (%d)\n",
217224857Snwhitehorn			    err);
218224857Snwhitehorn			goto fail_destroy_dmamap;
219224857Snwhitehorn		}
220224857Snwhitehorn
221224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
222224857Snwhitehorn	}
223224857Snwhitehorn
224224857Snwhitehorn	/* Setup CAM */
225224857Snwhitehorn
226224857Snwhitehorn	devq = cam_simq_alloc(PS3CDROM_MAX_XFERS - 1);
227224857Snwhitehorn	if (!devq) {
228224857Snwhitehorn		device_printf(dev, "Could not allocate SIM queue\n");
229224857Snwhitehorn		err = ENOMEM;
230224857Snwhitehorn		goto fail_destroy_dmatag;
231224857Snwhitehorn	}
232224857Snwhitehorn
233224857Snwhitehorn	sc->sc_sim = cam_sim_alloc(ps3cdrom_action, ps3cdrom_poll, "ps3cdrom",
234224857Snwhitehorn	    sc, device_get_unit(dev), &sc->sc_mtx, PS3CDROM_MAX_XFERS - 1, 0,
235224857Snwhitehorn	    devq);
236224857Snwhitehorn	if (!sc->sc_sim) {
237224857Snwhitehorn		device_printf(dev, "Could not allocate SIM\n");
238224857Snwhitehorn		cam_simq_free(devq);
239224857Snwhitehorn		err = ENOMEM;
240224857Snwhitehorn		goto fail_destroy_dmatag;
241224857Snwhitehorn	}
242224857Snwhitehorn
243224857Snwhitehorn	/* Setup XPT */
244224857Snwhitehorn
245224857Snwhitehorn	PS3CDROM_LOCK(sc);
246224857Snwhitehorn
247224857Snwhitehorn	err = xpt_bus_register(sc->sc_sim, dev, 0);
248224857Snwhitehorn	if (err != CAM_SUCCESS) {
249224857Snwhitehorn		device_printf(dev, "Could not register XPT bus\n");
250224857Snwhitehorn		err = ENXIO;
251224857Snwhitehorn		PS3CDROM_UNLOCK(sc);
252224857Snwhitehorn		goto fail_free_sim;
253224857Snwhitehorn	}
254224857Snwhitehorn
255224857Snwhitehorn	err = xpt_create_path(&sc->sc_path, NULL, cam_sim_path(sc->sc_sim),
256224857Snwhitehorn	    CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
257224857Snwhitehorn	if (err != CAM_REQ_CMP) {
258224857Snwhitehorn		device_printf(dev, "Could not create XPT path\n");
259224857Snwhitehorn		err = ENOMEM;
260224857Snwhitehorn		PS3CDROM_UNLOCK(sc);
261224857Snwhitehorn		goto fail_unregister_xpt_bus;
262224857Snwhitehorn	}
263224857Snwhitehorn
264224857Snwhitehorn	xpt_setup_ccb(&csa.ccb_h, sc->sc_path, 5);
265224857Snwhitehorn	csa.ccb_h.func_code = XPT_SASYNC_CB;
266224857Snwhitehorn	csa.event_enable = AC_LOST_DEVICE;
267224857Snwhitehorn	csa.callback = ps3cdrom_async;
268224857Snwhitehorn	csa.callback_arg = sc->sc_sim;
269224857Snwhitehorn	xpt_action((union ccb *) &csa);
270224857Snwhitehorn
271224857Snwhitehorn	CAM_DEBUG(sc->sc_path, CAM_DEBUG_TRACE,
272224857Snwhitehorn	    ("registered SIM for ps3cdrom%d\n", device_get_unit(dev)));
273224857Snwhitehorn
274224857Snwhitehorn	PS3CDROM_UNLOCK(sc);
275224857Snwhitehorn
276224857Snwhitehorn	return (BUS_PROBE_SPECIFIC);
277224857Snwhitehorn
278224857Snwhitehornfail_unregister_xpt_bus:
279224857Snwhitehorn
280224857Snwhitehorn	xpt_bus_deregister(cam_sim_path(sc->sc_sim));
281224857Snwhitehorn
282224857Snwhitehornfail_free_sim:
283224857Snwhitehorn
284224857Snwhitehorn	cam_sim_free(sc->sc_sim, TRUE);
285224857Snwhitehorn
286224857Snwhitehornfail_destroy_dmamap:
287224857Snwhitehorn
288224857Snwhitehorn	while ((xp = TAILQ_FIRST(&sc->sc_free_xferq))) {
289224857Snwhitehorn		TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue);
290224857Snwhitehorn		bus_dmamap_destroy(sc->sc_dmatag, xp->x_dmamap);
291224857Snwhitehorn	}
292224857Snwhitehorn
293224857Snwhitehornfail_destroy_dmatag:
294224857Snwhitehorn
295224857Snwhitehorn	bus_dma_tag_destroy(sc->sc_dmatag);
296224857Snwhitehorn
297224857Snwhitehornfail_teardown_intr:
298224857Snwhitehorn
299224857Snwhitehorn	bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
300224857Snwhitehorn
301224857Snwhitehornfail_release_intr:
302224857Snwhitehorn
303224857Snwhitehorn	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
304224857Snwhitehorn
305224857Snwhitehornfail_destroy_lock:
306224857Snwhitehorn
307224857Snwhitehorn	PS3CDROM_LOCK_DESTROY(sc);
308224857Snwhitehorn
309224857Snwhitehorn	return (err);
310224857Snwhitehorn}
311224857Snwhitehorn
312224857Snwhitehornstatic int
313224857Snwhitehornps3cdrom_detach(device_t dev)
314224857Snwhitehorn{
315224857Snwhitehorn	struct ps3cdrom_softc *sc = device_get_softc(dev);
316224857Snwhitehorn	int i;
317224857Snwhitehorn
318224857Snwhitehorn	xpt_async(AC_LOST_DEVICE, sc->sc_path, NULL);
319224857Snwhitehorn	xpt_free_path(sc->sc_path);
320224857Snwhitehorn	xpt_bus_deregister(cam_sim_path(sc->sc_sim));
321224857Snwhitehorn	cam_sim_free(sc->sc_sim, TRUE);
322224857Snwhitehorn
323224857Snwhitehorn	for (i = 0; i < PS3CDROM_MAX_XFERS; i++)
324224857Snwhitehorn		bus_dmamap_destroy(sc->sc_dmatag, sc->sc_xfer[i].x_dmamap);
325224857Snwhitehorn
326224857Snwhitehorn	bus_dma_tag_destroy(sc->sc_dmatag);
327224857Snwhitehorn
328224857Snwhitehorn	bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
329224857Snwhitehorn	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
330224857Snwhitehorn
331224857Snwhitehorn	PS3CDROM_LOCK_DESTROY(sc);
332224857Snwhitehorn
333224857Snwhitehorn	return (0);
334224857Snwhitehorn}
335224857Snwhitehorn
336224857Snwhitehornstatic void
337224857Snwhitehornps3cdrom_action(struct cam_sim *sim, union ccb *ccb)
338224857Snwhitehorn{
339224857Snwhitehorn	struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *)cam_sim_softc(sim);
340224857Snwhitehorn	device_t dev = sc->sc_dev;
341224857Snwhitehorn	struct ps3cdrom_xfer *xp;
342224857Snwhitehorn	int err;
343224857Snwhitehorn
344224857Snwhitehorn	PS3CDROM_ASSERT_LOCKED(sc);
345224857Snwhitehorn
346224857Snwhitehorn	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
347224857Snwhitehorn	   ("function code 0x%02x\n", ccb->ccb_h.func_code));
348224857Snwhitehorn
349224857Snwhitehorn	switch (ccb->ccb_h.func_code) {
350224857Snwhitehorn	case XPT_SCSI_IO:
351224857Snwhitehorn		if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG)
352224857Snwhitehorn			break;
353224857Snwhitehorn
354224857Snwhitehorn		if(ccb->ccb_h.target_id > 0) {
355224857Snwhitehorn			ccb->ccb_h.status = CAM_TID_INVALID;
356224857Snwhitehorn			break;
357224857Snwhitehorn		}
358224857Snwhitehorn
359224857Snwhitehorn		if(ccb->ccb_h.target_lun > 0) {
360224857Snwhitehorn			ccb->ccb_h.status = CAM_LUN_INVALID;
361224857Snwhitehorn			break;
362224857Snwhitehorn		}
363224857Snwhitehorn
364224857Snwhitehorn		xp = TAILQ_FIRST(&sc->sc_free_xferq);
365224857Snwhitehorn
366224857Snwhitehorn		KASSERT(xp != NULL, ("no free transfers"));
367224857Snwhitehorn
368224857Snwhitehorn		xp->x_ccb = ccb;
369224857Snwhitehorn
370224857Snwhitehorn		TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue);
371224857Snwhitehorn
372251874Sscottl		err = bus_dmamap_load_ccb(sc->sc_dmatag, xp->x_dmamap,
373251874Sscottl		    ccb, ps3cdrom_transfer, xp, 0);
374224857Snwhitehorn		if (err && err != EINPROGRESS) {
375224857Snwhitehorn			device_printf(dev, "Could not load DMA map (%d)\n",
376224857Snwhitehorn			    err);
377224857Snwhitehorn
378224857Snwhitehorn			xp->x_ccb = NULL;
379224857Snwhitehorn			TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
380224857Snwhitehorn			ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
381224857Snwhitehorn			break;
382224857Snwhitehorn		}
383224857Snwhitehorn		return;
384224857Snwhitehorn	case XPT_SET_TRAN_SETTINGS:
385224857Snwhitehorn		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
386224857Snwhitehorn		break;
387224857Snwhitehorn	case XPT_GET_TRAN_SETTINGS:
388224857Snwhitehorn	{
389224857Snwhitehorn		struct ccb_trans_settings *cts = &ccb->cts;
390224857Snwhitehorn
391224857Snwhitehorn		cts->protocol = PROTO_SCSI;
392224857Snwhitehorn		cts->protocol_version = SCSI_REV_2;
393224857Snwhitehorn		cts->transport = XPORT_SPI;
394224857Snwhitehorn		cts->transport_version = 2;
395224857Snwhitehorn		cts->proto_specific.valid = 0;
396224857Snwhitehorn		cts->xport_specific.valid = 0;
397224857Snwhitehorn		ccb->ccb_h.status = CAM_REQ_CMP;
398224857Snwhitehorn		break;
399224857Snwhitehorn	}
400224857Snwhitehorn	case XPT_RESET_BUS:
401224857Snwhitehorn	case XPT_RESET_DEV:
402224857Snwhitehorn		ccb->ccb_h.status = CAM_REQ_CMP;
403224857Snwhitehorn		break;
404224857Snwhitehorn	case XPT_CALC_GEOMETRY:
405224857Snwhitehorn		cam_calc_geometry(&ccb->ccg, 1);
406224857Snwhitehorn		break;
407224857Snwhitehorn	case XPT_PATH_INQ:
408224857Snwhitehorn	{
409224857Snwhitehorn		struct ccb_pathinq *cpi = &ccb->cpi;
410224857Snwhitehorn
411224857Snwhitehorn		cpi->version_num = 1;
412224857Snwhitehorn		cpi->hba_inquiry = 0;
413224857Snwhitehorn		cpi->target_sprt = 0;
414224857Snwhitehorn		cpi->hba_inquiry = PI_SDTR_ABLE;
415224857Snwhitehorn		cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN | PIM_NO_6_BYTE;
416224857Snwhitehorn		cpi->hba_eng_cnt = 0;
417224857Snwhitehorn		bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags));
418224857Snwhitehorn		cpi->max_target = 0;
419224857Snwhitehorn		cpi->max_lun = 0;
420224857Snwhitehorn		cpi->initiator_id = 7;
421224857Snwhitehorn		cpi->bus_id = cam_sim_bus(sim);
422224857Snwhitehorn		cpi->unit_number = cam_sim_unit(sim);
423224857Snwhitehorn		cpi->base_transfer_speed = 150000;
424224857Snwhitehorn		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
425224857Snwhitehorn		strncpy(cpi->hba_vid, "Sony", HBA_IDLEN);
426224857Snwhitehorn		strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
427224857Snwhitehorn		cpi->transport = XPORT_SPI;
428224857Snwhitehorn		cpi->transport_version = 2;
429224857Snwhitehorn		cpi->protocol = PROTO_SCSI;
430224857Snwhitehorn		cpi->protocol_version = SCSI_REV_2;
431224857Snwhitehorn		cpi->maxio = PAGE_SIZE;
432224857Snwhitehorn		cpi->ccb_h.status = CAM_REQ_CMP;
433224857Snwhitehorn		break;
434224857Snwhitehorn	}
435224857Snwhitehorn	default:
436224857Snwhitehorn		CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
437224857Snwhitehorn		    ("unsupported function code 0x%02x\n",
438224857Snwhitehorn		    ccb->ccb_h.func_code));
439224857Snwhitehorn		ccb->ccb_h.status = CAM_REQ_INVALID;
440224857Snwhitehorn		break;
441224857Snwhitehorn	}
442224857Snwhitehorn
443224857Snwhitehorn	xpt_done(ccb);
444224857Snwhitehorn}
445224857Snwhitehorn
446224857Snwhitehornstatic void
447224857Snwhitehornps3cdrom_poll(struct cam_sim *sim)
448224857Snwhitehorn{
449224857Snwhitehorn	ps3cdrom_intr(cam_sim_softc(sim));
450224857Snwhitehorn}
451224857Snwhitehorn
452224857Snwhitehornstatic void
453224857Snwhitehornps3cdrom_async(void *callback_arg, u_int32_t code,
454224857Snwhitehorn	struct cam_path* path, void *arg)
455224857Snwhitehorn{
456224857Snwhitehorn	switch (code) {
457224857Snwhitehorn	case AC_LOST_DEVICE:
458224857Snwhitehorn		xpt_print_path(path);
459224857Snwhitehorn		break;
460224857Snwhitehorn	default:
461224857Snwhitehorn		break;
462224857Snwhitehorn	}
463224857Snwhitehorn}
464224857Snwhitehorn
465224857Snwhitehornstatic void
466224857Snwhitehornps3cdrom_intr(void *arg)
467224857Snwhitehorn{
468224857Snwhitehorn	struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *) arg;
469224857Snwhitehorn	device_t dev = sc->sc_dev;
470224857Snwhitehorn	uint64_t devid = ps3bus_get_device(dev);
471224857Snwhitehorn	struct ps3cdrom_xfer *xp;
472224857Snwhitehorn	union ccb *ccb;
473224857Snwhitehorn	u_int8_t *cdb, sense_key, asc, ascq;
474224857Snwhitehorn	uint64_t tag, status;
475224857Snwhitehorn
476224857Snwhitehorn	if (lv1_storage_get_async_status(devid, &tag, &status) != 0)
477224857Snwhitehorn		return;
478224857Snwhitehorn
479224857Snwhitehorn	PS3CDROM_LOCK(sc);
480224857Snwhitehorn
481224857Snwhitehorn	/* Find transfer with the returned tag */
482224857Snwhitehorn
483224857Snwhitehorn	TAILQ_FOREACH(xp, &sc->sc_active_xferq, x_queue) {
484224857Snwhitehorn		if (xp->x_tag == tag)
485224857Snwhitehorn			break;
486224857Snwhitehorn	}
487224857Snwhitehorn
488224857Snwhitehorn	if (xp) {
489224857Snwhitehorn		ccb = xp->x_ccb;
490224857Snwhitehorn		cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ?
491224857Snwhitehorn			    ccb->csio.cdb_io.cdb_ptr :
492224857Snwhitehorn			    ccb->csio.cdb_io.cdb_bytes;
493224857Snwhitehorn
494224857Snwhitehorn		CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
495224857Snwhitehorn		   ("ATAPI command 0x%02x tag 0x%016lx completed (0x%016lx)\n",
496224857Snwhitehorn		    cdb[0], tag, status));
497224857Snwhitehorn
498224857Snwhitehorn		if (!status) {
499224857Snwhitehorn			ccb->csio.scsi_status = SCSI_STATUS_OK;
500224857Snwhitehorn			ccb->csio.resid = 0;
501224857Snwhitehorn			ccb->ccb_h.status = CAM_REQ_CMP;
502224857Snwhitehorn		} else {
503224857Snwhitehorn			ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
504224857Snwhitehorn			ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
505224857Snwhitehorn
506224857Snwhitehorn			if (!ps3cdrom_decode_lv1_status(status, &sense_key,
507224857Snwhitehorn			    &asc, &ascq)) {
508224857Snwhitehorn
509224857Snwhitehorn				CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
510224857Snwhitehorn				   ("sense key 0x%02x asc 0x%02x ascq 0x%02x\n",
511224857Snwhitehorn				    sense_key, asc, ascq));
512224857Snwhitehorn
513226067Sken				scsi_set_sense_data(&ccb->csio.sense_data,
514226067Sken				    /*sense_format*/ SSD_TYPE_NONE,
515226067Sken				    /*current_error*/ 1,
516226067Sken				    sense_key,
517226067Sken				    asc,
518226067Sken				    ascq,
519226067Sken				    SSD_ELEM_NONE);
520226067Sken				ccb->csio.sense_len = SSD_FULL_SIZE;
521224857Snwhitehorn				ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR |
522224857Snwhitehorn				    CAM_AUTOSNS_VALID;
523224857Snwhitehorn			}
524224857Snwhitehorn
525224857Snwhitehorn			if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
526224857Snwhitehorn				ccb->csio.resid = ccb->csio.dxfer_len;
527224857Snwhitehorn		}
528224857Snwhitehorn
529224857Snwhitehorn		if (ccb->ccb_h.flags & CAM_DIR_IN)
530224857Snwhitehorn			bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
531224857Snwhitehorn			    BUS_DMASYNC_POSTREAD);
532224857Snwhitehorn
533224857Snwhitehorn		bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap);
534224857Snwhitehorn
535224857Snwhitehorn		xp->x_ccb = NULL;
536224857Snwhitehorn		TAILQ_REMOVE(&sc->sc_active_xferq, xp, x_queue);
537224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
538224857Snwhitehorn
539224857Snwhitehorn		xpt_done(ccb);
540224857Snwhitehorn	} else {
541224857Snwhitehorn		device_printf(dev,
542224857Snwhitehorn		    "Could not find transfer with tag 0x%016lx\n",  tag);
543224857Snwhitehorn	}
544224857Snwhitehorn
545224857Snwhitehorn	PS3CDROM_UNLOCK(sc);
546224857Snwhitehorn}
547224857Snwhitehorn
548224857Snwhitehornstatic void
549224857Snwhitehornps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
550224857Snwhitehorn{
551224857Snwhitehorn	struct ps3cdrom_xfer *xp = (struct ps3cdrom_xfer *) arg;
552224857Snwhitehorn	struct ps3cdrom_softc *sc = xp->x_sc;
553224857Snwhitehorn	device_t dev = sc->sc_dev;
554224857Snwhitehorn	uint64_t devid = ps3bus_get_device(dev);
555224857Snwhitehorn	union ccb *ccb = xp->x_ccb;
556224857Snwhitehorn	u_int8_t *cdb;
557224857Snwhitehorn	uint64_t start_sector, block_count;
558224857Snwhitehorn	int err;
559224857Snwhitehorn
560224857Snwhitehorn	KASSERT(nsegs == 1, ("invalid number of DMA segments"));
561224857Snwhitehorn
562224857Snwhitehorn	PS3CDROM_ASSERT_LOCKED(sc);
563224857Snwhitehorn
564224857Snwhitehorn	if (error) {
565224857Snwhitehorn		device_printf(dev, "Could not load DMA map (%d)\n",  error);
566224857Snwhitehorn
567224857Snwhitehorn		xp->x_ccb = NULL;
568224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
569224857Snwhitehorn		ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
570224857Snwhitehorn		xpt_done(ccb);
571224857Snwhitehorn		return;
572224857Snwhitehorn	}
573224857Snwhitehorn
574224857Snwhitehorn	cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ?
575224857Snwhitehorn		    ccb->csio.cdb_io.cdb_ptr :
576224857Snwhitehorn		    ccb->csio.cdb_io.cdb_bytes;
577224857Snwhitehorn
578224857Snwhitehorn	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
579224857Snwhitehorn	   ("ATAPI command 0x%02x cdb_len %d dxfer_len %d\n ", cdb[0],
580224857Snwhitehorn	    ccb->csio.cdb_len, ccb->csio.dxfer_len));
581224857Snwhitehorn
582224857Snwhitehorn	switch (cdb[0]) {
583224857Snwhitehorn	case READ_10:
584224857Snwhitehorn		start_sector = (cdb[2] << 24) | (cdb[3] << 16) |
585224857Snwhitehorn		    (cdb[4] << 8) | cdb[5];
586224857Snwhitehorn		block_count = (cdb[7] << 8) | cdb[8];
587224857Snwhitehorn
588224857Snwhitehorn		err = lv1_storage_read(devid, 0 /* region id */,
589224857Snwhitehorn		    start_sector, block_count, 0 /* flags */, segs[0].ds_addr,
590224857Snwhitehorn		    &xp->x_tag);
591224857Snwhitehorn		bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
592224857Snwhitehorn		    BUS_DMASYNC_POSTREAD);
593224857Snwhitehorn		break;
594224857Snwhitehorn	case WRITE_10:
595224857Snwhitehorn		start_sector = (cdb[2] << 24) | (cdb[3] << 16) |
596224857Snwhitehorn		    (cdb[4] << 8) | cdb[5];
597224857Snwhitehorn		block_count = (cdb[7] << 8) | cdb[8];
598224857Snwhitehorn
599224857Snwhitehorn		bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
600224857Snwhitehorn		    BUS_DMASYNC_PREWRITE);
601224857Snwhitehorn		err = lv1_storage_write(devid, 0 /* region id */,
602224857Snwhitehorn		    start_sector, block_count, 0 /* flags */,
603224857Snwhitehorn		    segs[0].ds_addr, &xp->x_tag);
604224857Snwhitehorn		break;
605224857Snwhitehorn	default:
606224857Snwhitehorn		{
607224857Snwhitehorn		struct lv1_atapi_cmd atapi_cmd;
608224857Snwhitehorn
609224857Snwhitehorn		bzero(&atapi_cmd, sizeof(atapi_cmd));
610224857Snwhitehorn		atapi_cmd.pktlen = 12;
611224857Snwhitehorn		bcopy(cdb, atapi_cmd.pkt, ccb->csio.cdb_len);
612224857Snwhitehorn
613224857Snwhitehorn		if (ccb->ccb_h.flags & CAM_DIR_IN) {
614224857Snwhitehorn			atapi_cmd.in_out = DIR_READ;
615224857Snwhitehorn			atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ?
616224857Snwhitehorn			    DMA_PROTO : PIO_DATA_IN_PROTO;
617224857Snwhitehorn		} else if (ccb->ccb_h.flags & CAM_DIR_OUT) {
618224857Snwhitehorn			atapi_cmd.in_out = DIR_WRITE;
619224857Snwhitehorn			atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ?
620224857Snwhitehorn			    DMA_PROTO : PIO_DATA_OUT_PROTO;
621224857Snwhitehorn		} else {
622224857Snwhitehorn			atapi_cmd.proto = NON_DATA_PROTO;
623224857Snwhitehorn		}
624224857Snwhitehorn
625224857Snwhitehorn		atapi_cmd.nblocks = atapi_cmd.arglen = segs[0].ds_len;
626224857Snwhitehorn		atapi_cmd.blksize = 1;
627224857Snwhitehorn		atapi_cmd.buf = segs[0].ds_addr;
628224857Snwhitehorn
629224857Snwhitehorn		if (ccb->ccb_h.flags & CAM_DIR_OUT)
630224857Snwhitehorn			bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
631224857Snwhitehorn			    BUS_DMASYNC_PREWRITE);
632224857Snwhitehorn
633224857Snwhitehorn		err = lv1_storage_send_device_command(devid,
634224857Snwhitehorn		    LV1_STORAGE_SEND_ATAPI_COMMAND, vtophys(&atapi_cmd),
635224857Snwhitehorn		    sizeof(atapi_cmd), atapi_cmd.buf, atapi_cmd.arglen,
636224857Snwhitehorn		    &xp->x_tag);
637224857Snwhitehorn
638224857Snwhitehorn		break;
639224857Snwhitehorn		}
640224857Snwhitehorn	}
641224857Snwhitehorn
642224857Snwhitehorn	if (err) {
643224857Snwhitehorn		device_printf(dev, "ATAPI command 0x%02x failed (%d)\n",
644224857Snwhitehorn		    cdb[0], err);
645224857Snwhitehorn
646224857Snwhitehorn		bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap);
647224857Snwhitehorn
648224857Snwhitehorn		xp->x_ccb = NULL;
649224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
650224857Snwhitehorn
651226067Sken		bzero(&ccb->csio.sense_data, sizeof(ccb->csio.sense_data));
652226067Sken		/* Invalid field in parameter list */
653226067Sken		scsi_set_sense_data(&ccb->csio.sense_data,
654226067Sken				    /*sense_format*/ SSD_TYPE_NONE,
655226067Sken				    /*current_error*/ 1,
656226067Sken				    /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
657226067Sken				    /*asc*/ 0x26,
658226067Sken				    /*ascq*/ 0x00,
659226067Sken				    SSD_ELEM_NONE);
660226067Sken
661226067Sken		ccb->csio.sense_len = SSD_FULL_SIZE;
662226067Sken		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
663224857Snwhitehorn		ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
664224857Snwhitehorn		xpt_done(ccb);
665224857Snwhitehorn	} else {
666224857Snwhitehorn		CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
667224857Snwhitehorn		   ("ATAPI command 0x%02x tag 0x%016lx submitted\n ", cdb[0],
668224857Snwhitehorn		   xp->x_tag));
669224857Snwhitehorn
670224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_active_xferq, xp, x_queue);
671224857Snwhitehorn		ccb->ccb_h.status |= CAM_SIM_QUEUED;
672224857Snwhitehorn	}
673224857Snwhitehorn}
674224857Snwhitehorn
675224857Snwhitehornstatic int
676224857Snwhitehornps3cdrom_decode_lv1_status(uint64_t status, u_int8_t *sense_key, u_int8_t *asc,
677224857Snwhitehorn    u_int8_t *ascq)
678224857Snwhitehorn{
679224857Snwhitehorn	if (((status >> 24) & 0xff) != SCSI_STATUS_CHECK_COND)
680224857Snwhitehorn		return -1;
681224857Snwhitehorn
682224857Snwhitehorn	*sense_key = (status >> 16) & 0xff;
683224857Snwhitehorn	*asc = (status >> 8) & 0xff;
684224857Snwhitehorn	*ascq = status & 0xff;
685224857Snwhitehorn
686224857Snwhitehorn	return (0);
687224857Snwhitehorn}
688224857Snwhitehorn
689224857Snwhitehornstatic device_method_t ps3cdrom_methods[] = {
690224857Snwhitehorn	DEVMETHOD(device_probe,		ps3cdrom_probe),
691224857Snwhitehorn	DEVMETHOD(device_attach,	ps3cdrom_attach),
692224857Snwhitehorn	DEVMETHOD(device_detach,	ps3cdrom_detach),
693224857Snwhitehorn	{0, 0},
694224857Snwhitehorn};
695224857Snwhitehorn
696224857Snwhitehornstatic driver_t ps3cdrom_driver = {
697224857Snwhitehorn	"ps3cdrom",
698224857Snwhitehorn	ps3cdrom_methods,
699224857Snwhitehorn	sizeof(struct ps3cdrom_softc),
700224857Snwhitehorn};
701224857Snwhitehorn
702224857Snwhitehornstatic devclass_t ps3cdrom_devclass;
703224857Snwhitehorn
704224857SnwhitehornDRIVER_MODULE(ps3cdrom, ps3bus, ps3cdrom_driver, ps3cdrom_devclass, 0, 0);
705224857SnwhitehornMODULE_DEPEND(ps3cdrom, cam, 1, 1, 1);
706