disk.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /*
  2. * usb/disk - usb mass storage file server
  3. * BUG: supports only the scsi command interface.
  4. * BUG: This should use /dev/sdfile to
  5. * use the kernel ether device code.
  6. */
  7. #include <u.h>
  8. #include <libc.h>
  9. #include <ctype.h>
  10. #include <fcall.h>
  11. #include <thread.h>
  12. #include "scsireq.h"
  13. #include "usb.h"
  14. #include "usbfs.h"
  15. #include "ums.h"
  16. enum
  17. {
  18. Qdir = 0,
  19. Qctl,
  20. Qraw,
  21. Qdata,
  22. Qmax,
  23. };
  24. typedef struct Dirtab Dirtab;
  25. struct Dirtab
  26. {
  27. char *name;
  28. int mode;
  29. };
  30. static Dirtab dirtab[] =
  31. {
  32. [Qdir] "/", DMDIR|0555,
  33. [Qctl] "ctl", 0444,
  34. [Qraw] "raw", 0640,
  35. [Qdata] "data", 0640,
  36. };
  37. /*
  38. * These are used by scuzz scsireq
  39. */
  40. int exabyte, force6bytecmds;
  41. long maxiosize = MaxIOsize;
  42. static int diskdebug;
  43. static int
  44. getmaxlun(Dev *dev)
  45. {
  46. uchar max;
  47. int r;
  48. max = 0;
  49. r = Rd2h|Rclass|Riface;
  50. if(usbcmd(dev, r, Getmaxlun, 0, 0, &max, 1) < 0){
  51. dprint(2, "disk: %s: getmaxlun failed: %r\n", dev->dir);
  52. }else
  53. dprint(2, "disk: %s: maxlun %d\n", dev->dir, max);
  54. return max;
  55. }
  56. static int
  57. umsreset(Ums *ums)
  58. {
  59. int r;
  60. r = Rh2d|Rclass|Riface;
  61. if(usbcmd(ums->dev, r, Umsreset, 0, 0, nil, 0) < 0){
  62. fprint(2, "disk: reset: %r\n");
  63. return -1;
  64. }
  65. return 0;
  66. }
  67. static int
  68. umsrecover(Ums *ums)
  69. {
  70. if(umsreset(ums) < 0)
  71. return -1;
  72. if(unstall(ums->dev, ums->epin, Ein) < 0)
  73. dprint(2, "disk: unstall epin: %r\n");
  74. /* do we need this when epin == epout? */
  75. if(unstall(ums->dev, ums->epout, Eout) < 0)
  76. dprint(2, "disk: unstall epout: %r\n");
  77. return 0;
  78. }
  79. static void
  80. umsfatal(Ums *ums)
  81. {
  82. int i;
  83. devctl(ums->dev, "detach");
  84. for(i = 0; i < ums->maxlun; i++)
  85. usbfsdel(&ums->lun[i].fs);
  86. }
  87. static int
  88. umscapacity(Umsc *lun)
  89. {
  90. uchar data[32];
  91. lun->blocks = 0;
  92. lun->capacity = 0;
  93. lun->lbsize = 0;
  94. if(SRrcapacity(lun, data) < 0 && SRrcapacity(lun, data) < 0)
  95. return -1;
  96. lun->blocks = GETBELONG(data);
  97. lun->lbsize = GETBELONG(data+4);
  98. if(lun->blocks == 0xFFFFFFFF){
  99. if(SRrcapacity16(lun, data) < 0){
  100. lun->lbsize = 0;
  101. lun->blocks = 0;
  102. return -1;
  103. }else{
  104. lun->lbsize = GETBELONG(data + 8);
  105. lun->blocks = (uvlong)GETBELONG(data)<<32 | GETBELONG(data + 4);
  106. }
  107. }
  108. lun->blocks++; /* SRcapacity returns LBA of last block */
  109. lun->capacity = (vlong)lun->blocks * lun->lbsize;
  110. return 0;
  111. }
  112. static int
  113. umsinit(Ums *ums)
  114. {
  115. uchar i;
  116. Umsc *lun;
  117. int some;
  118. umsreset(ums);
  119. ums->maxlun = getmaxlun(ums->dev);
  120. ums->lun = mallocz((ums->maxlun+1) * sizeof(*ums->lun), 1);
  121. some = 0;
  122. for(i = 0; i <= ums->maxlun; i++){
  123. lun = &ums->lun[i];
  124. lun->ums = ums;
  125. lun->umsc = lun;
  126. lun->lun = i;
  127. lun->flags = Fopen | Fusb | Frw10;
  128. if(SRinquiry(lun) < 0 && SRinquiry(lun) < 0)
  129. continue;
  130. if(lun->inquiry[0] != 0x00){
  131. /* not a disk */
  132. fprint(2, "%s: lun %d is not a disk (type %#02x)\n",
  133. argv0, i, lun->inquiry[0]);
  134. continue;
  135. }
  136. SRstart(lun, 1);
  137. /*
  138. * we ignore the device type reported by inquiry.
  139. * Some devices return a wrong value but would still work.
  140. */
  141. some++;
  142. lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
  143. umscapacity(lun);
  144. }
  145. if(some == 0){
  146. devctl(ums->dev, "detach");
  147. return -1;
  148. }
  149. return 0;
  150. }
  151. /*
  152. * called by SR*() commands provided by scuzz's scsireq
  153. */
  154. long
  155. umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
  156. {
  157. Cbw cbw;
  158. Csw csw;
  159. int n;
  160. Ums *ums;
  161. ums = umsc->ums;
  162. memcpy(cbw.signature, "USBC", 4);
  163. cbw.tag = ++ums->seq;
  164. cbw.datalen = data->count;
  165. cbw.flags = data->write? CbwDataOut: CbwDataIn;
  166. cbw.lun = umsc->lun;
  167. if(cmd->count < 1 || cmd->count > 16)
  168. print("%s: umsrequest: bad cmd count: %ld\n", argv0, cmd->count);
  169. cbw.len = cmd->count;
  170. assert(cmd->count <= sizeof(cbw.command));
  171. memcpy(cbw.command, cmd->p, cmd->count);
  172. memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
  173. werrstr(""); /* we use %r later even for n == 0 */
  174. if(diskdebug){
  175. fprint(2, "disk: cmd: tag %#lx: ", cbw.tag);
  176. for(n = 0; n < cbw.len; n++)
  177. fprint(2, " %2.2x", cbw.command[n]&0xFF);
  178. fprint(2, " datalen: %ld\n", cbw.datalen);
  179. }
  180. if(write(ums->epout->dfd, &cbw, CbwLen) != CbwLen){
  181. fprint(2, "disk: cmd: %r\n");
  182. goto Fail;
  183. }
  184. if(data->count != 0){
  185. if(data->write)
  186. n = write(ums->epout->dfd, data->p, data->count);
  187. else{
  188. memset(data->p, data->count, 0);
  189. n = read(ums->epin->dfd, data->p, data->count);
  190. }
  191. if(diskdebug)
  192. if(n < 0)
  193. fprint(2, "disk: data: %r\n");
  194. else
  195. fprint(2, "disk: data: %d bytes\n", n);
  196. if(n <= 0)
  197. if(data->write == 0)
  198. unstall(ums->dev, ums->epin, Ein);
  199. }
  200. n = read(ums->epin->dfd, &csw, CswLen);
  201. if(n <= 0){
  202. /* n == 0 means "stalled" */
  203. unstall(ums->dev, ums->epin, Ein);
  204. n = read(ums->epin->dfd, &csw, CswLen);
  205. }
  206. if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
  207. dprint(2, "disk: read n=%d: status: %r\n", n);
  208. goto Fail;
  209. }
  210. if(csw.tag != cbw.tag){
  211. dprint(2, "disk: status tag mismatch\n");
  212. goto Fail;
  213. }
  214. if(csw.status >= CswPhaseErr){
  215. dprint(2, "disk: phase error\n");
  216. goto Fail;
  217. }
  218. if(diskdebug){
  219. fprint(2, "status: %2.2ux residue: %ld\n",
  220. csw.status, csw.dataresidue);
  221. if(cbw.command[0] == ScmdRsense){
  222. fprint(2, "sense data:");
  223. for(n = 0; n < data->count - csw.dataresidue; n++)
  224. fprint(2, " %2.2x", data->p[n]);
  225. fprint(2, "\n");
  226. }
  227. }
  228. switch(csw.status){
  229. case CswOk:
  230. *status = STok;
  231. break;
  232. case CswFailed:
  233. *status = STcheck;
  234. break;
  235. default:
  236. dprint(2, "disk: phase error\n");
  237. goto Fail;
  238. }
  239. ums->nerrs = 0;
  240. return data->count - csw.dataresidue;
  241. Fail:
  242. *status = STharderr;
  243. if(ums->nerrs++ > 15){
  244. fprint(2, "disk: %s: too many errors: device detached\n", ums->dev->dir);
  245. umsfatal(ums);
  246. }else
  247. umsrecover(ums);
  248. return -1;
  249. }
  250. static int
  251. dwalk(Usbfs *fs, Fid *fid, char *name)
  252. {
  253. int i;
  254. Qid qid;
  255. qid = fid->qid;
  256. if((qid.type & QTDIR) == 0){
  257. werrstr("walk in non-directory");
  258. return -1;
  259. }
  260. if(strcmp(name, "..") == 0)
  261. return 0;
  262. for(i = 1; i < nelem(dirtab); i++)
  263. if(strcmp(name, dirtab[i].name) == 0){
  264. qid.path = i | fs->qid;
  265. qid.vers = 0;
  266. qid.type = dirtab[i].mode >> 24;
  267. fid->qid = qid;
  268. return 0;
  269. }
  270. werrstr(Enotfound);
  271. return -1;
  272. }
  273. static void
  274. dostat(Usbfs *fs, int path, Dir *d)
  275. {
  276. Dirtab *t;
  277. Umsc *lun;
  278. t = &dirtab[path];
  279. d->qid.path = path;
  280. d->qid.type = t->mode >> 24;
  281. d->mode = t->mode;
  282. d->name = t->name;
  283. lun = fs->aux;
  284. if(path == Qdata)
  285. d->length = lun->capacity;
  286. else
  287. d->length = 0;
  288. }
  289. static int
  290. dirgen(Usbfs *fs, Qid, int i, Dir *d, void*)
  291. {
  292. i++; /* skip dir */
  293. if(i >= Qmax)
  294. return -1;
  295. else{
  296. dostat(fs, i, d);
  297. d->qid.path |= fs->qid;
  298. return 0;
  299. }
  300. }
  301. static int
  302. dstat(Usbfs *fs, Qid qid, Dir *d)
  303. {
  304. int path;
  305. path = qid.path & ~fs->qid;
  306. dostat(fs, path, d);
  307. d->qid.path |= fs->qid;
  308. return 0;
  309. }
  310. static int
  311. dopen(Usbfs *fs, Fid *fid, int)
  312. {
  313. ulong path;
  314. Umsc *lun;
  315. path = fid->qid.path & ~fs->qid;
  316. lun = fs->aux;
  317. switch(path){
  318. case Qraw:
  319. lun->phase = Pcmd;
  320. break;
  321. }
  322. return 0;
  323. }
  324. /*
  325. * Upon SRread/SRwrite errors we assume the medium may have changed,
  326. * and ask again for the capacity of the media.
  327. * BUG: How to proceed to avoid confussing dossrv??
  328. */
  329. static long
  330. dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
  331. {
  332. long bno, nb, len, off, n;
  333. ulong path;
  334. char buf[1024], *p;
  335. char *s;
  336. char *e;
  337. Umsc *lun;
  338. Ums *ums;
  339. Qid q;
  340. q = fid->qid;
  341. path = fid->qid.path & ~fs->qid;
  342. ums = fs->dev->aux;
  343. lun = fs->aux;
  344. qlock(ums);
  345. switch(path){
  346. case Qdir:
  347. count = usbdirread(fs, q, data, count, offset, dirgen, nil);
  348. break;
  349. case Qctl:
  350. e = buf + sizeof(buf);
  351. s = seprint(buf, e, "%s lun %ld: ", fs->dev->dir, lun - &ums->lun[0]);
  352. if(lun->flags & Finqok)
  353. s = seprint(s, e, "inquiry %s ", lun->inq);
  354. if(lun->blocks > 0)
  355. s = seprint(s, e, "geometry %llud %ld", lun->blocks,
  356. lun->lbsize);
  357. s = seprint(s, e, "\n");
  358. count = usbreadbuf(data, count, offset, buf, s - buf);
  359. break;
  360. case Qraw:
  361. if(lun->lbsize <= 0 && umscapacity(lun) < 0){
  362. qunlock(ums);
  363. return -1;
  364. }
  365. switch(lun->phase){
  366. case Pcmd:
  367. qunlock(ums);
  368. werrstr("phase error");
  369. return -1;
  370. case Pdata:
  371. lun->data.p = (uchar*)data;
  372. lun->data.count = count;
  373. lun->data.write = 0;
  374. count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
  375. lun->phase = Pstatus;
  376. if(count < 0){
  377. lun->lbsize = 0; /* medium may have changed */
  378. qunlock(ums);
  379. return -1;
  380. }
  381. break;
  382. case Pstatus:
  383. n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
  384. count = usbreadbuf(data, count, 0LL, buf, n);
  385. lun->phase = Pcmd;
  386. break;
  387. }
  388. break;
  389. case Qdata:
  390. if(lun->lbsize <= 0 && umscapacity(lun) < 0){
  391. qunlock(ums);
  392. return -1;
  393. }
  394. bno = offset / lun->lbsize;
  395. nb = (offset + count + lun->lbsize - 1) / lun->lbsize - bno;
  396. if(bno + nb > lun->blocks)
  397. nb = lun->blocks - bno;
  398. if(bno >= lun->blocks || nb == 0){
  399. count = 0;
  400. break;
  401. }
  402. if(nb * lun->lbsize > maxiosize)
  403. nb = maxiosize / lun->lbsize;
  404. p = emallocz(nb * lun->lbsize, 0); /* could use a static buffer */
  405. lun->offset = offset / lun->lbsize;
  406. n = SRread(lun, p, nb * lun->lbsize);
  407. if(n < 0){
  408. free(p);
  409. lun->lbsize = 0; /* medium may have changed */
  410. qunlock(ums);
  411. return -1;
  412. }
  413. len = count;
  414. off = offset % lun->lbsize;
  415. if(off + len > n)
  416. len = n - off;
  417. count = len;
  418. memmove(data, p + off, len);
  419. free(p);
  420. break;
  421. }
  422. qunlock(ums);
  423. return count;
  424. }
  425. static long
  426. dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong offset)
  427. {
  428. int bno, nb, len, off;
  429. ulong path;
  430. char *p;
  431. Ums *ums;
  432. Umsc *lun;
  433. char *data;
  434. ums = fs->dev->aux;
  435. lun = fs->aux;
  436. path = fid->qid.path & ~fs->qid;
  437. data = buf;
  438. qlock(ums);
  439. switch(path){
  440. default:
  441. qunlock(ums);
  442. werrstr(Eperm);
  443. return -1;
  444. case Qraw:
  445. if(lun->lbsize <= 0 && umscapacity(lun) < 0){
  446. qunlock(ums);
  447. return -1;
  448. }
  449. switch(lun->phase){
  450. case Pcmd:
  451. if(count != 6 && count != 10){
  452. qunlock(ums);
  453. werrstr("bad command length");
  454. return -1;
  455. }
  456. memmove(lun->rawcmd, data, count);
  457. lun->cmd.p = lun->rawcmd;
  458. lun->cmd.count = count;
  459. lun->cmd.write = 1;
  460. lun->phase = Pdata;
  461. break;
  462. case Pdata:
  463. lun->data.p = (uchar*)data;
  464. lun->data.count = count;
  465. lun->data.write = 1;
  466. count = umsrequest(lun,&lun->cmd,&lun->data,&lun->status);
  467. lun->phase = Pstatus;
  468. if(count < 0){
  469. lun->lbsize = 0; /* medium may have changed */
  470. qunlock(ums);
  471. return -1;
  472. }
  473. break;
  474. case Pstatus:
  475. lun->phase = Pcmd;
  476. qunlock(ums);
  477. werrstr("phase error");
  478. return -1;
  479. }
  480. break;
  481. case Qdata:
  482. if(lun->lbsize <= 0 && umscapacity(lun) < 0){
  483. qunlock(ums);
  484. return -1;
  485. }
  486. bno = offset / lun->lbsize;
  487. nb = (offset + count + lun->lbsize-1) / lun->lbsize - bno;
  488. if(bno + nb > lun->blocks)
  489. nb = lun->blocks - bno;
  490. if(bno >= lun->blocks || nb == 0){
  491. count = 0;
  492. break;
  493. }
  494. if(nb * lun->lbsize > maxiosize)
  495. nb = maxiosize / lun->lbsize;
  496. p = emallocz(nb * lun->lbsize, 0);
  497. off = offset % lun->lbsize;
  498. len = count;
  499. if(off || (len % lun->lbsize) != 0){
  500. lun->offset = offset / lun->lbsize;
  501. count = SRread(lun, p, nb * lun->lbsize);
  502. if(count < 0){
  503. free(p);
  504. lun->lbsize = 0; /* medium may have changed */
  505. qunlock(ums);
  506. return -1;
  507. }
  508. if(off + len > count)
  509. len = count - off;
  510. }
  511. memmove(p+off, data, len);
  512. lun->offset = offset / lun->lbsize;
  513. count = SRwrite(lun, p, nb * lun->lbsize);
  514. if(count < 0){
  515. free(p);
  516. lun->lbsize = 0; /* medium may have changed */
  517. qunlock(ums);
  518. return -1;
  519. }
  520. if(off+len > count)
  521. len = count - off;
  522. count = len;
  523. free(p);
  524. break;
  525. }
  526. qunlock(ums);
  527. return count;
  528. }
  529. int
  530. findendpoints(Ums *ums)
  531. {
  532. Ep *ep;
  533. Usbdev *ud;
  534. ulong csp;
  535. ulong sc;
  536. int i;
  537. int epin, epout;
  538. epin = epout = -1;
  539. ud = ums->dev->usb;
  540. for(i = 0; i < nelem(ud->ep); i++){
  541. if((ep = ud->ep[i]) == nil)
  542. continue;
  543. csp = ep->iface->csp;
  544. sc = Subclass(csp);
  545. if(!(Class(csp) == Clstorage && (Proto(csp) == Protobulk)))
  546. continue;
  547. if(sc != Subatapi && sc != Sub8070 && sc != Subscsi)
  548. fprint(2, "disk: subclass %#ulx not supported. trying anyway\n", sc);
  549. if(ep->type == Ebulk){
  550. if(ep->dir == Eboth || ep->dir == Ein)
  551. if(epin == -1)
  552. epin = ep->id;
  553. if(ep->dir == Eboth || ep->dir == Eout)
  554. if(epout == -1)
  555. epout = ep->id;
  556. }
  557. }
  558. dprint(2, "disk: ep ids: in %d out %d\n", epin, epout);
  559. if(epin == -1 || epout == -1)
  560. return -1;
  561. ums->epin = openep(ums->dev, epin);
  562. if(ums->epin == nil){
  563. fprint(2, "disk: openep %d: %r\n", epin);
  564. return -1;
  565. }
  566. if(epout == epin){
  567. incref(ums->epin);
  568. ums->epout = ums->epin;
  569. }else
  570. ums->epout = openep(ums->dev, epout);
  571. if(ums->epout == nil){
  572. fprint(2, "disk: openep %d: %r\n", epout);
  573. closedev(ums->epin);
  574. return -1;
  575. }
  576. if(ums->epin == ums->epout)
  577. opendevdata(ums->epin, ORDWR);
  578. else{
  579. opendevdata(ums->epin, OREAD);
  580. opendevdata(ums->epout, OWRITE);
  581. }
  582. if(ums->epin->dfd < 0 || ums->epout->dfd < 0){
  583. fprint(2, "disk: open i/o ep data: %r\n");
  584. closedev(ums->epin);
  585. closedev(ums->epout);
  586. return -1;
  587. }
  588. dprint(2, "disk: ep in %s out %s\n", ums->epin->dir, ums->epout->dir);
  589. if(usbdebug > 1 || diskdebug > 2){
  590. devctl(ums->epin, "debug 1");
  591. devctl(ums->epout, "debug 1");
  592. devctl(ums->dev, "debug 1");
  593. }
  594. return 0;
  595. }
  596. static int
  597. usage(void)
  598. {
  599. werrstr("usage: usb/disk [-d]");
  600. return -1;
  601. }
  602. static void
  603. umsdevfree(void *a)
  604. {
  605. Ums *ums = a;
  606. if(ums == nil)
  607. return;
  608. closedev(ums->epin);
  609. closedev(ums->epout);
  610. ums->epin = ums->epout = nil;
  611. free(ums->lun);
  612. free(ums);
  613. }
  614. static Usbfs diskfs = {
  615. .walk = dwalk,
  616. .open = dopen,
  617. .read = dread,
  618. .write = dwrite,
  619. .stat = dstat,
  620. };
  621. int
  622. diskmain(Dev *dev, int argc, char **argv)
  623. {
  624. Ums *ums;
  625. Umsc *lun;
  626. int i;
  627. ARGBEGIN{
  628. case 'd':
  629. scsidebug(diskdebug);
  630. diskdebug++;
  631. break;
  632. default:
  633. return usage();
  634. }ARGEND
  635. if(argc != 0)
  636. return usage();
  637. ums = dev->aux = emallocz(sizeof(Ums), 1);
  638. ums->maxlun = -1;
  639. ums->dev = dev;
  640. dev->free = umsdevfree;
  641. if(findendpoints(ums) < 0){
  642. werrstr("disk: endpoints not found");
  643. return -1;
  644. }
  645. if(umsinit(ums) < 0){
  646. dprint(2, "disk: umsinit: %r\n");
  647. return -1;
  648. }
  649. for(i = 0; i <= ums->maxlun; i++){
  650. lun = &ums->lun[i];
  651. lun->fs = diskfs;
  652. snprint(lun->fs.name, sizeof(lun->fs.name), "sdU%d.%d", dev->id, i);
  653. lun->fs.dev = dev;
  654. incref(dev);
  655. lun->fs.aux = lun;
  656. usbfsadd(&lun->fs);
  657. }
  658. closedev(dev);
  659. return 0;
  660. }