Annotation of src/usr.bin/cdio/mmc.c, Revision 1.26
1.26 ! fgsch 1: /* $OpenBSD: mmc.c,v 1.25 2008/08/13 12:21:19 av Exp $ */
1.3 mjc 2: /*
3: * Copyright (c) 2006 Michael Coulter <mjc@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
1.1 mjc 18: #include <sys/limits.h>
19: #include <sys/types.h>
20: #include <sys/scsiio.h>
1.26 ! fgsch 21: #include <sys/param.h>
1.8 mjc 22: #include <scsi/cd.h>
23: #include <scsi/scsi_all.h>
24: #include <scsi/scsi_disk.h>
1.1 mjc 25: #include <err.h>
1.2 mjc 26: #include <errno.h>
1.1 mjc 27: #include <fcntl.h>
28: #include <stdio.h>
29: #include <string.h>
30: #include <unistd.h>
31: #include "extern.h"
32:
33: extern int fd;
1.26 ! fgsch 34: extern int mediacap[];
1.2 mjc 35: extern char *cdname;
1.20 av 36:
37: #define SCSI_GET_CONFIGURATION 0x46
38:
1.26 ! fgsch 39: #define MMC_FEATURE_HDR_LEN 8
1.25 av 40:
41: int
42: get_media_type(void)
43: {
44: scsireq_t scr;
45: char buf[32];
46: u_char disctype;
47: int rv, error;
48:
49: rv = MEDIATYPE_UNKNOWN;
50: memset(buf, 0, sizeof(buf));
51: memset(&scr, 0, sizeof(scr));
52:
53: scr.cmd[0] = READ_TOC;
54: scr.cmd[1] = 0x2; /* MSF */
55: scr.cmd[2] = 0x4; /* ATIP */
56: scr.cmd[8] = 0x20;
57:
58: scr.flags = SCCMD_ESCAPE | SCCMD_READ;
59: scr.databuf = buf;
60: scr.datalen = sizeof(buf);
61: scr.cmdlen = 10;
62: scr.timeout = 120000;
63: scr.senselen = SENSEBUFLEN;
64:
65: error = ioctl(fd, SCIOCCOMMAND, &scr);
66: if (error != -1 && scr.retsts == 0 && scr.datalen_used > 7) {
67: disctype = (buf[6] >> 6) & 0x1;
68: if (disctype == 0)
69: rv = MEDIATYPE_CDR;
70: else if (disctype == 1)
71: rv = MEDIATYPE_CDRW;
72: }
73:
74: return (rv);
75: }
1.20 av 76:
77: int
1.26 ! fgsch 78: get_media_capabilities(int *cap, int rt)
1.20 av 79: {
80: scsireq_t scr;
1.23 av 81: u_char buf[4096];
1.26 ! fgsch 82: u_int32_t i, dlen;
! 83: u_int16_t feature;
! 84: u_int8_t feature_len;
1.20 av 85: int error;
86:
1.26 ! fgsch 87: memset(cap, 0, MMC_FEATURE_MAX / 8);
1.20 av 88: memset(buf, 0, sizeof(buf));
89: memset(&scr, 0, sizeof(scr));
90:
91: scr.cmd[0] = SCSI_GET_CONFIGURATION;
1.26 ! fgsch 92: scr.cmd[1] = rt;
! 93: *(u_int16_t *)(scr.cmd + 7) = htobe16(sizeof(buf));
1.20 av 94:
95: scr.flags = SCCMD_ESCAPE | SCCMD_READ;
96: scr.databuf = buf;
97: scr.datalen = sizeof(buf);
98: scr.cmdlen = 10;
99: scr.timeout = 120000;
100: scr.senselen = SENSEBUFLEN;
101:
102: error = ioctl(fd, SCIOCCOMMAND, &scr);
103: if (error == -1 || scr.retsts != 0)
104: return (-1);
1.26 ! fgsch 105: if (scr.datalen_used < MMC_FEATURE_HDR_LEN)
! 106: return (-1); /* Can't get the header. */
! 107:
! 108: /* Include the whole header in the length. */
! 109: dlen = betoh32(*(u_int32_t *)buf) + 4;
! 110: if (dlen > scr.datalen_used)
! 111: dlen = scr.datalen_used;
! 112:
! 113: for (i = MMC_FEATURE_HDR_LEN; i + 3 < dlen; i += feature_len) {
! 114: feature_len = buf[i + 3] + 4;
! 115: if (feature_len + i > dlen)
! 116: break;
1.20 av 117:
118: feature = betoh16(*(u_int16_t *)(buf + i));
1.26 ! fgsch 119: if (feature >= MMC_FEATURE_MAX)
! 120: break;
1.20 av 121:
1.26 ! fgsch 122: setbit(cap, feature);
1.20 av 123: }
124:
125: return (0);
126: }
1.1 mjc 127:
128: int
1.22 av 129: set_speed(int wspeed)
130: {
131: scsireq_t scr;
132: int r;
133:
134: memset(&scr, 0, sizeof(scr));
1.24 fgsch 135: scr.cmd[0] = SET_CD_SPEED;
1.26 ! fgsch 136: scr.cmd[1] = (isset(mediacap, MMC_FEATURE_CDRW_CAV)) != 0;
1.22 av 137: *(u_int16_t *)(scr.cmd + 2) = htobe16(DRIVE_SPEED_OPTIMAL);
138: *(u_int16_t *)(scr.cmd + 4) = htobe16(wspeed);
139:
140: scr.cmdlen = 12;
141: scr.datalen = 0;
142: scr.timeout = 120000;
143: scr.flags = SCCMD_ESCAPE;
144: scr.senselen = SENSEBUFLEN;
145:
146: r = ioctl(fd, SCIOCCOMMAND, &scr);
147: return (r == 0 ? scr.retsts : -1);
148: }
149:
150: int
1.1 mjc 151: blank(void)
152: {
1.8 mjc 153: struct scsi_blank *scb;
1.1 mjc 154: scsireq_t scr;
155: int r;
156:
157: bzero(&scr, sizeof(scr));
1.8 mjc 158: scb = (struct scsi_blank *)scr.cmd;
159: scb->opcode = BLANK;
160: scb->byte2 |= BLANK_MINIMAL;
161: scr.cmdlen = sizeof(*scb);
1.1 mjc 162: scr.datalen = 0;
163: scr.timeout = 120000;
164: scr.flags = SCCMD_ESCAPE;
165: scr.senselen = SENSEBUFLEN;
166:
1.2 mjc 167: r = ioctl(fd, SCIOCCOMMAND, &scr);
1.1 mjc 168: return (r == 0 ? scr.retsts : -1);
169: }
170:
171: int
172: unit_ready(void)
173: {
1.8 mjc 174: struct scsi_test_unit_ready *scb;
1.1 mjc 175: scsireq_t scr;
176: int r;
177:
178: bzero(&scr, sizeof(scr));
1.8 mjc 179: scb = (struct scsi_test_unit_ready *)scr.cmd;
180: scb->opcode = TEST_UNIT_READY;
181: scr.cmdlen = sizeof(*scb);
1.1 mjc 182: scr.datalen = 0;
183: scr.timeout = 120000;
184: scr.flags = SCCMD_ESCAPE;
185: scr.senselen = SENSEBUFLEN;
186:
187: r = ioctl(fd, SCIOCCOMMAND, &scr);
188: return (r == 0 ? scr.retsts : -1);
189: }
190:
191: int
192: synchronize_cache(void)
193: {
1.8 mjc 194: struct scsi_synchronize_cache *scb;
1.1 mjc 195: scsireq_t scr;
196: int r;
197:
198: bzero(&scr, sizeof(scr));
1.8 mjc 199: scb = (struct scsi_synchronize_cache *)scr.cmd;
200: scb->opcode = SYNCHRONIZE_CACHE;
201: scr.cmdlen = sizeof(*scb);
1.1 mjc 202: scr.datalen = 0;
203: scr.timeout = 120000;
204: scr.flags = SCCMD_ESCAPE;
205: scr.senselen = SENSEBUFLEN;
206:
207: r = ioctl(fd, SCIOCCOMMAND, &scr);
208: return (r == 0 ? scr.retsts : -1);
209: }
210:
211: int
212: close_session(void)
213: {
1.8 mjc 214: struct scsi_close_track *scb;
1.1 mjc 215: scsireq_t scr;
216: int r;
217:
218: bzero(&scr, sizeof(scr));
1.8 mjc 219: scb = (struct scsi_close_track *)scr.cmd;
220: scb->opcode = CLOSE_TRACK;
221: scb->closefunc = CT_CLOSE_SESS;
222: scr.cmdlen = sizeof(*scb);
1.1 mjc 223: scr.datalen = 0;
224: scr.timeout = 120000;
225: scr.flags = SCCMD_ESCAPE;
226: scr.senselen = SENSEBUFLEN;
227:
228: r = ioctl(fd, SCIOCCOMMAND, &scr);
229: return (r == 0 ? scr.retsts : -1);
230: }
231:
232: int
1.5 mjc 233: writetao(struct track_head *thp)
1.1 mjc 234: {
1.15 deraadt 235: u_char modebuf[70], bdlen;
1.5 mjc 236: struct track_info *tr;
1.15 deraadt 237: int r, track = 0;
1.5 mjc 238:
1.1 mjc 239: if ((r = mode_sense_write(modebuf)) != SCCMD_OK) {
240: warnx("mode sense failed: %d", r);
241: return (r);
242: }
243: bdlen = modebuf[7];
244: modebuf[2+8+bdlen] |= 0x40; /* Buffer Underrun Free Enable */
245: modebuf[2+8+bdlen] |= 0x01; /* change write type to TAO */
246:
1.5 mjc 247: SLIST_FOREACH(tr, thp, track_list) {
1.15 deraadt 248: track++;
1.7 deraadt 249: switch (tr->type) {
1.5 mjc 250: case 'd':
1.1 mjc 251: modebuf[3+8+bdlen] = 0x04; /* track mode = data */
252: modebuf[4+8+bdlen] = 0x08; /* 2048 block track mode */
253: modebuf[8+8+bdlen] = 0x00; /* turn off XA */
1.5 mjc 254: break;
255: case 'a':
1.1 mjc 256: modebuf[3+8+bdlen] = 0x00; /* track mode = audio */
257: modebuf[4+8+bdlen] = 0x00; /* 2352 block track mode */
258: modebuf[8+8+bdlen] = 0x00; /* turn off XA */
1.5 mjc 259: break;
260: default:
261: warn("impossible tracktype detected");
262: break;
1.1 mjc 263: }
264: while (unit_ready() != SCCMD_OK)
265: continue;
266: if ((r = mode_select_write(modebuf)) != SCCMD_OK) {
1.15 deraadt 267: warnx("mode select failed: %d", r);
1.1 mjc 268: return (r);
269: }
1.22 av 270:
271: set_speed(tr->speed);
1.15 deraadt 272: writetrack(tr, track);
1.1 mjc 273: synchronize_cache();
274: }
1.15 deraadt 275: fprintf(stderr, "Closing session.\n");
1.1 mjc 276: close_session();
277: return (0);
278: }
279:
280: int
1.15 deraadt 281: writetrack(struct track_info *tr, int track)
1.1 mjc 282: {
1.15 deraadt 283: struct timeval tv, otv, atv;
284: u_char databuf[65536], nblk;
285: u_int end_lba, lba, tmp;
1.1 mjc 286: scsireq_t scr;
1.11 mjc 287: int r;
1.1 mjc 288:
1.5 mjc 289: nblk = 65535/tr->blklen;
1.1 mjc 290: bzero(&scr, sizeof(scr));
291: scr.timeout = 300000;
1.9 mjc 292: scr.cmd[0] = WRITE_BIG;
1.1 mjc 293: scr.cmd[1] = 0x00;
294: scr.cmd[8] = nblk; /* Transfer length in blocks (LSB) */
295: scr.cmdlen = 10;
296: scr.databuf = (caddr_t)databuf;
1.5 mjc 297: scr.datalen = nblk * tr->blklen;
1.1 mjc 298: scr.senselen = SENSEBUFLEN;
299: scr.flags = SCCMD_ESCAPE|SCCMD_WRITE;
300:
1.15 deraadt 301: timerclear(&otv);
302: atv.tv_sec = 1;
303: atv.tv_usec = 0;
304:
1.1 mjc 305: if (get_nwa(&lba) != SCCMD_OK) {
306: warnx("cannot get next writable address");
307: return (-1);
308: }
309: tmp = htobe32(lba); /* update lba in cdb */
310: memcpy(&scr.cmd[2], &tmp, sizeof(tmp));
311:
1.5 mjc 312: if (tr->sz / tr->blklen + 1 > UINT_MAX || tr->sz < tr->blklen) {
1.15 deraadt 313: warnx("file %s has invalid size", tr->file);
1.1 mjc 314: return (-1);
315: }
1.5 mjc 316: if (tr->sz % tr->blklen) {
1.7 deraadt 317: warnx("file %s is not multiple of block length %d",
318: tr->file, tr->blklen);
1.5 mjc 319: end_lba = tr->sz / tr->blklen + lba + 1;
1.1 mjc 320: } else {
1.5 mjc 321: end_lba = tr->sz / tr->blklen + lba;
1.1 mjc 322: }
1.21 av 323: if (lseek(tr->fd, tr->off, SEEK_SET) == -1)
324: err(1, "seek failed for file %s", tr->file);
1.13 deraadt 325: while (lba < end_lba && nblk != 0) {
1.1 mjc 326: while (lba + nblk <= end_lba) {
1.11 mjc 327: read(tr->fd, databuf, nblk * tr->blklen);
1.1 mjc 328: scr.cmd[8] = nblk;
1.5 mjc 329: scr.datalen = nblk * tr->blklen;
1.12 mjc 330: again:
1.1 mjc 331: r = ioctl(fd, SCIOCCOMMAND, &scr);
332: if (r != 0) {
1.17 deraadt 333: printf("\r%60s", "");
1.1 mjc 334: warn("ioctl failed while attempting to write");
335: return (-1);
1.12 mjc 336: }
337: if (scr.retsts == SCCMD_SENSE && scr.sense[2] == 0x2) {
338: usleep(1000);
339: goto again;
1.1 mjc 340: }
341: if (scr.retsts != SCCMD_OK) {
1.17 deraadt 342: printf("\r%60s", "");
1.7 deraadt 343: warnx("ioctl returned bad status while "
344: "attempting to write: %d",
345: scr.retsts);
1.1 mjc 346: return (r);
347: }
348: lba += nblk;
1.15 deraadt 349:
350: gettimeofday(&tv, NULL);
351: if (lba == end_lba || timercmp(&tv, &otv, >)) {
352: fprintf(stderr,
1.17 deraadt 353: "\rtrack %02d '%c' %08u/%08u %3d%%",
1.15 deraadt 354: track, tr->type,
355: lba, end_lba, 100 * lba / end_lba);
356: timeradd(&tv, &atv, &otv);
357: }
1.1 mjc 358: tmp = htobe32(lba); /* update lba in cdb */
359: memcpy(&scr.cmd[2], &tmp, sizeof(tmp));
360: }
361: nblk--;
362: }
1.13 deraadt 363: printf("\n");
1.11 mjc 364: close(tr->fd);
1.1 mjc 365: return (0);
366: }
367:
368: int
369: mode_sense_write(unsigned char buf[])
370: {
1.8 mjc 371: struct scsi_mode_sense_big *scb;
1.1 mjc 372: scsireq_t scr;
373: int r;
374:
375: bzero(&scr, sizeof(scr));
1.8 mjc 376: scb = (struct scsi_mode_sense_big *)scr.cmd;
377: scb->opcode = MODE_SENSE_BIG;
378: /* XXX: need to set disable block descriptors and check SCSI drive */
379: scb->page = WRITE_PARAM_PAGE;
380: scb->length[1] = 0x46; /* 16 for the header + size from pg. 89 mmc-r10a.pdf */
381: scr.cmdlen = sizeof(*scb);
1.1 mjc 382: scr.timeout = 4000;
383: scr.senselen = SENSEBUFLEN;
1.7 deraadt 384: scr.datalen= 0x46;
1.1 mjc 385: scr.flags = SCCMD_ESCAPE|SCCMD_READ;
386: scr.databuf = (caddr_t)buf;
387:
388: r = ioctl(fd, SCIOCCOMMAND, &scr);
389: return (r == 0 ? scr.retsts : -1);
390: }
391:
392: int
393: mode_select_write(unsigned char buf[])
394: {
1.8 mjc 395: struct scsi_mode_select_big *scb;
1.1 mjc 396: scsireq_t scr;
397: int r;
398:
399: bzero(&scr, sizeof(scr));
1.8 mjc 400: scb = (struct scsi_mode_select_big *)scr.cmd;
401: scb->opcode = MODE_SELECT_BIG;
1.10 deraadt 402:
403: /*
404: * INF-8020 says bit 4 in byte 2 is '1'
405: * INF-8090 refers to it as 'PF(1)' then doesn't
406: * describe it.
407: */
1.8 mjc 408: scb->byte2 = 0x10;
409: scb->length[1] = 2 + buf[1] + 256 * buf[0];
1.1 mjc 410: scr.timeout = 4000;
411: scr.senselen = SENSEBUFLEN;
1.8 mjc 412: scr.cmdlen = sizeof(*scb);
1.1 mjc 413: scr.datalen = 2 + buf[1] + 256 * buf[0];
414: scr.flags = SCCMD_ESCAPE|SCCMD_WRITE;
415: scr.databuf = (caddr_t)buf;
416:
417: r = ioctl(fd, SCIOCCOMMAND, &scr);
1.6 mjc 418: return (r == 0 ? scr.retsts : -1);
419: }
420:
421: int
422: get_disc_size(off_t *availblk)
423: {
424: u_char databuf[28];
1.8 mjc 425: struct scsi_read_track_info *scb;
1.6 mjc 426: scsireq_t scr;
1.15 deraadt 427: int r, tmp;
1.6 mjc 428:
429: bzero(&scr, sizeof(scr));
1.8 mjc 430: scb = (struct scsi_read_track_info *)scr.cmd;
1.6 mjc 431: scr.timeout = 4000;
432: scr.senselen = SENSEBUFLEN;
1.8 mjc 433: scb->opcode = READ_TRACK_INFO;
434: scb->addrtype = RTI_TRACK;
1.16 mjc 435: scb->addr[3] = 1;
436: scb->data_len[1] = 0x1c;
1.8 mjc 437: scr.cmdlen = sizeof(*scb);
1.7 deraadt 438: scr.datalen= 0x1c;
1.6 mjc 439: scr.flags = SCCMD_ESCAPE|SCCMD_READ;
440: scr.databuf = (caddr_t)databuf;
441:
442: r = ioctl(fd, SCIOCCOMMAND, &scr);
443: memcpy(&tmp, &databuf[16], sizeof(tmp));
444: *availblk = betoh32(tmp);
1.1 mjc 445: return (r == 0 ? scr.retsts : -1);
446: }
447:
448: int
449: get_nwa(int *nwa)
450: {
451: u_char databuf[28];
452: scsireq_t scr;
1.15 deraadt 453: int r, tmp;
1.1 mjc 454:
455: bzero(&scr, sizeof(scr));
456: scr.timeout = 4000;
457: scr.senselen = SENSEBUFLEN;
1.24 fgsch 458: scr.cmd[0] = READ_TRACK_INFO;
1.1 mjc 459: scr.cmd[1] = 0x01;
460: scr.cmd[5] = 0xff; /* Invisible Track */
461: scr.cmd[7] = 0x00;
462: scr.cmd[8] = 0x1c;
463: scr.cmdlen = 10;
1.7 deraadt 464: scr.datalen= 0x1c;
1.1 mjc 465: scr.flags = SCCMD_ESCAPE|SCCMD_READ;
466: scr.databuf = (caddr_t)databuf;
467:
468: r = ioctl(fd, SCIOCCOMMAND, &scr);
469: memcpy(&tmp, &databuf[12], sizeof(tmp));
470: *nwa = betoh32(tmp);
471: return (r == 0 ? scr.retsts : -1);
472: }