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); 60906943f9SDavid du Colombier }else 61906943f9SDavid du Colombier dprint(2, "disk: %s: maxlun %d\n", dev->dir, max); 62906943f9SDavid du Colombier return max; 630641ea09SDavid du Colombier } 640641ea09SDavid du Colombier 65906943f9SDavid du Colombier static int 66906943f9SDavid du Colombier umsreset(Ums *ums) 670641ea09SDavid du Colombier { 68906943f9SDavid du Colombier int r; 690641ea09SDavid du Colombier 70906943f9SDavid du Colombier r = Rh2d|Rclass|Riface; 71906943f9SDavid du Colombier if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){ 72906943f9SDavid du Colombier fprint(2, "disk: reset: %r\n"); 730641ea09SDavid du Colombier return -1; 740641ea09SDavid du Colombier } 75906943f9SDavid du Colombier return 0; 760641ea09SDavid du Colombier } 770641ea09SDavid du Colombier 78906943f9SDavid du Colombier static int 79906943f9SDavid du Colombier umsrecover(Ums *ums) 80906943f9SDavid du Colombier { 81906943f9SDavid du Colombier if(umsreset(ums) < 0) 820641ea09SDavid du Colombier return -1; 83906943f9SDavid du Colombier if(unstall(ums->dev, ums->epin, Ein) < 0) 84906943f9SDavid du Colombier dprint(2, "disk: unstall epin: %r\n"); 85906943f9SDavid du Colombier 86906943f9SDavid du Colombier /* do we need this when epin == epout? */ 87906943f9SDavid du Colombier if(unstall(ums->dev, ums->epout, Eout) < 0) 88906943f9SDavid du Colombier dprint(2, "disk: unstall epout: %r\n"); 89906943f9SDavid du Colombier return 0; 90906943f9SDavid du Colombier } 91906943f9SDavid du Colombier 92906943f9SDavid du Colombier static void 93906943f9SDavid du Colombier umsfatal(Ums *ums) 94906943f9SDavid du Colombier { 95906943f9SDavid du Colombier int i; 96906943f9SDavid du Colombier 97906943f9SDavid du Colombier devctl(ums->dev, "detach"); 98906943f9SDavid du Colombier for(i = 0; i < ums->maxlun; i++) 99906943f9SDavid du Colombier usbfsdel(&ums->lun[i].fs); 100906943f9SDavid du Colombier } 101906943f9SDavid du Colombier 102906943f9SDavid du Colombier static int 103906943f9SDavid du Colombier umscapacity(Umsc *lun) 104906943f9SDavid du Colombier { 105906943f9SDavid du Colombier uchar data[32]; 106906943f9SDavid du Colombier 1070641ea09SDavid du Colombier lun->blocks = 0; 1080641ea09SDavid du Colombier lun->capacity = 0; 1090641ea09SDavid du Colombier lun->lbsize = 0; 110906943f9SDavid du Colombier if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0) 111906943f9SDavid du Colombier return -1; 112906943f9SDavid du Colombier lun->blocks = GETBELONG(data); 113906943f9SDavid du Colombier lun->lbsize = GETBELONG(data+4); 114906943f9SDavid du Colombier if(lun->blocks == 0xFFFFFFFF){ 115906943f9SDavid du Colombier if(SRrcapacity16(lun, data) < 0){ 116906943f9SDavid du Colombier lun->lbsize = 0; 117906943f9SDavid du Colombier lun->blocks = 0; 118906943f9SDavid du Colombier return -1; 1190641ea09SDavid du Colombier }else{ 120906943f9SDavid du Colombier lun->lbsize = GETBELONG(data + 8); 121906943f9SDavid du Colombier lun->blocks = (uvlong)GETBELONG(data)<<32 | GETBELONG(data + 4); 122906943f9SDavid du Colombier } 123906943f9SDavid du Colombier } 1240641ea09SDavid du Colombier lun->blocks++; /* SRcapacity returns LBA of last block */ 1250641ea09SDavid du Colombier lun->capacity = (vlong)lun->blocks * lun->lbsize; 126906943f9SDavid du Colombier return 0; 1270641ea09SDavid du Colombier } 128906943f9SDavid du Colombier 129906943f9SDavid du Colombier static int 130906943f9SDavid du Colombier umsinit(Ums *ums) 131906943f9SDavid du Colombier { 132906943f9SDavid du Colombier uchar i; 133906943f9SDavid du Colombier Umsc *lun; 134906943f9SDavid du Colombier int some; 135906943f9SDavid du Colombier 136906943f9SDavid du Colombier umsreset(ums); 137906943f9SDavid du Colombier ums->maxlun = getmaxlun(ums->dev); 138906943f9SDavid du Colombier ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1); 139906943f9SDavid du Colombier some = 0; 140906943f9SDavid du Colombier for(i = 0; i <= ums->maxlun; i++){ 141906943f9SDavid du Colombier lun = &ums->lun[i]; 142906943f9SDavid du Colombier lun->ums = ums; 143906943f9SDavid du Colombier lun->umsc = lun; 144906943f9SDavid du Colombier lun->lun = i; 145906943f9SDavid du Colombier lun->flags = Fopen | Fusb | Frw10; 146906943f9SDavid du Colombier if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0) 147906943f9SDavid du Colombier continue; 148906943f9SDavid du Colombier if(lun->inquiry[0] != 0x00){ 149906943f9SDavid du Colombier /* not a disk */ 150906943f9SDavid du Colombier fprint(2, "%s: lun %d is not a disk (type %#02x)\n", 151906943f9SDavid du Colombier argv0, i, lun->inquiry[0]); 152906943f9SDavid du Colombier continue; 153906943f9SDavid du Colombier } 154906943f9SDavid du Colombier SRstart(lun, 1); 155906943f9SDavid du Colombier /* 156906943f9SDavid du Colombier * we ignore the device type reported by inquiry. 157906943f9SDavid du Colombier * Some devices return a wrong value but would still work. 158906943f9SDavid du Colombier */ 159906943f9SDavid du Colombier some++; 160906943f9SDavid du Colombier lun->inq = smprint("%.48s", (char *)lun->inquiry+8); 161906943f9SDavid du Colombier umscapacity(lun); 162906943f9SDavid du Colombier } 163906943f9SDavid du Colombier if(some == 0){ 164906943f9SDavid du Colombier devctl(ums->dev, "detach"); 165906943f9SDavid du Colombier return -1; 1660641ea09SDavid du Colombier } 1670641ea09SDavid du Colombier return 0; 1680641ea09SDavid du Colombier } 1690641ea09SDavid du Colombier 1700641ea09SDavid du Colombier 171906943f9SDavid du Colombier /* 172906943f9SDavid du Colombier * called by SR*() commands provided by scuzz's scsireq 173906943f9SDavid 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; 180906943f9SDavid du Colombier Ums *ums; 181906943f9SDavid du Colombier 182906943f9SDavid du Colombier ums = umsc->ums; 1830641ea09SDavid du Colombier 1840641ea09SDavid du Colombier memcpy(cbw.signature, "USBC", 4); 185906943f9SDavid 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; 189906943f9SDavid du Colombier if(cmd->count < 1 || cmd->count > 16) 190906943f9SDavid du Colombier print("%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count); 191906943f9SDavid du Colombier 1920641ea09SDavid du Colombier cbw.len = cmd->count; 193906943f9SDavid 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 197906943f9SDavid du Colombier werrstr(""); /* we use %r later even for n == 0 */ 198906943f9SDavid du Colombier 199906943f9SDavid du Colombier if(diskdebug){ 200906943f9SDavid 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 } 205906943f9SDavid du Colombier if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){ 206906943f9SDavid du Colombier fprint(2, "disk: cmd: %r\n"); 207906943f9SDavid du Colombier goto Fail; 2080641ea09SDavid du Colombier } 2090641ea09SDavid du Colombier if(data->count != 0){ 2100641ea09SDavid du Colombier if(data->write) 211906943f9SDavid du Colombier n = write(ums->epout->dfd, data->p, data->count); 212906943f9SDavid du Colombier else{ 213906943f9SDavid du Colombier memset(data->p, data->count, 0); 214906943f9SDavid du Colombier n = read(ums->epin->dfd, data->p, data->count); 2150641ea09SDavid du Colombier } 216906943f9SDavid du Colombier if(diskdebug) 217906943f9SDavid du Colombier if(n < 0) 218906943f9SDavid du Colombier fprint(2, "disk: data: %r\n"); 219906943f9SDavid du Colombier else 220906943f9SDavid du Colombier fprint(2, "disk: data: %d bytes\n", n); 221906943f9SDavid du Colombier if(n <= 0) 222906943f9SDavid du Colombier if(data->write == 0) 223906943f9SDavid du Colombier unstall(ums->dev, ums->epin, Ein); 2240641ea09SDavid du Colombier } 225906943f9SDavid du Colombier n = read(ums->epin->dfd, &csw, CswLen); 226906943f9SDavid du Colombier if(n <= 0){ 227906943f9SDavid du Colombier /* n == 0 means "stalled" */ 228906943f9SDavid du Colombier unstall(ums->dev, ums->epin, Ein); 229906943f9SDavid du Colombier n = read(ums->epin->dfd, &csw, CswLen); 2300641ea09SDavid du Colombier } 2310641ea09SDavid du Colombier if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){ 232906943f9SDavid du Colombier dprint(2, "disk: read n=%d: status: %r\n", n); 233906943f9SDavid du Colombier goto Fail; 2340641ea09SDavid du Colombier } 2350641ea09SDavid du Colombier if(csw.tag != cbw.tag){ 236906943f9SDavid du Colombier dprint(2, "disk: status tag mismatch\n"); 237906943f9SDavid du Colombier goto Fail; 2380641ea09SDavid du Colombier } 2390641ea09SDavid du Colombier if(csw.status >= CswPhaseErr){ 240906943f9SDavid du Colombier dprint(2, "disk: phase error\n"); 241906943f9SDavid du Colombier goto Fail; 2420641ea09SDavid du Colombier } 243906943f9SDavid 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 } 253906943f9SDavid du Colombier switch(csw.status){ 254906943f9SDavid du Colombier case CswOk: 2550641ea09SDavid du Colombier *status = STok; 256906943f9SDavid du Colombier break; 257906943f9SDavid du Colombier case CswFailed: 2580641ea09SDavid du Colombier *status = STcheck; 259906943f9SDavid du Colombier break; 260906943f9SDavid du Colombier default: 261906943f9SDavid du Colombier dprint(2, "disk: phase error\n"); 262906943f9SDavid du Colombier goto Fail; 263906943f9SDavid du Colombier } 264906943f9SDavid du Colombier ums->nerrs = 0; 2650641ea09SDavid du Colombier return data->count - csw.dataresidue; 2660641ea09SDavid du Colombier 267906943f9SDavid du Colombier Fail: 2680641ea09SDavid du Colombier *status = STharderr; 269906943f9SDavid du Colombier if(ums->nerrs++ > 15){ 270906943f9SDavid du Colombier fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir); 271906943f9SDavid du Colombier umsfatal(ums); 2720641ea09SDavid du Colombier }else 273906943f9SDavid du Colombier umsrecover(ums); 2740641ea09SDavid du Colombier return -1; 2750641ea09SDavid du Colombier } 2760641ea09SDavid du Colombier 277906943f9SDavid du Colombier static int 278906943f9SDavid du Colombier dwalk(Usbfs *fs, Fid *fid, char *name) 2790641ea09SDavid du Colombier { 280906943f9SDavid du Colombier int i; 281906943f9SDavid du Colombier Qid qid; 282906943f9SDavid du Colombier 283906943f9SDavid du Colombier qid = fid->qid; 284906943f9SDavid du Colombier if((qid.type & QTDIR) == 0){ 285906943f9SDavid du Colombier werrstr("walk in non-directory"); 286906943f9SDavid du Colombier return -1; 2870641ea09SDavid du Colombier } 2880641ea09SDavid du Colombier 2890641ea09SDavid du Colombier if(strcmp(name, "..") == 0) 290906943f9SDavid du Colombier return 0; 2910641ea09SDavid du Colombier 292906943f9SDavid du Colombier for(i = 1; i < nelem(dirtab); i++) 2930641ea09SDavid du Colombier if(strcmp(name, dirtab[i].name) == 0){ 294906943f9SDavid du Colombier qid.path = i | fs->qid; 295906943f9SDavid du Colombier qid.vers = 0; 296906943f9SDavid du Colombier qid.type = dirtab[i].mode >> 24; 297906943f9SDavid du Colombier fid->qid = qid; 298906943f9SDavid du Colombier return 0; 2990641ea09SDavid du Colombier } 300906943f9SDavid du Colombier werrstr(Enotfound); 301906943f9SDavid du Colombier return -1; 3020641ea09SDavid du Colombier } 3030641ea09SDavid du Colombier 304906943f9SDavid du Colombier static void 305906943f9SDavid 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 310906943f9SDavid du Colombier t = &dirtab[path]; 311906943f9SDavid du Colombier d->qid.path = path; 312906943f9SDavid du Colombier d->qid.type = t->mode >> 24; 313906943f9SDavid du Colombier d->mode = t->mode; 314906943f9SDavid du Colombier d->name = t->name; 315906943f9SDavid du Colombier lun = fs->aux; 316906943f9SDavid du Colombier if(path == Qdata) 317906943f9SDavid du Colombier d->length = lun->capacity; 318906943f9SDavid du Colombier else 319906943f9SDavid du Colombier d->length = 0; 320906943f9SDavid du Colombier } 321906943f9SDavid du Colombier 322906943f9SDavid du Colombier static int 323906943f9SDavid du Colombier dirgen(Usbfs *fs, Qid, int i, Dir *d, void*) 324906943f9SDavid du Colombier { 325906943f9SDavid du Colombier i++; /* skip dir */ 326906943f9SDavid du Colombier if(i >= Qmax) 327906943f9SDavid du Colombier return -1; 328906943f9SDavid du Colombier else{ 329906943f9SDavid du Colombier dostat(fs, i, d); 330906943f9SDavid du Colombier d->qid.path |= fs->qid; 331906943f9SDavid du Colombier return 0; 332906943f9SDavid du Colombier } 333906943f9SDavid du Colombier } 334906943f9SDavid du Colombier 335906943f9SDavid du Colombier static int 336906943f9SDavid du Colombier dstat(Usbfs *fs, Qid qid, Dir *d) 337906943f9SDavid du Colombier { 338906943f9SDavid du Colombier int path; 339906943f9SDavid du Colombier 340906943f9SDavid du Colombier path = qid.path & ~fs->qid; 341906943f9SDavid du Colombier dostat(fs, path, d); 342906943f9SDavid du Colombier d->qid.path |= fs->qid; 343906943f9SDavid du Colombier return 0; 344906943f9SDavid du Colombier } 345906943f9SDavid du Colombier 346906943f9SDavid du Colombier static int 347906943f9SDavid du Colombier dopen(Usbfs *fs, Fid *fid, int) 348906943f9SDavid du Colombier { 349906943f9SDavid du Colombier ulong path; 350906943f9SDavid du Colombier Umsc *lun; 351906943f9SDavid du Colombier 352906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 353906943f9SDavid du Colombier lun = fs->aux; 354906943f9SDavid du Colombier switch(path){ 355906943f9SDavid du Colombier case Qraw: 356906943f9SDavid du Colombier lun->phase = Pcmd; 3570641ea09SDavid du Colombier break; 358906943f9SDavid du Colombier } 359906943f9SDavid du Colombier return 0; 360906943f9SDavid du Colombier } 361906943f9SDavid du Colombier 362906943f9SDavid du Colombier /* 363906943f9SDavid du Colombier * Upon SRread/SRwrite errors we assume the medium may have changed, 364906943f9SDavid du Colombier * and ask again for the capacity of the media. 365906943f9SDavid du Colombier * BUG: How to proceed to avoid confussing dossrv?? 366906943f9SDavid du Colombier */ 367906943f9SDavid du Colombier static long 368906943f9SDavid du Colombier dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset) 369906943f9SDavid du Colombier { 370906943f9SDavid du Colombier long bno, nb, len, off, n; 371906943f9SDavid du Colombier ulong path; 372906943f9SDavid du Colombier char buf[1024], *p; 373906943f9SDavid du Colombier char *s; 374906943f9SDavid du Colombier char *e; 375906943f9SDavid du Colombier Umsc *lun; 376906943f9SDavid du Colombier Ums *ums; 377906943f9SDavid du Colombier Qid q; 378906943f9SDavid du Colombier 379906943f9SDavid du Colombier q = fid->qid; 380906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 381906943f9SDavid du Colombier ums = fs->dev->aux; 382906943f9SDavid du Colombier lun = fs->aux; 383906943f9SDavid du Colombier qlock(ums); 384906943f9SDavid du Colombier switch(path){ 385906943f9SDavid du Colombier case Qdir: 386906943f9SDavid du Colombier count = usbdirread(fs, q, data, count, offset, dirgen, nil); 3870641ea09SDavid du Colombier break; 3880641ea09SDavid du Colombier case Qctl: 389906943f9SDavid du Colombier e = buf + sizeof(buf); 390906943f9SDavid du Colombier s = seprint(buf, e, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]); 3910641ea09SDavid du Colombier if(lun->flags & Finqok) 392906943f9SDavid du Colombier s = seprint(s, e, "inquiry %s ", lun->inq); 3930641ea09SDavid du Colombier if(lun->blocks > 0) 394906943f9SDavid du Colombier s = seprint(s, e, "geometry %llud %ld", lun->blocks, 3950641ea09SDavid du Colombier lun->lbsize); 396906943f9SDavid du Colombier s = seprint(s, e, "\n"); 397906943f9SDavid du Colombier count = usbreadbuf(data, count, offset, buf, s - buf); 3980641ea09SDavid du Colombier break; 3990641ea09SDavid du Colombier case Qraw: 400906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 401906943f9SDavid du Colombier qunlock(ums); 402906943f9SDavid du Colombier return -1; 4030641ea09SDavid du Colombier } 4040641ea09SDavid du Colombier switch(lun->phase){ 4050641ea09SDavid du Colombier case Pcmd: 406906943f9SDavid du Colombier qunlock(ums); 407906943f9SDavid du Colombier werrstr("phase error"); 408906943f9SDavid du Colombier return -1; 4090641ea09SDavid du Colombier case Pdata: 410906943f9SDavid du Colombier lun->data.p = (uchar*)data; 411906943f9SDavid du Colombier lun->data.count = count; 4120641ea09SDavid du Colombier lun->data.write = 0; 413906943f9SDavid du Colombier count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 4140641ea09SDavid du Colombier lun->phase = Pstatus; 415906943f9SDavid du Colombier if(count < 0){ 416906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 417906943f9SDavid du Colombier qunlock(ums); 418906943f9SDavid 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); 423906943f9SDavid 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: 429906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 430906943f9SDavid du Colombier qunlock(ums); 431906943f9SDavid du Colombier return -1; 4320641ea09SDavid du Colombier } 433906943f9SDavid du Colombier bno = offset / lun->lbsize; 434906943f9SDavid 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){ 438906943f9SDavid 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; 443906943f9SDavid du Colombier p = emallocz(nb * lun->lbsize, 0); /* could use a static buffer */ 444906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 4450641ea09SDavid du Colombier n = SRread(lun, p, nb * lun->lbsize); 446906943f9SDavid du Colombier if(n < 0){ 4470641ea09SDavid du Colombier free(p); 448906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 449906943f9SDavid du Colombier qunlock(ums); 450906943f9SDavid du Colombier return -1; 4510641ea09SDavid du Colombier } 452906943f9SDavid du Colombier len = count; 453906943f9SDavid du Colombier off = offset % lun->lbsize; 454906943f9SDavid du Colombier if(off + len > n) 455906943f9SDavid du Colombier len = n - off; 456906943f9SDavid du Colombier count = len; 457906943f9SDavid du Colombier memmove(data, p + off, len); 4580641ea09SDavid du Colombier free(p); 4590641ea09SDavid du Colombier break; 4600641ea09SDavid du Colombier } 461906943f9SDavid du Colombier qunlock(ums); 462906943f9SDavid du Colombier return count; 4630641ea09SDavid du Colombier } 4640641ea09SDavid du Colombier 465906943f9SDavid du Colombier static long 466906943f9SDavid du Colombier dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset) 4670641ea09SDavid du Colombier { 468906943f9SDavid du Colombier int bno, nb, len, off; 4690641ea09SDavid du Colombier ulong path; 4700641ea09SDavid du Colombier char *p; 471906943f9SDavid du Colombier Ums *ums; 4720641ea09SDavid du Colombier Umsc *lun; 473906943f9SDavid du Colombier char *data; 4740641ea09SDavid du Colombier 475906943f9SDavid du Colombier ums = fs->dev->aux; 476906943f9SDavid du Colombier lun = fs->aux; 477906943f9SDavid du Colombier path = fid->qid.path & ~fs->qid; 478906943f9SDavid du Colombier data = buf; 479906943f9SDavid du Colombier qlock(ums); 480906943f9SDavid du Colombier switch(path){ 481906943f9SDavid du Colombier default: 482906943f9SDavid du Colombier qunlock(ums); 483906943f9SDavid du Colombier werrstr(Eperm); 484906943f9SDavid du Colombier return -1; 4850641ea09SDavid du Colombier case Qraw: 486906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 487906943f9SDavid du Colombier qunlock(ums); 488906943f9SDavid du Colombier return -1; 4890641ea09SDavid du Colombier } 4900641ea09SDavid du Colombier switch(lun->phase){ 4910641ea09SDavid du Colombier case Pcmd: 492906943f9SDavid du Colombier if(count != 6 && count != 10){ 493906943f9SDavid du Colombier qunlock(ums); 494906943f9SDavid du Colombier werrstr("bad command length"); 495906943f9SDavid du Colombier return -1; 4960641ea09SDavid du Colombier } 497906943f9SDavid du Colombier memmove(lun->rawcmd, data, count); 4980641ea09SDavid du Colombier lun->cmd.p = lun->rawcmd; 499906943f9SDavid 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: 504906943f9SDavid du Colombier lun->data.p = (uchar*)data; 505906943f9SDavid du Colombier lun->data.count = count; 5060641ea09SDavid du Colombier lun->data.write = 1; 507906943f9SDavid du Colombier count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status); 5080641ea09SDavid du Colombier lun->phase = Pstatus; 509906943f9SDavid du Colombier if(count < 0){ 510906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 511906943f9SDavid du Colombier qunlock(ums); 512906943f9SDavid du Colombier return -1; 5130641ea09SDavid du Colombier } 5140641ea09SDavid du Colombier break; 5150641ea09SDavid du Colombier case Pstatus: 5160641ea09SDavid du Colombier lun->phase = Pcmd; 517906943f9SDavid du Colombier qunlock(ums); 518906943f9SDavid du Colombier werrstr("phase error"); 519906943f9SDavid du Colombier return -1; 5200641ea09SDavid du Colombier } 5210641ea09SDavid du Colombier break; 5220641ea09SDavid du Colombier case Qdata: 523906943f9SDavid du Colombier if(lun->lbsize <= 0 && umscapacity(lun) < 0){ 524906943f9SDavid du Colombier qunlock(ums); 525906943f9SDavid du Colombier return -1; 5260641ea09SDavid du Colombier } 527906943f9SDavid du Colombier bno = offset / lun->lbsize; 528906943f9SDavid 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){ 532906943f9SDavid 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; 537906943f9SDavid du Colombier p = emallocz(nb * lun->lbsize, 0); 538906943f9SDavid du Colombier off = offset % lun->lbsize; 539906943f9SDavid du Colombier len = count; 540906943f9SDavid du Colombier if(off || (len % lun->lbsize) != 0){ 541906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 542906943f9SDavid du Colombier count = SRread(lun, p, nb * lun->lbsize); 543906943f9SDavid du Colombier if(count < 0){ 5440641ea09SDavid du Colombier free(p); 545906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 546906943f9SDavid du Colombier qunlock(ums); 547906943f9SDavid du Colombier return -1; 5480641ea09SDavid du Colombier } 549906943f9SDavid du Colombier if(off + len > count) 550906943f9SDavid du Colombier len = count - off; 5510641ea09SDavid du Colombier } 552906943f9SDavid du Colombier memmove(p+off, data, len); 553906943f9SDavid du Colombier lun->offset = offset / lun->lbsize; 554906943f9SDavid du Colombier count = SRwrite(lun, p, nb * lun->lbsize); 555906943f9SDavid du Colombier if(count < 0){ 5560641ea09SDavid du Colombier free(p); 557906943f9SDavid du Colombier lun->lbsize = 0; /* medium may have changed */ 558906943f9SDavid du Colombier qunlock(ums); 559906943f9SDavid du Colombier return -1; 5600641ea09SDavid du Colombier } 561906943f9SDavid du Colombier if(off+len > count) 562906943f9SDavid du Colombier len = count - off; 563906943f9SDavid du Colombier count = len; 5640641ea09SDavid du Colombier free(p); 5650641ea09SDavid du Colombier break; 5660641ea09SDavid du Colombier } 567906943f9SDavid du Colombier qunlock(ums); 568906943f9SDavid du Colombier return count; 5690641ea09SDavid du Colombier } 5700641ea09SDavid du Colombier 571906943f9SDavid du Colombier int 572906943f9SDavid du Colombier findendpoints(Ums *ums) 573906943f9SDavid du Colombier { 574906943f9SDavid du Colombier Ep *ep; 575906943f9SDavid du Colombier Usbdev *ud; 576906943f9SDavid du Colombier ulong csp; 577906943f9SDavid du Colombier ulong sc; 578906943f9SDavid du Colombier int i; 579906943f9SDavid du Colombier int epin, epout; 5800641ea09SDavid du Colombier 581906943f9SDavid du Colombier epin = epout = -1; 582906943f9SDavid du Colombier ud = ums->dev->usb; 583906943f9SDavid du Colombier for(i = 0; i < nelem(ud->ep); i++){ 584906943f9SDavid du Colombier if((ep = ud->ep[i]) == nil) 585906943f9SDavid du Colombier continue; 586906943f9SDavid du Colombier csp = ep->iface->csp; 587906943f9SDavid du Colombier sc = Subclass(csp); 588906943f9SDavid du Colombier if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk))) 589906943f9SDavid du Colombier continue; 590906943f9SDavid du Colombier if(sc != Subatapi && sc != Sub8070 && sc != Subscsi) 591906943f9SDavid du Colombier fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc); 592906943f9SDavid du Colombier if(ep->type == Ebulk){ 593906943f9SDavid du Colombier if(ep->dir == Eboth || ep->dir == Ein) 594906943f9SDavid du Colombier if(epin == -1) 595906943f9SDavid du Colombier epin = ep->id; 596906943f9SDavid du Colombier if(ep->dir == Eboth || ep->dir == Eout) 597906943f9SDavid du Colombier if(epout == -1) 598906943f9SDavid du Colombier epout = ep->id; 599906943f9SDavid du Colombier } 600906943f9SDavid du Colombier } 601906943f9SDavid du Colombier dprint(2, "disk: ep ids: in %d out %d\n", epin, epout); 602906943f9SDavid du Colombier if(epin == -1 || epout == -1) 603906943f9SDavid du Colombier return -1; 604906943f9SDavid du Colombier ums->epin = openep(ums->dev, epin); 605906943f9SDavid du Colombier if(ums->epin == nil){ 606906943f9SDavid du Colombier fprint(2, "disk: openep %d: %r\n", epin); 607906943f9SDavid du Colombier return -1; 608906943f9SDavid du Colombier } 609906943f9SDavid du Colombier if(epout == epin){ 610906943f9SDavid du Colombier incref(ums->epin); 611906943f9SDavid du Colombier ums->epout = ums->epin; 612906943f9SDavid du Colombier }else 613906943f9SDavid du Colombier ums->epout = openep(ums->dev, epout); 614906943f9SDavid du Colombier if(ums->epout == nil){ 615906943f9SDavid du Colombier fprint(2, "disk: openep %d: %r\n", epout); 616906943f9SDavid du Colombier closedev(ums->epin); 617906943f9SDavid du Colombier return -1; 618906943f9SDavid du Colombier } 619906943f9SDavid du Colombier if(ums->epin == ums->epout) 620906943f9SDavid du Colombier opendevdata(ums->epin, ORDWR); 621906943f9SDavid du Colombier else{ 622906943f9SDavid du Colombier opendevdata(ums->epin, OREAD); 623906943f9SDavid du Colombier opendevdata(ums->epout, OWRITE); 624906943f9SDavid du Colombier } 625906943f9SDavid du Colombier if(ums->epin->dfd < 0 || ums->epout->dfd < 0){ 626906943f9SDavid du Colombier fprint(2, "disk: open i/o ep data: %r\n"); 627906943f9SDavid du Colombier closedev(ums->epin); 628906943f9SDavid du Colombier closedev(ums->epout); 629906943f9SDavid du Colombier return -1; 630906943f9SDavid du Colombier } 631906943f9SDavid du Colombier dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir); 632*d37e33ffSDavid du Colombier 633*d37e33ffSDavid du Colombier devctl(ums->epin, "timeout 2000"); 634*d37e33ffSDavid du Colombier devctl(ums->epout, "timeout 2000"); 6350641ea09SDavid du Colombier 636906943f9SDavid du Colombier if(usbdebug > 1 || diskdebug > 2){ 637906943f9SDavid du Colombier devctl(ums->epin, "debug 1"); 638906943f9SDavid du Colombier devctl(ums->epout, "debug 1"); 639906943f9SDavid du Colombier devctl(ums->dev, "debug 1"); 640906943f9SDavid du Colombier } 641906943f9SDavid du Colombier return 0; 642906943f9SDavid du Colombier } 643906943f9SDavid du Colombier 644906943f9SDavid du Colombier static int 6450641ea09SDavid du Colombier usage(void) 6460641ea09SDavid du Colombier { 647906943f9SDavid du Colombier werrstr("usage: usb/disk [-d]"); 648906943f9SDavid du Colombier return -1; 6490641ea09SDavid du Colombier } 6500641ea09SDavid du Colombier 651906943f9SDavid du Colombier static void 652906943f9SDavid du Colombier umsdevfree(void *a) 6530641ea09SDavid du Colombier { 654906943f9SDavid du Colombier Ums *ums = a; 6550641ea09SDavid du Colombier 656906943f9SDavid du Colombier if(ums == nil) 657906943f9SDavid du Colombier return; 658906943f9SDavid du Colombier closedev(ums->epin); 659906943f9SDavid du Colombier closedev(ums->epout); 660906943f9SDavid du Colombier ums->epin = ums->epout = nil; 661906943f9SDavid du Colombier free(ums->lun); 662906943f9SDavid du Colombier free(ums); 663906943f9SDavid du Colombier } 664906943f9SDavid du Colombier 665906943f9SDavid du Colombier static Usbfs diskfs = { 666906943f9SDavid du Colombier .walk = dwalk, 667906943f9SDavid du Colombier .open = dopen, 668906943f9SDavid du Colombier .read = dread, 669906943f9SDavid du Colombier .write = dwrite, 670906943f9SDavid du Colombier .stat = dstat, 671906943f9SDavid du Colombier }; 672906943f9SDavid du Colombier 673906943f9SDavid du Colombier int 674906943f9SDavid du Colombier diskmain(Dev *dev, int argc, char **argv) 675906943f9SDavid du Colombier { 676906943f9SDavid du Colombier Ums *ums; 677906943f9SDavid du Colombier Umsc *lun; 678906943f9SDavid du Colombier int i; 6790641ea09SDavid du Colombier 6800641ea09SDavid du Colombier ARGBEGIN{ 6810641ea09SDavid du Colombier case 'd': 682906943f9SDavid du Colombier scsidebug(diskdebug); 683906943f9SDavid du Colombier diskdebug++; 6840641ea09SDavid du Colombier break; 6850641ea09SDavid du Colombier default: 686906943f9SDavid du Colombier return usage(); 6870641ea09SDavid du Colombier }ARGEND 688906943f9SDavid du Colombier if(argc != 0) 689906943f9SDavid du Colombier return usage(); 6900641ea09SDavid du Colombier 691906943f9SDavid du Colombier ums = dev->aux = emallocz(sizeof(Ums), 1); 692906943f9SDavid du Colombier ums->maxlun = -1; 693906943f9SDavid du Colombier ums->dev = dev; 694906943f9SDavid du Colombier dev->free = umsdevfree; 695906943f9SDavid du Colombier if(findendpoints(ums) < 0){ 696906943f9SDavid du Colombier werrstr("disk: endpoints not found"); 697906943f9SDavid du Colombier return -1; 6980641ea09SDavid du Colombier } 699906943f9SDavid du Colombier if(umsinit(ums) < 0){ 700906943f9SDavid du Colombier dprint(2, "disk: umsinit: %r\n"); 701906943f9SDavid du Colombier return -1; 702906943f9SDavid du Colombier } 7030641ea09SDavid du Colombier 704906943f9SDavid du Colombier for(i = 0; i <= ums->maxlun; i++){ 705906943f9SDavid du Colombier lun = &ums->lun[i]; 706906943f9SDavid du Colombier lun->fs = diskfs; 707906943f9SDavid du Colombier snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", dev->id, i); 708906943f9SDavid du Colombier lun->fs.dev = dev; 709906943f9SDavid du Colombier incref(dev); 710906943f9SDavid du Colombier lun->fs.aux = lun; 711906943f9SDavid du Colombier usbfsadd(&lun->fs); 712906943f9SDavid du Colombier } 713906943f9SDavid du Colombier closedev(dev); 714906943f9SDavid du Colombier return 0; 7150641ea09SDavid du Colombier } 716