Home | History | Annotate | Line # | Download | only in usb
umass_isdata.c revision 1.30
      1 /*	$NetBSD: umass_isdata.c,v 1.29 2013/10/30 15:37:49 drochner 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 (at) 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.29 2013/10/30 15:37:49 drochner 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 
     59 int umass_wd_attach(struct umass_softc *);
     60 
     61 #include <dev/ata/atareg.h>
     62 #include <dev/ata/atavar.h>
     63 
     64 /* XXX move this */
     65 struct 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 
     87 struct 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
    101 int	uisdatadebug = 0;
    102 #else
    103 #define DPRINTF(x)
    104 #define DPRINTFN(n,x)
    105 #endif
    106 
    107 int  uisdata_bio(struct ata_drive_datas *, struct ata_bio *);
    108 int  uisdata_bio1(struct ata_drive_datas *, struct ata_bio *);
    109 void uisdata_reset_drive(struct ata_drive_datas *, int, uint32_t *);
    110 void uisdata_reset_channel(struct ata_channel *, int);
    111 int  uisdata_exec_command(struct ata_drive_datas *, struct ata_command *);
    112 int  uisdata_get_params(struct ata_drive_datas *, u_int8_t, struct ataparams *);
    113 int  uisdata_addref(struct ata_drive_datas *);
    114 void uisdata_delref(struct ata_drive_datas *);
    115 void uisdata_kill_pending(struct ata_drive_datas *);
    116 
    117 void uisdata_bio_cb(struct umass_softc *, void *, int, int);
    118 void uisdata_exec_cb(struct umass_softc *, void *, int, int);
    119 int  uwdprint(void *, const char *);
    120 
    121 const 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 
    133 struct ata_cmd {
    134 	u_int8_t ac_signature0;
    135 	u_int8_t ac_signature1;
    136 
    137 	u_int8_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 	u_int8_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 	u_int8_t ac_transfer_blocksize;
    158 
    159 	u_int8_t ac_alternate_status;
    160 #define ac_device_control ac_alternate_status
    161 	u_int8_t ac_error;
    162 #define ac_features ac_error
    163 
    164 	u_int8_t ac_sector_count;
    165 	u_int8_t ac_sector_number;
    166 	u_int8_t ac_cylinder_low;
    167 	u_int8_t ac_cylinder_high;
    168 	u_int8_t ac_device_head;
    169 
    170 	u_int8_t ac_status;
    171 #define ac_command ac_status
    172 
    173 	u_int8_t ac_reserved[3];
    174 };
    175 
    176 #define ATA_DELAY 10000 /* 10s for a drive I/O */
    177 
    178 int
    179 umass_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 		return (EIO);
    200 	DPRINTF(("umass_wd_attach info:\n  EventNotification=0x%02x "
    201 		 "ExternalClock=0x%02x ATAInitTimeout=0x%02x\n"
    202 		 "  ATAMisc1=0x%02x ATAMajorCommand=0x%02x "
    203 		 "ATAMinorCommand=0x%02x\n"
    204 		 "  ATAMisc2=0x%02x ATAMisc3=0x%02x\n",
    205 		 cf->EventNotification, cf->ExternalClock, cf->ATAInitTimeout,
    206 		 cf->ATAMisc1, cf->ATAMajorCommand, cf->ATAMinorCommand,
    207 		 cf->ATAMisc2, cf->ATAMisc3));
    208 
    209 	memset(&adev, 0, sizeof(struct ata_device));
    210 	adev.adev_bustype = &uisdata_bustype;
    211 	adev.adev_channel = 1;	/* XXX */
    212 	adev.adev_openings = 1;
    213 	adev.adev_drv_data = &scbus->sc_drv_data;
    214 	scbus->sc_drv_data.drive_type = ATA_DRIVET_ATA;
    215 	scbus->sc_drv_data.chnl_softc = sc;
    216 	scbus->base.sc_child = config_found(sc->sc_dev, &adev, uwdprint);
    217 
    218 	return (0);
    219 }
    220 
    221 
    222 void
    223 uisdata_bio_cb(struct umass_softc *sc, void *priv, int residue, int status)
    224 {
    225 	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
    226 	struct ata_bio *ata_bio = priv;
    227 	int s;
    228 
    229 	DPRINTF(("%s: residue=%d status=%d\n", __func__, residue, status));
    230 
    231 	s = splbio();
    232 	scbus->sc_ata_bio = NULL;
    233 	if (status != STATUS_CMD_OK)
    234 		ata_bio->error = ERR_DF; /* ??? */
    235 	else
    236 		ata_bio->error = NOERROR;
    237 	ata_bio->flags |= ATA_ITSDONE;
    238 
    239 	ata_bio->blkdone += ata_bio->nblks;
    240 	ata_bio->blkno += ata_bio->nblks;
    241 	ata_bio->bcount -= ata_bio->nbytes;
    242 	scbus->sc_skip += ata_bio->nbytes;
    243 	if (residue != 0) {
    244 		ata_bio->bcount += residue;
    245 	} else if (ata_bio->bcount > 0) {
    246 		DPRINTF(("%s: continue\n", __func__));
    247 		(void)uisdata_bio1(&scbus->sc_drv_data, ata_bio); /*XXX save drv*/
    248 		splx(s);
    249 		return;
    250 	}
    251 
    252 	if (ata_bio->flags & ATA_POLL) {
    253 		DPRINTF(("%s: wakeup %p\n", __func__, ata_bio));
    254 		wakeup(ata_bio);
    255 	} else {
    256 		(*scbus->sc_drv_data.drv_done)(scbus->sc_drv_data.drv_softc);
    257 	}
    258 	splx(s);
    259 }
    260 
    261 int
    262 uisdata_bio(struct ata_drive_datas *drv, struct ata_bio *ata_bio)
    263 {
    264 	struct umass_softc *sc = drv->chnl_softc;
    265 	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
    266 
    267 	scbus->sc_skip = 0;
    268 	return (uisdata_bio1(drv, ata_bio));
    269 }
    270 
    271 int
    272 uisdata_bio1(struct ata_drive_datas *drv, struct ata_bio *ata_bio)
    273 {
    274 	struct umass_softc *sc = drv->chnl_softc;
    275 	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
    276 	struct isd200_config *cf = &scbus->sc_isd_config;
    277 	struct ata_cmd ata;
    278 	u_int16_t cyl;
    279 	u_int8_t head, sect;
    280 	int dir;
    281 	long nbytes;
    282 	u_int nblks;
    283 
    284 	DPRINTF(("%s\n", __func__));
    285 	/* XXX */
    286 
    287 	if (ata_bio->flags & ATA_POLL) {
    288 		printf("%s: ATA_POLL not supported\n", __func__);
    289 		ata_bio->error = TIMEOUT;
    290 		ata_bio->flags |= ATA_ITSDONE;
    291 		return (ATACMD_COMPLETE);
    292 	}
    293 
    294 	if (scbus->sc_ata_bio != NULL) {
    295 		printf("%s: multiple uisdata_bio\n", __func__);
    296 		return (ATACMD_TRY_AGAIN);
    297 	} else
    298 		scbus->sc_ata_bio = ata_bio;
    299 
    300 	if (ata_bio->flags & ATA_LBA) {
    301 		sect = (ata_bio->blkno >> 0) & 0xff;
    302 		cyl = (ata_bio->blkno >> 8) & 0xffff;
    303 		head = (ata_bio->blkno >> 24) & 0x0f;
    304 		head |= WDSD_LBA;
    305 	} else {
    306 		int blkno = ata_bio->blkno;
    307 		sect = blkno % ata_bio->lp->d_nsectors;
    308 		sect++;    /* Sectors begin with 1, not 0. */
    309 		blkno /= ata_bio->lp->d_nsectors;
    310 		head = blkno % ata_bio->lp->d_ntracks;
    311 		blkno /= ata_bio->lp->d_ntracks;
    312 		cyl = blkno;
    313 		head |= WDSD_CHS;
    314 	}
    315 
    316 	nbytes = ata_bio->bcount;
    317 	if (ata_bio->flags & ATA_SINGLE)
    318 		nblks = 1;
    319 	else
    320 		nblks = min(ata_bio->multi, nbytes / ata_bio->lp->d_secsize);
    321 	nbytes = nblks * ata_bio->lp->d_secsize;
    322 	ata_bio->nblks = nblks;
    323 	ata_bio->nbytes = nbytes;
    324 
    325 	memset(&ata, 0, sizeof ata);
    326 	ata.ac_signature0 = cf->ATAMajorCommand;
    327 	ata.ac_signature1 = cf->ATAMinorCommand;
    328 	ata.ac_transfer_blocksize = 1;
    329 	ata.ac_sector_count = nblks;
    330 	ata.ac_sector_number = sect;
    331 	ata.ac_cylinder_high = cyl >> 8;
    332 	ata.ac_cylinder_low = cyl;
    333 	ata.ac_device_head = head;
    334 	ata.ac_register_select = AC_SelectSectorCount | AC_SelectSectorNumber |
    335 	    AC_SelectCylinderLow | AC_SelectCylinderHigh | AC_SelectDeviceHead |
    336 	    AC_SelectCommand;
    337 
    338 	dir = DIR_NONE;
    339 	if (ata_bio->bcount != 0) {
    340 		if (ata_bio->flags & ATA_READ)
    341 			dir = DIR_IN;
    342 		else
    343 			dir = DIR_OUT;
    344 	}
    345 
    346 	if (ata_bio->flags & ATA_READ) {
    347 		ata.ac_command = WDCC_READ;
    348 	} else {
    349 		ata.ac_command = WDCC_WRITE;
    350 	}
    351 	DPRINTF(("%s: bno=%" PRId64 " LBA=%d cyl=%d head=%d sect=%d "
    352 		 "count=%d multi=%d\n",
    353 		 __func__, ata_bio->blkno,
    354 		 (ata_bio->flags & ATA_LBA) != 0, cyl, head, sect,
    355 		 ata.ac_sector_count, ata_bio->multi));
    356 	DPRINTF(("    data=%p bcount=%ld, drive=%d\n", ata_bio->databuf,
    357 		 ata_bio->bcount, drv->drive));
    358 	sc->sc_methods->wire_xfer(sc, drv->drive, &ata, sizeof ata,
    359 				  ata_bio->databuf + scbus->sc_skip, nbytes,
    360 				  dir, ATA_DELAY, 0, uisdata_bio_cb, ata_bio);
    361 
    362 	while (ata_bio->flags & ATA_POLL) {
    363 		DPRINTF(("%s: tsleep %p\n", __func__, ata_bio));
    364 		if (tsleep(ata_bio, PZERO, "uisdatabl", 0)) {
    365 			ata_bio->error = TIMEOUT;
    366 			ata_bio->flags |= ATA_ITSDONE;
    367 			return (ATACMD_COMPLETE);
    368 		}
    369 	}
    370 
    371 	return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED;
    372 }
    373 
    374 void
    375 uisdata_reset_drive(struct ata_drive_datas *drv, int flags, uint32_t *sigp)
    376 {
    377 	DPRINTFN(-1,("%s\n", __func__));
    378 	KASSERT(sigp == NULL);
    379 	/* XXX what? */
    380 }
    381 
    382 void
    383 uisdata_reset_channel(struct ata_channel *chp, int flags)
    384 {
    385 	DPRINTFN(-1,("%s\n", __func__));
    386 	/* XXX what? */
    387 }
    388 
    389 void
    390 uisdata_exec_cb(struct umass_softc *sc, void *priv,
    391     int residue, int status)
    392 {
    393 	struct ata_command *cmd = priv;
    394 
    395 	DPRINTF(("%s: status=%d\n", __func__, status));
    396 	if (status != STATUS_CMD_OK)
    397 		cmd->flags |= AT_DF; /* XXX */
    398 	cmd->flags |= AT_DONE;
    399 	if (cmd->flags & (AT_READ | AT_WRITE))
    400 		cmd->flags |= AT_XFDONE;
    401 	if (cmd->flags & (AT_POLL | AT_WAIT)) {
    402 		DPRINTF(("%s: wakeup %p\n", __func__, cmd));
    403 		wakeup(cmd);
    404 	}
    405 }
    406 
    407 int
    408 uisdata_exec_command(struct ata_drive_datas *drv, struct ata_command *cmd)
    409 {
    410 	struct umass_softc *sc = drv->chnl_softc;
    411 	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
    412 	struct isd200_config *cf = &scbus->sc_isd_config;
    413 	int dir;
    414 	struct ata_cmd ata;
    415 
    416 	DPRINTF(("%s\n", __func__));
    417 	DPRINTF(("  r_command=0x%02x timeout=%d flags=0x%x bcount=%d\n",
    418 		 cmd->r_command, cmd->timeout, cmd->flags, cmd->bcount));
    419 
    420 	dir = DIR_NONE;
    421 	if (cmd->bcount != 0) {
    422 		if (cmd->flags & AT_READ)
    423 			dir = DIR_IN;
    424 		else
    425 			dir = DIR_OUT;
    426 	}
    427 
    428 	if (cmd->bcount > UMASS_MAX_TRANSFER_SIZE) {
    429 		printf("uisdata_exec_command: large datalen %d\n", cmd->bcount);
    430 		cmd->flags |= AT_ERROR;
    431 		goto done;
    432 	}
    433 
    434 	memset(&ata, 0, sizeof ata);
    435 	ata.ac_signature0 = cf->ATAMajorCommand;
    436 	ata.ac_signature1 = cf->ATAMinorCommand;
    437 	ata.ac_transfer_blocksize = 1;
    438 
    439 	switch (cmd->r_command) {
    440 	case WDCC_IDENTIFY:
    441 		ata.ac_register_select |= AC_SelectCommand;
    442 		ata.ac_command = WDCC_IDENTIFY;
    443 		break;
    444 	default:
    445 		printf("uisdata_exec_command: bad command 0x%02x\n",
    446 		       cmd->r_command);
    447 		cmd->flags |= AT_ERROR;
    448 		goto done;
    449 	}
    450 
    451 	DPRINTF(("%s: execute ATA command 0x%02x, drive=%d\n", __func__,
    452 		 ata.ac_command, drv->drive));
    453 	sc->sc_methods->wire_xfer(sc, drv->drive, &ata,
    454 				  sizeof ata, cmd->data, cmd->bcount, dir,
    455 				  cmd->timeout, 0, uisdata_exec_cb, cmd);
    456 	if (cmd->flags & (AT_POLL | AT_WAIT)) {
    457 #if 0
    458 		if (cmd->flags & AT_POLL)
    459 			printf("%s: AT_POLL not supported\n", __func__);
    460 #endif
    461 		DPRINTF(("%s: tsleep %p\n", __func__, cmd));
    462 		if (tsleep(cmd, PZERO, "uisdataex", 0)) {
    463 			cmd->flags |= AT_ERROR;
    464 			goto done;
    465 		}
    466 	}
    467 
    468 done:
    469 	return (ATACMD_COMPLETE);
    470 }
    471 
    472 int
    473 uisdata_addref(struct ata_drive_datas *drv)
    474 {
    475 	DPRINTF(("%s\n", __func__));
    476 	/* Nothing to do */
    477 	return (0);
    478 }
    479 
    480 void
    481 uisdata_delref(struct ata_drive_datas *drv)
    482 {
    483 	DPRINTF(("%s\n", __func__));
    484 	/* Nothing to do */
    485 }
    486 
    487 void
    488 uisdata_kill_pending(struct ata_drive_datas *drv)
    489 {
    490 	struct umass_softc *sc = drv->chnl_softc;
    491 	struct uisdata_softc *scbus = (struct uisdata_softc *)sc->bus;
    492 	struct ata_bio *ata_bio = scbus->sc_ata_bio;
    493 
    494 	DPRINTFN(-1,("%s\n", __func__));
    495 
    496 	if (ata_bio == NULL)
    497 		return;
    498 	scbus->sc_ata_bio = NULL;
    499 	ata_bio->flags |= ATA_ITSDONE;
    500 	ata_bio->error = ERR_NODEV;
    501 	ata_bio->r_error = WDCE_ABRT;
    502 	(*scbus->sc_drv_data.drv_done)(scbus->sc_drv_data.drv_softc);
    503 }
    504 
    505 int
    506 uisdata_get_params(struct ata_drive_datas *drvp, u_int8_t flags,
    507 		struct ataparams *prms)
    508 {
    509 	char tb[DEV_BSIZE];
    510 	struct ata_command ata_c;
    511 
    512 #if BYTE_ORDER == LITTLE_ENDIAN
    513 	int i;
    514 	u_int16_t *p;
    515 #endif
    516 
    517 	DPRINTF(("%s\n", __func__));
    518 
    519 	memset(tb, 0, DEV_BSIZE);
    520 	memset(prms, 0, sizeof(struct ataparams));
    521 	memset(&ata_c, 0, sizeof(struct ata_command));
    522 
    523 	ata_c.r_command = WDCC_IDENTIFY;
    524 	ata_c.timeout = 1000; /* 1s */
    525 	ata_c.flags = AT_READ | flags;
    526 	ata_c.data = tb;
    527 	ata_c.bcount = DEV_BSIZE;
    528 	if (uisdata_exec_command(drvp, &ata_c) != ATACMD_COMPLETE) {
    529 		DPRINTF(("uisdata_get_parms: wdc_exec_command failed\n"));
    530 		return (CMD_AGAIN);
    531 	}
    532 	if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
    533 		DPRINTF(("uisdata_get_parms: ata_c.flags=0x%x\n",
    534 			 ata_c.flags));
    535 		return (CMD_ERR);
    536 	} else {
    537 		/* Read in parameter block. */
    538 		memcpy(prms, tb, sizeof(struct ataparams));
    539 #if BYTE_ORDER == LITTLE_ENDIAN
    540 		/* XXX copied from ata.c */
    541 		/*
    542 		 * Shuffle string byte order.
    543 		 * ATAPI Mitsumi and NEC drives don't need this.
    544 		 */
    545 		if (prms->atap_config != WDC_CFG_CFA_MAGIC &&
    546 		    (prms->atap_config & WDC_CFG_ATAPI) &&
    547 		    ((prms->atap_model[0] == 'N' &&
    548 			prms->atap_model[1] == 'E') ||
    549 		     (prms->atap_model[0] == 'F' &&
    550 			 prms->atap_model[1] == 'X')))
    551 			return 0;
    552 		for (i = 0; i < sizeof(prms->atap_model); i += 2) {
    553 			p = (u_short *)(prms->atap_model + i);
    554 			*p = ntohs(*p);
    555 		}
    556 		for (i = 0; i < sizeof(prms->atap_serial); i += 2) {
    557 			p = (u_short *)(prms->atap_serial + i);
    558 			*p = ntohs(*p);
    559 		}
    560 		for (i = 0; i < sizeof(prms->atap_revision); i += 2) {
    561 			p = (u_short *)(prms->atap_revision + i);
    562 			*p = ntohs(*p);
    563 		}
    564 #endif
    565 		return CMD_OK;
    566 	}
    567 }
    568 
    569 
    570 /* XXX join with wdc.c routine? */
    571 int
    572 uwdprint(void *aux, const char *pnp)
    573 {
    574 	//struct ata_device *adev = aux;
    575 	if (pnp)
    576 		aprint_normal("wd at %s", pnp);
    577 #if 0
    578 	aprint_normal(" channel %d drive %d", adev->adev_channel,
    579 	    adev->adev_drv_data->drive);
    580 #endif
    581 	return (UNCONF);
    582 }
    583 
    584 
    585 #if 0
    586 
    587 int umass_wd_attach(struct umass_softc *);
    588 
    589 #if NWD > 0
    590 	case UMASS_CPROTO_ISD_ATA:
    591 		return (umass_wd_attach(sc));
    592 #endif
    593 
    594 #endif
    595