xref: /illumos-gate/usr/src/cmd/cdrw/mmc.c (revision f808c858)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 
34 #include "transport.h"
35 #include "mmc.h"
36 #include "util.h"
37 #include "main.h"
38 
39 int
40 test_unit_ready(int fd)
41 {
42 	struct uscsi_cmd *scmd;
43 
44 	scmd = get_uscsi_cmd();
45 	scmd->uscsi_flags = USCSI_SILENT;
46 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
47 	/* give length of cdb structure */
48 	scmd->uscsi_cdblen = 6;
49 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
50 		return (0);
51 	return (1);
52 }
53 
54 int
55 inquiry(int fd, uchar_t *inq)
56 {
57 	struct uscsi_cmd *scmd;
58 
59 	scmd = get_uscsi_cmd();
60 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
61 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
62 	scmd->uscsi_cdb[0] = INQUIRY_CMD;
63 	scmd->uscsi_cdb[4] = INQUIRY_DATA_LENGTH;
64 	scmd->uscsi_cdblen = 6;
65 	scmd->uscsi_bufaddr = (char *)inq;
66 	scmd->uscsi_buflen = INQUIRY_DATA_LENGTH;
67 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
68 		return (0);
69 	return (1);
70 }
71 
72 int
73 read_capacity(int fd, uchar_t *capbuf)
74 {
75 	struct uscsi_cmd *scmd;
76 
77 	scmd = get_uscsi_cmd();
78 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
79 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
80 	scmd->uscsi_cdb[0] = READ_CAP_CMD;
81 	scmd->uscsi_cdblen = 10;
82 	scmd->uscsi_bufaddr = (char *)capbuf;
83 	scmd->uscsi_buflen = 8;
84 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
85 		return (0);
86 	return (1);
87 }
88 
89 int
90 mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer)
91 {
92 	struct uscsi_cmd *scmd;
93 
94 	scmd = get_uscsi_cmd();
95 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
96 	scmd->uscsi_buflen = page_len;
97 	scmd->uscsi_bufaddr = (char *)buffer;
98 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
99 	scmd->uscsi_cdblen = 0xa;
100 	scmd->uscsi_cdb[0] = MODE_SENSE_10_CMD;
101 	if (dbd) {
102 		/* don't return any block descriptors */
103 		scmd->uscsi_cdb[1] = 0x8;
104 	}
105 	/* the page code we want */
106 	scmd->uscsi_cdb[2] = pc;
107 	/* allocation length */
108 	scmd->uscsi_cdb[7] = (page_len >> 8) & 0xff;
109 	scmd->uscsi_cdb[8] = page_len & 0xff;
110 
111 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
112 		return (0);
113 	return (1);
114 }
115 
116 int
117 mode_select(int fd, int page_len, uchar_t *buffer)
118 {
119 	struct uscsi_cmd *scmd;
120 
121 	scmd = get_uscsi_cmd();
122 	scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
123 	scmd->uscsi_buflen = page_len;
124 	scmd->uscsi_bufaddr = (char *)buffer;
125 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
126 	scmd->uscsi_cdblen = 0xa;
127 
128 	/* mode select (10) command */
129 	scmd->uscsi_cdb[0] = MODE_SELECT_10_CMD;
130 	scmd->uscsi_cdb[1] = 0x10;
131 
132 	/* parameter list length */
133 	scmd->uscsi_cdb[7] = (page_len >> 8) & 0xff;
134 	scmd->uscsi_cdb[8] = page_len & 0xff;
135 
136 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
137 		return (0);
138 	return (1);
139 }
140 
141 int
142 read_track_info(int fd, int trackno, uchar_t *ti)
143 {
144 	struct uscsi_cmd *scmd;
145 
146 	scmd = get_uscsi_cmd();
147 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
148 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
149 	scmd->uscsi_cdb[0] = READ_TRACK_CMD;
150 
151 	/* tell device we are giving it a track number */
152 	scmd->uscsi_cdb[1] = 1;
153 
154 	/* track number to read */
155 	if (trackno == -1)
156 		if (device_type == CD_RW) {
157 			((uchar_t *)scmd->uscsi_cdb)[5] = 0xff;
158 		} else {
159 			/* only 1 track is allowed on DVD media */
160 			scmd->uscsi_cdb[1] = 0;
161 			((uchar_t *)scmd->uscsi_cdb)[5] = 0;
162 		}
163 	else
164 		scmd->uscsi_cdb[5] = (uchar_t)trackno;
165 
166 	scmd->uscsi_cdb[8] = TRACK_INFO_SIZE;
167 	scmd->uscsi_cdblen = 10;
168 	scmd->uscsi_bufaddr = (char *)ti;
169 	scmd->uscsi_buflen = TRACK_INFO_SIZE;
170 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
171 		return (0);
172 	return (1);
173 }
174 
175 int
176 read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf)
177 {
178 	struct uscsi_cmd *scmd;
179 
180 	scmd = get_uscsi_cmd();
181 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
182 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
183 	scmd->uscsi_cdb[0] = READ_TOC_CMD;
184 	scmd->uscsi_cdb[2] = format & 0xf;
185 	scmd->uscsi_cdb[6] = trackno;
186 	scmd->uscsi_cdb[8] = buflen & 0xff;
187 	scmd->uscsi_cdb[7] = (buflen >> 8) & 0xff;
188 	scmd->uscsi_cdblen = 10;
189 	scmd->uscsi_bufaddr = (char *)buf;
190 	scmd->uscsi_buflen = buflen;
191 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
192 		return (0);
193 
194 	/* Fix for old SONY drives */
195 	if ((format == 0) && (buflen == 4) && (buf[0] == 0) && (buf[1] == 2)) {
196 		uint16_t toc_size;
197 
198 		toc_size = (((uint16_t)(buf[3] + 1)) * 8) + 2;
199 		load_scsi16(buf, toc_size);
200 	}
201 	return (1);
202 }
203 
204 int
205 read_header(int fd, uint32_t lba, uchar_t *buf)
206 {
207 	struct uscsi_cmd *scmd;
208 
209 	scmd = get_uscsi_cmd();
210 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
211 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
212 	scmd->uscsi_cdb[0] = READ_HDR_CMD;
213 
214 	/* Logical block address */
215 	load_scsi32(&scmd->uscsi_cdb[2], lba);
216 
217 	/* allocation length */
218 	scmd->uscsi_cdb[8] = 8;
219 	scmd->uscsi_cdblen = 10;
220 	scmd->uscsi_bufaddr = (char *)buf;
221 	scmd->uscsi_buflen = 8;
222 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
223 		return (0);
224 	return (1);
225 }
226 
227 int
228 read_disc_info(int fd, uchar_t *di)
229 {
230 	struct uscsi_cmd *scmd;
231 
232 	scmd = get_uscsi_cmd();
233 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
234 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
235 	scmd->uscsi_cdb[0] = READ_INFO_CMD;
236 	scmd->uscsi_cdb[8] = DISC_INFO_BLOCK_SIZE;
237 	scmd->uscsi_cdblen = 10;
238 	scmd->uscsi_bufaddr = (char *)di;
239 	scmd->uscsi_buflen = DISC_INFO_BLOCK_SIZE;
240 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
241 		return (0);
242 	return (1);
243 }
244 
245 /* Get information about the Logical Unit's capabilities */
246 int
247 get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf)
248 {
249 	struct uscsi_cmd *scmd;
250 
251 	scmd = get_uscsi_cmd();
252 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
253 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
254 
255 	/* Set OPERATION CODE in CDB */
256 	scmd->uscsi_cdb[0] = GET_CONFIG_CMD;
257 
258 	/*
259 	 * Set RT field in CDB, currently need at most one
260 	 * Feature Descriptor
261 	 */
262 	scmd->uscsi_cdb[1] = 0x2;
263 
264 	/* Set Starting Feature Number in CDB */
265 	scmd->uscsi_cdb[2] = (feature >> 8) & 0xff;
266 	scmd->uscsi_cdb[3] = feature & 0xff;
267 
268 	/* Set Allocation Length in CDB */
269 	scmd->uscsi_cdb[7] = (bufsize >> 8) & 0xff;
270 	scmd->uscsi_cdb[8] = bufsize & 0xff;
271 
272 	scmd->uscsi_cdblen = 10;
273 	scmd->uscsi_bufaddr = (char *)buf;
274 	scmd->uscsi_buflen = bufsize;
275 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
276 		return (0);
277 	return (1);
278 }
279 
280 int
281 read10(int fd, uint32_t start_blk, uint16_t nblk, uchar_t *buf,
282 	uint32_t bufsize)
283 {
284 	struct uscsi_cmd *scmd;
285 
286 	scmd = get_uscsi_cmd();
287 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
288 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
289 	scmd->uscsi_cdb[0] = READ_10_CMD;
290 	load_scsi32(&scmd->uscsi_cdb[2], start_blk);
291 	scmd->uscsi_cdb[8] = nblk & 0xff;
292 	scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff;
293 	scmd->uscsi_cdblen = 10;
294 	scmd->uscsi_bufaddr = (char *)buf;
295 	scmd->uscsi_buflen = bufsize;
296 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
297 		return (0);
298 	return (1);
299 }
300 
301 int
302 write10(int fd, uint32_t start_blk, uint16_t nblk, uchar_t *buf,
303 	uint32_t bufsize)
304 {
305 	struct uscsi_cmd *scmd;
306 
307 	scmd = get_uscsi_cmd();
308 	scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
309 	/*
310 	 * Some DVD drives take longer to write than
311 	 * the standard time, since they tend to generate
312 	 * the media TOC on the fly when the cache is full
313 	 */
314 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT * 3;
315 	scmd->uscsi_cdb[0] = WRITE_10_CMD;
316 	load_scsi32(&scmd->uscsi_cdb[2], start_blk);
317 	scmd->uscsi_cdb[8] = nblk & 0xff;
318 	scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff;
319 	scmd->uscsi_cdblen = 10;
320 	scmd->uscsi_bufaddr = (char *)buf;
321 	scmd->uscsi_buflen = bufsize;
322 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
323 		return (0);
324 	return (1);
325 }
326 
327 int
328 close_track(int fd, int trackno, int close_session, int immediate)
329 {
330 	struct uscsi_cmd *scmd;
331 
332 	scmd = get_uscsi_cmd();
333 	scmd->uscsi_flags = USCSI_SILENT;
334 	scmd->uscsi_cdb[0] = CLOSE_TRACK_CMD;
335 	if (immediate) {
336 		scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
337 		scmd->uscsi_cdb[1] = 1;
338 	} else {
339 		scmd->uscsi_timeout = 240;
340 	}
341 	if ((close_session) || (device_type == DVD_PLUS) ||
342 	    (device_type == DVD_PLUS_W)) {
343 		/* close the session */
344 		scmd->uscsi_cdb[2] = 2;
345 
346 	} else {
347 		/* Close the track but leave session open */
348 		scmd->uscsi_cdb[2] = 1;
349 		scmd->uscsi_cdb[5] = trackno & 0xff;
350 	}
351 
352 	/*
353 	 * DVD+R media are already formatted, we are using
354 	 * a special case to notify that drive to close
355 	 * track/session and null-fill the remaining space.
356 	 */
357 	if (device_type == DVD_PLUS) {
358 		scmd->uscsi_cdb[5] = 1; /* only 1 track */
359 
360 		if (close_session) {
361 			scmd->uscsi_cdb[2] = 6; /* session */
362 		} else {
363 			scmd->uscsi_cdb[2] = 1; /* track */
364 		}
365 	}
366 
367 	scmd->uscsi_cdblen = 10;
368 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
369 		return (0);
370 	return (1);
371 }
372 
373 int
374 blank_disc(int fd, int type, int immediate)
375 {
376 	struct uscsi_cmd *scmd;
377 
378 	scmd = get_uscsi_cmd();
379 	scmd->uscsi_flags = USCSI_SILENT;
380 
381 	if (immediate) {
382 		scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
383 		scmd->uscsi_cdb[1] = 0x10;
384 	} else {
385 		scmd->uscsi_timeout = 0x12c0;
386 	}
387 	((uchar_t *)scmd->uscsi_cdb)[0] = BLANK_CMD;
388 
389 	/* tell it to blank the last session or all of the disk */
390 	scmd->uscsi_cdb[1] |= type & 0x07;
391 	scmd->uscsi_cdblen = 12;
392 
393 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
394 		return (0);
395 	return (1);
396 }
397 
398 int
399 read_cd(int fd, uint32_t start_blk, uint16_t nblk, uchar_t sector_type,
400 	uchar_t *buf, uint32_t bufsize)
401 {
402 	struct uscsi_cmd *scmd;
403 
404 	scmd = get_uscsi_cmd();
405 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
406 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
407 	((uchar_t *)scmd->uscsi_cdb)[0] = READ_CD_CMD;
408 	scmd->uscsi_cdb[1] = (sector_type & 0x7) << 2;
409 	scmd->uscsi_cdb[5] = start_blk & 0xff;
410 	scmd->uscsi_cdb[4] = (start_blk >> 8) & 0xff;
411 	scmd->uscsi_cdb[3] = (start_blk >> 16) & 0xff;
412 	scmd->uscsi_cdb[2] = (start_blk >> 24) & 0xff;
413 	scmd->uscsi_cdb[8] = nblk & 0xff;
414 	scmd->uscsi_cdb[7] = (nblk >> 8) & 0xff;
415 	scmd->uscsi_cdb[9] = 0x10;
416 	scmd->uscsi_cdblen = 12;
417 	scmd->uscsi_bufaddr = (char *)buf;
418 	scmd->uscsi_buflen = bufsize;
419 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
420 		return (0);
421 	return (1);
422 }
423 
424 int
425 load_unload(int fd, int load)
426 {
427 	struct uscsi_cmd *scmd;
428 
429 	scmd = get_uscsi_cmd();
430 	scmd->uscsi_flags = USCSI_SILENT;
431 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
432 	scmd->uscsi_cdb[0] = START_STOP_CMD;
433 	if (load == 0) {
434 		/* unload medium */
435 		scmd->uscsi_cdb[4] = 2;
436 	} else {
437 		/* load medium */
438 		scmd->uscsi_cdb[4] = 3;
439 	}
440 	scmd->uscsi_cdblen = 6;
441 
442 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
443 		return (0);
444 	return (1);
445 }
446 
447 int
448 prevent_allow_mr(int fd, int op)
449 {
450 	struct uscsi_cmd *scmd;
451 
452 	scmd = get_uscsi_cmd();
453 	scmd->uscsi_flags = USCSI_SILENT;
454 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
455 	scmd->uscsi_cdb[0] = PREVENT_ALLOW_CMD;
456 	if (!op) {	/* prevent */
457 		scmd->uscsi_cdb[4] = 1;
458 	}
459 	scmd->uscsi_cdblen = 6;
460 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
461 		return (0);
462 	return (1);
463 }
464 
465 int
466 set_cd_speed(int fd, uint16_t read_speed, uint16_t write_speed)
467 {
468 	struct uscsi_cmd *scmd;
469 
470 	scmd = get_uscsi_cmd();
471 	scmd->uscsi_flags = USCSI_SILENT;
472 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
473 	scmd->uscsi_cdblen = 0xc;
474 	((uchar_t *)scmd->uscsi_cdb)[0] = SET_CD_SPEED;
475 	scmd->uscsi_cdb[2] = (read_speed >> 8) & 0xff;
476 	scmd->uscsi_cdb[3] = read_speed & 0xff;
477 	scmd->uscsi_cdb[4] = (write_speed >> 8) & 0xff;
478 	scmd->uscsi_cdb[5] = write_speed & 0xff;
479 
480 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
481 		return (0);
482 	return (1);
483 }
484 
485 int
486 get_performance(int fd, int get_write_performance, uchar_t *perf)
487 {
488 	struct uscsi_cmd *scmd;
489 
490 	scmd = get_uscsi_cmd();
491 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
492 	scmd->uscsi_buflen = GET_PERF_DATA_LEN;
493 	scmd->uscsi_bufaddr = (char *)perf;
494 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
495 	scmd->uscsi_cdblen = 0xc;
496 	((uchar_t *)scmd->uscsi_cdb)[0] = GET_PERFORMANCE_CMD;
497 	scmd->uscsi_cdb[1] = 0x10;
498 	if (get_write_performance)
499 		scmd->uscsi_cdb[1] |= 4;
500 	scmd->uscsi_cdb[9] = 2;
501 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
502 		return (0);
503 	return (1);
504 }
505 
506 int
507 set_streaming(int fd, uchar_t *buf)
508 {
509 	struct uscsi_cmd *scmd;
510 
511 	scmd = get_uscsi_cmd();
512 	scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
513 	scmd->uscsi_buflen = SET_STREAM_DATA_LEN;
514 	scmd->uscsi_bufaddr = (char *)buf;
515 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
516 	scmd->uscsi_cdblen = 0xc;
517 	((uchar_t *)scmd->uscsi_cdb)[0] = STREAM_CMD;
518 	scmd->uscsi_cdb[10] = SET_STREAM_DATA_LEN;
519 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
520 		return (0);
521 	return (1);
522 }
523 
524 int
525 rezero_unit(int fd)
526 {
527 	struct uscsi_cmd *scmd;
528 
529 	scmd = get_uscsi_cmd();
530 	scmd->uscsi_flags = USCSI_SILENT;
531 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
532 	scmd->uscsi_cdblen = 0x6;
533 	scmd->uscsi_cdb[0] = REZERO_UNIT_CMD;
534 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
535 		return (0);
536 	return (1);
537 }
538 
539 int
540 start_stop(int fd, int start)
541 {
542 	struct uscsi_cmd *scmd;
543 
544 	scmd = get_uscsi_cmd();
545 	scmd->uscsi_flags = USCSI_SILENT;
546 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
547 	scmd->uscsi_cdblen = 0x6;
548 	scmd->uscsi_cdb[0] = START_STOP_CMD;
549 	if (start) {
550 		scmd->uscsi_cdb[4] = 1;
551 	}
552 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
553 		return (0);
554 	return (1);
555 }
556 
557 int
558 flush_cache(int fd)
559 {
560 	struct uscsi_cmd *scmd;
561 
562 	scmd = get_uscsi_cmd();
563 	scmd->uscsi_flags = USCSI_SILENT;
564 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
565 	scmd->uscsi_cdblen = 10;
566 	scmd->uscsi_cdb[0] = SYNC_CACHE_CMD;
567 	if (device_type != CD_RW) {
568 		scmd->uscsi_cdb[1] = 0x2; /* Immediate */
569 	}
570 
571 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
572 		return (0);
573 	return (1);
574 }
575 
576 /*
577  * used for DVD- to reserve the size we want to write.
578  * This is used by the drive to generate a TOC.
579  */
580 int
581 set_reservation(int fd, ulong_t size)
582 {
583 	struct uscsi_cmd *scmd;
584 
585 	scmd = get_uscsi_cmd();
586 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
587 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
588 	scmd->uscsi_cdb[0] = SET_RESERVATION_CMD;
589 	scmd->uscsi_cdblen = 10;
590 	scmd->uscsi_cdb[5] = (uchar_t)(size >> 24);
591 	scmd->uscsi_cdb[6] = (uchar_t)(size >> 16);
592 	scmd->uscsi_cdb[7] = (uchar_t)(size >> 8);
593 	scmd->uscsi_cdb[8] = (uchar_t)size;
594 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
595 		return (0);
596 	return (1);
597 }
598 
599 /*
600  * Used for DVD+RW media to prepare the disk to write.
601  * It will also be used for packet mode writing when
602  * it becomes supported.
603  */
604 int
605 format_media(int fd)
606 {
607 	struct uscsi_cmd *scmd;
608 	uchar_t buf[20];
609 
610 	(void) memset(buf, 0, 20);
611 	scmd = get_uscsi_cmd();
612 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
613 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
614 
615 	scmd->uscsi_cdblen = 12;
616 	scmd->uscsi_cdb[0] = READ_FORMAT_CAP_CMD;
617 	scmd->uscsi_cdb[8] = 0x14; /* buffer length */
618 	scmd->uscsi_buflen = 20;
619 	scmd->uscsi_bufaddr = (char *)buf;
620 
621 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
622 		return (0);
623 
624 	/* RE-use cap structure */
625 
626 	scmd->uscsi_flags = USCSI_WRITE|USCSI_SILENT;
627 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
628 	scmd->uscsi_cdblen = 6;
629 	scmd->uscsi_cdb[0] = FORMAT_UNIT_CMD;
630 	/* full format */
631 	scmd->uscsi_cdb[1] = 0x11;
632 	scmd->uscsi_buflen = 12;
633 	buf[1] = 0x82; /* immediate and FOV */
634 	buf[3] = 8;	/* descriptor length */
635 	buf[8] = 0x98;	/* type = 26 DVD+RW format */
636 	buf[10] = 0;
637 
638 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
639 		return (0);
640 	return (1);
641 }
642 
643 
644 /*
645  * Prefered method of reading the media size. This is
646  * the only supported method on several newer drives.
647  */
648 uint32_t
649 read_format_capacity(int fd, uint_t *bsize)
650 {
651 	struct uscsi_cmd *scmd;
652 	uint32_t filesize;
653 	char buf[20];
654 
655 	scmd = get_uscsi_cmd();
656 	scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
657 	scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
658 	scmd->uscsi_cdblen = 12;
659 	scmd->uscsi_cdb[0] = READ_FORMAT_CAP_CMD;
660 	scmd->uscsi_cdb[8] = 0x14;
661 	scmd->uscsi_buflen = 20;
662 	scmd->uscsi_bufaddr = buf;
663 
664 	if ((uscsi_error = uscsi(fd, scmd)) < 0)
665 		return (0);
666 
667 	filesize =  (uint32_t)(((uchar_t)buf[4] << 24) +
668 	    ((uchar_t)buf[5] << 16) + ((uchar_t)buf[6] << 8) + (uchar_t)buf[7]);
669 
670 	*bsize = (uint16_t)(((uchar_t)buf[10] << 8) + (uchar_t)buf[11]);
671 
672 	return (filesize);
673 }
674 
675 /*
676  * Function:    ftr_supported
677  *
678  * Description: Check to see if a device supports a Feature
679  *
680  * Arguments:   fd      - file descriptor
681  *              feature - the MMC Feature for which we'd like to know
682  *                        if there's support
683  *
684  * Return Code: 1       - Feature is supported
685  *		0	- Feature is not supported
686  *
687  */
688 int
689 ftr_supported(int fd, uint16_t feature)
690 {
691 	size_t response_len;
692 	uchar_t *bufp;
693 	int ret;
694 
695 	response_len = MMC_FTR_HDR_LEN + MMC_FTR_DSCRPTR_BASE_LEN;
696 	bufp = (uchar_t *)my_zalloc(response_len);
697 
698 	/*
699 	 * If a Feature is supported, a device will return a Feature Descriptor
700 	 * for that Feature, and its Current Bit will be set.
701 	 */
702 	if (get_configuration(fd, feature, response_len, bufp) == 1) {
703 		/*
704 		 * To check that a Feature Descriptor was returned, we
705 		 * check to see if the Data Length field of the Feature
706 		 * Header holds a value greater than four.  To check if
707 		 * the Current Bit is set, we check bit 1 of byte 10.
708 		 */
709 		if (read_scsi32(bufp) > 4 && bufp[10] & 1)
710 			ret = 1;
711 		else
712 			ret = 0;
713 	} else {
714 		/* get_configuration failed */
715 		ret = 0;
716 	}
717 	free(bufp);
718 	return (ret);
719 }
720 
721 /*
722  * Function:    print_profile_name
723  *
724  * Description: Prints a list of the Profiles the device supports
725  *
726  * Parameters:  num     - hexadecimal representation of Profile
727  *              current - 1 if the Profile is Current, otherwise 0
728  */
729 static void
730 print_profile_name(uint16_t num, uchar_t current)
731 {
732 	(void) printf(" 0x%04x: ", num);
733 	switch (num) {
734 	case 0x0000:
735 		(void) printf("No Current Profile");
736 		break;
737 	case 0x0001:
738 		(void) printf("Non-Removable Disk");
739 		break;
740 	case 0x0002:
741 		(void) printf("Removable Disk");
742 		break;
743 	case 0x0003:
744 		(void) printf("Magneto-Optical Erasable");
745 		break;
746 	case 0x0004:
747 		(void) printf("Optical Write Once");
748 		break;
749 	case 0x0005:
750 		(void) printf("AS-MO");
751 		break;
752 	case 0x0008:
753 		(void) printf("CD-ROM");
754 		break;
755 	case 0x0009:
756 		(void) printf("CD-R");
757 		break;
758 	case 0x000A:
759 		(void) printf("CD-RW");
760 		break;
761 	case 0x0010:
762 		(void) printf("DVD-ROM");
763 		break;
764 	case 0x0011:
765 		(void) printf("DVD-R Sequential Recording");
766 		break;
767 	case 0x0012:
768 		(void) printf("DVD-RAM");
769 		break;
770 	case 0x0013:
771 		(void) printf("DVD-RW Restricted Overwrite");
772 		break;
773 	case 0x0014:
774 		(void) printf("DVD-RW Sequential Recording");
775 		break;
776 	case 0x001A:
777 		(void) printf("DVD+RW");
778 		break;
779 	case 0x001B:
780 		(void) printf("DVD+R");
781 		break;
782 	case 0x0020:
783 		(void) printf("DDCD-ROM");
784 		break;
785 	case 0x0021:
786 		(void) printf("DDCD-R");
787 		break;
788 	case 0x0022:
789 		(void) printf("DDCD-RW");
790 		break;
791 	case 0x002B:
792 		(void) printf("DVD+R Double Layer");
793 		break;
794 	case 0x0040:
795 		(void) printf("BD-ROM");
796 		break;
797 	case 0x0041:
798 		(void) printf("BD-R Sequential Recording (SRM) Profile");
799 		break;
800 	case 0x0042:
801 		(void) printf("BD-R Random Recording (RRM) Profile");
802 		break;
803 	case 0x0043:
804 		(void) printf("BD-RE");
805 		break;
806 	case 0xFFFF:
807 		(void) printf("Nonstandard Profile");
808 		break;
809 	default:
810 		break;
811 	}
812 	if (current == 1)
813 		(void) printf(" (Current Profile)");
814 	(void) printf("\n");
815 }
816 
817 /*
818  * Function: print_profile_list
819  *
820  * Description: Print a list of Profiles supported by the Logical Unit.
821  *
822  * Parameters:	fd 	- file descriptor for device whose list of
823  *			  profiles we wish to print
824  */
825 void
826 print_profile_list(int fd)
827 {
828 	size_t i;
829 	size_t buflen;
830 	uint16_t current;
831 	uint16_t other;
832 	uchar_t *bufp = (uchar_t *)my_zalloc(MMC_FTR_HDR_LEN);
833 
834 	/*
835 	 * First get_configuration call is used to determine amount of memory
836 	 * needed to hold all the Profiles.  The first four bytes of bufp
837 	 * concatenated tell us the number of bytes of memory we need but do
838 	 * not take themselves into account.  Therefore, add four, and
839 	 * allocate that number of bytes.
840 	 */
841 	if (get_configuration(fd, MMC_FTR_PRFL_LIST, MMC_FTR_HDR_LEN,
842 	    bufp)) {
843 		buflen = read_scsi32(bufp) + 4;
844 		free(bufp);
845 		bufp = (uchar_t *)my_zalloc(buflen);
846 
847 		/*
848 		 * Now get all the Profiles
849 		 */
850 		if (get_configuration(fd, MMC_FTR_PRFL_LIST, buflen, bufp)) {
851 			(void) printf("\nProfile List\n");
852 			(void) printf("---------------------------------\n");
853 
854 			/*
855 			 * Find out the Logical Unit's Current Profile
856 			 */
857 			current = read_scsi16(&bufp[6]);
858 
859 			/*
860 			 * Print out the Profile List and indicate which
861 			 * Profile is Current
862 			 */
863 			for (i = MMC_FTR_HDR_LEN + MMC_FTR_DSCRPTR_BASE_LEN;
864 			    i < buflen; i += MMC_PRFL_DSCRPTR_LEN) {
865 				other = read_scsi16(&bufp[i]);
866 				if (other == current)
867 					print_profile_name(other, 1);
868 				else
869 					print_profile_name(other, 0);
870 			}
871 			(void) printf("\n");
872 		}
873 	}
874 	free(bufp);
875 }
876