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