1/*	$NetBSD: umass_isdata.c,v 1.20 2011/08/23 16:16:43 christos Exp $	*/
2
3/*
4 * TODO:
5 *  get ATA registers on any kind of error
6 *  implement more commands (what is needed)
7 */
8
9/*
10 * Copyright (c) 2001 The NetBSD Foundation, Inc.
11 * All rights reserved.
12 *
13 * This code is derived from software contributed to The NetBSD Foundation
14 * by Lennart Augustsson (lennart@augustsson.net) at
15 * Carlstedt Research & Technology.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 *    notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 *    notice, this list of conditions and the following disclaimer in the
24 *    documentation and/or other materials provided with the distribution.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include <sys/cdefs.h>
40__KERNEL_RCSID(0, "$NetBSD: umass_isdata.c,v 1.20 2011/08/23 16:16:43 christos Exp $");
41
42#ifdef _KERNEL_OPT
43#include "opt_umass.h"
44#endif
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/kernel.h>
49#include <sys/conf.h>
50#include <sys/buf.h>
51#include <sys/device.h>
52#include <sys/proc.h>
53#include <sys/disklabel.h>
54#include <sys/malloc.h>
55
56#include <dev/usb/usb.h>
57#include <dev/usb/usbdi.h>
58#include <dev/usb/usbdi_util.h>
59
60#include <dev/usb/umassvar.h>
61#include <dev/usb/umass_isdata.h>
62
63int umass_wd_attach(struct umass_softc *);
64
65#include <dev/ata/atareg.h>
66#include <dev/ata/atavar.h>
67
68/* XXX move this */
69struct isd200_config {
70        uByte EventNotification;
71        uByte ExternalClock;
72        uByte ATAInitTimeout;
73        uByte ATAMisc1;
74#define ATATiming		0x0f
75#define ATAPIReset		0x10
76#define MasterSlaveSelection	0x20
77#define ATAPICommandBlockSize	0xc0
78        uByte ATAMajorCommand;
79        uByte ATAMinorCommand;
80	uByte ATAMisc2;
81#define LastLUNIdentifier	0x07
82#define DescriptOverride	0x08
83#define ATA3StateSuspend	0x10
84#define SkipDeviceBoot		0x20
85#define ConfigDescriptor2	0x40
86#define InitStatus		0x80
87	uByte ATAMisc3;
88#define SRSTEnable		0x01
89};
90
91struct uisdata_softc {
92	struct umassbus_softc	base;
93
94	struct ata_drive_datas	sc_drv_data;
95	struct isd200_config	sc_isd_config;
96	void			*sc_ata_bio;
97	u_long			sc_skip;
98};
99
100#undef DPRINTF
101#undef DPRINTFN
102#ifdef UISDATA_DEBUG
103#define DPRINTF(x)	if (uisdatadebug) printf x
104#define DPRINTFN(n,x)	if (uisdatadebug>(n)) printf x
105int	uisdatadebug = 0;
106#else
107#define DPRINTF(x)
108#define DPRINTFN(n,x)
109#endif
110
111int  uisdata_bio(struct ata_drive_datas *, struct ata_bio *);
112int  uisdata_bio1(struct ata_drive_datas *, struct ata_bio *);
113void uisdata_reset_drive(struct ata_drive_datas *, int);
114void uisdata_reset_channel(struct ata_channel *, int);
115int  uisdata_exec_command(struct ata_drive_datas *, struct ata_command *);
116int  uisdata_get_params(struct ata_drive_datas *, u_int8_t, struct ataparams *);
117int  uisdata_addref(struct ata_drive_datas *);
118void uisdata_delref(struct ata_drive_datas *);
119void uisdata_kill_pending(struct ata_drive_datas *);
120
121void uisdata_bio_cb(struct umass_softc *, void *, int, int);
122void uisdata_exec_cb(struct umass_softc *, void *, int, int);
123int  uwdprint(void *, const char *);
124
125const struct ata_bustype uisdata_bustype = {
126	SCSIPI_BUSTYPE_ATA,
127	uisdata_bio,
128	uisdata_reset_drive,
129	uisdata_reset_channel,
130	uisdata_exec_command,
131	uisdata_get_params,
132	uisdata_addref,
133	uisdata_delref,
134	uisdata_kill_pending,
135};
136
137struct ata_cmd {
138	u_int8_t ac_signature0;
139	u_int8_t ac_signature1;
140
141	u_int8_t ac_action_select;
142#define AC_ReadRegisterAccess		0x01
143#define AC_NoDeviceSelectionBit		0x02
144#define AC_NoBSYPollBit			0x04
145#define AC_IgnorePhaseErrorBit		0x08
146#define AC_IgnoreDeviceErrorBit		0x10
147
148	u_int8_t ac_register_select;
149#define AC_SelectAlternateStatus	0x01 /* R */
150#define AC_SelectDeviceControl		0x01 /* W */
151#define AC_SelectError			0x02 /* R */
152#define AC_SelectFeatures		0x02 /* W */
153#define AC_SelectSectorCount		0x04 /* RW */
154#define AC_SelectSectorNumber		0x08 /* RW */
155#define AC_SelectCylinderLow		0x10 /* RW */
156#define AC_SelectCylinderHigh		0x20 /* RW */
157#define AC_SelectDeviceHead		0x40 /* RW */
158#define AC_SelectStatus			0x80 /* R */
159#define AC_SelectCommand		0x80 /* W */
160
161	u_int8_t ac_transfer_blocksize;
162
163	u_int8_t ac_alternate_status;
164#define ac_device_control ac_alternate_status
165	u_int8_t ac_error;
166#define ac_features ac_error
167
168	u_int8_t ac_sector_count;
169	u_int8_t ac_sector_number;
170	u_int8_t ac_cylinder_low;
171	u_int8_t ac_cylinder_high;
172	u_int8_t ac_device_head;
173
174	u_int8_t ac_status;
175#define ac_command ac_status
176
177	u_int8_t ac_reserved[3];
178};
179
180#define ATA_DELAY 10000 /* 10s for a drive I/O */
181
182int
183umass_isdata_attach(struct umass_softc *sc)
184{
185	usb_device_request_t req;
186	usbd_status err;
187	struct ata_device adev;
188	struct uisdata_softc *scbus;
189	struct isd200_config *cf;
190
191	scbus = malloc(sizeof *scbus, M_DEVBUF, M_WAITOK | M_ZERO);
192	sc->bus = &scbus->base;
193	cf = &scbus->sc_isd_config;
194
195	req.bmRequestType = UT_READ_VENDOR_DEVICE;
196	req.bRequest = 0x02;
197	USETW(req.wValue, 0);
198	USETW(req.wIndex, 2);
199	USETW(req.wLength, sizeof *cf);
200
201	err = usbd_do_request(sc->sc_udev, &req, cf);
202	if (err)
203		return (EIO);
204	DPRINTF(("umass_wd_attach info:\n  EventNotification=0x%02x "
205		 "ExternalClock=0x%02x ATAInitTimeout=0x%02x\n"
206		 "  ATAMisc1=0x%02x ATAMajorCommand=0x%02x "
207		 "ATAMinorCommand=0x%02x\n"
208		 "  ATAMisc2=0x%02x ATAMisc3=0x%02x\n",
209		 cf->EventNotification, cf->ExternalClock, cf->ATAInitTimeout,
210		 cf->ATAMisc1, cf->ATAMajorCommand, cf->ATAMinorCommand,
211		 cf->ATAMisc2, cf->ATAMisc3));
212
213	memset(&adev, 0, sizeof(struct ata_device));
214	adev.adev_bustype = &uisdata_bustype;
215	adev.adev_channel = 1;	/* XXX */
216	adev.adev_openings = 1;
217	adev.adev_drv_data = &scbus->sc_drv_data;
218	scbus->sc_drv_data.drive_flags = DRIVE_ATA;
219	scbus->sc_drv_data.chnl_softc = sc;
220	scbus->base.sc_child = config_found(sc->sc_dev, &adev, uwdprint);
221
222	return (0);
223}
224
225
226void
227uisdata_bio_cb(struct umass_softc *sc, void *priv, int residue, int status)
228{
229	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
230	struct ata_bio *ata_bio = priv;
231	int s;
232
233	DPRINTF(("%s: residue=%d status=%d\n", __func__, residue, status));
234
235	s = splbio();
236	scbus->sc_ata_bio = NULL;
237	if (status != STATUS_CMD_OK)
238		ata_bio->error = ERR_DF; /* ??? */
239	else
240		ata_bio->error = NOERROR;
241	ata_bio->flags |= ATA_ITSDONE;
242
243	ata_bio->blkdone += ata_bio->nblks;
244	ata_bio->blkno += ata_bio->nblks;
245	ata_bio->bcount -= ata_bio->nbytes;
246	scbus->sc_skip += ata_bio->nbytes;
247	if (residue != 0) {
248		ata_bio->bcount += residue;
249	} else if (ata_bio->bcount > 0) {
250		DPRINTF(("%s: continue\n", __func__));
251		(void)uisdata_bio1(&scbus->sc_drv_data, ata_bio); /*XXX save drv*/
252		splx(s);
253		return;
254	}
255
256	if (ata_bio->flags & ATA_POLL) {
257		DPRINTF(("%s: wakeup %p\n", __func__, ata_bio));
258		wakeup(ata_bio);
259	} else {
260		(*scbus->sc_drv_data.drv_done)(scbus->sc_drv_data.drv_softc);
261	}
262	splx(s);
263}
264
265int
266uisdata_bio(struct ata_drive_datas *drv, struct ata_bio *ata_bio)
267{
268	struct umass_softc *sc = drv->chnl_softc;
269	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
270
271	scbus->sc_skip = 0;
272	return (uisdata_bio1(drv, ata_bio));
273}
274
275int
276uisdata_bio1(struct ata_drive_datas *drv, struct ata_bio *ata_bio)
277{
278	struct umass_softc *sc = drv->chnl_softc;
279	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
280	struct isd200_config *cf = &scbus->sc_isd_config;
281	struct ata_cmd ata;
282	u_int16_t cyl;
283	u_int8_t head, sect;
284	int dir;
285	long nbytes;
286	u_int nblks;
287
288	DPRINTF(("%s\n", __func__));
289	/* XXX */
290
291	if (ata_bio->flags & ATA_NOSLEEP) {
292		printf("%s: ATA_NOSLEEP not supported\n", __func__);
293		ata_bio->error = TIMEOUT;
294		ata_bio->flags |= ATA_ITSDONE;
295		return (ATACMD_COMPLETE);
296	}
297
298	if (scbus->sc_ata_bio != NULL) {
299		printf("%s: multiple uisdata_bio\n", __func__);
300		return (ATACMD_TRY_AGAIN);
301	} else
302		scbus->sc_ata_bio = ata_bio;
303
304	if (ata_bio->flags & ATA_LBA) {
305		sect = (ata_bio->blkno >> 0) & 0xff;
306		cyl = (ata_bio->blkno >> 8) & 0xffff;
307		head = (ata_bio->blkno >> 24) & 0x0f;
308		head |= WDSD_LBA;
309	} else {
310		int blkno = ata_bio->blkno;
311		sect = blkno % ata_bio->lp->d_nsectors;
312		sect++;    /* Sectors begin with 1, not 0. */
313		blkno /= ata_bio->lp->d_nsectors;
314		head = blkno % ata_bio->lp->d_ntracks;
315		blkno /= ata_bio->lp->d_ntracks;
316		cyl = blkno;
317		head |= WDSD_CHS;
318	}
319
320	nbytes = ata_bio->bcount;
321	if (ata_bio->flags & ATA_SINGLE)
322		nblks = 1;
323	else
324		nblks = min(ata_bio->multi, nbytes / ata_bio->lp->d_secsize);
325	nbytes = nblks * ata_bio->lp->d_secsize;
326	ata_bio->nblks = nblks;
327	ata_bio->nbytes = nbytes;
328
329	memset(&ata, 0, sizeof ata);
330	ata.ac_signature0 = cf->ATAMajorCommand;
331	ata.ac_signature1 = cf->ATAMinorCommand;
332	ata.ac_transfer_blocksize = 1;
333	ata.ac_sector_count = nblks;
334	ata.ac_sector_number = sect;
335	ata.ac_cylinder_high = cyl >> 8;
336	ata.ac_cylinder_low = cyl;
337	ata.ac_device_head = head;
338	ata.ac_register_select = AC_SelectSectorCount | AC_SelectSectorNumber |
339	    AC_SelectCylinderLow | AC_SelectCylinderHigh | AC_SelectDeviceHead |
340	    AC_SelectCommand;
341
342	dir = DIR_NONE;
343	if (ata_bio->bcount != 0) {
344		if (ata_bio->flags & ATA_READ)
345			dir = DIR_IN;
346		else
347			dir = DIR_OUT;
348	}
349
350	if (ata_bio->flags & ATA_READ) {
351		ata.ac_command = WDCC_READ;
352	} else {
353		ata.ac_command = WDCC_WRITE;
354	}
355	DPRINTF(("%s: bno=%" PRId64 " LBA=%d cyl=%d head=%d sect=%d "
356		 "count=%d multi=%d\n",
357		 __func__, ata_bio->blkno,
358		 (ata_bio->flags & ATA_LBA) != 0, cyl, head, sect,
359		 ata.ac_sector_count, ata_bio->multi));
360	DPRINTF(("    data=%p bcount=%ld, drive=%d\n", ata_bio->databuf,
361		 ata_bio->bcount, drv->drive));
362	sc->sc_methods->wire_xfer(sc, drv->drive, &ata, sizeof ata,
363				  ata_bio->databuf + scbus->sc_skip, nbytes,
364				  dir, ATA_DELAY, uisdata_bio_cb, ata_bio);
365
366	while (ata_bio->flags & ATA_POLL) {
367		DPRINTF(("%s: tsleep %p\n", __func__, ata_bio));
368		if (tsleep(ata_bio, PZERO, "uisdatabl", 0)) {
369			ata_bio->error = TIMEOUT;
370			ata_bio->flags |= ATA_ITSDONE;
371			return (ATACMD_COMPLETE);
372		}
373	}
374
375	return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED;
376}
377
378void
379uisdata_reset_drive(struct ata_drive_datas *drv, int flags)
380{
381	DPRINTFN(-1,("%s\n", __func__));
382	/* XXX what? */
383}
384
385void
386uisdata_reset_channel(struct ata_channel *chp, int flags)
387{
388	DPRINTFN(-1,("%s\n", __func__));
389	/* XXX what? */
390}
391
392void
393uisdata_exec_cb(struct umass_softc *sc, void *priv,
394    int residue, int status)
395{
396	struct ata_command *cmd = priv;
397
398	DPRINTF(("%s: status=%d\n", __func__, status));
399	if (status != STATUS_CMD_OK)
400		cmd->flags |= AT_DF; /* XXX */
401	cmd->flags |= AT_DONE;
402	if (cmd->flags & (AT_READ | AT_WRITE))
403		cmd->flags |= AT_XFDONE;
404	if (cmd->flags & (AT_POLL | AT_WAIT)) {
405		DPRINTF(("%s: wakeup %p\n", __func__, cmd));
406		wakeup(cmd);
407	}
408}
409
410int
411uisdata_exec_command(struct ata_drive_datas *drv, struct ata_command *cmd)
412{
413	struct umass_softc *sc = drv->chnl_softc;
414	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
415	struct isd200_config *cf = &scbus->sc_isd_config;
416	int dir;
417	struct ata_cmd ata;
418
419	DPRINTF(("%s\n", __func__));
420	DPRINTF(("  r_command=0x%02x timeout=%d flags=0x%x bcount=%d\n",
421		 cmd->r_command, cmd->timeout, cmd->flags, cmd->bcount));
422
423	dir = DIR_NONE;
424	if (cmd->bcount != 0) {
425		if (cmd->flags & AT_READ)
426			dir = DIR_IN;
427		else
428			dir = DIR_OUT;
429	}
430
431	if (cmd->bcount > UMASS_MAX_TRANSFER_SIZE) {
432		printf("uisdata_exec_command: large datalen %d\n", cmd->bcount);
433		cmd->flags |= AT_ERROR;
434		goto done;
435	}
436
437	memset(&ata, 0, sizeof ata);
438	ata.ac_signature0 = cf->ATAMajorCommand;
439	ata.ac_signature1 = cf->ATAMinorCommand;
440	ata.ac_transfer_blocksize = 1;
441
442	switch (cmd->r_command) {
443	case WDCC_IDENTIFY:
444		ata.ac_register_select |= AC_SelectCommand;
445		ata.ac_command = WDCC_IDENTIFY;
446		break;
447	default:
448		printf("uisdata_exec_command: bad command 0x%02x\n",
449		       cmd->r_command);
450		cmd->flags |= AT_ERROR;
451		goto done;
452	}
453
454	DPRINTF(("%s: execute ATA command 0x%02x, drive=%d\n", __func__,
455		 ata.ac_command, drv->drive));
456	sc->sc_methods->wire_xfer(sc, drv->drive, &ata,
457				  sizeof ata, cmd->data, cmd->bcount, dir,
458				  cmd->timeout, uisdata_exec_cb, cmd);
459	if (cmd->flags & (AT_POLL | AT_WAIT)) {
460#if 0
461		if (cmd->flags & AT_POLL)
462			printf("%s: AT_POLL not supported\n", __func__);
463#endif
464		DPRINTF(("%s: tsleep %p\n", __func__, cmd));
465		if (tsleep(cmd, PZERO, "uisdataex", 0)) {
466			cmd->flags |= AT_ERROR;
467			goto done;
468		}
469	}
470
471done:
472	return (ATACMD_COMPLETE);
473}
474
475int
476uisdata_addref(struct ata_drive_datas *drv)
477{
478	DPRINTF(("%s\n", __func__));
479	/* Nothing to do */
480	return (0);
481}
482
483void
484uisdata_delref(struct ata_drive_datas *drv)
485{
486	DPRINTF(("%s\n", __func__));
487	/* Nothing to do */
488}
489
490void
491uisdata_kill_pending(struct ata_drive_datas *drv)
492{
493	struct umass_softc *sc = drv->chnl_softc;
494	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
495	struct ata_bio *ata_bio = scbus->sc_ata_bio;
496
497	DPRINTFN(-1,("%s\n", __func__));
498
499	if (ata_bio == NULL)
500		return;
501	scbus->sc_ata_bio = NULL;
502	ata_bio->flags |= ATA_ITSDONE;
503	ata_bio->error = ERR_NODEV;
504	ata_bio->r_error = WDCE_ABRT;
505	(*scbus->sc_drv_data.drv_done)(scbus->sc_drv_data.drv_softc);
506}
507
508int
509uisdata_get_params(struct ata_drive_datas *drvp, u_int8_t flags,
510		struct ataparams *prms)
511{
512	char tb[DEV_BSIZE];
513	struct ata_command ata_c;
514
515#if BYTE_ORDER == LITTLE_ENDIAN
516	int i;
517	u_int16_t *p;
518#endif
519
520	DPRINTF(("%s\n", __func__));
521
522	memset(tb, 0, DEV_BSIZE);
523	memset(prms, 0, sizeof(struct ataparams));
524	memset(&ata_c, 0, sizeof(struct ata_command));
525
526	ata_c.r_command = WDCC_IDENTIFY;
527	ata_c.timeout = 1000; /* 1s */
528	ata_c.flags = AT_READ | flags;
529	ata_c.data = tb;
530	ata_c.bcount = DEV_BSIZE;
531	if (uisdata_exec_command(drvp, &ata_c) != ATACMD_COMPLETE) {
532		DPRINTF(("uisdata_get_parms: wdc_exec_command failed\n"));
533		return (CMD_AGAIN);
534	}
535	if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
536		DPRINTF(("uisdata_get_parms: ata_c.flags=0x%x\n",
537			 ata_c.flags));
538		return (CMD_ERR);
539	} else {
540		/* Read in parameter block. */
541		memcpy(prms, tb, sizeof(struct ataparams));
542#if BYTE_ORDER == LITTLE_ENDIAN
543		/* XXX copied from ata.c */
544		/*
545		 * Shuffle string byte order.
546		 * ATAPI Mitsumi and NEC drives don't need this.
547		 */
548		if ((prms->atap_config & WDC_CFG_ATAPI_MASK) ==
549		    WDC_CFG_ATAPI &&
550		    ((prms->atap_model[0] == 'N' &&
551			prms->atap_model[1] == 'E') ||
552		     (prms->atap_model[0] == 'F' &&
553			 prms->atap_model[1] == 'X')))
554			return 0;
555		for (i = 0; i < sizeof(prms->atap_model); i += 2) {
556			p = (u_short *)(prms->atap_model + i);
557			*p = ntohs(*p);
558		}
559		for (i = 0; i < sizeof(prms->atap_serial); i += 2) {
560			p = (u_short *)(prms->atap_serial + i);
561			*p = ntohs(*p);
562		}
563		for (i = 0; i < sizeof(prms->atap_revision); i += 2) {
564			p = (u_short *)(prms->atap_revision + i);
565			*p = ntohs(*p);
566		}
567#endif
568		return CMD_OK;
569	}
570}
571
572
573/* XXX join with wdc.c routine? */
574int
575uwdprint(void *aux, const char *pnp)
576{
577	//struct ata_device *adev = aux;
578	if (pnp)
579		aprint_normal("wd at %s", pnp);
580#if 0
581	aprint_normal(" channel %d drive %d", adev->adev_channel,
582	    adev->adev_drv_data->drive);
583#endif
584	return (UNCONF);
585}
586
587
588#if 0
589
590int umass_wd_attach(struct umass_softc *);
591
592#if NWD > 0
593	case UMASS_CPROTO_ISD_ATA:
594		return (umass_wd_attach(sc));
595#endif
596
597#endif
598