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