xref: /openbsd-src/usr.bin/cdio/mmc.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: mmc.c,v 1.26 2008/08/30 10:41:38 fgsch Exp $	*/
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 
18 #include <sys/limits.h>
19 #include <sys/types.h>
20 #include <sys/scsiio.h>
21 #include <sys/param.h>
22 #include <scsi/cd.h>
23 #include <scsi/scsi_all.h>
24 #include <scsi/scsi_disk.h>
25 #include <err.h>
26 #include <errno.h>
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;
34 extern int mediacap[];
35 extern char *cdname;
36 
37 #define SCSI_GET_CONFIGURATION		0x46
38 
39 #define MMC_FEATURE_HDR_LEN		8
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 }
76 
77 int
78 get_media_capabilities(int *cap, int rt)
79 {
80 	scsireq_t scr;
81 	u_char buf[4096];
82 	u_int32_t i, dlen;
83 	u_int16_t feature;
84 	u_int8_t feature_len;
85 	int error;
86 
87 	memset(cap, 0, MMC_FEATURE_MAX / 8);
88 	memset(buf, 0, sizeof(buf));
89 	memset(&scr, 0, sizeof(scr));
90 
91 	scr.cmd[0] = SCSI_GET_CONFIGURATION;
92 	scr.cmd[1] = rt;
93 	*(u_int16_t *)(scr.cmd + 7) = htobe16(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 < 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;
117 
118 		feature = betoh16(*(u_int16_t *)(buf + i));
119 		if (feature >= MMC_FEATURE_MAX)
120 			break;
121 
122 		setbit(cap, feature);
123 	}
124 
125 	return (0);
126 }
127 
128 int
129 set_speed(int wspeed)
130 {
131 	scsireq_t scr;
132 	int r;
133 
134 	memset(&scr, 0, sizeof(scr));
135 	scr.cmd[0] = SET_CD_SPEED;
136 	scr.cmd[1] = (isset(mediacap, MMC_FEATURE_CDRW_CAV)) != 0;
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
151 blank(void)
152 {
153 	struct scsi_blank *scb;
154 	scsireq_t scr;
155 	int r;
156 
157 	bzero(&scr, sizeof(scr));
158 	scb = (struct scsi_blank *)scr.cmd;
159 	scb->opcode = BLANK;
160 	scb->byte2 |= BLANK_MINIMAL;
161 	scr.cmdlen = sizeof(*scb);
162 	scr.datalen = 0;
163 	scr.timeout = 120000;
164 	scr.flags = SCCMD_ESCAPE;
165 	scr.senselen = SENSEBUFLEN;
166 
167 	r = ioctl(fd, SCIOCCOMMAND, &scr);
168 	return (r == 0 ? scr.retsts : -1);
169 }
170 
171 int
172 unit_ready(void)
173 {
174 	struct scsi_test_unit_ready *scb;
175 	scsireq_t scr;
176 	int r;
177 
178 	bzero(&scr, sizeof(scr));
179 	scb = (struct scsi_test_unit_ready *)scr.cmd;
180 	scb->opcode = TEST_UNIT_READY;
181 	scr.cmdlen = sizeof(*scb);
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 {
194 	struct scsi_synchronize_cache *scb;
195 	scsireq_t scr;
196 	int r;
197 
198 	bzero(&scr, sizeof(scr));
199 	scb = (struct scsi_synchronize_cache *)scr.cmd;
200 	scb->opcode = SYNCHRONIZE_CACHE;
201 	scr.cmdlen = sizeof(*scb);
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 {
214 	struct scsi_close_track *scb;
215 	scsireq_t scr;
216 	int r;
217 
218 	bzero(&scr, sizeof(scr));
219 	scb = (struct scsi_close_track *)scr.cmd;
220 	scb->opcode = CLOSE_TRACK;
221 	scb->closefunc = CT_CLOSE_SESS;
222 	scr.cmdlen = sizeof(*scb);
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
233 writetao(struct track_head *thp)
234 {
235 	u_char modebuf[70], bdlen;
236 	struct track_info *tr;
237 	int r, track = 0;
238 
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 
247 	SLIST_FOREACH(tr, thp, track_list) {
248 		track++;
249 		switch (tr->type) {
250 		case 'd':
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 */
254 			break;
255 		case 'a':
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 */
259 			break;
260 		default:
261 			warn("impossible tracktype detected");
262 			break;
263 		}
264 		while (unit_ready() != SCCMD_OK)
265 			continue;
266 		if ((r = mode_select_write(modebuf)) != SCCMD_OK) {
267 			warnx("mode select failed: %d", r);
268 			return (r);
269 		}
270 
271 		set_speed(tr->speed);
272 		writetrack(tr, track);
273 		synchronize_cache();
274 	}
275 	fprintf(stderr, "Closing session.\n");
276 	close_session();
277 	return (0);
278 }
279 
280 int
281 writetrack(struct track_info *tr, int track)
282 {
283 	struct timeval tv, otv, atv;
284 	u_char databuf[65536], nblk;
285 	u_int end_lba, lba, tmp;
286 	scsireq_t scr;
287 	int r;
288 
289 	nblk = 65535/tr->blklen;
290 	bzero(&scr, sizeof(scr));
291 	scr.timeout = 300000;
292 	scr.cmd[0] = WRITE_BIG;
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;
297 	scr.datalen = nblk * tr->blklen;
298 	scr.senselen = SENSEBUFLEN;
299 	scr.flags = SCCMD_ESCAPE|SCCMD_WRITE;
300 
301 	timerclear(&otv);
302 	atv.tv_sec = 1;
303 	atv.tv_usec = 0;
304 
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 
312 	if (tr->sz / tr->blklen + 1 > UINT_MAX || tr->sz < tr->blklen) {
313 		warnx("file %s has invalid size", tr->file);
314 		return (-1);
315 	}
316 	if (tr->sz % tr->blklen) {
317 		warnx("file %s is not multiple of block length %d",
318 		    tr->file, tr->blklen);
319 		end_lba = tr->sz / tr->blklen + lba + 1;
320 	} else {
321 		end_lba = tr->sz / tr->blklen + lba;
322 	}
323 	if (lseek(tr->fd, tr->off, SEEK_SET) == -1)
324 		err(1, "seek failed for file %s", tr->file);
325 	while (lba < end_lba && nblk != 0) {
326 		while (lba + nblk <= end_lba) {
327 			read(tr->fd, databuf, nblk * tr->blklen);
328 			scr.cmd[8] = nblk;
329 			scr.datalen = nblk * tr->blklen;
330 again:
331 			r = ioctl(fd, SCIOCCOMMAND, &scr);
332 			if (r != 0) {
333 				printf("\r%60s", "");
334 				warn("ioctl failed while attempting to write");
335 				return (-1);
336 			}
337 			if (scr.retsts == SCCMD_SENSE && scr.sense[2] == 0x2) {
338 				usleep(1000);
339 				goto again;
340 			}
341 			if (scr.retsts != SCCMD_OK) {
342 				printf("\r%60s", "");
343 				warnx("ioctl returned bad status while "
344 				    "attempting to write: %d",
345 				    scr.retsts);
346 				return (r);
347 			}
348 			lba += nblk;
349 
350 			gettimeofday(&tv, NULL);
351 			if (lba == end_lba || timercmp(&tv, &otv, >)) {
352 				fprintf(stderr,
353 				    "\rtrack %02d '%c' %08u/%08u %3d%%",
354 				    track, tr->type,
355 				    lba, end_lba, 100 * lba / end_lba);
356 				timeradd(&tv, &atv, &otv);
357 			}
358 			tmp = htobe32(lba); /* update lba in cdb */
359 			memcpy(&scr.cmd[2], &tmp, sizeof(tmp));
360 		}
361 		nblk--;
362 	}
363 	printf("\n");
364 	close(tr->fd);
365 	return (0);
366 }
367 
368 int
369 mode_sense_write(unsigned char buf[])
370 {
371 	struct scsi_mode_sense_big *scb;
372 	scsireq_t scr;
373 	int r;
374 
375 	bzero(&scr, sizeof(scr));
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);
382 	scr.timeout = 4000;
383 	scr.senselen = SENSEBUFLEN;
384 	scr.datalen= 0x46;
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 {
395 	struct scsi_mode_select_big *scb;
396 	scsireq_t scr;
397 	int r;
398 
399 	bzero(&scr, sizeof(scr));
400 	scb = (struct scsi_mode_select_big *)scr.cmd;
401 	scb->opcode = MODE_SELECT_BIG;
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 	 */
408 	scb->byte2 = 0x10;
409 	scb->length[1] = 2 + buf[1] + 256 * buf[0];
410 	scr.timeout = 4000;
411 	scr.senselen = SENSEBUFLEN;
412 	scr.cmdlen = sizeof(*scb);
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);
418 	return (r == 0 ? scr.retsts : -1);
419 }
420 
421 int
422 get_disc_size(off_t *availblk)
423 {
424 	u_char databuf[28];
425 	struct scsi_read_track_info *scb;
426 	scsireq_t scr;
427 	int r, tmp;
428 
429 	bzero(&scr, sizeof(scr));
430 	scb = (struct scsi_read_track_info *)scr.cmd;
431 	scr.timeout = 4000;
432 	scr.senselen = SENSEBUFLEN;
433 	scb->opcode = READ_TRACK_INFO;
434 	scb->addrtype = RTI_TRACK;
435 	scb->addr[3] = 1;
436 	scb->data_len[1] = 0x1c;
437 	scr.cmdlen = sizeof(*scb);
438 	scr.datalen= 0x1c;
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);
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;
453 	int r, tmp;
454 
455 	bzero(&scr, sizeof(scr));
456 	scr.timeout = 4000;
457 	scr.senselen = SENSEBUFLEN;
458 	scr.cmd[0] = READ_TRACK_INFO;
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;
464 	scr.datalen= 0x1c;
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 }
473