10641ea09SDavid du Colombier /* 20641ea09SDavid du Colombier * usb/disk - usb mass storage file server 3*906943f9SDavid du Colombier * BUG: supports only the scsi command interface. 4*906943f9SDavid du Colombier * BUG: This should use /dev/sdfile to 5*906943f9SDavid du Colombier * use the kernel ether device code. 60641ea09SDavid du Colombier */ 7*906943f9SDavid du Colombier 80641ea09SDavid du Colombier #include <u.h> 90641ea09SDavid du Colombier #include <libc.h> 100641ea09SDavid du Colombier #include <ctype.h> 110641ea09SDavid du Colombier #include <fcall.h> 120641ea09SDavid du Colombier #include <thread.h> 130641ea09SDavid du Colombier #include "scsireq.h" 140641ea09SDavid du Colombier #include "usb.h" 15*906943f9SDavid du Colombier #include "usbfs.h" 16*906943f9SDavid du Colombier #include "ums.h" 170641ea09SDavid du Colombier 18*906943f9SDavid du Colombier enum 19*906943f9SDavid du Colombier { 200641ea09SDavid du Colombier Qdir = 0, 210641ea09SDavid du Colombier Qctl, 220641ea09SDavid du Colombier Qraw, 230641ea09SDavid du Colombier Qdata, 24*906943f9SDavid du Colombier Qmax, 250641ea09SDavid du Colombier }; 260641ea09SDavid du Colombier 270641ea09SDavid du Colombier typedef struct Dirtab Dirtab; 28*906943f9SDavid du Colombier struct Dirtab 29*906943f9SDavid du Colombier { 300641ea09SDavid du Colombier char *name; 310641ea09SDavid du Colombier int mode; 320641ea09SDavid du Colombier }; 33*906943f9SDavid du Colombier 34*906943f9SDavid du Colombier static Dirtab dirtab[] = 35*906943f9SDavid du Colombier { 36*906943f9SDavid du Colombier [Qdir] "/", DMDIR|0555, 37*906943f9SDavid du Colombier [Qctl] "ctl", 0444, 38*906943f9SDavid du Colombier [Qraw] "raw", 0640, 39*906943f9SDavid du Colombier [Qdata] "data", 0640, 400641ea09SDavid du Colombier }; 410641ea09SDavid du Colombier 420641ea09SDavid du Colombier /* 43*906943f9SDavid du Colombier * These are used by scuzz scsireq 440641ea09SDavid du Colombier */ 45*906943f9SDavid du Colombier int exabyte, force6bytecmds; 46*906943f9SDavid du Colombier long maxiosize = MaxIOsize; 470641ea09SDavid du Colombier 48*906943f9SDavid du Colombier static int diskdebug; 490641ea09SDavid du Colombier 50*906943f9SDavid du Colombier static int 51*906943f9SDavid du Colombier getmaxlun(Dev *dev) 520641ea09SDavid du Colombier { 530641ea09SDavid du Colombier uchar max; 54*906943f9SDavid du Colombier int r; 550641ea09SDavid du Colombier 566a5dc222SDavid du Colombier max = 0; 57*906943f9SDavid du Colombier r = Rd2h|Rclass|Riface; 58*906943f9SDavid du Colombier if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){ 59*906943f9SDavid du Colombier dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir); 60*906943f9SDavid du Colombier }else 61*906943f9SDavid du Colombier dprint(2, "disk: %s: maxlun %d\n", dev->dir, max); 62*906943f9SDavid du Colombier return max; 630641ea09SDavid du Colombier } 640641ea09SDavid du Colombier 65*906943f9SDavid du Colombier static int 66*906943f9SDavid du Colombier umsreset(Ums *ums) 670641ea09SDavid du Colombier { 68*906943f9SDavid du Colombier int r; 690641ea09SDavid du Colombier 70*906943f9SDavid du Colombier r = Rh2d|Rclass|Riface; 71*906943f9SDavid du Colombier if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){ 72*906943f9SDavid du Colombier fprint(2, "disk: reset: %r\n"); 730641ea09SDavid du Colombier return -1; 740641ea09SDavid du Colombier } 75*906943f9SDavid du Colombier return 0; 760641ea09SDavid du Colombier } 770641ea09SDavid du Colombier 78*906943f9SDavid du Colombier static int 79*906943f9SDavid du Colombier umsrecover(Ums *ums) 80*906943f9SDavid du Colombier { 81*906943f9SDavid du Colombier if(umsreset(ums) < 0) 820641ea09SDavid du Colombier return -1; 83*906943f9SDavid du Colombier if(unstall(ums->dev, ums->epin, Ein) < 0) 84*906943f9SDavid du Colombier dprint(2, "disk: unstall epin: %r\n"); 85*906943f9SDavid du Colombier 86*906943f9SDavid du Colombier /* do we need this when epin == epout? */ 87*906943f9SDavid du Colombier if(unstall(ums->dev, ums->epout, Eout) < 0) 88*906943f9SDavid du Colombier dprint(2, "disk: unstall epout: %r\n"); 89*906943f9SDavid du Colombier return 0; 90*906943f9SDavid du Colombier } 91*906943f9SDavid du Colombier 92*906943f9SDavid du Colombier static void 93*906943f9SDavid du Colombier umsfatal(Ums *ums) 94*906943f9SDavid du Colombier { 95*906943f9SDavid du Colombier int i; 96*906943f9SDavid du Colombier 97*906943f9SDavid du Colombier devctl(ums->dev, "detach"); 98*906943f9SDavid du Colombier for(i = 0; i < ums->maxlun; i++) 99*906943f9SDavid du Colombier usbfsdel(&ums->lun[i].fs); 100*906943f9SDavid du Colombier } 101*906943f9SDavid du Colombier 102*906943f9SDavid du Colombier static int 103*906943f9SDavid du Colombier umscapacity(Umsc *lun) 104*906943f9SDavid du Colombier { 105*906943f9SDavid du Colombier uchar data[32]; 106*906943f9SDavid du Colombier 1070641ea09SDavid du Colombier lun->blocks = 0; 1080641ea09SDavid du Colombier lun->capacity = 0; 1090641ea09SDavid du Colombier lun->lbsize = 0; 110*906943f9SDavid du Colombier if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0) 111*906943f9SDavid du Colombier return -1; 112*906943f9SDavid du Colombier lun->blocks = GETBELONG(data); 113*906943f9SDavid du Colombier lun->lbsize = GETBELONG(data+4); 114*906943f9SDavid du Colombier if(lun->blocks == 0xFFFFFFFF){ 115*906943f9SDavid du Colombier if(SRrcapacity16(lun, data) < 0){ 116*906943f9SDavid du Colombier lun->lbsize = 0; 117*906943f9SDavid du Colombier lun->blocks = 0; 118*906943f9SDavid du Colombier return -1; 1190641ea09SDavid du Colombier }else{ 120*906943f9SDavid du Colombier lun->lbsize = GETBELONG(data + 8); 121*906943f9SDavid du Colombier lun->blocks = (uvlong)GETBELONG(data)<<32 | GETBELONG(data + 4); 122*906943f9SDavid du Colombier } 123*906943f9SDavid du Colombier } 1240641ea09SDavid du Colombier lun->blocks++; /* SRcapacity returns LBA of last block */ 1250641ea09SDavid du Colombier lun->capacity = (vlong)lun->blocks * lun->lbsize; 126*906943f9SDavid du Colombier return 0; 1270641ea09SDavid du Colombier } 128*906943f9SDavid du Colombier 129*906943f9SDavid du Colombier static int 130*906943f9SDavid du Colombier umsinit(Ums *ums) 131*906943f9SDavid du Colombier { 132*906943f9SDavid du Colombier uchar i; 133*906943f9SDavid du Colombier Umsc *lun; 134*906943f9SDavid du Colombier int some; 135*906943f9SDavid du Colombier 136*906943f9SDavid du Colombier umsreset(ums); 137*906943f9SDavid du Colombier ums->maxlun = getmaxlun(ums->dev); 138*906943f9SDavid du Colombier ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1); 139*906943f9SDavid du Colombier some = 0; 140*906943f9SDavid du Colombier for(i = 0; i <= ums->maxlun; i++){ 141*906943f9SDavid du Colombier lun = &ums->lun[i]; 142*906943f9SDavid du Colombier lun->ums = ums; 143*906943f9SDavid du Colombier lun->umsc = lun; 144*906943f9SDavid du Colombier lun->lun = i; 145*906943f9SDavid du Colombier lun->flags = Fopen | Fusb | Frw10; 146*906943f9SDavid du Colombier if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0) 147*906943f9SDavid du Colombier continue; 148*906943f9SDavid du Colombier if(lun->inquiry[0] != 0x00){ 149*906943f9SDavid du Colombier /* not a disk */ 150*906943f9SDavid du Colombier fprint(2, "%s: lun %d is not a disk (type %#02x)\n", 151*906943f9SDavid du Colombier argv0, i, lun->inquiry[0]); 152*906943f9SDavid du Colombier continue; 153*906943f9SDavid du Colombier } 154*906943f9SDavid du Colombier SRstart(lun, 1); 155*906943f9SDavid du Colombier /* 156*906943f9SDavid du Colombier * we ignore the device type reported by inquiry. 157*906943f9SDavid du Colombier * Some devices return a wrong value but would still work. 158*906943f9SDavid du Colombier */ 159*906943f9SDavid du Colombier some++; 160*906943f9SDavid du Colombier lun->inq = smprint("%.48s", (char *)lun->inquiry+8); 161*906943f9SDavid du Colombier umscapacity(lun); 162*906943f9SDavid du Colombier } 163*906943f9SDavid du Colombier if(some == 0){ 164*906943f9SDavid du Colombier devctl(ums->dev, "detach"); 165*906943f9SDavid du Colombier return -1; 1660641ea09SDavid du Colombier } 1670641ea09SDavid du Colombier return 0; 1680641ea09SDavid du Colombier } 1690641ea09SDavid du Colombier 1700641ea09SDavid du Colombier 171*906943f9SDavid du Colombier /* 172*906943f9SDavid du Colombier * called by SR*() commands provided by scuzz's scsireq 173*906943f9SDavid du Colombier */ 1740641ea09SDavid du Colombier long 1750641ea09SDavid du Colombier umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status) 1760641ea09SDavid du Colombier { 1770641ea09SDavid du Colombier Cbw cbw; 1780641ea09SDavid du Colombier Csw csw; 1790641ea09SDavid du Colombier int n; 180*906943f9SDavid du Colombier Ums *ums; 181*906943f9SDavid du Colombier 182*906943f9SDavid du Colombier ums = umsc->ums; 1830641ea09SDavid du Colombier 1840641ea09SDavid du Colombier memcpy(cbw.signature, "USBC", 4); 185*906943f9SDavid du Colombier cbw.tag = ++ums->seq; 1860641ea09SDavid du Colombier cbw.datalen = data->count; 1870641ea09SDavid du Colombier cbw.flags = data->write? CbwDataOut: CbwDataIn; 1880641ea09SDavid du Colombier cbw.lun = umsc->lun; 189*906943f9SDavid du Colombier if(cmd->count < 1 || cmd->count > 16) 190*906943f9SDavid du Colombier print("%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count); 191*906943f9SDavid du Colombier 1920641ea09SDavid du Colombier cbw.len = cmd->count; 193*906943f9SDavid du Colombier assert(cmd->count <= sizeof(cbw.command)); 1940641ea09SDavid du Colombier memcpy(cbw.command, cmd->p, cmd->count); 1950641ea09SDavid du Colombier memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count); 1960641ea09SDavid du Colombier 197*906943f9SDavid du Colombier werrstr(""); /* we use %r later even for n == 0 */ 198*906943f9SDavid du Colombier 199*906943f9SDavid du Colombier if(diskdebug){ 200*906943f9SDavid du Colombier fprint(2, "disk: cmd: tag %#lx: ", cbw.tag); 2010641ea09SDavid du Colombier for(n = 0; n < cbw.len; n++) 2020641ea09SDavid du Colombier fprint(2, " %2.2x", cbw.command[n]&0xFF); 2030641ea09SDavid du Colombier fprint(2, " datalen: %ld\n", cbw.datalen); 2040641ea09SDavid du Colombier } 205*906943f9SDavid du Colombier if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){ 206*906943f9SDavid du Colombier fprint(2, "disk: cmd: %r\n"); 207*906943f9SDavid du Colombier goto Fail; 2080641ea09SDavid du Colombier } 2090641ea09SDavid du Colombier if(data->count != 0){ 2100641ea09SDavid du Colombier if(data->write) 211*906943f9SDavid du Colombier n = write(ums->epout->dfd, data->p, data->count); 212*906943f9SDavid du Colombier else{ 213*906943f9SDavid du Colombier memset(data->p, data->count, 0); 214*906943f9SDavid du Colombier n = read(ums->epin->dfd, data->p, data->count); 2150641ea09SDavid du Colombier } 216*906943f9SDavid du Colombier if(diskdebug) 217*906943f9SDavid du Colombier if(n < 0) 218*906943f9SDavid du Colombier fprint(2, "disk: data: %r\n"); 219*906943f9SDavid du Colombier else 220*906943f9SDavid du Colombier fprint(2, "disk: data: %d bytes\n", n); 221*906943f9SDavid du Colombier if(n <= 0) 222*906943f9SDavid du Colombier if(data->write == 0) 223*906943f9SDavid du Colombier unstall(ums->dev, ums->epin, Ein); 2240641ea09SDavid du Colombier } 225*906943f9SDavid du Colombier n = read(ums->epin->dfd, &csw, CswLen); 226*906943f9SDavid du Colombier if(n <= 0){ 227*906943f9SDavid du Colombier /* n == 0 means "stalled" */ 228*906943f9SDavid du Colombier unstall(ums->dev, ums->epin, Ein); 229*906943f9SDavid du Colombier n = read(ums->epin->dfd, &csw, CswLen); 2300641ea09SDavid du Colombier } 2310641ea09SDavid du Colombier if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){ 232*906943f9SDavid du Colombier dprint(2, "disk: read n=%d: status: %r\n", n); 233*906943f9SDavid du Colombier goto Fail; 2340641ea09SDavid du Colombier } 2350641ea09SDavid du Colombier if(csw.tag != cbw.tag){ 236*906943f9SDavid du Colombier dprint(2, "disk: status tag mismatch\n"); 237*906943f9SDavid du Colombier goto Fail; 2380641ea09SDavid du Colombier } 2390641ea09SDavid du Colombier if(csw.status >= CswPhaseErr){ 240*906943f9SDavid du Colombier dprint(2, "disk: phase error\n"); 241*906943f9SDavid du Colombier goto Fail; 2420641ea09SDavid du Colombier } 243*906943f9SDavid du Colombier if(diskdebug){ 2440641ea09SDavid du Colombier fprint(2, "status: %2.2ux residue: %ld\n", 2450641ea09SDavid du Colombier csw.status, csw.dataresidue); 2460641ea09SDavid du Colombier if(cbw.command[0] == ScmdRsense){ 2470641ea09SDavid du Colombier fprint(2, "sense data:"); 2480641ea09SDavid du Colombier for(n = 0; n < data->count - csw.dataresidue; n++) 2490641ea09SDavid du Colombier fprint(2, " %2.2x", data->p[n]); 2500641ea09SDavid du Colombier fprint(2, "\n"); 2510641ea09SDavid du Colombier } 2520641ea09SDavid du Colombier } 253*906943f9SDavid du Colombier switch(csw.status){ 254*906943f9SDavid du Colombier case CswOk: 2550641ea09SDavid du Colombier *status = STok; 256*906943f9SDavid du Colombier break; 257*906943f9SDavid du Colombier case CswFailed: 2580641ea09SDavid du Colombier *status = STcheck; 259*906943f9SDavid du Colombier break; 260*906943f9SDavid du Colombier default: 261*906943f9SDavid du Colombier dprint(2, "disk: phase error\n"); 262*906943f9SDavid du Colombier goto Fail; 263*906943f9SDavid du Colombier } 264*906943f9SDavid du Colombier ums->nerrs = 0; 2650641ea09SDavid du Colombier return data->count - csw.dataresidue; 2660641ea09SDavid du Colombier 267*906943f9SDavid du Colombier Fail: 2680641ea09SDavid du Colombier *status = STharderr; 269*906943f9SDavid du Colombier if(ums->nerrs++ > 15){ 270*906943f9SDavid du Colombier fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir); 271*906943f9SDavid du Colombier umsfatal(ums); 2720641ea09SDavid du Colombier }else 273*906943f9SDavid du Colombier umsrecover(ums); 2740641ea09SDavid du Colombier return -1; 2750641ea09SDavid du Colombier } 2760641ea09SDavid du Colombier 277*906943f9SDavid du Colombier static int 278*906943f9SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name) 2790641ea09SDavid du Colombier { 280*906943f9SDavid du Colombier int i; 281*906943f9SDavid du Colombier Qid qid; 282*906943f9SDavid du Colombier 283*906943f9SDavid du Colombier qid = fid->qid; 284*906943f9SDavid du Colombier if((qid.type & QTDIR) == 0){ 285*906943f9SDavid du Colombier werrstr("walk in non-directory"); 286*906943f9SDavid du Colombier return -1; 2870641ea09SDavid du Colombier } 2880641ea09SDavid du Colombier 2890641ea09SDavid du Colombier if(strcmp(name, "..") == 0) 290*906943f9SDavid du Colombier return 0; 2910641ea09SDavid du Colombier 292*906943f9SDavid du Colombier for(i = 1; i < nelem(dirtab); i++) 2930641ea09SDavid du Colombier if(strcmp(name, dirtab[i].name) == 0){ 294*906943f9SDavid du Colombier qid.path = i | fs->qid; 295*906943f9SDavid du Colombier qid.vers = 0; 296*906943f9SDavid du Colombier qid.type = dirtab[i].mode >> 24; 297*906943f9SDavid du Colombier fid->qid = qid; 298*906943f9SDavid du Colombier return 0; 2990641ea09SDavid du Colombier } 300*906943f9SDavid du Colombier werrstr(Enotfound); 301*906943f9SDavid du Colombier return -1; 3020641ea09SDavid du Colombier } 3030641ea09SDavid du Colombier 304*906943f9SDavid du Colombier static void 305*906943f9SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d) 3060641ea09SDavid du Colombier { 3070641ea09SDavid du Colombier Dirtab *t; 3080641ea09SDavid du Colombier Umsc *lun; 3090641ea09SDavid du Colombier 310*906943f9SDavid du Colombier t = &dirtab[path]; 311*906943f9SDavid du Colombier d->qid.path = path; 312*906943f9SDavid du Colombier d->qid.type = t->mode >> 24; 313*906943f9SDavid du Colombier d->mode = t->mode; 314*906943f9SDavid du Colombier d->name = t->name; 315*906943f9SDavid du Colombier lun = fs->aux; 316*906943f9SDavid du Colombier if(path == Qdata) 317*906943f9SDavid du Colombier d->length = lun->capacity; 318*906943f9SDavid du Colombier else 319*906943f9SDavid du Colombier d->length = 0; 320*906943f9SDavid du Colombier } 321*906943f9SDavid du Colombier 322*906943f9SDavid du Colombier static int 323*906943f9SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void*) 324*906943f9SDavid du Colombier { 325*906943f9SDavid du Colombier i++; /* skip dir */ 326*906943f9SDavid du Colombier if(i >= Qmax) 327*906943f9SDavid du Colombier return -1; 328*906943f9SDavid du Colombier else{ 329*906943f9SDavid du Colombier dostat(fs, i, d); 330*906943f9SDavid du Colombier d->qid.path |= fs->qid; 331*906943f9SDavid du Colombier return 0; 332*906943f9SDavid du Colombier } 333*906943f9SDavid du Colombier } 334*906943f9SDavid du Colombier 335*906943f9SDavid du Colombier static int 336*906943f9SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d) 337*906943f9SDavid du Colombier { 338*906943f9SDavid du Colombier int path; 339*906943f9SDavid du Colombier 340*906943f9SDavid du Colombier path = qid.path & ~fs->qid; 341*906943f9SDavid du Colombier dostat(fs, path, d); 342*906943f9SDavid du Colombier d->qid.path |= fs->qid; 343*906943f9SDavid du Colombier return 0; 344*906943f9SDavid du Colombier } 345*906943f9SDavid du Colombier 346*906943f9SDavid du Colombier static int 347*906943f9SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int) 348*906943f9SDavid du Colombier { 349*906943f9SDavid du Colombier ulong path; 350*906943f9SDavid du Colombier Umsc *lun; 351*906943f9SDavid du Colombier 352*906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 353*906943f9SDavid du Colombier lun = fs->aux; 354*906943f9SDavid du Colombier switch(path){ 355*906943f9SDavid du Colombier case Qraw: 356*906943f9SDavid du Colombier lun->phase = Pcmd; 3570641ea09SDavid du Colombier break; 358*906943f9SDavid du Colombier } 359*906943f9SDavid du Colombier return 0; 360*906943f9SDavid du Colombier } 361*906943f9SDavid du Colombier 362*906943f9SDavid du Colombier /* 363*906943f9SDavid du Colombier * Upon SRread/SRwrite errors we assume the medium may have changed, 364*906943f9SDavid du Colombier * and ask again for the capacity of the media. 365*906943f9SDavid du Colombier * BUG: How to proceed to avoid confussing dossrv?? 366*906943f9SDavid du Colombier */ 367*906943f9SDavid du Colombier static long 368*906943f9SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) 369*906943f9SDavid du Colombier { 370*906943f9SDavid du Colombier long bno, nb, len, off, n; 371*906943f9SDavid du Colombier ulong path; 372*906943f9SDavid du Colombier char buf[1024], *p; 373*906943f9SDavid du Colombier char *s; 374*906943f9SDavid du Colombier char *e; 375*906943f9SDavid du Colombier Umsc *lun; 376*906943f9SDavid du Colombier Ums *ums; 377*906943f9SDavid du Colombier Qid q; 378*906943f9SDavid du Colombier 379*906943f9SDavid du Colombier q = fid->qid; 380*906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 381*906943f9SDavid du Colombier ums = fs->dev->aux; 382*906943f9SDavid du Colombier lun = fs->aux; 383*906943f9SDavid du Colombier qlock(ums); 384*906943f9SDavid du Colombier switch(path){ 385*906943f9SDavid du Colombier case Qdir: 386*906943f9SDavid du Colombier count = usbdirread(fs, q, data, count, offset, dirgen, nil); 3870641ea09SDavid du Colombier break; 3880641ea09SDavid du Colombier case Qctl: 389*906943f9SDavid du Colombier e = buf + sizeof(buf); 390*906943f9SDavid du Colombier s = seprint(buf, e, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]); 3910641ea09SDavid du Colombier if(lun->flags & Finqok) 392*906943f9SDavid du Colombier s = seprint(s, e, "inquiry %s ", lun->inq); 3930641ea09SDavid du Colombier if(lun->blocks > 0) 394*906943f9SDavid du Colombier s = seprint(s, e, "geometry %llud %ld", lun->blocks, 3950641ea09SDavid du Colombier lun->lbsize); 396*906943f9SDavid du Colombier s = seprint(s, e, "\n"); 397*906943f9SDavid du Colombier count = usbreadbuf(data, count, offset, buf, s - buf); 3980641ea09SDavid du Colombier break; 3990641ea09SDavid du Colombier case Qraw: 400*906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 401*906943f9SDavid du Colombier qunlock(ums); 402*906943f9SDavid du Colombier return -1; 4030641ea09SDavid du Colombier } 4040641ea09SDavid du Colombier switch(lun->phase){ 4050641ea09SDavid du Colombier case Pcmd: 406*906943f9SDavid du Colombier qunlock(ums); 407*906943f9SDavid du Colombier werrstr("phase error"); 408*906943f9SDavid du Colombier return -1; 4090641ea09SDavid du Colombier case Pdata: 410*906943f9SDavid du Colombier lun->data.p = (uchar*)data; 411*906943f9SDavid du Colombier lun->data.count = count; 4120641ea09SDavid du Colombier lun->data.write = 0; 413*906943f9SDavid du Colombier count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 4140641ea09SDavid du Colombier lun->phase = Pstatus; 415*906943f9SDavid du Colombier if(count < 0){ 416*906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 417*906943f9SDavid du Colombier qunlock(ums); 418*906943f9SDavid du Colombier return -1; 4190641ea09SDavid du Colombier } 4200641ea09SDavid du Colombier break; 4210641ea09SDavid du Colombier case Pstatus: 4220641ea09SDavid du Colombier n = snprint(buf, sizeof buf, "%11.0ud ", lun->status); 423*906943f9SDavid du Colombier count = usbreadbuf(data, count, 0LL, buf, n); 4240641ea09SDavid du Colombier lun->phase = Pcmd; 4250641ea09SDavid du Colombier break; 4260641ea09SDavid du Colombier } 4270641ea09SDavid du Colombier break; 4280641ea09SDavid du Colombier case Qdata: 429*906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 430*906943f9SDavid du Colombier qunlock(ums); 431*906943f9SDavid du Colombier return -1; 4320641ea09SDavid du Colombier } 433*906943f9SDavid du Colombier bno = offset / lun->lbsize; 434*906943f9SDavid du Colombier nb = (offset + count + lun->lbsize - 1) / lun->lbsize - bno; 4350641ea09SDavid du Colombier if(bno + nb > lun->blocks) 4360641ea09SDavid du Colombier nb = lun->blocks - bno; 4370641ea09SDavid du Colombier if(bno >= lun->blocks || nb == 0){ 438*906943f9SDavid du Colombier count = 0; 4390641ea09SDavid du Colombier break; 4400641ea09SDavid du Colombier } 4410641ea09SDavid du Colombier if(nb * lun->lbsize > maxiosize) 4420641ea09SDavid du Colombier nb = maxiosize / lun->lbsize; 443*906943f9SDavid du Colombier p = emallocz(nb * lun->lbsize, 0); /* could use a static buffer */ 444*906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 4450641ea09SDavid du Colombier n = SRread(lun, p, nb * lun->lbsize); 446*906943f9SDavid du Colombier if(n < 0){ 4470641ea09SDavid du Colombier free(p); 448*906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 449*906943f9SDavid du Colombier qunlock(ums); 450*906943f9SDavid du Colombier return -1; 4510641ea09SDavid du Colombier } 452*906943f9SDavid du Colombier len = count; 453*906943f9SDavid du Colombier off = offset % lun->lbsize; 454*906943f9SDavid du Colombier if(off + len > n) 455*906943f9SDavid du Colombier len = n - off; 456*906943f9SDavid du Colombier count = len; 457*906943f9SDavid du Colombier memmove(data, p + off, len); 4580641ea09SDavid du Colombier free(p); 4590641ea09SDavid du Colombier break; 4600641ea09SDavid du Colombier } 461*906943f9SDavid du Colombier qunlock(ums); 462*906943f9SDavid du Colombier return count; 4630641ea09SDavid du Colombier } 4640641ea09SDavid du Colombier 465*906943f9SDavid du Colombier static long 466*906943f9SDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset) 4670641ea09SDavid du Colombier { 468*906943f9SDavid du Colombier int bno, nb, len, off; 4690641ea09SDavid du Colombier ulong path; 4700641ea09SDavid du Colombier char *p; 471*906943f9SDavid du Colombier Ums *ums; 4720641ea09SDavid du Colombier Umsc *lun; 473*906943f9SDavid du Colombier char *data; 4740641ea09SDavid du Colombier 475*906943f9SDavid du Colombier ums = fs->dev->aux; 476*906943f9SDavid du Colombier lun = fs->aux; 477*906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 478*906943f9SDavid du Colombier data = buf; 479*906943f9SDavid du Colombier qlock(ums); 480*906943f9SDavid du Colombier switch(path){ 481*906943f9SDavid du Colombier default: 482*906943f9SDavid du Colombier qunlock(ums); 483*906943f9SDavid du Colombier werrstr(Eperm); 484*906943f9SDavid du Colombier return -1; 4850641ea09SDavid du Colombier case Qraw: 486*906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 487*906943f9SDavid du Colombier qunlock(ums); 488*906943f9SDavid du Colombier return -1; 4890641ea09SDavid du Colombier } 4900641ea09SDavid du Colombier switch(lun->phase){ 4910641ea09SDavid du Colombier case Pcmd: 492*906943f9SDavid du Colombier if(count != 6 && count != 10){ 493*906943f9SDavid du Colombier qunlock(ums); 494*906943f9SDavid du Colombier werrstr("bad command length"); 495*906943f9SDavid du Colombier return -1; 4960641ea09SDavid du Colombier } 497*906943f9SDavid du Colombier memmove(lun->rawcmd, data, count); 4980641ea09SDavid du Colombier lun->cmd.p = lun->rawcmd; 499*906943f9SDavid du Colombier lun->cmd.count = count; 5000641ea09SDavid du Colombier lun->cmd.write = 1; 5010641ea09SDavid du Colombier lun->phase = Pdata; 5020641ea09SDavid du Colombier break; 5030641ea09SDavid du Colombier case Pdata: 504*906943f9SDavid du Colombier lun->data.p = (uchar*)data; 505*906943f9SDavid du Colombier lun->data.count = count; 5060641ea09SDavid du Colombier lun->data.write = 1; 507*906943f9SDavid du Colombier count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 5080641ea09SDavid du Colombier lun->phase = Pstatus; 509*906943f9SDavid du Colombier if(count < 0){ 510*906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 511*906943f9SDavid du Colombier qunlock(ums); 512*906943f9SDavid du Colombier return -1; 5130641ea09SDavid du Colombier } 5140641ea09SDavid du Colombier break; 5150641ea09SDavid du Colombier case Pstatus: 5160641ea09SDavid du Colombier lun->phase = Pcmd; 517*906943f9SDavid du Colombier qunlock(ums); 518*906943f9SDavid du Colombier werrstr("phase error"); 519*906943f9SDavid du Colombier return -1; 5200641ea09SDavid du Colombier } 5210641ea09SDavid du Colombier break; 5220641ea09SDavid du Colombier case Qdata: 523*906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 524*906943f9SDavid du Colombier qunlock(ums); 525*906943f9SDavid du Colombier return -1; 5260641ea09SDavid du Colombier } 527*906943f9SDavid du Colombier bno = offset / lun->lbsize; 528*906943f9SDavid du Colombier nb = (offset + count + lun->lbsize-1) / lun->lbsize - bno; 5290641ea09SDavid du Colombier if(bno + nb > lun->blocks) 5300641ea09SDavid du Colombier nb = lun->blocks - bno; 5310641ea09SDavid du Colombier if(bno >= lun->blocks || nb == 0){ 532*906943f9SDavid du Colombier count = 0; 5330641ea09SDavid du Colombier break; 5340641ea09SDavid du Colombier } 5350641ea09SDavid du Colombier if(nb * lun->lbsize > maxiosize) 5360641ea09SDavid du Colombier nb = maxiosize / lun->lbsize; 537*906943f9SDavid du Colombier p = emallocz(nb * lun->lbsize, 0); 538*906943f9SDavid du Colombier off = offset % lun->lbsize; 539*906943f9SDavid du Colombier len = count; 540*906943f9SDavid du Colombier if(off || (len % lun->lbsize) != 0){ 541*906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 542*906943f9SDavid du Colombier count = SRread(lun, p, nb * lun->lbsize); 543*906943f9SDavid du Colombier if(count < 0){ 5440641ea09SDavid du Colombier free(p); 545*906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 546*906943f9SDavid du Colombier qunlock(ums); 547*906943f9SDavid du Colombier return -1; 5480641ea09SDavid du Colombier } 549*906943f9SDavid du Colombier if(off + len > count) 550*906943f9SDavid du Colombier len = count - off; 5510641ea09SDavid du Colombier } 552*906943f9SDavid du Colombier memmove(p+off, data, len); 553*906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 554*906943f9SDavid du Colombier count = SRwrite(lun, p, nb * lun->lbsize); 555*906943f9SDavid du Colombier if(count < 0){ 5560641ea09SDavid du Colombier free(p); 557*906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 558*906943f9SDavid du Colombier qunlock(ums); 559*906943f9SDavid du Colombier return -1; 5600641ea09SDavid du Colombier } 561*906943f9SDavid du Colombier if(off+len > count) 562*906943f9SDavid du Colombier len = count - off; 563*906943f9SDavid du Colombier count = len; 5640641ea09SDavid du Colombier free(p); 5650641ea09SDavid du Colombier break; 5660641ea09SDavid du Colombier } 567*906943f9SDavid du Colombier qunlock(ums); 568*906943f9SDavid du Colombier return count; 5690641ea09SDavid du Colombier } 5700641ea09SDavid du Colombier 571*906943f9SDavid du Colombier int 572*906943f9SDavid du Colombier findendpoints(Ums *ums) 573*906943f9SDavid du Colombier { 574*906943f9SDavid du Colombier Ep *ep; 575*906943f9SDavid du Colombier Usbdev *ud; 576*906943f9SDavid du Colombier ulong csp; 577*906943f9SDavid du Colombier ulong sc; 578*906943f9SDavid du Colombier int i; 579*906943f9SDavid du Colombier int epin, epout; 5800641ea09SDavid du Colombier 581*906943f9SDavid du Colombier epin = epout = -1; 582*906943f9SDavid du Colombier ud = ums->dev->usb; 583*906943f9SDavid du Colombier for(i = 0; i < nelem(ud->ep); i++){ 584*906943f9SDavid du Colombier if((ep = ud->ep[i]) == nil) 585*906943f9SDavid du Colombier continue; 586*906943f9SDavid du Colombier csp = ep->iface->csp; 587*906943f9SDavid du Colombier sc = Subclass(csp); 588*906943f9SDavid du Colombier if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk))) 589*906943f9SDavid du Colombier continue; 590*906943f9SDavid du Colombier if(sc != Subatapi && sc != Sub8070 && sc != Subscsi) 591*906943f9SDavid du Colombier fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc); 592*906943f9SDavid du Colombier if(ep->type == Ebulk){ 593*906943f9SDavid du Colombier if(ep->dir == Eboth || ep->dir == Ein) 594*906943f9SDavid du Colombier if(epin == -1) 595*906943f9SDavid du Colombier epin = ep->id; 596*906943f9SDavid du Colombier if(ep->dir == Eboth || ep->dir == Eout) 597*906943f9SDavid du Colombier if(epout == -1) 598*906943f9SDavid du Colombier epout = ep->id; 599*906943f9SDavid du Colombier } 600*906943f9SDavid du Colombier } 601*906943f9SDavid du Colombier dprint(2, "disk: ep ids: in %d out %d\n", epin, epout); 602*906943f9SDavid du Colombier if(epin == -1 || epout == -1) 603*906943f9SDavid du Colombier return -1; 604*906943f9SDavid du Colombier ums->epin = openep(ums->dev, epin); 605*906943f9SDavid du Colombier if(ums->epin == nil){ 606*906943f9SDavid du Colombier fprint(2, "disk: openep %d: %r\n", epin); 607*906943f9SDavid du Colombier return -1; 608*906943f9SDavid du Colombier } 609*906943f9SDavid du Colombier if(epout == epin){ 610*906943f9SDavid du Colombier incref(ums->epin); 611*906943f9SDavid du Colombier ums->epout = ums->epin; 612*906943f9SDavid du Colombier }else 613*906943f9SDavid du Colombier ums->epout = openep(ums->dev, epout); 614*906943f9SDavid du Colombier if(ums->epout == nil){ 615*906943f9SDavid du Colombier fprint(2, "disk: openep %d: %r\n", epout); 616*906943f9SDavid du Colombier closedev(ums->epin); 617*906943f9SDavid du Colombier return -1; 618*906943f9SDavid du Colombier } 619*906943f9SDavid du Colombier if(ums->epin == ums->epout) 620*906943f9SDavid du Colombier opendevdata(ums->epin, ORDWR); 621*906943f9SDavid du Colombier else{ 622*906943f9SDavid du Colombier opendevdata(ums->epin, OREAD); 623*906943f9SDavid du Colombier opendevdata(ums->epout, OWRITE); 624*906943f9SDavid du Colombier } 625*906943f9SDavid du Colombier if(ums->epin->dfd < 0 || ums->epout->dfd < 0){ 626*906943f9SDavid du Colombier fprint(2, "disk: open i/o ep data: %r\n"); 627*906943f9SDavid du Colombier closedev(ums->epin); 628*906943f9SDavid du Colombier closedev(ums->epout); 629*906943f9SDavid du Colombier return -1; 630*906943f9SDavid du Colombier } 631*906943f9SDavid du Colombier dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir); 6320641ea09SDavid du Colombier 633*906943f9SDavid du Colombier if(usbdebug > 1 || diskdebug > 2){ 634*906943f9SDavid du Colombier devctl(ums->epin, "debug 1"); 635*906943f9SDavid du Colombier devctl(ums->epout, "debug 1"); 636*906943f9SDavid du Colombier devctl(ums->dev, "debug 1"); 637*906943f9SDavid du Colombier } 638*906943f9SDavid du Colombier return 0; 639*906943f9SDavid du Colombier } 640*906943f9SDavid du Colombier 641*906943f9SDavid du Colombier static int 6420641ea09SDavid du Colombier usage(void) 6430641ea09SDavid du Colombier { 644*906943f9SDavid du Colombier werrstr("usage: usb/disk [-d]"); 645*906943f9SDavid du Colombier return -1; 6460641ea09SDavid du Colombier } 6470641ea09SDavid du Colombier 648*906943f9SDavid du Colombier static void 649*906943f9SDavid du Colombier umsdevfree(void *a) 6500641ea09SDavid du Colombier { 651*906943f9SDavid du Colombier Ums *ums = a; 6520641ea09SDavid du Colombier 653*906943f9SDavid du Colombier if(ums == nil) 654*906943f9SDavid du Colombier return; 655*906943f9SDavid du Colombier closedev(ums->epin); 656*906943f9SDavid du Colombier closedev(ums->epout); 657*906943f9SDavid du Colombier ums->epin = ums->epout = nil; 658*906943f9SDavid du Colombier free(ums->lun); 659*906943f9SDavid du Colombier free(ums); 660*906943f9SDavid du Colombier } 661*906943f9SDavid du Colombier 662*906943f9SDavid du Colombier static Usbfs diskfs = { 663*906943f9SDavid du Colombier .walk = dwalk, 664*906943f9SDavid du Colombier .open = dopen, 665*906943f9SDavid du Colombier .read = dread, 666*906943f9SDavid du Colombier .write = dwrite, 667*906943f9SDavid du Colombier .stat = dstat, 668*906943f9SDavid du Colombier }; 669*906943f9SDavid du Colombier 670*906943f9SDavid du Colombier int 671*906943f9SDavid du Colombier diskmain(Dev *dev, int argc, char **argv) 672*906943f9SDavid du Colombier { 673*906943f9SDavid du Colombier Ums *ums; 674*906943f9SDavid du Colombier Umsc *lun; 675*906943f9SDavid du Colombier int i; 6760641ea09SDavid du Colombier 6770641ea09SDavid du Colombier ARGBEGIN{ 6780641ea09SDavid du Colombier case 'd': 679*906943f9SDavid du Colombier scsidebug(diskdebug); 680*906943f9SDavid du Colombier diskdebug++; 6810641ea09SDavid du Colombier break; 6820641ea09SDavid du Colombier default: 683*906943f9SDavid du Colombier return usage(); 6840641ea09SDavid du Colombier }ARGEND 685*906943f9SDavid du Colombier if(argc != 0) 686*906943f9SDavid du Colombier return usage(); 6870641ea09SDavid du Colombier 688*906943f9SDavid du Colombier ums = dev->aux = emallocz(sizeof(Ums), 1); 689*906943f9SDavid du Colombier ums->maxlun = -1; 690*906943f9SDavid du Colombier ums->dev = dev; 691*906943f9SDavid du Colombier dev->free = umsdevfree; 692*906943f9SDavid du Colombier if(findendpoints(ums) < 0){ 693*906943f9SDavid du Colombier werrstr("disk: endpoints not found"); 694*906943f9SDavid du Colombier return -1; 6950641ea09SDavid du Colombier } 696*906943f9SDavid du Colombier if(umsinit(ums) < 0){ 697*906943f9SDavid du Colombier dprint(2, "disk: umsinit: %r\n"); 698*906943f9SDavid du Colombier return -1; 699*906943f9SDavid du Colombier } 7000641ea09SDavid du Colombier 701*906943f9SDavid du Colombier for(i = 0; i <= ums->maxlun; i++){ 702*906943f9SDavid du Colombier lun = &ums->lun[i]; 703*906943f9SDavid du Colombier lun->fs = diskfs; 704*906943f9SDavid du Colombier snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", dev->id, i); 705*906943f9SDavid du Colombier lun->fs.dev = dev; 706*906943f9SDavid du Colombier incref(dev); 707*906943f9SDavid du Colombier lun->fs.aux = lun; 708*906943f9SDavid du Colombier usbfsadd(&lun->fs); 709*906943f9SDavid du Colombier } 710*906943f9SDavid du Colombier closedev(dev); 711*906943f9SDavid du Colombier return 0; 7120641ea09SDavid du Colombier } 713