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