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