10641ea09SDavid du Colombier /* 20641ea09SDavid du Colombier * usb/disk - usb mass storage file server 3906943f9SDavid du Colombier * BUG: supports only the scsi command interface. 4906943f9SDavid du Colombier * BUG: This should use /dev/sdfile to 5906943f9SDavid du Colombier * use the kernel ether device code. 60641ea09SDavid du Colombier */ 7906943f9SDavid 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" 15906943f9SDavid du Colombier #include "usbfs.h" 16906943f9SDavid du Colombier #include "ums.h" 170641ea09SDavid du Colombier 18906943f9SDavid du Colombier enum 19906943f9SDavid du Colombier { 200641ea09SDavid du Colombier Qdir = 0, 210641ea09SDavid du Colombier Qctl, 220641ea09SDavid du Colombier Qraw, 230641ea09SDavid du Colombier Qdata, 24906943f9SDavid du Colombier Qmax, 250641ea09SDavid du Colombier }; 260641ea09SDavid du Colombier 270641ea09SDavid du Colombier typedef struct Dirtab Dirtab; 28906943f9SDavid du Colombier struct Dirtab 29906943f9SDavid du Colombier { 300641ea09SDavid du Colombier char *name; 310641ea09SDavid du Colombier int mode; 320641ea09SDavid du Colombier }; 33906943f9SDavid du Colombier 34906943f9SDavid du Colombier static Dirtab dirtab[] = 35906943f9SDavid du Colombier { 36906943f9SDavid du Colombier [Qdir] "/", DMDIR|0555, 37906943f9SDavid du Colombier [Qctl] "ctl", 0444, 38906943f9SDavid du Colombier [Qraw] "raw", 0640, 39906943f9SDavid du Colombier [Qdata] "data", 0640, 400641ea09SDavid du Colombier }; 410641ea09SDavid du Colombier 420641ea09SDavid du Colombier /* 43906943f9SDavid du Colombier * These are used by scuzz scsireq 440641ea09SDavid du Colombier */ 45906943f9SDavid du Colombier int exabyte, force6bytecmds; 46906943f9SDavid du Colombier long maxiosize = MaxIOsize; 470641ea09SDavid du Colombier 48906943f9SDavid du Colombier static int diskdebug; 490641ea09SDavid du Colombier 50906943f9SDavid du Colombier static int 51906943f9SDavid du Colombier getmaxlun(Dev *dev) 520641ea09SDavid du Colombier { 530641ea09SDavid du Colombier uchar max; 54906943f9SDavid du Colombier int r; 550641ea09SDavid du Colombier 566a5dc222SDavid du Colombier max = 0; 57906943f9SDavid du Colombier r = Rd2h|Rclass|Riface; 58906943f9SDavid du Colombier if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){ 59906943f9SDavid du Colombier dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir); 60*14dd39c1SDavid du Colombier }else{ 61*14dd39c1SDavid du Colombier max &= 017; /* 15 is the max. allowed */ 62906943f9SDavid du Colombier dprint(2, "disk: %s: maxlun %d\n", dev->dir, max); 63*14dd39c1SDavid du Colombier } 64906943f9SDavid du Colombier return max; 650641ea09SDavid du Colombier } 660641ea09SDavid du Colombier 67906943f9SDavid du Colombier static int 68906943f9SDavid du Colombier umsreset(Ums *ums) 690641ea09SDavid du Colombier { 70906943f9SDavid du Colombier int r; 710641ea09SDavid du Colombier 72906943f9SDavid du Colombier r = Rh2d|Rclass|Riface; 73906943f9SDavid du Colombier if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){ 74906943f9SDavid du Colombier fprint(2, "disk: reset: %r\n"); 750641ea09SDavid du Colombier return -1; 760641ea09SDavid du Colombier } 77906943f9SDavid du Colombier return 0; 780641ea09SDavid du Colombier } 790641ea09SDavid du Colombier 80906943f9SDavid du Colombier static int 81906943f9SDavid du Colombier umsrecover(Ums *ums) 82906943f9SDavid du Colombier { 83906943f9SDavid du Colombier if(umsreset(ums) < 0) 840641ea09SDavid du Colombier return -1; 85906943f9SDavid du Colombier if(unstall(ums->dev, ums->epin, Ein) < 0) 86906943f9SDavid du Colombier dprint(2, "disk: unstall epin: %r\n"); 87906943f9SDavid du Colombier 88906943f9SDavid du Colombier /* do we need this when epin == epout? */ 89906943f9SDavid du Colombier if(unstall(ums->dev, ums->epout, Eout) < 0) 90906943f9SDavid du Colombier dprint(2, "disk: unstall epout: %r\n"); 91906943f9SDavid du Colombier return 0; 92906943f9SDavid du Colombier } 93906943f9SDavid du Colombier 94906943f9SDavid du Colombier static void 95906943f9SDavid du Colombier umsfatal(Ums *ums) 96906943f9SDavid du Colombier { 97906943f9SDavid du Colombier int i; 98906943f9SDavid du Colombier 99906943f9SDavid du Colombier devctl(ums->dev, "detach"); 100906943f9SDavid du Colombier for(i = 0; i < ums->maxlun; i++) 101906943f9SDavid du Colombier usbfsdel(&ums->lun[i].fs); 102906943f9SDavid du Colombier } 103906943f9SDavid du Colombier 104906943f9SDavid du Colombier static int 105906943f9SDavid du Colombier umscapacity(Umsc *lun) 106906943f9SDavid du Colombier { 107906943f9SDavid du Colombier uchar data[32]; 108906943f9SDavid du Colombier 1090641ea09SDavid du Colombier lun->blocks = 0; 1100641ea09SDavid du Colombier lun->capacity = 0; 1110641ea09SDavid du Colombier lun->lbsize = 0; 112906943f9SDavid du Colombier if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0) 113906943f9SDavid du Colombier return -1; 114906943f9SDavid du Colombier lun->blocks = GETBELONG(data); 115906943f9SDavid du Colombier lun->lbsize = GETBELONG(data+4); 116906943f9SDavid du Colombier if(lun->blocks == 0xFFFFFFFF){ 117906943f9SDavid du Colombier if(SRrcapacity16(lun, data) < 0){ 118906943f9SDavid du Colombier lun->lbsize = 0; 119906943f9SDavid du Colombier lun->blocks = 0; 120906943f9SDavid du Colombier return -1; 1210641ea09SDavid du Colombier }else{ 122906943f9SDavid du Colombier lun->lbsize = GETBELONG(data + 8); 123906943f9SDavid du Colombier lun->blocks = (uvlong)GETBELONG(data)<<32 | GETBELONG(data + 4); 124906943f9SDavid du Colombier } 125906943f9SDavid du Colombier } 1260641ea09SDavid du Colombier lun->blocks++; /* SRcapacity returns LBA of last block */ 1270641ea09SDavid du Colombier lun->capacity = (vlong)lun->blocks * lun->lbsize; 128906943f9SDavid du Colombier return 0; 1290641ea09SDavid du Colombier } 130906943f9SDavid du Colombier 131906943f9SDavid du Colombier static int 132906943f9SDavid du Colombier umsinit(Ums *ums) 133906943f9SDavid du Colombier { 134906943f9SDavid du Colombier uchar i; 135906943f9SDavid du Colombier Umsc *lun; 136906943f9SDavid du Colombier int some; 137906943f9SDavid du Colombier 138906943f9SDavid du Colombier umsreset(ums); 139906943f9SDavid du Colombier ums->maxlun = getmaxlun(ums->dev); 140906943f9SDavid du Colombier ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1); 141906943f9SDavid du Colombier some = 0; 142906943f9SDavid du Colombier for(i = 0; i <= ums->maxlun; i++){ 143906943f9SDavid du Colombier lun = &ums->lun[i]; 144906943f9SDavid du Colombier lun->ums = ums; 145906943f9SDavid du Colombier lun->umsc = lun; 146906943f9SDavid du Colombier lun->lun = i; 147906943f9SDavid du Colombier lun->flags = Fopen | Fusb | Frw10; 148906943f9SDavid du Colombier if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0) 149906943f9SDavid du Colombier continue; 150906943f9SDavid du Colombier if(lun->inquiry[0] != 0x00){ 151906943f9SDavid du Colombier /* not a disk */ 152906943f9SDavid du Colombier fprint(2, "%s: lun %d is not a disk (type %#02x)\n", 153906943f9SDavid du Colombier argv0, i, lun->inquiry[0]); 154906943f9SDavid du Colombier continue; 155906943f9SDavid du Colombier } 156906943f9SDavid du Colombier SRstart(lun, 1); 157906943f9SDavid du Colombier /* 158906943f9SDavid du Colombier * we ignore the device type reported by inquiry. 159906943f9SDavid du Colombier * Some devices return a wrong value but would still work. 160906943f9SDavid du Colombier */ 161906943f9SDavid du Colombier some++; 162906943f9SDavid du Colombier lun->inq = smprint("%.48s", (char *)lun->inquiry+8); 163906943f9SDavid du Colombier umscapacity(lun); 164906943f9SDavid du Colombier } 165906943f9SDavid du Colombier if(some == 0){ 166906943f9SDavid du Colombier devctl(ums->dev, "detach"); 167906943f9SDavid du Colombier return -1; 1680641ea09SDavid du Colombier } 1690641ea09SDavid du Colombier return 0; 1700641ea09SDavid du Colombier } 1710641ea09SDavid du Colombier 1720641ea09SDavid du Colombier 173906943f9SDavid du Colombier /* 174906943f9SDavid du Colombier * called by SR*() commands provided by scuzz's scsireq 175906943f9SDavid du Colombier */ 1760641ea09SDavid du Colombier long 1770641ea09SDavid du Colombier umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status) 1780641ea09SDavid du Colombier { 1790641ea09SDavid du Colombier Cbw cbw; 1800641ea09SDavid du Colombier Csw csw; 1810641ea09SDavid du Colombier int n; 182906943f9SDavid du Colombier Ums *ums; 183906943f9SDavid du Colombier 184906943f9SDavid du Colombier ums = umsc->ums; 1850641ea09SDavid du Colombier 1860641ea09SDavid du Colombier memcpy(cbw.signature, "USBC", 4); 187906943f9SDavid du Colombier cbw.tag = ++ums->seq; 1880641ea09SDavid du Colombier cbw.datalen = data->count; 1890641ea09SDavid du Colombier cbw.flags = data->write? CbwDataOut: CbwDataIn; 1900641ea09SDavid du Colombier cbw.lun = umsc->lun; 191906943f9SDavid du Colombier if(cmd->count < 1 || cmd->count > 16) 192906943f9SDavid du Colombier print("%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count); 193906943f9SDavid du Colombier 1940641ea09SDavid du Colombier cbw.len = cmd->count; 195906943f9SDavid du Colombier assert(cmd->count <= sizeof(cbw.command)); 1960641ea09SDavid du Colombier memcpy(cbw.command, cmd->p, cmd->count); 1970641ea09SDavid du Colombier memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count); 1980641ea09SDavid du Colombier 199906943f9SDavid du Colombier werrstr(""); /* we use %r later even for n == 0 */ 200906943f9SDavid du Colombier 201906943f9SDavid du Colombier if(diskdebug){ 202906943f9SDavid du Colombier fprint(2, "disk: cmd: tag %#lx: ", cbw.tag); 2030641ea09SDavid du Colombier for(n = 0; n < cbw.len; n++) 2040641ea09SDavid du Colombier fprint(2, " %2.2x", cbw.command[n]&0xFF); 2050641ea09SDavid du Colombier fprint(2, " datalen: %ld\n", cbw.datalen); 2060641ea09SDavid du Colombier } 207906943f9SDavid du Colombier if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){ 208906943f9SDavid du Colombier fprint(2, "disk: cmd: %r\n"); 209906943f9SDavid du Colombier goto Fail; 2100641ea09SDavid du Colombier } 2110641ea09SDavid du Colombier if(data->count != 0){ 2120641ea09SDavid du Colombier if(data->write) 213906943f9SDavid du Colombier n = write(ums->epout->dfd, data->p, data->count); 214906943f9SDavid du Colombier else{ 215906943f9SDavid du Colombier memset(data->p, data->count, 0); 216906943f9SDavid du Colombier n = read(ums->epin->dfd, data->p, data->count); 2170641ea09SDavid du Colombier } 218906943f9SDavid du Colombier if(diskdebug) 219906943f9SDavid du Colombier if(n < 0) 220906943f9SDavid du Colombier fprint(2, "disk: data: %r\n"); 221906943f9SDavid du Colombier else 222906943f9SDavid du Colombier fprint(2, "disk: data: %d bytes\n", n); 223906943f9SDavid du Colombier if(n <= 0) 224906943f9SDavid du Colombier if(data->write == 0) 225906943f9SDavid du Colombier unstall(ums->dev, ums->epin, Ein); 2260641ea09SDavid du Colombier } 227906943f9SDavid du Colombier n = read(ums->epin->dfd, &csw, CswLen); 228906943f9SDavid du Colombier if(n <= 0){ 229906943f9SDavid du Colombier /* n == 0 means "stalled" */ 230906943f9SDavid du Colombier unstall(ums->dev, ums->epin, Ein); 231906943f9SDavid du Colombier n = read(ums->epin->dfd, &csw, CswLen); 2320641ea09SDavid du Colombier } 2330641ea09SDavid du Colombier if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){ 234906943f9SDavid du Colombier dprint(2, "disk: read n=%d: status: %r\n", n); 235906943f9SDavid du Colombier goto Fail; 2360641ea09SDavid du Colombier } 2370641ea09SDavid du Colombier if(csw.tag != cbw.tag){ 238906943f9SDavid du Colombier dprint(2, "disk: status tag mismatch\n"); 239906943f9SDavid du Colombier goto Fail; 2400641ea09SDavid du Colombier } 2410641ea09SDavid du Colombier if(csw.status >= CswPhaseErr){ 242906943f9SDavid du Colombier dprint(2, "disk: phase error\n"); 243906943f9SDavid du Colombier goto Fail; 2440641ea09SDavid du Colombier } 245906943f9SDavid du Colombier if(diskdebug){ 2460641ea09SDavid du Colombier fprint(2, "status: %2.2ux residue: %ld\n", 2470641ea09SDavid du Colombier csw.status, csw.dataresidue); 2480641ea09SDavid du Colombier if(cbw.command[0] == ScmdRsense){ 2490641ea09SDavid du Colombier fprint(2, "sense data:"); 2500641ea09SDavid du Colombier for(n = 0; n < data->count - csw.dataresidue; n++) 2510641ea09SDavid du Colombier fprint(2, " %2.2x", data->p[n]); 2520641ea09SDavid du Colombier fprint(2, "\n"); 2530641ea09SDavid du Colombier } 2540641ea09SDavid du Colombier } 255906943f9SDavid du Colombier switch(csw.status){ 256906943f9SDavid du Colombier case CswOk: 2570641ea09SDavid du Colombier *status = STok; 258906943f9SDavid du Colombier break; 259906943f9SDavid du Colombier case CswFailed: 2600641ea09SDavid du Colombier *status = STcheck; 261906943f9SDavid du Colombier break; 262906943f9SDavid du Colombier default: 263906943f9SDavid du Colombier dprint(2, "disk: phase error\n"); 264906943f9SDavid du Colombier goto Fail; 265906943f9SDavid du Colombier } 266906943f9SDavid du Colombier ums->nerrs = 0; 2670641ea09SDavid du Colombier return data->count - csw.dataresidue; 2680641ea09SDavid du Colombier 269906943f9SDavid du Colombier Fail: 2700641ea09SDavid du Colombier *status = STharderr; 271906943f9SDavid du Colombier if(ums->nerrs++ > 15){ 272906943f9SDavid du Colombier fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir); 273906943f9SDavid du Colombier umsfatal(ums); 2740641ea09SDavid du Colombier }else 275906943f9SDavid du Colombier umsrecover(ums); 2760641ea09SDavid du Colombier return -1; 2770641ea09SDavid du Colombier } 2780641ea09SDavid du Colombier 279906943f9SDavid du Colombier static int 280906943f9SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name) 2810641ea09SDavid du Colombier { 282906943f9SDavid du Colombier int i; 283906943f9SDavid du Colombier Qid qid; 284906943f9SDavid du Colombier 285906943f9SDavid du Colombier qid = fid->qid; 286906943f9SDavid du Colombier if((qid.type & QTDIR) == 0){ 287906943f9SDavid du Colombier werrstr("walk in non-directory"); 288906943f9SDavid du Colombier return -1; 2890641ea09SDavid du Colombier } 2900641ea09SDavid du Colombier 2910641ea09SDavid du Colombier if(strcmp(name, "..") == 0) 292906943f9SDavid du Colombier return 0; 2930641ea09SDavid du Colombier 294906943f9SDavid du Colombier for(i = 1; i < nelem(dirtab); i++) 2950641ea09SDavid du Colombier if(strcmp(name, dirtab[i].name) == 0){ 296906943f9SDavid du Colombier qid.path = i | fs->qid; 297906943f9SDavid du Colombier qid.vers = 0; 298906943f9SDavid du Colombier qid.type = dirtab[i].mode >> 24; 299906943f9SDavid du Colombier fid->qid = qid; 300906943f9SDavid du Colombier return 0; 3010641ea09SDavid du Colombier } 302906943f9SDavid du Colombier werrstr(Enotfound); 303906943f9SDavid du Colombier return -1; 3040641ea09SDavid du Colombier } 3050641ea09SDavid du Colombier 306906943f9SDavid du Colombier static void 307906943f9SDavid du Colombier dostat(Usbfs *fs, int path, Dir *d) 3080641ea09SDavid du Colombier { 3090641ea09SDavid du Colombier Dirtab *t; 3100641ea09SDavid du Colombier Umsc *lun; 3110641ea09SDavid du Colombier 312906943f9SDavid du Colombier t = &dirtab[path]; 313906943f9SDavid du Colombier d->qid.path = path; 314906943f9SDavid du Colombier d->qid.type = t->mode >> 24; 315906943f9SDavid du Colombier d->mode = t->mode; 316906943f9SDavid du Colombier d->name = t->name; 317906943f9SDavid du Colombier lun = fs->aux; 318906943f9SDavid du Colombier if(path == Qdata) 319906943f9SDavid du Colombier d->length = lun->capacity; 320906943f9SDavid du Colombier else 321906943f9SDavid du Colombier d->length = 0; 322906943f9SDavid du Colombier } 323906943f9SDavid du Colombier 324906943f9SDavid du Colombier static int 325906943f9SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void*) 326906943f9SDavid du Colombier { 327906943f9SDavid du Colombier i++; /* skip dir */ 328906943f9SDavid du Colombier if(i >= Qmax) 329906943f9SDavid du Colombier return -1; 330906943f9SDavid du Colombier else{ 331906943f9SDavid du Colombier dostat(fs, i, d); 332906943f9SDavid du Colombier d->qid.path |= fs->qid; 333906943f9SDavid du Colombier return 0; 334906943f9SDavid du Colombier } 335906943f9SDavid du Colombier } 336906943f9SDavid du Colombier 337906943f9SDavid du Colombier static int 338906943f9SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d) 339906943f9SDavid du Colombier { 340906943f9SDavid du Colombier int path; 341906943f9SDavid du Colombier 342906943f9SDavid du Colombier path = qid.path & ~fs->qid; 343906943f9SDavid du Colombier dostat(fs, path, d); 344906943f9SDavid du Colombier d->qid.path |= fs->qid; 345906943f9SDavid du Colombier return 0; 346906943f9SDavid du Colombier } 347906943f9SDavid du Colombier 348906943f9SDavid du Colombier static int 349906943f9SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int) 350906943f9SDavid du Colombier { 351906943f9SDavid du Colombier ulong path; 352906943f9SDavid du Colombier Umsc *lun; 353906943f9SDavid du Colombier 354906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 355906943f9SDavid du Colombier lun = fs->aux; 356906943f9SDavid du Colombier switch(path){ 357906943f9SDavid du Colombier case Qraw: 358906943f9SDavid du Colombier lun->phase = Pcmd; 3590641ea09SDavid du Colombier break; 360906943f9SDavid du Colombier } 361906943f9SDavid du Colombier return 0; 362906943f9SDavid du Colombier } 363906943f9SDavid du Colombier 364906943f9SDavid du Colombier /* 365906943f9SDavid du Colombier * Upon SRread/SRwrite errors we assume the medium may have changed, 366906943f9SDavid du Colombier * and ask again for the capacity of the media. 367906943f9SDavid du Colombier * BUG: How to proceed to avoid confussing dossrv?? 368906943f9SDavid du Colombier */ 369906943f9SDavid du Colombier static long 370906943f9SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) 371906943f9SDavid du Colombier { 372906943f9SDavid du Colombier long bno, nb, len, off, n; 373906943f9SDavid du Colombier ulong path; 374906943f9SDavid du Colombier char buf[1024], *p; 375906943f9SDavid du Colombier char *s; 376906943f9SDavid du Colombier char *e; 377906943f9SDavid du Colombier Umsc *lun; 378906943f9SDavid du Colombier Ums *ums; 379906943f9SDavid du Colombier Qid q; 380906943f9SDavid du Colombier 381906943f9SDavid du Colombier q = fid->qid; 382906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 383906943f9SDavid du Colombier ums = fs->dev->aux; 384906943f9SDavid du Colombier lun = fs->aux; 385906943f9SDavid du Colombier qlock(ums); 386906943f9SDavid du Colombier switch(path){ 387906943f9SDavid du Colombier case Qdir: 388906943f9SDavid du Colombier count = usbdirread(fs, q, data, count, offset, dirgen, nil); 3890641ea09SDavid du Colombier break; 3900641ea09SDavid du Colombier case Qctl: 391906943f9SDavid du Colombier e = buf + sizeof(buf); 392906943f9SDavid du Colombier s = seprint(buf, e, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]); 3930641ea09SDavid du Colombier if(lun->flags & Finqok) 394906943f9SDavid du Colombier s = seprint(s, e, "inquiry %s ", lun->inq); 3950641ea09SDavid du Colombier if(lun->blocks > 0) 396906943f9SDavid du Colombier s = seprint(s, e, "geometry %llud %ld", lun->blocks, 3970641ea09SDavid du Colombier lun->lbsize); 398906943f9SDavid du Colombier s = seprint(s, e, "\n"); 399906943f9SDavid du Colombier count = usbreadbuf(data, count, offset, buf, s - buf); 4000641ea09SDavid du Colombier break; 4010641ea09SDavid du Colombier case Qraw: 402906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 403906943f9SDavid du Colombier qunlock(ums); 404906943f9SDavid du Colombier return -1; 4050641ea09SDavid du Colombier } 4060641ea09SDavid du Colombier switch(lun->phase){ 4070641ea09SDavid du Colombier case Pcmd: 408906943f9SDavid du Colombier qunlock(ums); 409906943f9SDavid du Colombier werrstr("phase error"); 410906943f9SDavid du Colombier return -1; 4110641ea09SDavid du Colombier case Pdata: 412906943f9SDavid du Colombier lun->data.p = (uchar*)data; 413906943f9SDavid du Colombier lun->data.count = count; 4140641ea09SDavid du Colombier lun->data.write = 0; 415906943f9SDavid du Colombier count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 4160641ea09SDavid du Colombier lun->phase = Pstatus; 417906943f9SDavid du Colombier if(count < 0){ 418906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 419906943f9SDavid du Colombier qunlock(ums); 420906943f9SDavid du Colombier return -1; 4210641ea09SDavid du Colombier } 4220641ea09SDavid du Colombier break; 4230641ea09SDavid du Colombier case Pstatus: 4240641ea09SDavid du Colombier n = snprint(buf, sizeof buf, "%11.0ud ", lun->status); 425906943f9SDavid du Colombier count = usbreadbuf(data, count, 0LL, buf, n); 4260641ea09SDavid du Colombier lun->phase = Pcmd; 4270641ea09SDavid du Colombier break; 4280641ea09SDavid du Colombier } 4290641ea09SDavid du Colombier break; 4300641ea09SDavid du Colombier case Qdata: 431906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 432906943f9SDavid du Colombier qunlock(ums); 433906943f9SDavid du Colombier return -1; 4340641ea09SDavid du Colombier } 435906943f9SDavid du Colombier bno = offset / lun->lbsize; 436906943f9SDavid du Colombier nb = (offset + count + lun->lbsize - 1) / lun->lbsize - bno; 4370641ea09SDavid du Colombier if(bno + nb > lun->blocks) 4380641ea09SDavid du Colombier nb = lun->blocks - bno; 4390641ea09SDavid du Colombier if(bno >= lun->blocks || nb == 0){ 440906943f9SDavid du Colombier count = 0; 4410641ea09SDavid du Colombier break; 4420641ea09SDavid du Colombier } 4430641ea09SDavid du Colombier if(nb * lun->lbsize > maxiosize) 4440641ea09SDavid du Colombier nb = maxiosize / lun->lbsize; 445906943f9SDavid du Colombier p = emallocz(nb * lun->lbsize, 0); /* could use a static buffer */ 446906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 4470641ea09SDavid du Colombier n = SRread(lun, p, nb * lun->lbsize); 448906943f9SDavid du Colombier if(n < 0){ 4490641ea09SDavid du Colombier free(p); 450906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 451906943f9SDavid du Colombier qunlock(ums); 452906943f9SDavid du Colombier return -1; 4530641ea09SDavid du Colombier } 454906943f9SDavid du Colombier len = count; 455906943f9SDavid du Colombier off = offset % lun->lbsize; 456906943f9SDavid du Colombier if(off + len > n) 457906943f9SDavid du Colombier len = n - off; 458906943f9SDavid du Colombier count = len; 459906943f9SDavid du Colombier memmove(data, p + off, len); 4600641ea09SDavid du Colombier free(p); 4610641ea09SDavid du Colombier break; 4620641ea09SDavid du Colombier } 463906943f9SDavid du Colombier qunlock(ums); 464906943f9SDavid du Colombier return count; 4650641ea09SDavid du Colombier } 4660641ea09SDavid du Colombier 467906943f9SDavid du Colombier static long 468906943f9SDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset) 4690641ea09SDavid du Colombier { 470906943f9SDavid du Colombier int bno, nb, len, off; 4710641ea09SDavid du Colombier ulong path; 4720641ea09SDavid du Colombier char *p; 473906943f9SDavid du Colombier Ums *ums; 4740641ea09SDavid du Colombier Umsc *lun; 475906943f9SDavid du Colombier char *data; 4760641ea09SDavid du Colombier 477906943f9SDavid du Colombier ums = fs->dev->aux; 478906943f9SDavid du Colombier lun = fs->aux; 479906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 480906943f9SDavid du Colombier data = buf; 481906943f9SDavid du Colombier qlock(ums); 482906943f9SDavid du Colombier switch(path){ 483906943f9SDavid du Colombier default: 484906943f9SDavid du Colombier qunlock(ums); 485906943f9SDavid du Colombier werrstr(Eperm); 486906943f9SDavid du Colombier return -1; 4870641ea09SDavid du Colombier case Qraw: 488906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 489906943f9SDavid du Colombier qunlock(ums); 490906943f9SDavid du Colombier return -1; 4910641ea09SDavid du Colombier } 4920641ea09SDavid du Colombier switch(lun->phase){ 4930641ea09SDavid du Colombier case Pcmd: 494906943f9SDavid du Colombier if(count != 6 && count != 10){ 495906943f9SDavid du Colombier qunlock(ums); 496906943f9SDavid du Colombier werrstr("bad command length"); 497906943f9SDavid du Colombier return -1; 4980641ea09SDavid du Colombier } 499906943f9SDavid du Colombier memmove(lun->rawcmd, data, count); 5000641ea09SDavid du Colombier lun->cmd.p = lun->rawcmd; 501906943f9SDavid du Colombier lun->cmd.count = count; 5020641ea09SDavid du Colombier lun->cmd.write = 1; 5030641ea09SDavid du Colombier lun->phase = Pdata; 5040641ea09SDavid du Colombier break; 5050641ea09SDavid du Colombier case Pdata: 506906943f9SDavid du Colombier lun->data.p = (uchar*)data; 507906943f9SDavid du Colombier lun->data.count = count; 5080641ea09SDavid du Colombier lun->data.write = 1; 509906943f9SDavid du Colombier count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 5100641ea09SDavid du Colombier lun->phase = Pstatus; 511906943f9SDavid du Colombier if(count < 0){ 512906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 513906943f9SDavid du Colombier qunlock(ums); 514906943f9SDavid du Colombier return -1; 5150641ea09SDavid du Colombier } 5160641ea09SDavid du Colombier break; 5170641ea09SDavid du Colombier case Pstatus: 5180641ea09SDavid du Colombier lun->phase = Pcmd; 519906943f9SDavid du Colombier qunlock(ums); 520906943f9SDavid du Colombier werrstr("phase error"); 521906943f9SDavid du Colombier return -1; 5220641ea09SDavid du Colombier } 5230641ea09SDavid du Colombier break; 5240641ea09SDavid du Colombier case Qdata: 525906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 526906943f9SDavid du Colombier qunlock(ums); 527906943f9SDavid du Colombier return -1; 5280641ea09SDavid du Colombier } 529906943f9SDavid du Colombier bno = offset / lun->lbsize; 530906943f9SDavid du Colombier nb = (offset + count + lun->lbsize-1) / lun->lbsize - bno; 5310641ea09SDavid du Colombier if(bno + nb > lun->blocks) 5320641ea09SDavid du Colombier nb = lun->blocks - bno; 5330641ea09SDavid du Colombier if(bno >= lun->blocks || nb == 0){ 534906943f9SDavid du Colombier count = 0; 5350641ea09SDavid du Colombier break; 5360641ea09SDavid du Colombier } 5370641ea09SDavid du Colombier if(nb * lun->lbsize > maxiosize) 5380641ea09SDavid du Colombier nb = maxiosize / lun->lbsize; 539906943f9SDavid du Colombier p = emallocz(nb * lun->lbsize, 0); 540906943f9SDavid du Colombier off = offset % lun->lbsize; 541906943f9SDavid du Colombier len = count; 542906943f9SDavid du Colombier if(off || (len % lun->lbsize) != 0){ 543906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 544906943f9SDavid du Colombier count = SRread(lun, p, nb * lun->lbsize); 545906943f9SDavid du Colombier if(count < 0){ 5460641ea09SDavid du Colombier free(p); 547906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 548906943f9SDavid du Colombier qunlock(ums); 549906943f9SDavid du Colombier return -1; 5500641ea09SDavid du Colombier } 551906943f9SDavid du Colombier if(off + len > count) 552906943f9SDavid du Colombier len = count - off; 5530641ea09SDavid du Colombier } 554906943f9SDavid du Colombier memmove(p+off, data, len); 555906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 556906943f9SDavid du Colombier count = SRwrite(lun, p, nb * lun->lbsize); 557906943f9SDavid du Colombier if(count < 0){ 5580641ea09SDavid du Colombier free(p); 559906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 560906943f9SDavid du Colombier qunlock(ums); 561906943f9SDavid du Colombier return -1; 5620641ea09SDavid du Colombier } 563906943f9SDavid du Colombier if(off+len > count) 564906943f9SDavid du Colombier len = count - off; 565906943f9SDavid du Colombier count = len; 5660641ea09SDavid du Colombier free(p); 5670641ea09SDavid du Colombier break; 5680641ea09SDavid du Colombier } 569906943f9SDavid du Colombier qunlock(ums); 570906943f9SDavid du Colombier return count; 5710641ea09SDavid du Colombier } 5720641ea09SDavid du Colombier 573906943f9SDavid du Colombier int 574906943f9SDavid du Colombier findendpoints(Ums *ums) 575906943f9SDavid du Colombier { 576906943f9SDavid du Colombier Ep *ep; 577906943f9SDavid du Colombier Usbdev *ud; 578906943f9SDavid du Colombier ulong csp; 579906943f9SDavid du Colombier ulong sc; 580906943f9SDavid du Colombier int i; 581906943f9SDavid du Colombier int epin, epout; 5820641ea09SDavid du Colombier 583906943f9SDavid du Colombier epin = epout = -1; 584906943f9SDavid du Colombier ud = ums->dev->usb; 585906943f9SDavid du Colombier for(i = 0; i < nelem(ud->ep); i++){ 586906943f9SDavid du Colombier if((ep = ud->ep[i]) == nil) 587906943f9SDavid du Colombier continue; 588906943f9SDavid du Colombier csp = ep->iface->csp; 589906943f9SDavid du Colombier sc = Subclass(csp); 590906943f9SDavid du Colombier if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk))) 591906943f9SDavid du Colombier continue; 592906943f9SDavid du Colombier if(sc != Subatapi && sc != Sub8070 && sc != Subscsi) 593906943f9SDavid du Colombier fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc); 594906943f9SDavid du Colombier if(ep->type == Ebulk){ 595906943f9SDavid du Colombier if(ep->dir == Eboth || ep->dir == Ein) 596906943f9SDavid du Colombier if(epin == -1) 597906943f9SDavid du Colombier epin = ep->id; 598906943f9SDavid du Colombier if(ep->dir == Eboth || ep->dir == Eout) 599906943f9SDavid du Colombier if(epout == -1) 600906943f9SDavid du Colombier epout = ep->id; 601906943f9SDavid du Colombier } 602906943f9SDavid du Colombier } 603906943f9SDavid du Colombier dprint(2, "disk: ep ids: in %d out %d\n", epin, epout); 604906943f9SDavid du Colombier if(epin == -1 || epout == -1) 605906943f9SDavid du Colombier return -1; 606906943f9SDavid du Colombier ums->epin = openep(ums->dev, epin); 607906943f9SDavid du Colombier if(ums->epin == nil){ 608906943f9SDavid du Colombier fprint(2, "disk: openep %d: %r\n", epin); 609906943f9SDavid du Colombier return -1; 610906943f9SDavid du Colombier } 611906943f9SDavid du Colombier if(epout == epin){ 612906943f9SDavid du Colombier incref(ums->epin); 613906943f9SDavid du Colombier ums->epout = ums->epin; 614906943f9SDavid du Colombier }else 615906943f9SDavid du Colombier ums->epout = openep(ums->dev, epout); 616906943f9SDavid du Colombier if(ums->epout == nil){ 617906943f9SDavid du Colombier fprint(2, "disk: openep %d: %r\n", epout); 618906943f9SDavid du Colombier closedev(ums->epin); 619906943f9SDavid du Colombier return -1; 620906943f9SDavid du Colombier } 621906943f9SDavid du Colombier if(ums->epin == ums->epout) 622906943f9SDavid du Colombier opendevdata(ums->epin, ORDWR); 623906943f9SDavid du Colombier else{ 624906943f9SDavid du Colombier opendevdata(ums->epin, OREAD); 625906943f9SDavid du Colombier opendevdata(ums->epout, OWRITE); 626906943f9SDavid du Colombier } 627906943f9SDavid du Colombier if(ums->epin->dfd < 0 || ums->epout->dfd < 0){ 628906943f9SDavid du Colombier fprint(2, "disk: open i/o ep data: %r\n"); 629906943f9SDavid du Colombier closedev(ums->epin); 630906943f9SDavid du Colombier closedev(ums->epout); 631906943f9SDavid du Colombier return -1; 632906943f9SDavid du Colombier } 633906943f9SDavid du Colombier dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir); 634d37e33ffSDavid du Colombier 635d37e33ffSDavid du Colombier devctl(ums->epin, "timeout 2000"); 636d37e33ffSDavid du Colombier devctl(ums->epout, "timeout 2000"); 6370641ea09SDavid du Colombier 638906943f9SDavid du Colombier if(usbdebug > 1 || diskdebug > 2){ 639906943f9SDavid du Colombier devctl(ums->epin, "debug 1"); 640906943f9SDavid du Colombier devctl(ums->epout, "debug 1"); 641906943f9SDavid du Colombier devctl(ums->dev, "debug 1"); 642906943f9SDavid du Colombier } 643906943f9SDavid du Colombier return 0; 644906943f9SDavid du Colombier } 645906943f9SDavid du Colombier 646906943f9SDavid du Colombier static int 6470641ea09SDavid du Colombier usage(void) 6480641ea09SDavid du Colombier { 649906943f9SDavid du Colombier werrstr("usage: usb/disk [-d]"); 650906943f9SDavid du Colombier return -1; 6510641ea09SDavid du Colombier } 6520641ea09SDavid du Colombier 653906943f9SDavid du Colombier static void 654906943f9SDavid du Colombier umsdevfree(void *a) 6550641ea09SDavid du Colombier { 656906943f9SDavid du Colombier Ums *ums = a; 6570641ea09SDavid du Colombier 658906943f9SDavid du Colombier if(ums == nil) 659906943f9SDavid du Colombier return; 660906943f9SDavid du Colombier closedev(ums->epin); 661906943f9SDavid du Colombier closedev(ums->epout); 662906943f9SDavid du Colombier ums->epin = ums->epout = nil; 663906943f9SDavid du Colombier free(ums->lun); 664906943f9SDavid du Colombier free(ums); 665906943f9SDavid du Colombier } 666906943f9SDavid du Colombier 667906943f9SDavid du Colombier static Usbfs diskfs = { 668906943f9SDavid du Colombier .walk = dwalk, 669906943f9SDavid du Colombier .open = dopen, 670906943f9SDavid du Colombier .read = dread, 671906943f9SDavid du Colombier .write = dwrite, 672906943f9SDavid du Colombier .stat = dstat, 673906943f9SDavid du Colombier }; 674906943f9SDavid du Colombier 675906943f9SDavid du Colombier int 676906943f9SDavid du Colombier diskmain(Dev *dev, int argc, char **argv) 677906943f9SDavid du Colombier { 678906943f9SDavid du Colombier Ums *ums; 679906943f9SDavid du Colombier Umsc *lun; 680906943f9SDavid du Colombier int i; 6810641ea09SDavid du Colombier 6820641ea09SDavid du Colombier ARGBEGIN{ 6830641ea09SDavid du Colombier case 'd': 684906943f9SDavid du Colombier scsidebug(diskdebug); 685906943f9SDavid du Colombier diskdebug++; 6860641ea09SDavid du Colombier break; 6870641ea09SDavid du Colombier default: 688906943f9SDavid du Colombier return usage(); 6890641ea09SDavid du Colombier }ARGEND 690906943f9SDavid du Colombier if(argc != 0) 691906943f9SDavid du Colombier return usage(); 6920641ea09SDavid du Colombier 693906943f9SDavid du Colombier ums = dev->aux = emallocz(sizeof(Ums), 1); 694906943f9SDavid du Colombier ums->maxlun = -1; 695906943f9SDavid du Colombier ums->dev = dev; 696906943f9SDavid du Colombier dev->free = umsdevfree; 697906943f9SDavid du Colombier if(findendpoints(ums) < 0){ 698906943f9SDavid du Colombier werrstr("disk: endpoints not found"); 699906943f9SDavid du Colombier return -1; 7000641ea09SDavid du Colombier } 701906943f9SDavid du Colombier if(umsinit(ums) < 0){ 702906943f9SDavid du Colombier dprint(2, "disk: umsinit: %r\n"); 703906943f9SDavid du Colombier return -1; 704906943f9SDavid du Colombier } 7050641ea09SDavid du Colombier 706906943f9SDavid du Colombier for(i = 0; i <= ums->maxlun; i++){ 707906943f9SDavid du Colombier lun = &ums->lun[i]; 708906943f9SDavid du Colombier lun->fs = diskfs; 709906943f9SDavid du Colombier snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", dev->id, i); 710906943f9SDavid du Colombier lun->fs.dev = dev; 711906943f9SDavid du Colombier incref(dev); 712906943f9SDavid du Colombier lun->fs.aux = lun; 713906943f9SDavid du Colombier usbfsadd(&lun->fs); 714906943f9SDavid du Colombier } 715906943f9SDavid du Colombier closedev(dev); 716906943f9SDavid du Colombier return 0; 7170641ea09SDavid du Colombier } 718