devsd.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. /*
  2. * Storage Device.
  3. */
  4. #include "all.h"
  5. #include "io.h"
  6. #include "mem.h"
  7. #include "sd.h"
  8. #include "fs.h"
  9. #include "compat.h"
  10. #undef error
  11. #define parttrace 0
  12. extern SDifc sdataifc; // , sdmv50xxifc;
  13. SDifc* sdifc[] = {
  14. &sdataifc,
  15. // &sdmv50xxifc,
  16. nil,
  17. };
  18. static SDev* sdlist;
  19. static SDunit** sdunit;
  20. static int sdnunit;
  21. static int _sdmask;
  22. static int cdmask;
  23. static int sdmask;
  24. static QLock sdqlock;
  25. enum {
  26. Rawcmd,
  27. Rawdata,
  28. Rawstatus,
  29. };
  30. void
  31. sdaddpart(SDunit* unit, char* name, Devsize start, Devsize end)
  32. {
  33. SDpart *pp;
  34. int i, partno;
  35. if(parttrace)
  36. print("add %d %s %s %lld %lld\n", unit->npart, unit->name,
  37. name, start, end);
  38. /*
  39. * Check name not already used
  40. * and look for a free slot.
  41. */
  42. if(unit->part != nil){
  43. partno = -1;
  44. for(i = 0; i < SDnpart; i++){
  45. pp = &unit->part[i];
  46. if(!pp->valid){
  47. if(partno == -1)
  48. partno = i;
  49. break;
  50. }
  51. if(strcmp(name, pp->name) == 0){
  52. if(pp->start == start && pp->end == end){
  53. if(parttrace)
  54. print("already present\n");
  55. return;
  56. }
  57. }
  58. }
  59. }else{
  60. if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){
  61. if(parttrace)
  62. print("malloc failed\n");
  63. return;
  64. }
  65. partno = 0;
  66. }
  67. /*
  68. * Check there is a free slot and size and extent are valid.
  69. */
  70. if(partno == -1 || start > end || end > unit->sectors){
  71. print("cannot add %s!%s [%llud,%llud) to disk [0,%llud): %s\n",
  72. unit->name, name, start, end, unit->sectors,
  73. partno==-1 ? "no free partitions" :
  74. "partition boundaries out of range");
  75. return;
  76. }
  77. pp = &unit->part[partno];
  78. pp->start = start;
  79. pp->end = end;
  80. strncpy(pp->name, name, NAMELEN);
  81. pp->valid = 1;
  82. unit->npart++;
  83. }
  84. void
  85. sddelpart(SDunit* unit, char* name)
  86. {
  87. int i;
  88. SDpart *pp;
  89. if(parttrace)
  90. print("del %d %s %s\n", unit->npart, unit->name, name);
  91. /*
  92. * Look for the partition to delete.
  93. * Can't delete if someone still has it open.
  94. * If it's the last valid partition zap the
  95. * whole table.
  96. */
  97. pp = unit->part;
  98. for(i = 0; i < SDnpart; i++){
  99. if(strncmp(name, pp->name, NAMELEN) == 0)
  100. break;
  101. pp++;
  102. }
  103. if(i >= SDnpart)
  104. return;
  105. pp->valid = 0;
  106. unit->npart--;
  107. if(unit->npart == 0){
  108. free(unit->part);
  109. unit->part = nil;
  110. }
  111. }
  112. static int
  113. sdinitpart(SDunit* unit)
  114. {
  115. unit->sectors = unit->secsize = 0;
  116. unit->npart = 0;
  117. if(unit->part){
  118. free(unit->part);
  119. unit->part = nil;
  120. }
  121. if(unit->inquiry[0] & 0xC0)
  122. return 0;
  123. switch(unit->inquiry[0] & 0x1F){
  124. case 0x00: /* DA */
  125. case 0x04: /* WORM */
  126. case 0x05: /* CD-ROM */
  127. case 0x07: /* MO */
  128. break;
  129. default:
  130. return 0;
  131. }
  132. if(unit->dev->ifc->online == nil || unit->dev->ifc->online(unit) == 0)
  133. return 0;
  134. sdaddpart(unit, "data", 0, unit->sectors);
  135. return 1;
  136. }
  137. SDunit*
  138. sdgetunit(SDev* sdev, int subno)
  139. {
  140. int index;
  141. SDunit *unit;
  142. /*
  143. * Associate a unit with a given device and sub-unit
  144. * number on that device.
  145. * The device will be probed if it has not already been
  146. * successfully accessed.
  147. */
  148. qlock(&sdqlock);
  149. index = sdev->index+subno;
  150. unit = sdunit[index];
  151. if(unit == nil){
  152. if((unit = malloc(sizeof(SDunit))) == nil){
  153. qunlock(&sdqlock);
  154. return nil;
  155. }
  156. if(sdev->enabled == 0 && sdev->ifc->enable)
  157. sdev->ifc->enable(sdev);
  158. sdev->enabled = 1;
  159. snprint(unit->name, NAMELEN, "sd%c%d", sdev->idno, subno);
  160. unit->subno = subno;
  161. unit->dev = sdev;
  162. /*
  163. * No need to lock anything here as this is only
  164. * called before the unit is made available in the
  165. * sdunit[] array.
  166. */
  167. if(unit->dev->ifc->verify(unit) == 0){
  168. qunlock(&sdqlock);
  169. free(unit);
  170. return nil;
  171. }
  172. sdunit[index] = unit;
  173. }
  174. qunlock(&sdqlock);
  175. return unit;
  176. }
  177. SDunit*
  178. sdindex2unit(int index)
  179. {
  180. SDev *sdev;
  181. /*
  182. * Associate a unit with a given index into the top-level
  183. * device directory.
  184. * The device will be probed if it has not already been
  185. * successfully accessed.
  186. */
  187. for(sdev = sdlist; sdev != nil; sdev = sdev->next)
  188. if(index >= sdev->index && index < sdev->index+sdev->nunit)
  189. return sdgetunit(sdev, index-sdev->index);
  190. return nil;
  191. }
  192. static void
  193. _sddetach(void)
  194. {
  195. SDev *sdev;
  196. for(sdev = sdlist; sdev != nil; sdev = sdev->next){
  197. if(sdev->enabled == 0)
  198. continue;
  199. if(sdev->ifc->disable)
  200. sdev->ifc->disable(sdev);
  201. sdev->enabled = 0;
  202. }
  203. }
  204. static int
  205. _sdinit(void)
  206. {
  207. ulong m;
  208. int i;
  209. SDev *sdev, *tail;
  210. SDunit *unit;
  211. /*
  212. * Probe all configured controllers and make a list
  213. * of devices found, accumulating a possible maximum number
  214. * of units attached and marking each device with an index
  215. * into the linear top-level directory array of units.
  216. */
  217. tail = nil;
  218. for(i = 0; sdifc[i] != nil; i++){
  219. if((sdev = sdifc[i]->pnp()) == nil)
  220. continue;
  221. if(sdlist != nil)
  222. tail->next = sdev;
  223. else
  224. sdlist = sdev;
  225. for(tail = sdev; tail->next != nil; tail = tail->next){
  226. sdev->index = sdnunit;
  227. sdnunit += tail->nunit;
  228. }
  229. tail->index = sdnunit;
  230. sdnunit += tail->nunit;
  231. }
  232. /*
  233. * Legacy and option code goes here. This will be hard...
  234. */
  235. /*
  236. * The maximum number of possible units is known, allocate
  237. * placeholders for their datastructures; the units will be
  238. * probed and structures allocated when attached.
  239. * Allocate controller names for the different types.
  240. */
  241. if(sdnunit == 0)
  242. return 0;
  243. if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil)
  244. return 0;
  245. // sddetach = _sddetach;
  246. for(i = 0; sdifc[i] != nil; i++){
  247. if(sdifc[i]->id)
  248. sdifc[i]->id(sdlist);
  249. }
  250. m = 0;
  251. cdmask = sdmask = 0;
  252. for(i=0; i<sdnunit && i < 32; i++) {
  253. unit = sdindex2unit(i);
  254. if(unit == nil)
  255. continue;
  256. sdinitpart(unit);
  257. // partition(unit);
  258. if(unit->npart > 0){ /* BUG */
  259. if((unit->inquiry[0] & 0x1F) == 0x05)
  260. cdmask |= (1<<i);
  261. else
  262. sdmask |= (1<<i);
  263. m |= (1<<i);
  264. }
  265. }
  266. //notesdinfo();
  267. _sdmask = m;
  268. return m;
  269. }
  270. int
  271. cdinit(void)
  272. {
  273. if(sdnunit == 0)
  274. _sdinit();
  275. return cdmask;
  276. }
  277. int
  278. sdinit(void)
  279. {
  280. if(sdnunit == 0)
  281. _sdinit();
  282. return sdmask;
  283. }
  284. void
  285. sdinitdev(int i, char *s)
  286. {
  287. SDunit *unit;
  288. unit = sdindex2unit(i);
  289. strcpy(s, unit->name);
  290. }
  291. void
  292. sdprintdevs(int i)
  293. {
  294. char *s;
  295. SDunit *unit;
  296. unit = sdindex2unit(i);
  297. for(i=0; i<unit->npart; i++){
  298. s = unit->part[i].name;
  299. if(strncmp(s, "dos", 3) == 0
  300. || strncmp(s, "9fat", 4) == 0
  301. || strncmp(s, "fs", 2) == 0)
  302. print(" %s!%s", unit->name, s);
  303. }
  304. }
  305. SDpart*
  306. sdfindpart(SDunit *unit, char *name)
  307. {
  308. int i;
  309. if(parttrace)
  310. print("findpart %d %s %s\t\n", unit->npart, unit->name, name);
  311. for(i=0; i<unit->npart; i++) {
  312. if(parttrace)
  313. print("%s...", unit->part[i].name);
  314. if(strcmp(unit->part[i].name, name) == 0){
  315. if(parttrace)
  316. print("\n");
  317. return &unit->part[i];
  318. }
  319. }
  320. if(parttrace)
  321. print("not found\n");
  322. return nil;
  323. }
  324. typedef struct Scsicrud Scsicrud;
  325. struct Scsicrud {
  326. Fs fs;
  327. Off offset;
  328. SDunit *unit;
  329. SDpart *part;
  330. };
  331. long
  332. sdread(Fs *vcrud, void *v, long n)
  333. {
  334. Scsicrud *crud;
  335. long x;
  336. crud = (Scsicrud*)vcrud;
  337. x = sdbio(crud->unit, crud->part, v, n, crud->offset);
  338. if(x > 0)
  339. crud->offset += x;
  340. return x;
  341. }
  342. Off
  343. sdseek(Fs *vcrud, Off seek)
  344. {
  345. ((Scsicrud*)vcrud)->offset = seek;
  346. return seek;
  347. }
  348. #ifdef notdef
  349. void*
  350. sdgetfspart(int i, char *s, int chatty)
  351. {
  352. SDunit *unit;
  353. SDpart *p;
  354. Scsicrud *crud;
  355. if(cdmask&(1<<i)){
  356. if(strcmp(s, "cdboot") != 0)
  357. return nil;
  358. }else if(sdmask&(1<<i)){
  359. if(strcmp(s, "cdboot") == 0)
  360. return nil;
  361. }
  362. unit = sdindex2unit(i);
  363. if((p = sdfindpart(unit, s)) == nil){
  364. if(chatty)
  365. print("unknown partition %s!%s\n", unit->name, s);
  366. return nil;
  367. }
  368. if(p->crud == nil) {
  369. crud = malloc(sizeof(Scsicrud));
  370. crud->fs.dev = i;
  371. crud->fs.diskread = sdread;
  372. crud->fs.diskseek = sdseek;
  373. // crud->start = 0;
  374. crud->unit = unit;
  375. crud->part = p;
  376. if(dosinit(&crud->fs) < 0 && dosinit(&crud->fs) < 0 && kfsinit(&crud->fs) < 0){
  377. if(chatty)
  378. print("partition %s!%s does not contain a DOS or KFS file system\n",
  379. unit->name, s);
  380. return nil;
  381. }
  382. p->crud = crud;
  383. }
  384. return p->crud;
  385. }
  386. /*
  387. * Leave partitions around for devsd to pick up.
  388. * (Needed by boot process; more extensive
  389. * partitioning is done by termrc or cpurc).
  390. */
  391. void
  392. sdaddconf(int i)
  393. {
  394. SDunit *unit;
  395. SDpart *pp;
  396. unit = sdindex2unit(i);
  397. /*
  398. * If there were no partitions (just data and partition), don't bother.
  399. */
  400. if(unit->npart<= 1 || (unit->npart==2 && strcmp(unit->part[1].name, "partition")==0))
  401. return;
  402. addconf("%spart=", unit->name);
  403. for(i=1, pp=&unit->part[i]; i<unit->npart; i++, pp++) /* skip 0, which is "data" */
  404. addconf("%s%s %ld %ld", i==1 ? "" : "/", pp->name,
  405. pp->start, pp->end);
  406. addconf("\n");
  407. }
  408. int
  409. sdboot(int dev, char *pname, Boot *b)
  410. {
  411. char *file;
  412. Fs *fs;
  413. if((file = strchr(pname, '!')) == nil) {
  414. print("syntax is sdC0!partition!file\n");
  415. return -1;
  416. }
  417. *file++ = '\0';
  418. fs = sdgetfspart(dev, pname, 1);
  419. if(fs == nil)
  420. return -1;
  421. return fsboot(fs, file, b);
  422. }
  423. #endif
  424. long
  425. sdbio(SDunit *unit, SDpart *pp, void* va, long len, Off off)
  426. {
  427. Off l;
  428. Off bno, max, nb, offset;
  429. char *a;
  430. static uchar *b;
  431. static ulong bsz;
  432. a = va;
  433. memset(a, 0xDA, len);
  434. qlock(&unit->ctl);
  435. if(unit->changed){
  436. qunlock(&unit->ctl);
  437. return 0;
  438. }
  439. /*
  440. * Check the request is within bounds.
  441. * Removeable drives are locked throughout the I/O
  442. * in case the media changes unexpectedly.
  443. * Non-removeable drives are not locked during the I/O
  444. * to allow the hardware to optimise if it can; this is
  445. * a little fast and loose.
  446. * It's assumed that non-removable media parameters
  447. * (sectors, secsize) can't change once the drive has
  448. * been brought online.
  449. */
  450. bno = (off/unit->secsize) + pp->start;
  451. nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
  452. max = SDmaxio/unit->secsize;
  453. if(nb > max)
  454. nb = max;
  455. if(bno+nb > pp->end)
  456. nb = pp->end - bno;
  457. if(bno >= pp->end || nb == 0){
  458. qunlock(&unit->ctl);
  459. return 0;
  460. }
  461. if(!(unit->inquiry[1] & 0x80))
  462. qunlock(&unit->ctl);
  463. if(bsz < nb*unit->secsize){
  464. b = malloc(nb*unit->secsize);
  465. bsz = nb*unit->secsize;
  466. }
  467. // b = sdmalloc(nb*unit->secsize);
  468. // if(b == nil)
  469. // return 0;
  470. offset = off%unit->secsize;
  471. l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
  472. if(l < 0) {
  473. // sdfree(b);
  474. return 0;
  475. }
  476. if(l < offset)
  477. len = 0;
  478. else if(len > l - offset)
  479. len = l - offset;
  480. if(len)
  481. memmove(a, b+offset, len);
  482. // sdfree(b);
  483. if(unit->inquiry[1] & 0x80)
  484. qunlock(&unit->ctl);
  485. return len;
  486. }
  487. #ifdef DMA
  488. long
  489. sdrio(SDreq *r, void* a, long n)
  490. {
  491. if(n >= SDmaxio || n < 0)
  492. return 0;
  493. r->data = nil;
  494. if(n){
  495. if((r->data = malloc(n)) == nil)
  496. return 0;
  497. if(r->write)
  498. memmove(r->data, a, n);
  499. }
  500. r->dlen = n;
  501. if(r->unit->dev->ifc->rio(r) != SDok){
  502. // cgascreenputs("1", 1);
  503. if(r->data != nil){
  504. sdfree(r->data);
  505. r->data = nil;
  506. }
  507. return 0;
  508. }
  509. // cgascreenputs("2", 1);
  510. if(!r->write && r->rlen > 0)
  511. memmove(a, r->data, r->rlen);
  512. // cgascreenputs("3", 1);
  513. if(r->data != nil){
  514. sdfree(r->data);
  515. r->data = nil;
  516. }
  517. // cgascreenputs("4", 1);
  518. return r->rlen;
  519. }
  520. #endif /* DMA */
  521. void*
  522. sdmalloc(void *p, ulong sz)
  523. {
  524. if(p != nil) {
  525. memset(p, 0, sz);
  526. return p;
  527. }
  528. return malloc(sz);
  529. }
  530. /*
  531. * SCSI simulation for non-SCSI devices
  532. */
  533. int
  534. sdsetsense(SDreq *r, int status, int key, int asc, int ascq)
  535. {
  536. int len;
  537. SDunit *unit;
  538. unit = r->unit;
  539. unit->sense[2] = key;
  540. unit->sense[12] = asc;
  541. unit->sense[13] = ascq;
  542. if(status == SDcheck && !(r->flags & SDnosense)){
  543. /* request sense case from sdfakescsi */
  544. len = sizeof unit->sense;
  545. if(len > sizeof r->sense-1)
  546. len = sizeof r->sense-1;
  547. memmove(r->sense, unit->sense, len);
  548. unit->sense[2] = 0;
  549. unit->sense[12] = 0;
  550. unit->sense[13] = 0;
  551. r->flags |= SDvalidsense;
  552. return SDok;
  553. }
  554. return status;
  555. }
  556. int
  557. sdfakescsi(SDreq *r, void *info, int ilen)
  558. {
  559. uchar *cmd, *p;
  560. uvlong len;
  561. SDunit *unit;
  562. cmd = r->cmd;
  563. r->rlen = 0;
  564. unit = r->unit;
  565. /*
  566. * Rewrite read(6)/write(6) into read(10)/write(10).
  567. */
  568. switch(cmd[0]){
  569. case 0x08: /* read */
  570. case 0x0A: /* write */
  571. cmd[9] = 0;
  572. cmd[8] = cmd[4];
  573. cmd[7] = 0;
  574. cmd[6] = 0;
  575. cmd[5] = cmd[3];
  576. cmd[4] = cmd[2];
  577. cmd[3] = cmd[1] & 0x0F;
  578. cmd[2] = 0;
  579. cmd[1] &= 0xE0;
  580. cmd[0] |= 0x20;
  581. break;
  582. }
  583. /*
  584. * Map SCSI commands into ATA commands for discs.
  585. * Fail any command with a LUN except INQUIRY which
  586. * will return 'logical unit not supported'.
  587. */
  588. if((cmd[1]>>5) && cmd[0] != 0x12)
  589. return sdsetsense(r, SDcheck, 0x05, 0x25, 0);
  590. switch(cmd[0]){
  591. default:
  592. return sdsetsense(r, SDcheck, 0x05, 0x20, 0);
  593. case 0x00: /* test unit ready */
  594. return sdsetsense(r, SDok, 0, 0, 0);
  595. case 0x03: /* request sense */
  596. if(cmd[4] < sizeof unit->sense)
  597. len = cmd[4];
  598. else
  599. len = sizeof unit->sense;
  600. if(r->data && r->dlen >= len){
  601. memmove(r->data, unit->sense, len);
  602. r->rlen = len;
  603. }
  604. return sdsetsense(r, SDok, 0, 0, 0);
  605. case 0x12: /* inquiry */
  606. /* warning: useless or misleading comparison: UCHAR < 0x100 */
  607. if(cmd[4] < sizeof unit->inquiry)
  608. len = cmd[4];
  609. else
  610. len = sizeof unit->inquiry;
  611. if(r->data && r->dlen >= len){
  612. memmove(r->data, r->sense, len);
  613. r->rlen = len;
  614. }
  615. return sdsetsense(r, SDok, 0, 0, 0);
  616. case 0x1B: /* start/stop unit */
  617. /*
  618. * nop for now, can use power management later.
  619. */
  620. return sdsetsense(r, SDok, 0, 0, 0);
  621. case 0x25: /* read capacity */
  622. if((cmd[1] & 0x01) || cmd[2] || cmd[3])
  623. return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
  624. if(r->data == nil || r->dlen < 8)
  625. return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
  626. /*
  627. * Read capacity returns the LBA of the last sector.
  628. */
  629. len = unit->sectors - 1;
  630. p = r->data;
  631. *p++ = len>>24;
  632. *p++ = len>>16;
  633. *p++ = len>>8;
  634. *p++ = len;
  635. len = 512;
  636. *p++ = len>>24;
  637. *p++ = len>>16;
  638. *p++ = len>>8;
  639. *p++ = len;
  640. r->rlen = p - (uchar*)r->data;
  641. return sdsetsense(r, SDok, 0, 0, 0);
  642. case 0x9E: /* long read capacity */
  643. if((cmd[1] & 0x01) || cmd[2] || cmd[3])
  644. return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
  645. if(r->data == nil || r->dlen < 8)
  646. return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
  647. /*
  648. * Read capcity returns the LBA of the last sector.
  649. */
  650. len = unit->sectors - 1;
  651. p = r->data;
  652. *p++ = len>>56;
  653. *p++ = len>>48;
  654. *p++ = len>>40;
  655. *p++ = len>>32;
  656. *p++ = len>>24;
  657. *p++ = len>>16;
  658. *p++ = len>>8;
  659. *p++ = len;
  660. len = 512;
  661. *p++ = len>>24;
  662. *p++ = len>>16;
  663. *p++ = len>>8;
  664. *p++ = len;
  665. r->rlen = p - (uchar*)r->data;
  666. return sdsetsense(r, SDok, 0, 0, 0);
  667. case 0x5A: /* mode sense */
  668. return sdmodesense(r, cmd, info, ilen);
  669. case 0x28: /* read */
  670. case 0x2A: /* write */
  671. return SDnostatus;
  672. }
  673. }
  674. int
  675. sdmodesense(SDreq *r, uchar *cmd, void *info, int ilen)
  676. {
  677. int len;
  678. uchar *data;
  679. /*
  680. * Fake a vendor-specific request with page code 0,
  681. * return the drive info.
  682. */
  683. if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
  684. return sdsetsense(r, SDcheck, 0x05, 0x24, 0);
  685. len = (cmd[7]<<8)|cmd[8];
  686. if(len == 0)
  687. return SDok;
  688. if(len < 8+ilen)
  689. return sdsetsense(r, SDcheck, 0x05, 0x1A, 0);
  690. if(r->data == nil || r->dlen < len)
  691. return sdsetsense(r, SDcheck, 0x05, 0x20, 1);
  692. data = r->data;
  693. memset(data, 0, 8);
  694. data[0] = ilen>>8;
  695. data[1] = ilen;
  696. if(ilen)
  697. memmove(data+8, info, ilen);
  698. r->rlen = 8+ilen;
  699. return sdsetsense(r, SDok, 0, 0, 0);
  700. }