1/* $NetBSD: umass_isdata.c,v 1.32 2016/04/23 10:15:32 skrll 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.32 2016/04/23 10:15:32 skrll Exp $");
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/kernel.h>
45#include <sys/conf.h>
46#include <sys/buf.h>
47#include <sys/device.h>
48#include <sys/proc.h>
49#include <sys/disklabel.h>
50#include <sys/malloc.h>
51
52#include <dev/usb/usb.h>
53#include <dev/usb/usbdi.h>
54#include <dev/usb/usbdi_util.h>
55
56#include <dev/usb/umassvar.h>
57#include <dev/usb/umass_isdata.h>
58
59int umass_wd_attach(struct umass_softc *);
60
61#include <dev/ata/atareg.h>
62#include <dev/ata/atavar.h>
63
64/* XXX move this */
65struct isd200_config {
66 uByte EventNotification;
67 uByte ExternalClock;
68 uByte ATAInitTimeout;
69 uByte ATAMisc1;
70#define ATATiming 0x0f
71#define ATAPIReset 0x10
72#define MasterSlaveSelection 0x20
73#define ATAPICommandBlockSize 0xc0
74 uByte ATAMajorCommand;
75 uByte ATAMinorCommand;
76 uByte ATAMisc2;
77#define LastLUNIdentifier 0x07
78#define DescriptOverride 0x08
79#define ATA3StateSuspend 0x10
80#define SkipDeviceBoot 0x20
81#define ConfigDescriptor2 0x40
82#define InitStatus 0x80
83 uByte ATAMisc3;
84#define SRSTEnable 0x01
85};
86
87struct uisdata_softc {
88 struct umassbus_softc base;
89
90 struct ata_drive_datas sc_drv_data;
91 struct isd200_config sc_isd_config;
92 void *sc_ata_bio;
93 u_long sc_skip;
94};
95
96#undef DPRINTF
97#undef DPRINTFN
98#ifdef UISDATA_DEBUG
99#define DPRINTF(x) if (uisdatadebug) printf x
100#define DPRINTFN(n,x) if (uisdatadebug>(n)) printf x
101int uisdatadebug = 0;
102#else
103#define DPRINTF(x)
104#define DPRINTFN(n,x)
105#endif
106
107int uisdata_bio(struct ata_drive_datas *, struct ata_bio *);
108int uisdata_bio1(struct ata_drive_datas *, struct ata_bio *);
109void uisdata_reset_drive(struct ata_drive_datas *, int, uint32_t *);
110void uisdata_reset_channel(struct ata_channel *, int);
111int uisdata_exec_command(struct ata_drive_datas *, struct ata_command *);
112int uisdata_get_params(struct ata_drive_datas *, uint8_t, struct ataparams *);
113int uisdata_addref(struct ata_drive_datas *);
114void uisdata_delref(struct ata_drive_datas *);
115void uisdata_kill_pending(struct ata_drive_datas *);
116
117void uisdata_bio_cb(struct umass_softc *, void *, int, int);
118void uisdata_exec_cb(struct umass_softc *, void *, int, int);
119int uwdprint(void *, const char *);
120
121const struct ata_bustype uisdata_bustype = {
122 SCSIPI_BUSTYPE_ATA,
123 uisdata_bio,
124 uisdata_reset_drive,
125 uisdata_reset_channel,
126 uisdata_exec_command,
127 uisdata_get_params,
128 uisdata_addref,
129 uisdata_delref,
130 uisdata_kill_pending,
131};
132
133struct ata_cmd {
134 uint8_t ac_signature0;
135 uint8_t ac_signature1;
136
137 uint8_t ac_action_select;
138#define AC_ReadRegisterAccess 0x01
139#define AC_NoDeviceSelectionBit 0x02
140#define AC_NoBSYPollBit 0x04
141#define AC_IgnorePhaseErrorBit 0x08
142#define AC_IgnoreDeviceErrorBit 0x10
143
144 uint8_t ac_register_select;
145#define AC_SelectAlternateStatus 0x01 /* R */
146#define AC_SelectDeviceControl 0x01 /* W */
147#define AC_SelectError 0x02 /* R */
148#define AC_SelectFeatures 0x02 /* W */
149#define AC_SelectSectorCount 0x04 /* RW */
150#define AC_SelectSectorNumber 0x08 /* RW */
151#define AC_SelectCylinderLow 0x10 /* RW */
152#define AC_SelectCylinderHigh 0x20 /* RW */
153#define AC_SelectDeviceHead 0x40 /* RW */
154#define AC_SelectStatus 0x80 /* R */
155#define AC_SelectCommand 0x80 /* W */
156
157 uint8_t ac_transfer_blocksize;
158
159 uint8_t ac_alternate_status;
160#define ac_device_control ac_alternate_status
161 uint8_t ac_error;
162#define ac_features ac_error
163
164 uint8_t ac_sector_count;
165 uint8_t ac_sector_number;
166 uint8_t ac_cylinder_low;
167 uint8_t ac_cylinder_high;
168 uint8_t ac_device_head;
169
170 uint8_t ac_status;
171#define ac_command ac_status
172
173 uint8_t ac_reserved[3];
174};
175
176#define ATA_DELAY 10000 /* 10s for a drive I/O */
177
178int
179umass_isdata_attach(struct umass_softc *sc)
180{
181 usb_device_request_t req;
182 usbd_status err;
183 struct ata_device adev;
184 struct uisdata_softc *scbus;
185 struct isd200_config *cf;
186
187 scbus = malloc(sizeof(*scbus), M_DEVBUF, M_WAITOK | M_ZERO);
188 sc->bus = &scbus->base;
189 cf = &scbus->sc_isd_config;
190
191 req.bmRequestType = UT_READ_VENDOR_DEVICE;
192 req.bRequest = 0x02;
193 USETW(req.wValue, 0);
194 USETW(req.wIndex, 2);
195 USETW(req.wLength, sizeof(*cf));
196
197 err = usbd_do_request(sc->sc_udev, &req, cf);
198 if (err) {
199 sc->bus = NULL;
200 free(scbus, M_DEVBUF);
201 return EIO;
202 }
203 DPRINTF(("umass_wd_attach info:\n EventNotification=0x%02x "
204 "ExternalClock=0x%02x ATAInitTimeout=0x%02x\n"
205 " ATAMisc1=0x%02x ATAMajorCommand=0x%02x "
206 "ATAMinorCommand=0x%02x\n"
207 " ATAMisc2=0x%02x ATAMisc3=0x%02x\n",
208 cf->EventNotification, cf->ExternalClock, cf->ATAInitTimeout,
209 cf->ATAMisc1, cf->ATAMajorCommand, cf->ATAMinorCommand,
210 cf->ATAMisc2, cf->ATAMisc3));
211
212 memset(&adev, 0, sizeof(struct ata_device));
213 adev.adev_bustype = &uisdata_bustype;
214 adev.adev_channel = 1; /* XXX */
215 adev.adev_openings = 1;
216 adev.adev_drv_data = &scbus->sc_drv_data;
217 scbus->sc_drv_data.drive_type = ATA_DRIVET_ATA;
218 scbus->sc_drv_data.chnl_softc = sc;
219 scbus->base.sc_child = config_found(sc->sc_dev, &adev, uwdprint);
220
221 return 0;
222}
223
224
225void
226uisdata_bio_cb(struct umass_softc *sc, void *priv, int residue, int status)
227{
228 struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
229 struct ata_bio *ata_bio = priv;
230 int s;
231
232 DPRINTF(("%s: residue=%d status=%d\n", __func__, residue, status));
233
234 s = splbio();
235 scbus->sc_ata_bio = NULL;
236 if (status != STATUS_CMD_OK)
237 ata_bio->error = ERR_DF; /* ??? */
238 else
239 ata_bio->error = NOERROR;
240 ata_bio->flags |= ATA_ITSDONE;
241
242 ata_bio->blkdone += ata_bio->nblks;
243 ata_bio->blkno += ata_bio->nblks;
244 ata_bio->bcount -= ata_bio->nbytes;
245 scbus->sc_skip += ata_bio->nbytes;
246 if (residue != 0) {
247 ata_bio->bcount += residue;
248 } else if (ata_bio->bcount > 0) {
249 DPRINTF(("%s: continue\n", __func__));
250 (void)uisdata_bio1(&scbus->sc_drv_data, ata_bio); /*XXX save drv*/
251 splx(s);
252 return;
253 }
254
255 if (ata_bio->flags & ATA_POLL) {
256 DPRINTF(("%s: wakeup %p\n", __func__, ata_bio));
257 wakeup(ata_bio);
258 } else {
259 (*scbus->sc_drv_data.drv_done)(scbus->sc_drv_data.drv_softc);
260 }
261 splx(s);
262}
263
264int
265uisdata_bio(struct ata_drive_datas *drv, struct ata_bio *ata_bio)
266{
267 struct umass_softc *sc = drv->chnl_softc;
268 struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
269
270 scbus->sc_skip = 0;
271 return uisdata_bio1(drv, ata_bio);
272}
273
274int
275uisdata_bio1(struct ata_drive_datas *drv, struct ata_bio *ata_bio)
276{
277 struct umass_softc *sc = drv->chnl_softc;
278 struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
279 struct isd200_config *cf = &scbus->sc_isd_config;
280 struct ata_cmd ata;
281 uint16_t cyl;
282 uint8_t head, sect;
283 int dir;
284 long nbytes;
285 u_int nblks;
286
287 DPRINTF(("%s\n", __func__));
288 /* XXX */
289
290 if (ata_bio->flags & ATA_POLL) {
291 printf("%s: ATA_POLL not supported\n", __func__);
292 ata_bio->error = TIMEOUT;
293 ata_bio->flags |= ATA_ITSDONE;
294 return ATACMD_COMPLETE;
295 }
296
297 if (scbus->sc_ata_bio != NULL) {
298 printf("%s: multiple uisdata_bio\n", __func__);
299 return ATACMD_TRY_AGAIN;
300 } else
301 scbus->sc_ata_bio = ata_bio;
302
303 if (ata_bio->flags & ATA_LBA) {
304 sect = (ata_bio->blkno >> 0) & 0xff;
305 cyl = (ata_bio->blkno >> 8) & 0xffff;
306 head = (ata_bio->blkno >> 24) & 0x0f;
307 head |= WDSD_LBA;
308 } else {
309 int blkno = ata_bio->blkno;
310 sect = blkno % ata_bio->lp->d_nsectors;
311 sect++; /* Sectors begin with 1, not 0. */
312 blkno /= ata_bio->lp->d_nsectors;
313 head = blkno % ata_bio->lp->d_ntracks;
314 blkno /= ata_bio->lp->d_ntracks;
315 cyl = blkno;
316 head |= WDSD_CHS;
317 }
318
319 nbytes = ata_bio->bcount;
320 if (ata_bio->flags & ATA_SINGLE)
321 nblks = 1;
322 else
323 nblks = min(ata_bio->multi, nbytes / ata_bio->lp->d_secsize);
324 nbytes = nblks * ata_bio->lp->d_secsize;
325 ata_bio->nblks = nblks;
326 ata_bio->nbytes = nbytes;
327
328 memset(&ata, 0, sizeof(ata));
329 ata.ac_signature0 = cf->ATAMajorCommand;
330 ata.ac_signature1 = cf->ATAMinorCommand;
331 ata.ac_transfer_blocksize = 1;
332 ata.ac_sector_count = nblks;
333 ata.ac_sector_number = sect;
334 ata.ac_cylinder_high = cyl >> 8;
335 ata.ac_cylinder_low = cyl;
336 ata.ac_device_head = head;
337 ata.ac_register_select = AC_SelectSectorCount | AC_SelectSectorNumber |
338 AC_SelectCylinderLow | AC_SelectCylinderHigh | AC_SelectDeviceHead |
339 AC_SelectCommand;
340
341 dir = DIR_NONE;
342 if (ata_bio->bcount != 0) {
343 if (ata_bio->flags & ATA_READ)
344 dir = DIR_IN;
345 else
346 dir = DIR_OUT;
347 }
348
349 if (ata_bio->flags & ATA_READ) {
350 ata.ac_command = WDCC_READ;
351 } else {
352 ata.ac_command = WDCC_WRITE;
353 }
354 DPRINTF(("%s: bno=%" PRId64 " LBA=%d cyl=%d head=%d sect=%d "
355 "count=%d multi=%d\n",
356 __func__, ata_bio->blkno,
357 (ata_bio->flags & ATA_LBA) != 0, cyl, head, sect,
358 ata.ac_sector_count, ata_bio->multi));
359 DPRINTF((" data=%p bcount=%ld, drive=%d\n", ata_bio->databuf,
360 ata_bio->bcount, drv->drive));
361 sc->sc_methods->wire_xfer(sc, drv->drive, &ata, sizeof(ata),
362 ata_bio->databuf + scbus->sc_skip, nbytes,
363 dir, ATA_DELAY, 0, uisdata_bio_cb, ata_bio);
364
365 while (ata_bio->flags & ATA_POLL) {
366 DPRINTF(("%s: tsleep %p\n", __func__, ata_bio));
367 if (tsleep(ata_bio, PZERO, "uisdatabl", 0)) {
368 ata_bio->error = TIMEOUT;
369 ata_bio->flags |= ATA_ITSDONE;
370 return ATACMD_COMPLETE;
371 }
372 }
373
374 return ata_bio->flags & ATA_ITSDONE ? ATACMD_COMPLETE : ATACMD_QUEUED;
375}
376
377void
378uisdata_reset_drive(struct ata_drive_datas *drv, int flags, uint32_t *sigp)
379{
380 DPRINTFN(-1,("%s\n", __func__));
381 KASSERT(sigp == NULL);
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, 0, 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, uint8_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 uint16_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_CFA_MAGIC &&
549 (prms->atap_config & 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