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