10641ea09SDavid du Colombier /* 20641ea09SDavid du Colombier * usb/disk - usb mass storage file server 33a827ddcSDavid du Colombier * 43a827ddcSDavid du Colombier * supports only the scsi command interface, not ata. 50641ea09SDavid du Colombier */ 6906943f9SDavid du Colombier 70641ea09SDavid du Colombier #include <u.h> 80641ea09SDavid du Colombier #include <libc.h> 90641ea09SDavid du Colombier #include <ctype.h> 100641ea09SDavid du Colombier #include <fcall.h> 110641ea09SDavid du Colombier #include <thread.h> 120641ea09SDavid du Colombier #include "scsireq.h" 130641ea09SDavid du Colombier #include "usb.h" 14906943f9SDavid du Colombier #include "usbfs.h" 15906943f9SDavid du Colombier #include "ums.h" 160641ea09SDavid du Colombier 17906943f9SDavid du Colombier enum 18906943f9SDavid du Colombier { 190641ea09SDavid du Colombier Qdir = 0, 200641ea09SDavid du Colombier Qctl, 210641ea09SDavid du Colombier Qraw, 220641ea09SDavid du Colombier Qdata, 23906943f9SDavid du Colombier Qmax, 240641ea09SDavid du Colombier }; 250641ea09SDavid du Colombier 260641ea09SDavid du Colombier typedef struct Dirtab Dirtab; 27906943f9SDavid du Colombier struct Dirtab 28906943f9SDavid du Colombier { 290641ea09SDavid du Colombier char *name; 300641ea09SDavid du Colombier int mode; 310641ea09SDavid du Colombier }; 32906943f9SDavid du Colombier 33906943f9SDavid du Colombier static Dirtab dirtab[] = 34906943f9SDavid du Colombier { 35906943f9SDavid du Colombier [Qdir] "/", DMDIR|0555, 36d5789509SDavid du Colombier [Qctl] "ctl", 0664, /* nothing secret here */ 37906943f9SDavid du Colombier [Qraw] "raw", 0640, 38906943f9SDavid du Colombier [Qdata] "data", 0640, 390641ea09SDavid du Colombier }; 400641ea09SDavid du Colombier 410641ea09SDavid du Colombier /* 42906943f9SDavid du Colombier * These are used by scuzz scsireq 430641ea09SDavid du Colombier */ 44906943f9SDavid du Colombier int exabyte, force6bytecmds; 450641ea09SDavid du Colombier 467d7728c9SDavid du Colombier int diskdebug; 470641ea09SDavid du Colombier 483a827ddcSDavid du Colombier static void 493a827ddcSDavid du Colombier ding(void *, char *msg) 503a827ddcSDavid du Colombier { 513a827ddcSDavid du Colombier if(strstr(msg, "alarm") != nil) 523a827ddcSDavid du Colombier noted(NCONT); 533a827ddcSDavid du Colombier noted(NDFLT); 543a827ddcSDavid du Colombier } 553a827ddcSDavid du Colombier 56906943f9SDavid du Colombier static int 57906943f9SDavid du Colombier getmaxlun(Dev *dev) 580641ea09SDavid du Colombier { 590641ea09SDavid du Colombier uchar max; 60906943f9SDavid du Colombier int r; 610641ea09SDavid du Colombier 626a5dc222SDavid du Colombier max = 0; 63906943f9SDavid du Colombier r = Rd2h|Rclass|Riface; 64906943f9SDavid du Colombier if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){ 65906943f9SDavid du Colombier dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir); 6614dd39c1SDavid du Colombier }else{ 6714dd39c1SDavid du Colombier max &= 017; /* 15 is the max. allowed */ 68906943f9SDavid du Colombier dprint(2, "disk: %s: maxlun %d\n", dev->dir, max); 6914dd39c1SDavid du Colombier } 70906943f9SDavid du Colombier return max; 710641ea09SDavid du Colombier } 720641ea09SDavid du Colombier 73906943f9SDavid du Colombier static int 74906943f9SDavid du Colombier umsreset(Ums *ums) 750641ea09SDavid du Colombier { 76906943f9SDavid du Colombier int r; 770641ea09SDavid du Colombier 78906943f9SDavid du Colombier r = Rh2d|Rclass|Riface; 79906943f9SDavid du Colombier if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){ 80906943f9SDavid du Colombier fprint(2, "disk: reset: %r\n"); 810641ea09SDavid du Colombier return -1; 820641ea09SDavid du Colombier } 83906943f9SDavid du Colombier return 0; 840641ea09SDavid du Colombier } 850641ea09SDavid du Colombier 86906943f9SDavid du Colombier static int 87906943f9SDavid du Colombier umsrecover(Ums *ums) 88906943f9SDavid du Colombier { 89906943f9SDavid du Colombier if(umsreset(ums) < 0) 900641ea09SDavid du Colombier return -1; 91906943f9SDavid du Colombier if(unstall(ums->dev, ums->epin, Ein) < 0) 92906943f9SDavid du Colombier dprint(2, "disk: unstall epin: %r\n"); 93906943f9SDavid du Colombier 94906943f9SDavid du Colombier /* do we need this when epin == epout? */ 95906943f9SDavid du Colombier if(unstall(ums->dev, ums->epout, Eout) < 0) 96906943f9SDavid du Colombier dprint(2, "disk: unstall epout: %r\n"); 97906943f9SDavid du Colombier return 0; 98906943f9SDavid du Colombier } 99906943f9SDavid du Colombier 100906943f9SDavid du Colombier static void 101906943f9SDavid du Colombier umsfatal(Ums *ums) 102906943f9SDavid du Colombier { 103906943f9SDavid du Colombier int i; 104906943f9SDavid du Colombier 105906943f9SDavid du Colombier devctl(ums->dev, "detach"); 106906943f9SDavid du Colombier for(i = 0; i < ums->maxlun; i++) 107906943f9SDavid du Colombier usbfsdel(&ums->lun[i].fs); 108906943f9SDavid du Colombier } 109906943f9SDavid du Colombier 110906943f9SDavid du Colombier static int 1113a827ddcSDavid du Colombier ispow2(uvlong ul) 1123a827ddcSDavid du Colombier { 1133a827ddcSDavid du Colombier return (ul & (ul - 1)) == 0; 1143a827ddcSDavid du Colombier } 1153a827ddcSDavid du Colombier 1163a827ddcSDavid du Colombier /* 1173a827ddcSDavid du Colombier * return smallest power of 2 >= n 1183a827ddcSDavid du Colombier */ 1193a827ddcSDavid du Colombier static int 1203a827ddcSDavid du Colombier log2(int n) 1213a827ddcSDavid du Colombier { 1223a827ddcSDavid du Colombier int i; 1233a827ddcSDavid du Colombier 1243a827ddcSDavid du Colombier for(i = 0; (1 << i) < n; i++) 1253a827ddcSDavid du Colombier ; 1263a827ddcSDavid du Colombier return i; 1273a827ddcSDavid du Colombier } 1283a827ddcSDavid du Colombier 1293a827ddcSDavid du Colombier static int 130906943f9SDavid du Colombier umscapacity(Umsc *lun) 131906943f9SDavid du Colombier { 132906943f9SDavid du Colombier uchar data[32]; 133906943f9SDavid du Colombier 1340641ea09SDavid du Colombier lun->blocks = 0; 1350641ea09SDavid du Colombier lun->capacity = 0; 1360641ea09SDavid du Colombier lun->lbsize = 0; 1377d7728c9SDavid du Colombier memset(data, 0, sizeof data); 138906943f9SDavid du Colombier if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0) 139906943f9SDavid du Colombier return -1; 140906943f9SDavid du Colombier lun->blocks = GETBELONG(data); 141906943f9SDavid du Colombier lun->lbsize = GETBELONG(data+4); 142906943f9SDavid du Colombier if(lun->blocks == 0xFFFFFFFF){ 143906943f9SDavid du Colombier if(SRrcapacity16(lun, data) < 0){ 144906943f9SDavid du Colombier lun->lbsize = 0; 145906943f9SDavid du Colombier lun->blocks = 0; 146906943f9SDavid du Colombier return -1; 1470641ea09SDavid du Colombier }else{ 148906943f9SDavid du Colombier lun->lbsize = GETBELONG(data + 8); 1497d7728c9SDavid du Colombier lun->blocks = (uvlong)GETBELONG(data)<<32 | 1507d7728c9SDavid du Colombier GETBELONG(data + 4); 151906943f9SDavid du Colombier } 152906943f9SDavid du Colombier } 1530641ea09SDavid du Colombier lun->blocks++; /* SRcapacity returns LBA of last block */ 1540641ea09SDavid du Colombier lun->capacity = (vlong)lun->blocks * lun->lbsize; 1557d7728c9SDavid du Colombier if(diskdebug) 1567d7728c9SDavid du Colombier fprint(2, "disk: logical block size %lud, # blocks %llud\n", 1577d7728c9SDavid du Colombier lun->lbsize, lun->blocks); 158906943f9SDavid du Colombier return 0; 1590641ea09SDavid du Colombier } 160906943f9SDavid du Colombier 161906943f9SDavid du Colombier static int 162906943f9SDavid du Colombier umsinit(Ums *ums) 163906943f9SDavid du Colombier { 164906943f9SDavid du Colombier uchar i; 165906943f9SDavid du Colombier Umsc *lun; 166906943f9SDavid du Colombier int some; 167906943f9SDavid du Colombier 168906943f9SDavid du Colombier umsreset(ums); 169906943f9SDavid du Colombier ums->maxlun = getmaxlun(ums->dev); 170906943f9SDavid du Colombier ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1); 171906943f9SDavid du Colombier some = 0; 172906943f9SDavid du Colombier for(i = 0; i <= ums->maxlun; i++){ 173906943f9SDavid du Colombier lun = &ums->lun[i]; 174906943f9SDavid du Colombier lun->ums = ums; 175906943f9SDavid du Colombier lun->umsc = lun; 176906943f9SDavid du Colombier lun->lun = i; 177906943f9SDavid du Colombier lun->flags = Fopen | Fusb | Frw10; 178d5789509SDavid du Colombier if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0){ 179d5789509SDavid du Colombier dprint(2, "disk: lun %d inquiry failed\n", i); 180906943f9SDavid du Colombier continue; 181d5789509SDavid du Colombier } 182d5789509SDavid du Colombier switch(lun->inquiry[0]){ 183d5789509SDavid du Colombier case Devdir: 184d5789509SDavid du Colombier case Devworm: /* a little different than the others */ 185d5789509SDavid du Colombier case Devcd: 186d5789509SDavid du Colombier case Devmo: 187d5789509SDavid du Colombier break; 188d5789509SDavid du Colombier default: 189d5789509SDavid du Colombier fprint(2, "disk: lun %d is not a disk (type %#02x)\n", 190d5789509SDavid du Colombier i, lun->inquiry[0]); 191906943f9SDavid du Colombier continue; 192906943f9SDavid du Colombier } 193906943f9SDavid du Colombier SRstart(lun, 1); 194906943f9SDavid du Colombier /* 195906943f9SDavid du Colombier * we ignore the device type reported by inquiry. 196906943f9SDavid du Colombier * Some devices return a wrong value but would still work. 197906943f9SDavid du Colombier */ 198906943f9SDavid du Colombier some++; 199906943f9SDavid du Colombier lun->inq = smprint("%.48s", (char *)lun->inquiry+8); 200906943f9SDavid du Colombier umscapacity(lun); 201906943f9SDavid du Colombier } 202906943f9SDavid du Colombier if(some == 0){ 203d5789509SDavid du Colombier dprint(2, "disk: all luns failed\n"); 204906943f9SDavid du Colombier devctl(ums->dev, "detach"); 205906943f9SDavid du Colombier return -1; 2060641ea09SDavid du Colombier } 2070641ea09SDavid du Colombier return 0; 2080641ea09SDavid du Colombier } 2090641ea09SDavid du Colombier 2100641ea09SDavid du Colombier 211906943f9SDavid du Colombier /* 212906943f9SDavid du Colombier * called by SR*() commands provided by scuzz's scsireq 213906943f9SDavid du Colombier */ 2140641ea09SDavid du Colombier long 2150641ea09SDavid du Colombier umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status) 2160641ea09SDavid du Colombier { 2170641ea09SDavid du Colombier Cbw cbw; 2180641ea09SDavid du Colombier Csw csw; 2193a827ddcSDavid du Colombier int n, nio, left; 220906943f9SDavid du Colombier Ums *ums; 221906943f9SDavid du Colombier 222906943f9SDavid du Colombier ums = umsc->ums; 2230641ea09SDavid du Colombier 2240641ea09SDavid du Colombier memcpy(cbw.signature, "USBC", 4); 225906943f9SDavid du Colombier cbw.tag = ++ums->seq; 2260641ea09SDavid du Colombier cbw.datalen = data->count; 2270641ea09SDavid du Colombier cbw.flags = data->write? CbwDataOut: CbwDataIn; 2280641ea09SDavid du Colombier cbw.lun = umsc->lun; 229906943f9SDavid du Colombier if(cmd->count < 1 || cmd->count > 16) 2302806980eSDavid du Colombier fprint(2, "disk: umsrequest: bad cmd count: %ld\n", cmd->count); 231906943f9SDavid du Colombier 2320641ea09SDavid du Colombier cbw.len = cmd->count; 233906943f9SDavid du Colombier assert(cmd->count <= sizeof(cbw.command)); 2340641ea09SDavid du Colombier memcpy(cbw.command, cmd->p, cmd->count); 2350641ea09SDavid du Colombier memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count); 2360641ea09SDavid du Colombier 237906943f9SDavid du Colombier werrstr(""); /* we use %r later even for n == 0 */ 238906943f9SDavid du Colombier if(diskdebug){ 239906943f9SDavid du Colombier fprint(2, "disk: cmd: tag %#lx: ", cbw.tag); 2400641ea09SDavid du Colombier for(n = 0; n < cbw.len; n++) 2410641ea09SDavid du Colombier fprint(2, " %2.2x", cbw.command[n]&0xFF); 2420641ea09SDavid du Colombier fprint(2, " datalen: %ld\n", cbw.datalen); 2430641ea09SDavid du Colombier } 2443a827ddcSDavid du Colombier 2453a827ddcSDavid du Colombier /* issue tunnelled scsi command */ 246906943f9SDavid du Colombier if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){ 247906943f9SDavid du Colombier fprint(2, "disk: cmd: %r\n"); 248906943f9SDavid du Colombier goto Fail; 2490641ea09SDavid du Colombier } 2503a827ddcSDavid du Colombier 2513a827ddcSDavid du Colombier /* transfer the data */ 252d5789509SDavid du Colombier nio = data->count; 2533a827ddcSDavid du Colombier if(nio != 0){ 2540641ea09SDavid du Colombier if(data->write) 2553a827ddcSDavid du Colombier n = write(ums->epout->dfd, data->p, nio); 256906943f9SDavid du Colombier else{ 2573a827ddcSDavid du Colombier n = read(ums->epin->dfd, data->p, nio); 2583a827ddcSDavid du Colombier left = nio - n; 2593a827ddcSDavid du Colombier if (n >= 0 && left > 0) /* didn't fill data->p? */ 2603a827ddcSDavid du Colombier memset(data->p + n, 0, left); 2610641ea09SDavid du Colombier } 2623a827ddcSDavid du Colombier nio = n; 263906943f9SDavid du Colombier if(diskdebug) 264906943f9SDavid du Colombier if(n < 0) 265906943f9SDavid du Colombier fprint(2, "disk: data: %r\n"); 266906943f9SDavid du Colombier else 267906943f9SDavid du Colombier fprint(2, "disk: data: %d bytes\n", n); 268906943f9SDavid du Colombier if(n <= 0) 269906943f9SDavid du Colombier if(data->write == 0) 270906943f9SDavid du Colombier unstall(ums->dev, ums->epin, Ein); 2710641ea09SDavid du Colombier } 2723a827ddcSDavid du Colombier 2733a827ddcSDavid du Colombier /* read the transfer's status */ 274906943f9SDavid du Colombier n = read(ums->epin->dfd, &csw, CswLen); 275906943f9SDavid du Colombier if(n <= 0){ 276906943f9SDavid du Colombier /* n == 0 means "stalled" */ 277906943f9SDavid du Colombier unstall(ums->dev, ums->epin, Ein); 278906943f9SDavid du Colombier n = read(ums->epin->dfd, &csw, CswLen); 2790641ea09SDavid du Colombier } 2803a827ddcSDavid du Colombier 2810641ea09SDavid du Colombier if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){ 282906943f9SDavid du Colombier dprint(2, "disk: read n=%d: status: %r\n", n); 283906943f9SDavid du Colombier goto Fail; 2840641ea09SDavid du Colombier } 2850641ea09SDavid du Colombier if(csw.tag != cbw.tag){ 286906943f9SDavid du Colombier dprint(2, "disk: status tag mismatch\n"); 287906943f9SDavid du Colombier goto Fail; 2880641ea09SDavid du Colombier } 2890641ea09SDavid du Colombier if(csw.status >= CswPhaseErr){ 290906943f9SDavid du Colombier dprint(2, "disk: phase error\n"); 291906943f9SDavid du Colombier goto Fail; 2920641ea09SDavid du Colombier } 293d5789509SDavid du Colombier if(csw.dataresidue == 0 || ums->wrongresidues) 294d5789509SDavid du Colombier csw.dataresidue = data->count - nio; 295906943f9SDavid du Colombier if(diskdebug){ 296d5789509SDavid du Colombier fprint(2, "disk: status: %2.2ux residue: %ld\n", 2970641ea09SDavid du Colombier csw.status, csw.dataresidue); 2980641ea09SDavid du Colombier if(cbw.command[0] == ScmdRsense){ 2990641ea09SDavid du Colombier fprint(2, "sense data:"); 3000641ea09SDavid du Colombier for(n = 0; n < data->count - csw.dataresidue; n++) 3010641ea09SDavid du Colombier fprint(2, " %2.2x", data->p[n]); 3020641ea09SDavid du Colombier fprint(2, "\n"); 3030641ea09SDavid du Colombier } 3040641ea09SDavid du Colombier } 305906943f9SDavid du Colombier switch(csw.status){ 306906943f9SDavid du Colombier case CswOk: 3070641ea09SDavid du Colombier *status = STok; 308906943f9SDavid du Colombier break; 309906943f9SDavid du Colombier case CswFailed: 3100641ea09SDavid du Colombier *status = STcheck; 311906943f9SDavid du Colombier break; 312906943f9SDavid du Colombier default: 313906943f9SDavid du Colombier dprint(2, "disk: phase error\n"); 314906943f9SDavid du Colombier goto Fail; 315906943f9SDavid du Colombier } 316906943f9SDavid du Colombier ums->nerrs = 0; 3170641ea09SDavid du Colombier return data->count - csw.dataresidue; 3180641ea09SDavid du Colombier 319906943f9SDavid du Colombier Fail: 3200641ea09SDavid du Colombier *status = STharderr; 321906943f9SDavid du Colombier if(ums->nerrs++ > 15){ 322906943f9SDavid du Colombier fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir); 323906943f9SDavid du Colombier umsfatal(ums); 3240641ea09SDavid du Colombier }else 325906943f9SDavid du Colombier umsrecover(ums); 3260641ea09SDavid du Colombier return -1; 3270641ea09SDavid du Colombier } 3280641ea09SDavid du Colombier 329906943f9SDavid du Colombier static int 330906943f9SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name) 3310641ea09SDavid du Colombier { 332906943f9SDavid du Colombier int i; 333906943f9SDavid du Colombier Qid qid; 334906943f9SDavid du Colombier 335906943f9SDavid du Colombier qid = fid->qid; 336906943f9SDavid du Colombier if((qid.type & QTDIR) == 0){ 337906943f9SDavid du Colombier werrstr("walk in non-directory"); 338906943f9SDavid du Colombier return -1; 3390641ea09SDavid du Colombier } 3400641ea09SDavid du Colombier 3410641ea09SDavid du Colombier if(strcmp(name, "..") == 0) 342906943f9SDavid du Colombier return 0; 3430641ea09SDavid du Colombier 344906943f9SDavid du Colombier for(i = 1; i < nelem(dirtab); i++) 3450641ea09SDavid du Colombier if(strcmp(name, dirtab[i].name) == 0){ 346906943f9SDavid du Colombier qid.path = i | fs->qid; 347906943f9SDavid du Colombier qid.vers = 0; 348906943f9SDavid du Colombier qid.type = dirtab[i].mode >> 24; 349906943f9SDavid du Colombier fid->qid = qid; 350906943f9SDavid du Colombier return 0; 3510641ea09SDavid du Colombier } 352906943f9SDavid du Colombier werrstr(Enotfound); 353906943f9SDavid du Colombier return -1; 3540641ea09SDavid du Colombier } 3550641ea09SDavid du Colombier 356906943f9SDavid du Colombier static void 357906943f9SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d) 3580641ea09SDavid du Colombier { 3590641ea09SDavid du Colombier Dirtab *t; 3600641ea09SDavid du Colombier Umsc *lun; 3610641ea09SDavid du Colombier 362906943f9SDavid du Colombier t = &dirtab[path]; 363906943f9SDavid du Colombier d->qid.path = path; 364906943f9SDavid du Colombier d->qid.type = t->mode >> 24; 365906943f9SDavid du Colombier d->mode = t->mode; 366906943f9SDavid du Colombier d->name = t->name; 367906943f9SDavid du Colombier lun = fs->aux; 368906943f9SDavid du Colombier if(path == Qdata) 369906943f9SDavid du Colombier d->length = lun->capacity; 370906943f9SDavid du Colombier else 371906943f9SDavid du Colombier d->length = 0; 372906943f9SDavid du Colombier } 373906943f9SDavid du Colombier 374906943f9SDavid du Colombier static int 375906943f9SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void*) 376906943f9SDavid du Colombier { 377906943f9SDavid du Colombier i++; /* skip dir */ 378906943f9SDavid du Colombier if(i >= Qmax) 379906943f9SDavid du Colombier return -1; 380906943f9SDavid du Colombier else{ 381906943f9SDavid du Colombier dostat(fs, i, d); 382906943f9SDavid du Colombier d->qid.path |= fs->qid; 383906943f9SDavid du Colombier return 0; 384906943f9SDavid du Colombier } 385906943f9SDavid du Colombier } 386906943f9SDavid du Colombier 387906943f9SDavid du Colombier static int 388906943f9SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d) 389906943f9SDavid du Colombier { 390906943f9SDavid du Colombier int path; 391906943f9SDavid du Colombier 392906943f9SDavid du Colombier path = qid.path & ~fs->qid; 393906943f9SDavid du Colombier dostat(fs, path, d); 394906943f9SDavid du Colombier d->qid.path |= fs->qid; 395906943f9SDavid du Colombier return 0; 396906943f9SDavid du Colombier } 397906943f9SDavid du Colombier 398906943f9SDavid du Colombier static int 399906943f9SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int) 400906943f9SDavid du Colombier { 401906943f9SDavid du Colombier ulong path; 402906943f9SDavid du Colombier Umsc *lun; 403906943f9SDavid du Colombier 404906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 405906943f9SDavid du Colombier lun = fs->aux; 406906943f9SDavid du Colombier switch(path){ 407906943f9SDavid du Colombier case Qraw: 408906943f9SDavid du Colombier lun->phase = Pcmd; 4090641ea09SDavid du Colombier break; 410906943f9SDavid du Colombier } 411906943f9SDavid du Colombier return 0; 412906943f9SDavid du Colombier } 413906943f9SDavid du Colombier 414906943f9SDavid du Colombier /* 4153a827ddcSDavid du Colombier * check i/o parameters and compute values needed later. 4163a827ddcSDavid du Colombier * we shift & mask manually to avoid run-time calls to _divv and _modv, 4173a827ddcSDavid du Colombier * since we don't need general division nor its cost. 4183a827ddcSDavid du Colombier */ 4193a827ddcSDavid du Colombier static int 4203a827ddcSDavid du Colombier setup(Umsc *lun, char *data, int count, vlong offset) 4213a827ddcSDavid du Colombier { 4223a827ddcSDavid du Colombier long nb, lbsize, lbshift, lbmask; 4233a827ddcSDavid du Colombier uvlong bno; 4243a827ddcSDavid du Colombier 4253a827ddcSDavid du Colombier if(count < 0 || lun->lbsize <= 0 && umscapacity(lun) < 0 || 4263a827ddcSDavid du Colombier lun->lbsize == 0) 4273a827ddcSDavid du Colombier return -1; 4283a827ddcSDavid du Colombier lbsize = lun->lbsize; 4293a827ddcSDavid du Colombier assert(ispow2(lbsize)); 4303a827ddcSDavid du Colombier lbshift = log2(lbsize); 4313a827ddcSDavid du Colombier lbmask = lbsize - 1; 4323a827ddcSDavid du Colombier 4333a827ddcSDavid du Colombier bno = offset >> lbshift; /* offset / lbsize */ 4343a827ddcSDavid du Colombier nb = ((offset + count + lbsize - 1) >> lbshift) - bno; 4353a827ddcSDavid du Colombier 4363a827ddcSDavid du Colombier if(bno + nb > lun->blocks) /* past end of device? */ 4373a827ddcSDavid du Colombier nb = lun->blocks - bno; 4383a827ddcSDavid du Colombier if(nb * lbsize > Maxiosize) 4393a827ddcSDavid du Colombier nb = Maxiosize / lbsize; 4403a827ddcSDavid du Colombier lun->nb = nb; 4413a827ddcSDavid du Colombier if(bno >= lun->blocks || nb == 0) 4423a827ddcSDavid du Colombier return 0; 4433a827ddcSDavid du Colombier 4443a827ddcSDavid du Colombier lun->offset = bno; 4453a827ddcSDavid du Colombier lun->off = offset & lbmask; /* offset % lbsize */ 4463a827ddcSDavid du Colombier if(lun->off == 0 && (count & lbmask) == 0) 4473a827ddcSDavid du Colombier lun->bufp = data; 4483a827ddcSDavid du Colombier else 4493a827ddcSDavid du Colombier /* not transferring full, aligned blocks; need intermediary */ 4503a827ddcSDavid du Colombier lun->bufp = lun->buf; 4513a827ddcSDavid du Colombier return count; 4523a827ddcSDavid du Colombier } 4533a827ddcSDavid du Colombier 4543a827ddcSDavid du Colombier /* 455906943f9SDavid du Colombier * Upon SRread/SRwrite errors we assume the medium may have changed, 456906943f9SDavid du Colombier * and ask again for the capacity of the media. 457194f7e8cSDavid du Colombier * BUG: How to proceed to avoid confusing dossrv?? 458194f7e8cSDavid du Colombier * 459194f7e8cSDavid du Colombier * ctl reads must match the format documented in sd(3) exactly 460194f7e8cSDavid du Colombier * to interoperate with the rest of the system. 461906943f9SDavid du Colombier */ 462906943f9SDavid du Colombier static long 463906943f9SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) 464906943f9SDavid du Colombier { 4653a827ddcSDavid du Colombier long n; 466906943f9SDavid du Colombier ulong path; 4673a827ddcSDavid du Colombier char buf[1024]; 468ed868a7cSDavid du Colombier char *s, *e; 469906943f9SDavid du Colombier Umsc *lun; 470906943f9SDavid du Colombier Ums *ums; 471906943f9SDavid du Colombier Qid q; 472906943f9SDavid du Colombier 473906943f9SDavid du Colombier q = fid->qid; 474906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 475906943f9SDavid du Colombier ums = fs->dev->aux; 476906943f9SDavid du Colombier lun = fs->aux; 4773a827ddcSDavid du Colombier 478906943f9SDavid du Colombier qlock(ums); 479906943f9SDavid du Colombier switch(path){ 480906943f9SDavid du Colombier case Qdir: 481906943f9SDavid du Colombier count = usbdirread(fs, q, data, count, offset, dirgen, nil); 4820641ea09SDavid du Colombier break; 4830641ea09SDavid du Colombier case Qctl: 484*3e33a36fSDavid du Colombier /* 485*3e33a36fSDavid du Colombier * Some usb disks need an extra opportunity to divulge their 486*3e33a36fSDavid du Colombier * capacity (e.g. M-Systems/SanDisk 1GB flash drive). 487*3e33a36fSDavid du Colombier */ 488*3e33a36fSDavid du Colombier if(lun->lbsize <= 0) 489*3e33a36fSDavid du Colombier umscapacity(lun); 490*3e33a36fSDavid du Colombier 491194f7e8cSDavid du Colombier s = buf; 492906943f9SDavid du Colombier e = buf + sizeof(buf); 4930641ea09SDavid du Colombier if(lun->flags & Finqok) 494194f7e8cSDavid du Colombier s = seprint(s, e, "inquiry %s lun %ld: %s\n", 495194f7e8cSDavid du Colombier fs->dev->dir, lun - &ums->lun[0], lun->inq); 4960641ea09SDavid du Colombier if(lun->blocks > 0) 497194f7e8cSDavid du Colombier s = seprint(s, e, "geometry %llud %ld\n", 4983a827ddcSDavid du Colombier lun->blocks, lun->lbsize); 499906943f9SDavid du Colombier count = usbreadbuf(data, count, offset, buf, s - buf); 5000641ea09SDavid du Colombier break; 5010641ea09SDavid du Colombier case Qraw: 502906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 5033a827ddcSDavid du Colombier count = -1; 5043a827ddcSDavid du Colombier break; 5050641ea09SDavid du Colombier } 5060641ea09SDavid du Colombier switch(lun->phase){ 5070641ea09SDavid du Colombier case Pcmd: 508906943f9SDavid du Colombier qunlock(ums); 509906943f9SDavid du Colombier werrstr("phase error"); 510906943f9SDavid du Colombier return -1; 5110641ea09SDavid du Colombier case Pdata: 5123a827ddcSDavid du Colombier lun->data.p = data; 513906943f9SDavid du Colombier lun->data.count = count; 5140641ea09SDavid du Colombier lun->data.write = 0; 515906943f9SDavid du Colombier count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 5160641ea09SDavid du Colombier lun->phase = Pstatus; 5173a827ddcSDavid du Colombier if(count < 0) 518906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 5190641ea09SDavid du Colombier break; 5200641ea09SDavid du Colombier case Pstatus: 5210641ea09SDavid du Colombier n = snprint(buf, sizeof buf, "%11.0ud ", lun->status); 522906943f9SDavid du Colombier count = usbreadbuf(data, count, 0LL, buf, n); 5230641ea09SDavid du Colombier lun->phase = Pcmd; 5240641ea09SDavid du Colombier break; 5250641ea09SDavid du Colombier } 5260641ea09SDavid du Colombier break; 5270641ea09SDavid du Colombier case Qdata: 5283a827ddcSDavid du Colombier count = setup(lun, data, count, offset); 5293a827ddcSDavid du Colombier if (count <= 0) 5300641ea09SDavid du Colombier break; 5313a827ddcSDavid du Colombier n = SRread(lun, lun->bufp, lun->nb * lun->lbsize); 532906943f9SDavid du Colombier if(n < 0){ 533906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 5343a827ddcSDavid du Colombier count = -1; 5353a827ddcSDavid du Colombier } else if (lun->bufp == data) 5363a827ddcSDavid du Colombier count = n; 5373a827ddcSDavid du Colombier else{ 5383a827ddcSDavid du Colombier /* 5393a827ddcSDavid du Colombier * if n == lun->nb*lun->lbsize (as expected), 5403a827ddcSDavid du Colombier * just copy count bytes. 5413a827ddcSDavid du Colombier */ 5423a827ddcSDavid du Colombier if(lun->off + count > n) 5433a827ddcSDavid du Colombier count = n - lun->off; /* short read */ 5443a827ddcSDavid du Colombier if(count > 0) 5453a827ddcSDavid du Colombier memmove(data, lun->bufp + lun->off, count); 5460641ea09SDavid du Colombier } 5470641ea09SDavid du Colombier break; 5480641ea09SDavid du Colombier } 549906943f9SDavid du Colombier qunlock(ums); 550906943f9SDavid du Colombier return count; 5510641ea09SDavid du Colombier } 5520641ea09SDavid du Colombier 553906943f9SDavid du Colombier static long 5543a827ddcSDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) 5550641ea09SDavid du Colombier { 5563a827ddcSDavid du Colombier long len, ocount; 5570641ea09SDavid du Colombier ulong path; 5583a827ddcSDavid du Colombier uvlong bno; 559906943f9SDavid du Colombier Ums *ums; 5600641ea09SDavid du Colombier Umsc *lun; 5610641ea09SDavid du Colombier 562906943f9SDavid du Colombier ums = fs->dev->aux; 563906943f9SDavid du Colombier lun = fs->aux; 564906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 5653a827ddcSDavid du Colombier 566906943f9SDavid du Colombier qlock(ums); 567906943f9SDavid du Colombier switch(path){ 568906943f9SDavid du Colombier default: 569906943f9SDavid du Colombier werrstr(Eperm); 5703a827ddcSDavid du Colombier count = -1; 5713a827ddcSDavid du Colombier break; 572d5789509SDavid du Colombier case Qctl: 573d5789509SDavid du Colombier dprint(2, "usb/disk: ctl ignored\n"); 5743a827ddcSDavid du Colombier break; 5750641ea09SDavid du Colombier case Qraw: 576906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 5773a827ddcSDavid du Colombier count = -1; 5783a827ddcSDavid du Colombier break; 5790641ea09SDavid du Colombier } 5800641ea09SDavid du Colombier switch(lun->phase){ 5810641ea09SDavid du Colombier case Pcmd: 582906943f9SDavid du Colombier if(count != 6 && count != 10){ 583906943f9SDavid du Colombier qunlock(ums); 584906943f9SDavid du Colombier werrstr("bad command length"); 585906943f9SDavid du Colombier return -1; 5860641ea09SDavid du Colombier } 587906943f9SDavid du Colombier memmove(lun->rawcmd, data, count); 5880641ea09SDavid du Colombier lun->cmd.p = lun->rawcmd; 589906943f9SDavid du Colombier lun->cmd.count = count; 5900641ea09SDavid du Colombier lun->cmd.write = 1; 5910641ea09SDavid du Colombier lun->phase = Pdata; 5920641ea09SDavid du Colombier break; 5930641ea09SDavid du Colombier case Pdata: 5943a827ddcSDavid du Colombier lun->data.p = data; 595906943f9SDavid du Colombier lun->data.count = count; 5960641ea09SDavid du Colombier lun->data.write = 1; 597906943f9SDavid du Colombier count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 5980641ea09SDavid du Colombier lun->phase = Pstatus; 5993a827ddcSDavid du Colombier if(count < 0) 600906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 6010641ea09SDavid du Colombier break; 6020641ea09SDavid du Colombier case Pstatus: 6030641ea09SDavid du Colombier lun->phase = Pcmd; 604906943f9SDavid du Colombier werrstr("phase error"); 6053a827ddcSDavid du Colombier count = -1; 6063a827ddcSDavid du Colombier break; 6070641ea09SDavid du Colombier } 6080641ea09SDavid du Colombier break; 6090641ea09SDavid du Colombier case Qdata: 6103a827ddcSDavid du Colombier len = ocount = count; 6113a827ddcSDavid du Colombier count = setup(lun, data, count, offset); 6123a827ddcSDavid du Colombier if (count <= 0) 6133a827ddcSDavid du Colombier break; 6143a827ddcSDavid du Colombier bno = lun->offset; 6153a827ddcSDavid du Colombier if (lun->bufp == lun->buf) { 6163a827ddcSDavid du Colombier count = SRread(lun, lun->bufp, lun->nb * lun->lbsize); 6173a827ddcSDavid du Colombier if(count < 0) { 6183a827ddcSDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 6190641ea09SDavid du Colombier break; 6200641ea09SDavid du Colombier } 6213a827ddcSDavid du Colombier /* 6223a827ddcSDavid du Colombier * if count == lun->nb*lun->lbsize, as expected, just 6233a827ddcSDavid du Colombier * copy len (the original count) bytes of user data. 6243a827ddcSDavid du Colombier */ 6253a827ddcSDavid du Colombier if(lun->off + len > count) 6263a827ddcSDavid du Colombier len = count - lun->off; /* short read */ 6273a827ddcSDavid du Colombier if(len > 0) 6283a827ddcSDavid du Colombier memmove(lun->bufp + lun->off, data, len); 6293a827ddcSDavid du Colombier } 6303a827ddcSDavid du Colombier 6313a827ddcSDavid du Colombier lun->offset = bno; 6323a827ddcSDavid du Colombier count = SRwrite(lun, lun->bufp, lun->nb * lun->lbsize); 6333a827ddcSDavid du Colombier if(count < 0) 634906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 6353a827ddcSDavid du Colombier else{ 6363a827ddcSDavid du Colombier if(lun->off + len > count) 6373a827ddcSDavid du Colombier count -= lun->off; /* short write */ 6383a827ddcSDavid du Colombier /* never report more bytes written than requested */ 6393a827ddcSDavid du Colombier if(count < 0) 6403a827ddcSDavid du Colombier count = 0; 6413a827ddcSDavid du Colombier else if(count > ocount) 6423a827ddcSDavid du Colombier count = ocount; 6430641ea09SDavid du Colombier } 6440641ea09SDavid du Colombier break; 6450641ea09SDavid du Colombier } 646906943f9SDavid du Colombier qunlock(ums); 647906943f9SDavid du Colombier return count; 6480641ea09SDavid du Colombier } 6490641ea09SDavid du Colombier 650906943f9SDavid du Colombier int 651906943f9SDavid du Colombier findendpoints(Ums *ums) 652906943f9SDavid du Colombier { 653906943f9SDavid du Colombier Ep *ep; 654906943f9SDavid du Colombier Usbdev *ud; 655ed868a7cSDavid du Colombier ulong csp, sc; 656ed868a7cSDavid du Colombier int i, epin, epout; 6570641ea09SDavid du Colombier 658906943f9SDavid du Colombier epin = epout = -1; 659906943f9SDavid du Colombier ud = ums->dev->usb; 660906943f9SDavid du Colombier for(i = 0; i < nelem(ud->ep); i++){ 661906943f9SDavid du Colombier if((ep = ud->ep[i]) == nil) 662906943f9SDavid du Colombier continue; 663906943f9SDavid du Colombier csp = ep->iface->csp; 664906943f9SDavid du Colombier sc = Subclass(csp); 665906943f9SDavid du Colombier if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk))) 666906943f9SDavid du Colombier continue; 667906943f9SDavid du Colombier if(sc != Subatapi && sc != Sub8070 && sc != Subscsi) 668906943f9SDavid du Colombier fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc); 669906943f9SDavid du Colombier if(ep->type == Ebulk){ 670906943f9SDavid du Colombier if(ep->dir == Eboth || ep->dir == Ein) 671906943f9SDavid du Colombier if(epin == -1) 672906943f9SDavid du Colombier epin = ep->id; 673906943f9SDavid du Colombier if(ep->dir == Eboth || ep->dir == Eout) 674906943f9SDavid du Colombier if(epout == -1) 675906943f9SDavid du Colombier epout = ep->id; 676906943f9SDavid du Colombier } 677906943f9SDavid du Colombier } 678906943f9SDavid du Colombier dprint(2, "disk: ep ids: in %d out %d\n", epin, epout); 679906943f9SDavid du Colombier if(epin == -1 || epout == -1) 680906943f9SDavid du Colombier return -1; 681906943f9SDavid du Colombier ums->epin = openep(ums->dev, epin); 682906943f9SDavid du Colombier if(ums->epin == nil){ 683906943f9SDavid du Colombier fprint(2, "disk: openep %d: %r\n", epin); 684906943f9SDavid du Colombier return -1; 685906943f9SDavid du Colombier } 686906943f9SDavid du Colombier if(epout == epin){ 687906943f9SDavid du Colombier incref(ums->epin); 688906943f9SDavid du Colombier ums->epout = ums->epin; 689906943f9SDavid du Colombier }else 690906943f9SDavid du Colombier ums->epout = openep(ums->dev, epout); 691906943f9SDavid du Colombier if(ums->epout == nil){ 692906943f9SDavid du Colombier fprint(2, "disk: openep %d: %r\n", epout); 693906943f9SDavid du Colombier closedev(ums->epin); 694906943f9SDavid du Colombier return -1; 695906943f9SDavid du Colombier } 696906943f9SDavid du Colombier if(ums->epin == ums->epout) 697906943f9SDavid du Colombier opendevdata(ums->epin, ORDWR); 698906943f9SDavid du Colombier else{ 699906943f9SDavid du Colombier opendevdata(ums->epin, OREAD); 700906943f9SDavid du Colombier opendevdata(ums->epout, OWRITE); 701906943f9SDavid du Colombier } 702906943f9SDavid du Colombier if(ums->epin->dfd < 0 || ums->epout->dfd < 0){ 703906943f9SDavid du Colombier fprint(2, "disk: open i/o ep data: %r\n"); 704906943f9SDavid du Colombier closedev(ums->epin); 705906943f9SDavid du Colombier closedev(ums->epout); 706906943f9SDavid du Colombier return -1; 707906943f9SDavid du Colombier } 708906943f9SDavid du Colombier dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir); 709d37e33ffSDavid du Colombier 710d37e33ffSDavid du Colombier devctl(ums->epin, "timeout 2000"); 711d37e33ffSDavid du Colombier devctl(ums->epout, "timeout 2000"); 7120641ea09SDavid du Colombier 713906943f9SDavid du Colombier if(usbdebug > 1 || diskdebug > 2){ 714906943f9SDavid du Colombier devctl(ums->epin, "debug 1"); 715906943f9SDavid du Colombier devctl(ums->epout, "debug 1"); 716906943f9SDavid du Colombier devctl(ums->dev, "debug 1"); 717906943f9SDavid du Colombier } 718906943f9SDavid du Colombier return 0; 719906943f9SDavid du Colombier } 720906943f9SDavid du Colombier 721906943f9SDavid du Colombier static int 7220641ea09SDavid du Colombier usage(void) 7230641ea09SDavid du Colombier { 724ed868a7cSDavid du Colombier werrstr("usage: usb/disk [-d] [-N nb]"); 725906943f9SDavid du Colombier return -1; 7260641ea09SDavid du Colombier } 7270641ea09SDavid du Colombier 728906943f9SDavid du Colombier static void 729906943f9SDavid du Colombier umsdevfree(void *a) 7300641ea09SDavid du Colombier { 731906943f9SDavid du Colombier Ums *ums = a; 7320641ea09SDavid du Colombier 733906943f9SDavid du Colombier if(ums == nil) 734906943f9SDavid du Colombier return; 735906943f9SDavid du Colombier closedev(ums->epin); 736906943f9SDavid du Colombier closedev(ums->epout); 737906943f9SDavid du Colombier ums->epin = ums->epout = nil; 738906943f9SDavid du Colombier free(ums->lun); 739906943f9SDavid du Colombier free(ums); 740906943f9SDavid du Colombier } 741906943f9SDavid du Colombier 742906943f9SDavid du Colombier static Usbfs diskfs = { 743906943f9SDavid du Colombier .walk = dwalk, 744906943f9SDavid du Colombier .open = dopen, 745906943f9SDavid du Colombier .read = dread, 746906943f9SDavid du Colombier .write = dwrite, 747906943f9SDavid du Colombier .stat = dstat, 748906943f9SDavid du Colombier }; 749906943f9SDavid du Colombier 750906943f9SDavid du Colombier int 751906943f9SDavid du Colombier diskmain(Dev *dev, int argc, char **argv) 752906943f9SDavid du Colombier { 753906943f9SDavid du Colombier Ums *ums; 754906943f9SDavid du Colombier Umsc *lun; 755ed868a7cSDavid du Colombier int i, devid; 7560641ea09SDavid du Colombier 757ed868a7cSDavid du Colombier devid = dev->id; 7580641ea09SDavid du Colombier ARGBEGIN{ 7590641ea09SDavid du Colombier case 'd': 760906943f9SDavid du Colombier scsidebug(diskdebug); 761906943f9SDavid du Colombier diskdebug++; 7620641ea09SDavid du Colombier break; 763ed868a7cSDavid du Colombier case 'N': 764ed868a7cSDavid du Colombier devid = atoi(EARGF(usage())); 765ed868a7cSDavid du Colombier break; 7660641ea09SDavid du Colombier default: 767906943f9SDavid du Colombier return usage(); 7680641ea09SDavid du Colombier }ARGEND 769ed868a7cSDavid du Colombier if(argc != 0) { 770906943f9SDavid du Colombier return usage(); 771ed868a7cSDavid du Colombier } 7720641ea09SDavid du Colombier 7733a827ddcSDavid du Colombier // notify(ding); 774906943f9SDavid du Colombier ums = dev->aux = emallocz(sizeof(Ums), 1); 775906943f9SDavid du Colombier ums->maxlun = -1; 776906943f9SDavid du Colombier ums->dev = dev; 777906943f9SDavid du Colombier dev->free = umsdevfree; 778906943f9SDavid du Colombier if(findendpoints(ums) < 0){ 779906943f9SDavid du Colombier werrstr("disk: endpoints not found"); 780906943f9SDavid du Colombier return -1; 7810641ea09SDavid du Colombier } 782d5789509SDavid du Colombier 783d5789509SDavid du Colombier /* 784d5789509SDavid du Colombier * SanDISK 512M gets residues wrong. 785d5789509SDavid du Colombier */ 786d5789509SDavid du Colombier if(dev->usb->vid == 0x0781 && dev->usb->did == 0x5150) 787d5789509SDavid du Colombier ums->wrongresidues = 1; 788d5789509SDavid du Colombier 789906943f9SDavid du Colombier if(umsinit(ums) < 0){ 790906943f9SDavid du Colombier dprint(2, "disk: umsinit: %r\n"); 791906943f9SDavid du Colombier return -1; 792906943f9SDavid du Colombier } 7930641ea09SDavid du Colombier 794906943f9SDavid du Colombier for(i = 0; i <= ums->maxlun; i++){ 795906943f9SDavid du Colombier lun = &ums->lun[i]; 796906943f9SDavid du Colombier lun->fs = diskfs; 797ed868a7cSDavid du Colombier snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", devid, i); 798906943f9SDavid du Colombier lun->fs.dev = dev; 799906943f9SDavid du Colombier incref(dev); 800906943f9SDavid du Colombier lun->fs.aux = lun; 801906943f9SDavid du Colombier usbfsadd(&lun->fs); 802906943f9SDavid du Colombier } 803906943f9SDavid du Colombier return 0; 8040641ea09SDavid du Colombier } 805