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