xref: /netbsd-src/usr.sbin/mmcformat/mmcformat.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /* $NetBSD: mmcformat.c,v 1.2 2008/05/18 13:08:58 tron Exp $ */
2 
3 /*
4  * Copyright (c) 2006, 2008 Reinoud Zandijk
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <assert.h>
37 #include <limits.h>
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <inttypes.h>
41 
42 #include "uscsilib.h"
43 
44 
45 /* globals */
46 struct uscsi_dev dev;
47 extern int scsilib_verbose;
48 
49 /* #define DEBUG(a) {a;} */
50 #define DEBUG(a) ;
51 
52 
53 static uint64_t
54 getmtime(void)
55 {
56 	struct timeval tp;
57 
58 	gettimeofday(&tp, NULL);
59 	return (uint64_t) 1000000 * tp.tv_sec + tp.tv_usec;
60 }
61 
62 
63 static void
64 print_eta(uint32_t progress, uint64_t now, uint64_t start_time)
65 {
66 	int hours, minutes, seconds;
67 	uint64_t tbusy, ttot_est, eta;
68 
69 	if (progress == 0) {
70 		printf(" ETA --:--:--");
71 		return;
72 	}
73 	tbusy    = now - start_time;
74 	ttot_est = (tbusy * 0x10000) / progress;
75 	eta      = (ttot_est - tbusy) / 1000000;
76 
77 	hours   = (int) (eta/3600);
78 	minutes = (int) (eta/60) % 60;
79 	seconds = (int)  eta % 60;
80 	printf(" ETA %02d:%02d:%02d", hours, minutes, seconds);
81 }
82 
83 
84 static void
85 uscsi_waitop(struct uscsi_dev *mydev)
86 {
87 	scsicmd cmd;
88 	struct uscsi_sense sense;
89 	uint64_t start_time;
90 	uint32_t progress;
91 	uint8_t buffer[256];
92 	int asc, ascq;
93 	int cnt = 0;
94 
95 	bzero(cmd, SCSI_CMD_LEN);
96 	bzero(buffer, sizeof(buffer));
97 
98 	/*
99 	 * not be to unpatient... give the drive some time to start or it
100 	 * might break off
101 	 */
102 
103 	start_time = getmtime();
104 	sleep(10);
105 
106 	progress = 0;
107 	while (progress < 0x10000) {
108 		/* we need a command that is NOT going to stop the formatting */
109 		bzero(cmd, SCSI_CMD_LEN);
110 		cmd[0] = 0;			/* test unit ready */
111 		uscsi_command(SCSI_READCMD, mydev,
112 			cmd, 6, buffer, 0, 10000, &sense);
113 
114 		/*
115 		 * asc may be `not-ready' or `no-sense'. ascq for format in
116 		 * progress is 4 too
117 		 */
118 		asc  = sense.asc;
119 		ascq = sense.ascq;
120 		if (((asc == 0) && (ascq == 4)) || (asc == 4)) {
121 			/* drive not ready : operation/format in progress */
122 			if (sense.skey_valid) {
123 				progress = sense.sense_key;
124 			} else {
125 				/* finished */
126 				progress = 0x10000;
127 			}
128 		}
129 		/* check if drive is ready again, ifso break out loop */
130 		if ((asc == 0) && (ascq == 0)) {
131 			progress = 0x10000;
132 		}
133 
134 		printf("%3d %% ", (100 * progress / 0x10000));
135 		printf("%c", "|/-\\" [cnt++ %4]);   /* twirl */
136 
137 		/* print ETA */
138 		print_eta(progress, getmtime(), start_time);
139 
140 		fflush(stdout);
141 		sleep(1);
142 		printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
143 		fflush(stdout);
144 	}
145 	printf("\n");
146 
147 	return;
148 }
149 
150 
151 static char const *
152 print_mmc_profile(int profile)
153 {
154 	static char scrap[100];
155 
156 	switch (profile) {
157 	case 0x00 : return "Unknown[0] profile";
158 	case 0x01 : return "Non removeable disc";
159 	case 0x02 : return "Removable disc";
160 	case 0x03 : return "Magneto Optical with sector erase";
161 	case 0x04 : return "Magneto Optical write once";
162 	case 0x05 : return "Advance Storage Magneto Optical";
163 	case 0x08 : return "CD-ROM";
164 	case 0x09 : return "CD-R recordable";
165 	case 0x0a : return "CD-RW rewritable";
166 	case 0x10 : return "DVD-ROM";
167 	case 0x11 : return "DVD-R sequential";
168 	case 0x12 : return "DVD-RAM rewritable";
169 	case 0x13 : return "DVD-RW restricted overwrite";
170 	case 0x14 : return "DVD-RW sequential";
171 	case 0x1a : return "DVD+RW rewritable";
172 	case 0x1b : return "DVD+R recordable";
173 	case 0x20 : return "DDCD readonly";
174 	case 0x21 : return "DDCD-R recordable";
175 	case 0x22 : return "DDCD-RW rewritable";
176 	case 0x2b : return "DVD+R double layer";
177 	case 0x40 : return "BD-ROM";
178 	case 0x41 : return "BD-R Sequential Recording (SRM)";
179 	case 0x42 : return "BD-R Random Recording (RRM)";
180 	case 0x43 : return "BD-RE rewritable";
181 	}
182 	sprintf(scrap, "Reserved profile 0x%02x", profile);
183 	return scrap;
184 }
185 
186 
187 static int
188 uscsi_get_mmc_profile(struct uscsi_dev *mydev, int *mmc_profile)
189 {
190 	scsicmd	 cmd;
191 	uint8_t  buf[32];
192 	int error;
193 
194 	*mmc_profile = 0;
195 
196 	bzero(cmd, SCSI_CMD_LEN);
197 	cmd[ 0] = 0x46;				/* Get configuration */
198 	cmd[ 8] = 32;				/* just a small buffer size */
199 	cmd[ 9] = 0;				/* control */
200 	error = uscsi_command(SCSI_READCMD, mydev, cmd, 10, buf, 32, 30000, NULL);
201 	if (!error) {
202 		*mmc_profile = buf[7] | (buf[6] << 8);
203 	}
204 
205 	return error;
206 }
207 
208 
209 static int
210 uscsi_set_packet_parameters(struct uscsi_dev *mydev, int blockingnr)
211 {
212 	scsicmd  cmd;
213 	int      val_len;
214 	uint8_t  res[10000], *pos;
215 	int      error;
216 
217 	/* Set up CD/DVD recording parameters */
218 	DEBUG(printf("Setting device's recording parameters\n"));
219 
220 	val_len = 0x32+2+8;
221 	bzero(res, val_len);
222 
223 	pos = res + 8;
224 
225 	bzero(cmd, SCSI_CMD_LEN);
226 	pos[ 0] = 0x05;		/* page code 5 : cd writing		*/
227 	pos[ 1] = 0x32;		/* length in bytes			*/
228 	pos[ 2] = 0;		/* write type 0 : packet/incremental	*/
229 
230 	/* next session OK, data packet, rec. incr. fixed packets	*/
231 	pos[ 3] = (3<<6) | 32 | 5;
232 	pos[ 4] = 10;		/* ISO mode 2; XA form 1		*/
233 	pos[ 8] = 0x20;		/* CD-ROM XA disc or DDCD disc		*/
234 	pos[10] = (blockingnr >> 24) & 0xff;	/* MSB packet size 	*/
235 	pos[11] = (blockingnr >> 16) & 0xff;
236 	pos[12] = (blockingnr >>  8) & 0xff;
237 	pos[13] = (blockingnr      ) & 0xff;	/* LSB packet size 	*/
238 
239 	bzero(cmd, SCSI_CMD_LEN);
240 	cmd[0] = 0x55;			/* MODE SELECT (10)		*/
241 	cmd[1] = 16;			/* PF format			*/
242 	cmd[7] = val_len >> 8;		/* length of blob		*/
243 	cmd[8] = val_len & 0xff;
244 	cmd[9] = 0;			/* control			*/
245 
246 	error = uscsi_command(SCSI_WRITECMD, mydev,
247 			cmd, 10, res, val_len, 30000, NULL);
248 	if (error) {
249 		perror("While WRTITING parameter page 5");
250 		return error;
251 	}
252 
253 	/* flag OK */
254 	return 0;
255 }
256 
257 
258 static int
259 get_format_capabilities(struct uscsi_dev *mydev, uint8_t *buf, uint32_t *len)
260 {
261 	scsicmd		cmd;
262 	int		list_length;
263 	int		buf_len = 512, trans_len;
264 	int		error;
265 
266 	assert(*len >= buf_len);
267 	bzero(buf, buf_len);
268 
269 	trans_len = 12;				/* only fixed header first */
270 	bzero(cmd, SCSI_CMD_LEN);
271 	cmd[0] = 0x23;				/* Read format capabilities */
272 	cmd[7] = trans_len >> 8;		/* MSB allocation length */
273 	cmd[8] = trans_len & 0xff;		/* LSB allocation length */
274 	cmd[9] = 0;				/* control */
275 	error = uscsi_command(SCSI_READCMD, mydev,
276 			cmd, 10, buf, trans_len, 30000, NULL);
277 	if (error) {
278 		fprintf(stderr, "While reading format capabilities : %s\n",
279 			strerror(error));
280 		return error;
281 	}
282 
283 	list_length = buf[ 3];
284 
285 	if (list_length % 8) {
286 		printf( "\t\tWarning: violating SCSI spec,"
287 			"capacity list length ought to be multiple of 8\n");
288 		printf("\t\tInterpreting as including header of 4 bytes\n");
289 		assert(list_length % 8 == 4);
290 		list_length -= 4;
291 	}
292 
293 	/* read in full capacity list */
294 	trans_len = 12 + list_length;		/* complete structure */
295 	bzero(cmd, SCSI_CMD_LEN);
296 	cmd[0] = 0x23;				/* Read format capabilities */
297 	cmd[7] = trans_len >> 8;		/* MSB allocation length */
298 	cmd[8] = trans_len & 0xff;		/* LSB allocation length */
299 	cmd[9] = 0;				/* control */
300 	error = uscsi_command(SCSI_READCMD, mydev,
301 			cmd, 10, buf, trans_len, 30000, NULL);
302 	if (error) {
303 		fprintf(stderr, "While reading format capabilities : %s\n",
304 			strerror(error));
305 		return error;
306 	}
307 
308 	*len = list_length;
309 	return 0;
310 }
311 
312 
313 static void
314 print_format(int format_tp, uint32_t num_blks, uint32_t param,
315 	int dscr_type, int verbose, int *supported)
316 {
317 	char const *format_str, *nblks_str, *param_str, *user_spec;
318 
319 	format_str = nblks_str = param_str = "reserved";
320 	user_spec = "";
321 	*supported = 1;
322 
323 	switch (format_tp) {
324 	case  0x00 :
325 		format_str = "full format capacity";
326 		nblks_str  = "sectors";
327 		param_str  = "block length in bytes";
328 		user_spec  = "'-F [-b blockingnr]'";
329 		break;
330 	case  0x01 :
331 		format_str = "spare area expansion";
332 		nblks_str  = "extension in blocks";
333 		param_str  = "block length in bytes";
334 		user_spec  = "'-S'";
335 		break;
336 	/* 0x02 - 0x03 reserved */
337 	case  0x04 :
338 		format_str = "variable length zone'd format";
339 		nblks_str  = "zone length";
340 		param_str  = "zone number";
341 		*supported = 0;
342 		break;
343 	case  0x05 :
344 		format_str = "fixed length zone'd format";
345 		nblks_str  = "zone lenght";
346 		param_str  = "last zone number";
347 		*supported = 0;
348 		break;
349 	/* 0x06 - 0x0f reserved */
350 	case  0x10 :
351 		format_str = "CD-RW/DVD-RW full packet format";
352 		nblks_str  = "adressable blocks";
353 		param_str  = "fixed packet size/ECC blocksize in sectors";
354 		user_spec  = "'-F -p [-b blockingnr]'";
355 		break;
356 	case  0x11 :
357 		format_str = "CD-RW/DVD-RW grow session";
358 		nblks_str  = "adressable blocks";
359 		param_str  = "fixed packet size/ECC blocksize in sectors";
360 		user_spec  = "'-G'";
361 		break;
362 	case  0x12 :
363 		format_str = "CD-RW/DVD-RW add session";
364 		nblks_str  = "adressable blocks";
365 		param_str  = "maximum fixed packet size/ECC blocksize "
366 			     "in sectors";
367 		*supported = 0;
368 		break;
369 	case  0x13 :
370 		format_str = "DVD-RW max growth of last complete session";
371 		nblks_str  = "adressable blocks";
372 		param_str  = "ECC blocksize in sectors";
373 		user_spec  = "'-G'";
374 		break;
375 	case  0x14 :
376 		format_str = "DVD-RW quick grow last session";
377 		nblks_str  = "adressable blocks";
378 		param_str  = "ECC blocksize in sectors";
379 		*supported = 0;
380 		break;
381 	case  0x15 :
382 		format_str = "DVD-RW quick full format";
383 		nblks_str  = "adressable blocks";
384 		param_str  = "ECC blocksize in sectors";
385 		*supported = 0;
386 		break;
387 	/* 0x16 - 0x23 reserved */
388 	case  0x24 :
389 		format_str = "background MRW format";
390 		nblks_str  = "Defect Management Area blocks";
391 		param_str  = "not used";
392 		user_spec  = "'[-R] [-s] [-w] -F -M [-b blockingnr]'";
393 		break;
394 	/* 0x25 reserved */
395 	case  0x26 :
396 		format_str = "background DVD+RW full format";
397 		nblks_str  = "sectors";
398 		param_str  = "not used";
399 		user_spec  = "'[-R] [-w] -F'";
400 		break;
401 	/* 0x27 - 0x2f reserved */
402 	case  0x30 :
403 		format_str = "BD-RE full format with spare area";
404 		nblks_str  = "blocks";
405 		param_str  = "total spare area size in clusters";
406 		user_spec  = "'[-s] -F'";
407 		break;
408 	case  0x31 :
409 		format_str = "BD-RE full format without spare area";
410 		nblks_str  = "blocks";
411 		param_str  = "block length in bytes";
412 		user_spec  = "'-F'";
413 		break;
414 	/* 0x32 - 0x3f reserved */
415 	default :
416 		break;
417 	}
418 
419 	if (verbose) {
420 		printf("\n\tFormat type 0x%02x : %s\n", format_tp, format_str);
421 
422 		switch (dscr_type) {
423 		case  1 :
424 			printf( "\t\tUnformatted media,"
425 				"maximum formatted capacity\n");
426 			break;
427 		case  2 :
428 			printf( "\t\tFormatted media,"
429 				"current formatted capacity\n");
430 			break;
431 		case  3 :
432 			printf( "\t\tNo media present or incomplete session, "
433 				"maximum formatted capacity\n");
434 			break;
435 		default :
436 			printf("\t\tUnspecified descriptor type\n");
437 			break;
438 		}
439 
440 		printf("\t\tNumber of blocks : %12d\t(%s)\n",
441 			num_blks, nblks_str);
442 		printf("\t\tParameter        : %12d\t(%s)\n",
443 			param, param_str);
444 
445 		if (format_tp == 0x24) {
446 			printf( "\t\tExpert select    : "
447 				"'-X 0x%02x:0xffffff:0' or "
448 				"'-X 0x%02x:0xffff0000:0'\n",
449 				format_tp, format_tp);
450 		} else {
451 			printf( "\t\tExpert select    : "
452 				"'-X 0x%02x:%d:%d'\n",
453 				format_tp, num_blks, param);
454 		}
455 		if (*supported) {
456 			printf("\t\tmmc_format arg   : %s\n", user_spec);
457 		} else {
458 			printf("\t\t** not supported **\n");
459 		}
460 	}
461 }
462 
463 
464 static void
465 process_format_caps(uint8_t *buf, int list_length, int verbose,
466 	uint8_t *allow, uint32_t *blks, uint32_t *params)
467 {
468 	uint32_t	num_blks, param;
469 	uint8_t	       *fcd;
470 	int		dscr_type, format_tp;
471 	int		supported;
472 
473 	bzero(allow,  255);
474 	bzero(blks,   255*4);
475 	bzero(params, 255*4);
476 
477 	fcd = buf + 4;
478 	list_length -= 4;		/* strip header */
479 
480 	if (verbose)
481 		printf("\tCurrent/max capacity followed by additional capacity,"
482 			"reported length of %d bytes (8/entry)\n", list_length);
483 
484 	while (list_length > 0) {
485 		num_blks    = fcd[ 3]        | (fcd[ 2] << 8) |
486 			     (fcd[ 1] << 16) | (fcd[ 0] << 24);
487 		dscr_type   = fcd[ 4] & 3;
488 		format_tp   = fcd[ 4] >> 2;
489 		param       = fcd[ 7] | (fcd[ 6] << 8) |  (fcd[ 5] << 16);
490 
491 		print_format(format_tp, num_blks, param, dscr_type, verbose,
492 			&supported);
493 
494 		 allow[format_tp] = 1;	/* TODO = supported? */
495 		  blks[format_tp] = num_blks;
496 		params[format_tp] = param;
497 
498 		fcd += 8;
499 		list_length-=8;
500 	}
501 }
502 
503 
504 
505 /* format a CD-RW disc */
506 /* old style format 7 */
507 static int
508 uscsi_format_cdrw_mode7(struct uscsi_dev *mydev, uint32_t blocks)
509 {
510 	scsicmd cmd;
511 	struct uscsi_sense sense;
512 	uint32_t param;
513 	uint8_t  buffer[16];
514 	int cnt, error;
515 
516 	param = cnt = 0;
517 
518 	if (blocks % 32) {
519 		blocks -= blocks % 32;
520 	}
521 
522 	bzero(cmd, SCSI_CMD_LEN);
523 	bzero(buffer, sizeof(buffer));
524 
525 	cmd[0] = 0x04;			/* format unit 			   */
526 	cmd[1] = 0x17;			/* parameter list format 7 follows */
527 	cmd[5] = 0;			/* control			   */
528 
529 	/* format list header */
530 	buffer[ 0] = 0;			/* reserved			   */
531 	buffer[ 1] = 0x80 | 0x02;	/* Valid info, immediate return	   */
532 	buffer[ 2] = 0;			/* MSB format descriptor length	   */
533 	buffer[ 3] = 8;			/* LSB ...			   */
534 
535 	/*
536 	 * for CD-RW the initialisation pattern bit is reserved, but there IS
537 	 * one
538 	 */
539 
540 	buffer[ 4] = 0;			/* no header			   */
541 	buffer[ 5] = 0;			/* default pattern		   */
542 	buffer[ 6] = 0;			/* pattern length MSB		   */
543 	buffer[ 7] = 0;			/* pattern length LSB		   */
544 
545 	/* 8 bytes of format descriptor */
546 	/* (s)ession bit 1<<7, (g)row bit 1<<6  */
547 	/* SG	action	*/
548 	/* 00	format disc with number of user data blocks	*/
549 	/* 10	create new session with number of data blocks	*/
550 	/* x1	grow session to be number of data blocks	*/
551 
552 	buffer[ 8] = 0x00;		/* session and grow bits (7 and 6)  */
553 	buffer[ 9] = 0;			/* reserved */
554 	buffer[10] = 0;			/* reserved */
555 	buffer[11] = 0;			/* reserved */
556 	buffer[12] = (blocks >> 24) & 0xff;	/* blocks MSB	*/
557 	buffer[13] = (blocks >> 16) & 0xff;
558 	buffer[14] = (blocks >>  8) & 0xff;
559 	buffer[15] = (blocks      ) & 0xff;	/* blocks LSB	*/
560 
561 	/* this will take a while .... */
562 	error = uscsi_command(SCSI_WRITECMD, mydev,
563 			cmd, 6, buffer, sizeof(buffer), UINT_MAX, &sense);
564 	if (error)
565 		return error;
566 
567 	uscsi_waitop(mydev);
568 	return 0;
569 }
570 
571 
572 static int
573 uscsi_format_disc(struct uscsi_dev *mydev, int immed, int format_type,
574 	uint32_t blocks, uint32_t param, int certification, int cmplist)
575 {
576 	scsicmd cmd;
577 	struct uscsi_sense sense;
578 	uint8_t  buffer[16], fmt_flags;
579 	int error;
580 
581 	fmt_flags = 0x80;		/* valid info flag */
582 	if (immed)
583 		fmt_flags |= 2;
584 	if (certification == 0)
585 		fmt_flags |= 32;
586 
587 	if (cmplist)
588 		cmplist = 8;
589 
590 #if 0
591 	if (mmc_profile != 0x43) {
592 		/* certification specifier only valid for BD-RE */
593 		certification = 0;
594 	}
595 #endif
596 
597 	bzero(cmd, SCSI_CMD_LEN);
598 	bzero(buffer, sizeof(buffer));
599 
600 	cmd[0] = 0x04;			/* format unit 			   */
601 	cmd[1] = 0x11 | cmplist;	/* parameter list format 1 follows */
602 	cmd[5] = 0;			/* control			   */
603 
604 	/* format list header */
605 	buffer[ 0] = 0;			/* reserved			   */
606 	buffer[ 1] = 0x80 | fmt_flags;	/* Valid info, flags follow	   */
607 	buffer[ 2] = 0;			/* MSB format descriptor length    */
608 	buffer[ 3] = 8;			/* LSB ...			   */
609 
610 	/* 8 bytes of format descriptor */
611 	buffer[ 4] = (blocks >> 24) & 0xff;	/* blocks MSB	     */
612 	buffer[ 5] = (blocks >> 16) & 0xff;
613 	buffer[ 6] = (blocks >>  8) & 0xff;
614 	buffer[ 7] = (blocks      ) & 0xff;	/* blocks LSB	     */
615 	buffer[ 8] = (format_type << 2) | certification;
616 	buffer[ 9] = (param  >> 16) & 0xff;	/* parameter MSB     */
617 	buffer[10] = (param  >>  8) & 0xff;	/*	packet size  */
618 	buffer[11] = (param       ) & 0xff;	/* parameter LSB     */
619 
620 	/* this will take a while .... */
621 	error = uscsi_command(SCSI_WRITECMD, mydev,
622 			cmd, 6, buffer, 12, UINT_MAX, &sense);
623 	if (error)
624 		return error;
625 
626 	if (immed)
627 		uscsi_waitop(mydev);
628 
629 	return 0;
630 }
631 
632 
633 static int
634 uscsi_blank_disc(struct uscsi_dev *mydev)
635 {
636 	scsicmd cmd;
637 	int error;
638 
639 	/* XXX check if the device can blank! */
640 
641 
642 	/* blank disc */
643 	bzero(cmd, SCSI_CMD_LEN);
644 	cmd[ 0] = 0xA1;			/* blank			*/
645 	cmd[ 1] = 16;			/* Immediate, blank complete	*/
646 	cmd[11] = 0;			/* control			*/
647 
648 	/* this will take a while .... */
649 	error = uscsi_command(SCSI_WRITECMD, mydev,
650 			cmd, 12, NULL, 0, UINT_MAX, NULL);
651 	if (error)
652 		return error;
653 
654 	uscsi_waitop(mydev);
655 	return 0;
656 }
657 
658 
659 static int
660 usage(char *program)
661 {
662 	fprintf(stderr, "\n");
663 	fprintf(stderr, "Usage: %s [options] devicename\n", program);
664 	fprintf(stderr,
665 	"-B		blank cd-rw disc before formatting\n"
666 	"-F		format cd-rw disc\n"
667 	"-O		CD-RW formatting 'old-style' for old CD-RW drives\n"
668 	"-M		select MRW format\n"
669 	"-R		restart MRW & DVD+RW format\n"
670 	"-G		grow last CD-RW/DVD-RW session\n"
671 	"-S		grow spare space DVD-RAM/BD-RE\n"
672 	"-s		format DVD+MRW/BD-RE with extra spare space\n"
673 	"-w		wait until completion of background format\n"
674 	"-p		explicitly set packet format\n"
675 	"-c num		media certification for DVD-RAM/BD-RE : "
676 			"0 no, 1 full, 2 quick\n"
677 	"-r		recompile defect list for DVD-RAM (cmplist)\n"
678 	"-h -H -I	help/inquiry formats\n"
679 	"-X format	expert format selector form 'fmt:blks:param' with -c\n"
680 	"-b blockingnr	in sectors (for CD-RW)\n"
681 	"-D 		verbose SCSI command errors\n"
682 	);
683 	return 1;
684 }
685 
686 
687 extern char	*optarg;
688 extern int	 optind;
689 extern int	 optreset;
690 
691 
692 int
693 main(int argc, char *argv[])
694 {
695 	struct uscsi_addr saddr;
696 	uint32_t blks[256], params[256];
697 	uint32_t format_type, format_blks, format_param, blockingnr;
698 	uint8_t  allow[256];
699 	uint8_t caps[512];
700 	uint32_t caps_len = sizeof(caps);
701 	char *progname;
702 	int blank, format, mrw, background;
703 	int inquiry, spare, oldtimer;
704 	int expert;
705 	int restart_format, grow_session, grow_spare, packet_wr;
706 	int mmc_profile, flag, error, display_usage;
707 	int certification, cmplist;
708 	int wait_until_finished;
709 	progname = strdup(argv[0]);
710 	if (argc == 1) {
711 		return usage(progname);
712 	}
713 
714 	blank               = 0;
715 	format              = 0;
716 	mrw                 = 0;
717 	restart_format      = 0;
718 	grow_session        = 0;
719 	grow_spare          = 0;
720 	wait_until_finished = 0;
721 	packet_wr           = 0;
722 	certification       = 1;
723 	cmplist             = 0;
724 	inquiry             = 0;
725 	spare               = 0;
726 	inquiry             = 0;
727 	oldtimer            = 0;
728 	expert              = 0;
729 	display_usage       = 0;
730 	blockingnr          = 32;
731 	uscsilib_verbose    = 0;
732 	while ((flag = getopt(argc, argv, "BFMRGSwpsc:rhHIX:Ob:D")) != -1) {
733 		switch (flag) {
734 			case 'B' :
735 				blank = 1;
736 				break;
737 			case 'F' :
738 				format = 1;
739 				break;
740 			case 'M' :
741 				mrw = 1;
742 				break;
743 			case 'R' :
744 				restart_format = 1;
745 				break;
746 			case 'G' :
747 				grow_session = 1;
748 				break;
749 			case 'S' :
750 				grow_spare = 1;
751 				break;
752 			case 'w' :
753 				wait_until_finished = 1;
754 				break;
755 			case 'p' :
756 				packet_wr = 1;
757 				break;
758 			case 's' :
759 				spare = 1;
760 				break;
761 			case 'c' :
762 				certification = atoi(optarg);
763 				break;
764 			case 'r' :
765 				cmplist = 1;
766 				break;
767 			case 'h' :
768 			case 'H' :
769 				display_usage = 1;
770 			case 'I' :
771 				inquiry = 1;
772 				break;
773 			case 'X' :
774 				/* TODO parse expert mode string */
775 				printf("-X not implemented yet\n");
776 				expert = 1;
777 				exit(1);
778 				break;
779 			case 'O' :
780 				/* oldtimer CD-RW format */
781 				oldtimer = 1;
782 				format   = 1;
783 				break;
784 			case 'b' :
785 				blockingnr = atoi(optarg);
786 				break;
787 			case 'D' :
788 				uscsilib_verbose = 1;
789 				break;
790 			default :
791 				return usage(progname);
792 		}
793 	}
794 	argv += optind;
795 	argc -= optind;
796 
797 	if ((!blank && !format && !grow_session && !grow_spare) &&
798 	    (!expert && !inquiry)) {
799 		fprintf(stderr, "%s : at least one of -B, -F, -G, -S, -X or -I "
800 				"needs to be specified\n\n", progname);
801 		return usage(progname);
802 	}
803 
804 	if (format + grow_session + grow_spare + expert > 1) {
805 		fprintf(stderr, "%s : at most one of -F, -G, -S or -X "
806 				"needs to be specified\n\n", progname);
807 		return usage(progname);
808 	}
809 
810 	if (argc != 1) return usage(progname);
811 
812 	/* Open the device */
813 	dev.dev_name = strdup(*argv);
814 	printf("Opening device %s\n", dev.dev_name);
815 	error = uscsi_open(&dev);
816 	if (error) {
817 		fprintf(stderr, "Device failed to open : %s\n",
818 			strerror(error));
819 		exit(1);
820 	}
821 
822 	error = uscsi_check_for_scsi(&dev);
823 	if (error) {
824 		fprintf(stderr, "sorry, not a SCSI/ATAPI device : %s\n",
825 			strerror(error));
826 		exit(1);
827 	}
828 
829 	error = uscsi_identify(&dev, &saddr);
830 	if (error) {
831 		fprintf(stderr, "SCSI/ATAPI identify returned : %s\n",
832 			strerror(error));
833 		exit(1);
834 	}
835 
836 	printf("\nDevice identifies itself as : ");
837 
838 	if (saddr.type == USCSI_TYPE_SCSI) {
839 		printf("SCSI   busnum = %d, target = %d, lun = %d\n",
840 			saddr.addr.scsi.scbus, saddr.addr.scsi.target,
841 			saddr.addr.scsi.lun);
842 	} else {
843 		printf("ATAPI  busnum = %d, drive = %d\n",
844 			saddr.addr.atapi.atbus, saddr.addr.atapi.drive);
845 	}
846 
847 	printf("\n");
848 
849 	/* get MMC profile */
850 	error = uscsi_get_mmc_profile(&dev, &mmc_profile);
851 	if (error) {
852 		fprintf(stderr,
853 			"Can't get the disc's MMC profile because of :"
854 			" %s\n", strerror(error));
855 		fprintf(stderr, "aborting\n");
856 		uscsi_close(&dev);
857 		return 1;
858 	}
859 
860 	/* blank disc section */
861 	if (blank) {
862 		printf("\nBlanking disc.... "); fflush(stdout);
863 		error = uscsi_blank_disc(&dev);
864 
865 		if (error) {
866 			printf("fail\n"); fflush(stdout);
867 			fprintf(stderr,
868 				"Blanking failed because of : %s\n",
869 				strerror(error));
870 			uscsi_close(&dev);
871 
872 			return 1;
873 		} else {
874 			printf("success!\n\n");
875 		}
876 	}
877 
878 	/* re-get MMC profile */
879 	error = uscsi_get_mmc_profile(&dev, &mmc_profile);
880 	if (error) {
881 		fprintf(stderr,
882 			"Can't get the disc's MMC profile because of : %s\n",
883 			strerror(error));
884 		fprintf(stderr, "aborting\n");
885 		uscsi_close(&dev);
886 		return 1;
887 	}
888 
889 	error = get_format_capabilities(&dev, caps, &caps_len);
890 	if (error)
891 		exit(1);
892 
893 	process_format_caps(caps, caps_len, inquiry, allow, blks, params);
894 
895 	format_type = 0;
896 	/* expert format section */
897 	if (expert) {
898 	}
899 
900 	if (!format && !grow_spare && !grow_session) {
901 		/* we're done */
902 		if (display_usage)
903 			usage(progname);
904 		uscsi_close(&dev);
905 		exit(0);
906 	}
907 
908 	/* normal format section */
909 	if (format) {
910 		/* get current mmc profile of disc */
911 
912 		if (oldtimer && mmc_profile != 0x0a) {
913 			printf("Oldtimer flag only defined for CD-RW; "
914 				"ignored\n");
915 		}
916 
917 		switch (mmc_profile) {
918 		case 0x12 :	/* DVD-RAM	*/
919 			format_type = 0x00;
920 			break;
921 		case 0x0a :	/* CD-RW	*/
922 			format_type = mrw ? 0x24 : 0x10;
923 			packet_wr   = 1;
924 			break;
925 		case 0x13 :	/* DVD-RW restricted overwrite */
926 		case 0x14 :	/* DVD-RW sequential 		*/
927 			format_type = 0x10;
928 			/*
929 			 * Some drives suddenly stop supporting this format
930 			 * type when packet_wr = 1
931 			 */
932 			packet_wr   = 0;
933 			break;
934 		case 0x1a :	/* DVD+RW	*/
935 			format_type = mrw ? 0x24 : 0x26;
936 			break;
937 		case 0x43 :	/* BD-RE	*/
938 			format_type = spare ? 0x30 : 0x31;
939 			break;
940 		default :
941 			fprintf(stderr, "Can't format discs of type %s\n",
942 				print_mmc_profile(mmc_profile));
943 			uscsi_close(&dev);
944 			exit(1);
945 		}
946 	}
947 
948 	if (grow_spare) {
949 		switch (mmc_profile) {
950 		case 0x12 :	/* DVD-RAM */
951 		case 0x43 :	/* BD-RE   */
952 			format_type = 0x01;
953 			break;
954 		default :
955 			fprintf(stderr,
956 				"Can't grow spare area for discs of type %s\n",
957 				print_mmc_profile(mmc_profile));
958 			uscsi_close(&dev);
959 			exit(1);
960 		}
961 	}
962 
963 	if (grow_session) {
964 		switch (mmc_profile) {
965 		case 0x0a :	/* CD-RW */
966 			format_type = 0x11;
967 			break;
968 		case 0x13 :	/* DVD-RW restricted overwrite */
969 		case 0x14 :	/* DVD-RW sequential ? */
970 			format_type = 0x13;
971 			break;
972 		default :
973 			uscsi_close(&dev);
974 			fprintf(stderr,
975 				"Can't grow session for discs of type %s\n",
976 				print_mmc_profile(mmc_profile));
977 			exit(1);
978 		}
979 	}
980 
981 	/* check if format type is allowed */
982 	format_blks  = blks[format_type];
983 	format_param = params[format_type];
984 	if (!allow[format_type]) {
985 		if (!inquiry)
986 			process_format_caps(caps, caps_len, 1, allow,
987 				blks, params);
988 
989 		printf("\n");
990 		fflush(stdout);
991 		fprintf(stderr,
992 			"Drive indicates it can't format with deduced format "
993 			"type 0x%02x\n", format_type);
994 		uscsi_close(&dev);
995 		exit(1);
996 	}
997 
998 	if (restart_format && !((mmc_profile == 0x1a) || (format_type == 0x24)))
999 	{
1000 		fprintf(stderr,
1001 			"Format restarting only for MRW formats or DVD+RW "
1002 			"formats\n");
1003 		uscsi_close(&dev);
1004 		exit(1);
1005 	}
1006 
1007 	if (restart_format && !wait_until_finished) {
1008 		printf( "Warning : format restarting without waiting for it be "
1009 			"finished is prolly not handy\n");
1010 	}
1011 
1012 	/* explicitly select packet write just in case */
1013 	if (packet_wr) {
1014 		printf("Explicitly setting packet type and blocking number\n");
1015 		error = uscsi_set_packet_parameters(&dev, blockingnr);
1016 		if (error) {
1017 			fprintf(stderr,
1018 				"Can't set packet writing and blocking number: "
1019 				"%s\n", strerror(error));
1020 			uscsi_close(&dev);
1021 			exit(1);
1022 		}
1023 	}
1024 
1025 	/* determine if formatting is done in the background */
1026 	background = 0;
1027 	if (format_type == 0x24) background = 1;
1028 	if (format_type == 0x26) background = 1;
1029 
1030 	/* special case format type 0x24 : MRW */
1031 	if (format_type == 0x24) {
1032 		format_blks  = spare ? 0xffff0000 : 0xffffffff;
1033 		format_param = restart_format;
1034 	}
1035 	/* special case format type 0x26 : DVD+RW */
1036 	if (format_type == 0x26) {
1037 		format_param = restart_format;
1038 	}
1039 
1040 	/* verbose to the user */
1041 	DEBUG(
1042 		printf("Actual format selected: "
1043 			"format_type 0x%02x, blks %d, param %d, "
1044 			"certification %d, cmplist %d\n",
1045 			format_type, format_blks, format_param,
1046 			certification, cmplist);
1047 	);
1048 	printf("\nFormatting.... "); fflush(stdout);
1049 
1050 	/* formatting time! */
1051 	if (oldtimer) {
1052 		error = uscsi_format_cdrw_mode7(&dev, format_blks);
1053 		background = 0;
1054 	} else {
1055 		error = uscsi_format_disc(&dev, !background, format_type,
1056 				format_blks, format_param, certification,
1057 				cmplist);
1058 	}
1059 
1060 	/* what now? */
1061 	if (error) {
1062 		printf("fail\n"); fflush(stdout);
1063 		fprintf(stderr, "Formatting failed because of : %s\n",
1064 			strerror(error));
1065 	} else {
1066 		if (background) {
1067 			printf("background formatting in progress\n");
1068 			if (wait_until_finished) {
1069 				printf("Waiting for completion ... ");
1070 				uscsi_waitop(&dev);
1071 			}
1072 			/* explicitly do NOT close disc ... (for now) */
1073 			return 0;
1074 		} else {
1075 			printf("success!\n\n");
1076 		}
1077 	}
1078 
1079 	/* finish up */
1080 	uscsi_close(&dev);
1081 
1082 	return error;
1083 }
1084 
1085