xref: /netbsd-src/usr.sbin/mmcformat/mmcformat.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /* $NetBSD: mmcformat.c,v 1.6 2019/12/26 04:53:12 msaitoh 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		trans_len;
264 	size_t		buf_len = 512;
265 	int		error;
266 
267 	assert(*len >= buf_len);
268 	bzero(buf, buf_len);
269 
270 	trans_len = 12;				/* only fixed header first */
271 	bzero(cmd, SCSI_CMD_LEN);
272 	cmd[0] = 0x23;				/* Read format capabilities */
273 	cmd[7] = trans_len >> 8;		/* MSB allocation length */
274 	cmd[8] = trans_len & 0xff;		/* LSB allocation length */
275 	cmd[9] = 0;				/* control */
276 	error = uscsi_command(SCSI_READCMD, mydev,
277 			cmd, 10, buf, trans_len, 30000, NULL);
278 	if (error) {
279 		fprintf(stderr, "While reading format capabilities : %s\n",
280 			strerror(error));
281 		return error;
282 	}
283 
284 	list_length = buf[ 3];
285 
286 	if (list_length % 8) {
287 		printf( "\t\tWarning: violating SCSI spec,"
288 			"capacity list length ought to be multiple of 8\n");
289 		printf("\t\tInterpreting as including header of 4 bytes\n");
290 		assert(list_length % 8 == 4);
291 		list_length -= 4;
292 	}
293 
294 	/* read in full capacity list */
295 	trans_len = 12 + list_length;		/* complete structure */
296 	bzero(cmd, SCSI_CMD_LEN);
297 	cmd[0] = 0x23;				/* Read format capabilities */
298 	cmd[7] = trans_len >> 8;		/* MSB allocation length */
299 	cmd[8] = trans_len & 0xff;		/* LSB allocation length */
300 	cmd[9] = 0;				/* control */
301 	error = uscsi_command(SCSI_READCMD, mydev,
302 			cmd, 10, buf, trans_len, 30000, NULL);
303 	if (error) {
304 		fprintf(stderr, "While reading format capabilities : %s\n",
305 			strerror(error));
306 		return error;
307 	}
308 
309 	*len = list_length;
310 	return 0;
311 }
312 
313 
314 static void
315 print_format(int format_tp, uint32_t num_blks, uint32_t param,
316 	int dscr_type, int verbose, int *supported)
317 {
318 	char const *format_str, *nblks_str, *param_str, *user_spec;
319 
320 	format_str = nblks_str = param_str = "reserved";
321 	user_spec = "";
322 	*supported = 1;
323 
324 	switch (format_tp) {
325 	case  0x00 :
326 		format_str = "full format capacity";
327 		nblks_str  = "sectors";
328 		param_str  = "block length in bytes";
329 		user_spec  = "'-F [-b blockingnr]'";
330 		break;
331 	case  0x01 :
332 		format_str = "spare area expansion";
333 		nblks_str  = "extension in blocks";
334 		param_str  = "block length in bytes";
335 		user_spec  = "'-S'";
336 		break;
337 	/* 0x02 - 0x03 reserved */
338 	case  0x04 :
339 		format_str = "variable length zone'd format";
340 		nblks_str  = "zone length";
341 		param_str  = "zone number";
342 		*supported = 0;
343 		break;
344 	case  0x05 :
345 		format_str = "fixed length zone'd format";
346 		nblks_str  = "zone length";
347 		param_str  = "last zone number";
348 		*supported = 0;
349 		break;
350 	/* 0x06 - 0x0f reserved */
351 	case  0x10 :
352 		format_str = "CD-RW/DVD-RW full packet format";
353 		nblks_str  = "adressable blocks";
354 		param_str  = "fixed packet size/ECC blocksize in sectors";
355 		user_spec  = "'-F -p [-b blockingnr]'";
356 		break;
357 	case  0x11 :
358 		format_str = "CD-RW/DVD-RW grow session";
359 		nblks_str  = "adressable blocks";
360 		param_str  = "fixed packet size/ECC blocksize in sectors";
361 		user_spec  = "'-G'";
362 		break;
363 	case  0x12 :
364 		format_str = "CD-RW/DVD-RW add session";
365 		nblks_str  = "adressable blocks";
366 		param_str  = "maximum fixed packet size/ECC blocksize "
367 			     "in sectors";
368 		*supported = 0;
369 		break;
370 	case  0x13 :
371 		format_str = "DVD-RW max growth of last complete session";
372 		nblks_str  = "adressable blocks";
373 		param_str  = "ECC blocksize in sectors";
374 		user_spec  = "'-G'";
375 		break;
376 	case  0x14 :
377 		format_str = "DVD-RW quick grow last session";
378 		nblks_str  = "adressable blocks";
379 		param_str  = "ECC blocksize in sectors";
380 		*supported = 0;
381 		break;
382 	case  0x15 :
383 		format_str = "DVD-RW quick full format";
384 		nblks_str  = "adressable blocks";
385 		param_str  = "ECC blocksize in sectors";
386 		*supported = 0;
387 		break;
388 	/* 0x16 - 0x23 reserved */
389 	case  0x24 :
390 		format_str = "background MRW format";
391 		nblks_str  = "Defect Management Area blocks";
392 		param_str  = "not used";
393 		user_spec  = "'[-R] [-s] [-w] -F -M [-b blockingnr]'";
394 		break;
395 	/* 0x25 reserved */
396 	case  0x26 :
397 		format_str = "background DVD+RW full format";
398 		nblks_str  = "sectors";
399 		param_str  = "not used";
400 		user_spec  = "'[-R] [-w] -F'";
401 		break;
402 	/* 0x27 - 0x2f reserved */
403 	case  0x30 :
404 		format_str = "BD-RE full format with spare area";
405 		nblks_str  = "blocks";
406 		param_str  = "total spare area size in clusters";
407 		user_spec  = "'[-s] -F'";
408 		break;
409 	case  0x31 :
410 		format_str = "BD-RE full format without spare area";
411 		nblks_str  = "blocks";
412 		param_str  = "block length in bytes";
413 		user_spec  = "'-F'";
414 		break;
415 	/* 0x32 - 0x3f reserved */
416 	default :
417 		break;
418 	}
419 
420 	if (verbose) {
421 		printf("\n\tFormat type 0x%02x : %s\n", format_tp, format_str);
422 
423 		switch (dscr_type) {
424 		case  1 :
425 			printf( "\t\tUnformatted media,"
426 				"maximum formatted capacity\n");
427 			break;
428 		case  2 :
429 			printf( "\t\tFormatted media,"
430 				"current formatted capacity\n");
431 			break;
432 		case  3 :
433 			printf( "\t\tNo media present or incomplete session, "
434 				"maximum formatted capacity\n");
435 			break;
436 		default :
437 			printf("\t\tUnspecified descriptor type\n");
438 			break;
439 		}
440 
441 		printf("\t\tNumber of blocks : %12d\t(%s)\n",
442 			num_blks, nblks_str);
443 		printf("\t\tParameter        : %12d\t(%s)\n",
444 			param, param_str);
445 
446 		if (format_tp == 0x24) {
447 			printf( "\t\tExpert select    : "
448 				"'-X 0x%02x:0xffffff:0' or "
449 				"'-X 0x%02x:0xffff0000:0'\n",
450 				format_tp, format_tp);
451 		} else {
452 			printf( "\t\tExpert select    : "
453 				"'-X 0x%02x:%d:%d'\n",
454 				format_tp, num_blks, param);
455 		}
456 		if (*supported) {
457 			printf("\t\tmmc_format arg   : %s\n", user_spec);
458 		} else {
459 			printf("\t\t** not supported **\n");
460 		}
461 	}
462 }
463 
464 
465 static void
466 process_format_caps(uint8_t *buf, int list_length, int verbose,
467 	uint8_t *allow, uint32_t *blks, uint32_t *params)
468 {
469 	uint32_t	num_blks, param;
470 	uint8_t	       *fcd;
471 	int		dscr_type, format_tp;
472 	int		supported;
473 
474 	bzero(allow,  255);
475 	bzero(blks,   255*4);
476 	bzero(params, 255*4);
477 
478 	fcd = buf + 4;
479 	list_length -= 4;		/* strip header */
480 
481 	if (verbose)
482 		printf("\tCurrent/max capacity followed by additional capacity,"
483 			"reported length of %d bytes (8/entry)\n", list_length);
484 
485 	while (list_length > 0) {
486 		num_blks    = fcd[ 3]        | (fcd[ 2] << 8) |
487 			     (fcd[ 1] << 16) | (fcd[ 0] << 24);
488 		dscr_type   = fcd[ 4] & 3;
489 		format_tp   = fcd[ 4] >> 2;
490 		param       = fcd[ 7] | (fcd[ 6] << 8) |  (fcd[ 5] << 16);
491 
492 		print_format(format_tp, num_blks, param, dscr_type, verbose,
493 			&supported);
494 
495 		 allow[format_tp] = 1;	/* TODO = supported? */
496 		  blks[format_tp] = num_blks;
497 		params[format_tp] = param;
498 
499 		fcd += 8;
500 		list_length-=8;
501 	}
502 }
503 
504 
505 
506 /* format a CD-RW disc */
507 /* old style format 7 */
508 static int
509 uscsi_format_cdrw_mode7(struct uscsi_dev *mydev, uint32_t blocks)
510 {
511 	scsicmd cmd;
512 	struct uscsi_sense sense;
513 	uint8_t  buffer[16];
514 	int error;
515 
516 	if (blocks % 32) {
517 		blocks -= blocks % 32;
518 	}
519 
520 	bzero(cmd, SCSI_CMD_LEN);
521 	bzero(buffer, sizeof(buffer));
522 
523 	cmd[0] = 0x04;			/* format unit 			   */
524 	cmd[1] = 0x17;			/* parameter list format 7 follows */
525 	cmd[5] = 0;			/* control			   */
526 
527 	/* format list header */
528 	buffer[ 0] = 0;			/* reserved			   */
529 	buffer[ 1] = 0x80 | 0x02;	/* Valid info, immediate return	   */
530 	buffer[ 2] = 0;			/* MSB format descriptor length	   */
531 	buffer[ 3] = 8;			/* LSB ...			   */
532 
533 	/*
534 	 * for CD-RW the initialisation pattern bit is reserved, but there IS
535 	 * one
536 	 */
537 
538 	buffer[ 4] = 0;			/* no header			   */
539 	buffer[ 5] = 0;			/* default pattern		   */
540 	buffer[ 6] = 0;			/* pattern length MSB		   */
541 	buffer[ 7] = 0;			/* pattern length LSB		   */
542 
543 	/* 8 bytes of format descriptor */
544 	/* (s)ession bit 1<<7, (g)row bit 1<<6  */
545 	/* SG	action	*/
546 	/* 00	format disc with number of user data blocks	*/
547 	/* 10	create new session with number of data blocks	*/
548 	/* x1	grow session to be number of data blocks	*/
549 
550 	buffer[ 8] = 0x00;		/* session and grow bits (7 and 6)  */
551 	buffer[ 9] = 0;			/* reserved */
552 	buffer[10] = 0;			/* reserved */
553 	buffer[11] = 0;			/* reserved */
554 	buffer[12] = (blocks >> 24) & 0xff;	/* blocks MSB	*/
555 	buffer[13] = (blocks >> 16) & 0xff;
556 	buffer[14] = (blocks >>  8) & 0xff;
557 	buffer[15] = (blocks      ) & 0xff;	/* blocks LSB	*/
558 
559 	/* this will take a while .... */
560 	error = uscsi_command(SCSI_WRITECMD, mydev,
561 			cmd, 6, buffer, sizeof(buffer), UINT_MAX, &sense);
562 	if (error)
563 		return error;
564 
565 	uscsi_waitop(mydev);
566 	return 0;
567 }
568 
569 
570 static int
571 uscsi_format_disc(struct uscsi_dev *mydev, int immed, int format_type,
572 	uint32_t blocks, uint32_t param, int certification, int cmplist)
573 {
574 	scsicmd cmd;
575 	struct uscsi_sense sense;
576 	uint8_t  buffer[16], fmt_flags;
577 	int error;
578 
579 	fmt_flags = 0x80;		/* valid info flag */
580 	if (immed)
581 		fmt_flags |= 2;
582 	if (certification == 0)
583 		fmt_flags |= 32;
584 
585 	if (cmplist)
586 		cmplist = 8;
587 
588 #if 0
589 	if (mmc_profile != 0x43) {
590 		/* certification specifier only valid for BD-RE */
591 		certification = 0;
592 	}
593 #endif
594 
595 	bzero(cmd, SCSI_CMD_LEN);
596 	bzero(buffer, sizeof(buffer));
597 
598 	cmd[0] = 0x04;			/* format unit 			   */
599 	cmd[1] = 0x11 | cmplist;	/* parameter list format 1 follows */
600 	cmd[5] = 0;			/* control			   */
601 
602 	/* format list header */
603 	buffer[ 0] = 0;			/* reserved			   */
604 	buffer[ 1] = 0x80 | fmt_flags;	/* Valid info, flags follow	   */
605 	buffer[ 2] = 0;			/* MSB format descriptor length    */
606 	buffer[ 3] = 8;			/* LSB ...			   */
607 
608 	/* 8 bytes of format descriptor */
609 	buffer[ 4] = (blocks >> 24) & 0xff;	/* blocks MSB	     */
610 	buffer[ 5] = (blocks >> 16) & 0xff;
611 	buffer[ 6] = (blocks >>  8) & 0xff;
612 	buffer[ 7] = (blocks      ) & 0xff;	/* blocks LSB	     */
613 	buffer[ 8] = (format_type << 2) | certification;
614 	buffer[ 9] = (param  >> 16) & 0xff;	/* parameter MSB     */
615 	buffer[10] = (param  >>  8) & 0xff;	/*	packet size  */
616 	buffer[11] = (param       ) & 0xff;	/* parameter LSB     */
617 
618 	/* this will take a while .... */
619 	error = uscsi_command(SCSI_WRITECMD, mydev,
620 			cmd, 6, buffer, 12, UINT_MAX, &sense);
621 	if (error)
622 		return error;
623 
624 	if (immed)
625 		uscsi_waitop(mydev);
626 
627 	return 0;
628 }
629 
630 
631 static int
632 uscsi_blank_disc(struct uscsi_dev *mydev)
633 {
634 	scsicmd cmd;
635 	int error;
636 
637 	/* XXX check if the device can blank! */
638 
639 
640 	/* blank disc */
641 	bzero(cmd, SCSI_CMD_LEN);
642 	cmd[ 0] = 0xA1;			/* blank			*/
643 	cmd[ 1] = 16;			/* Immediate, blank complete	*/
644 	cmd[11] = 0;			/* control			*/
645 
646 	/* this will take a while .... */
647 	error = uscsi_command(SCSI_WRITECMD, mydev,
648 			cmd, 12, NULL, 0, UINT_MAX, NULL);
649 	if (error)
650 		return error;
651 
652 	uscsi_waitop(mydev);
653 	return 0;
654 }
655 
656 
657 static int
658 usage(char *program)
659 {
660 	fprintf(stderr, "\n");
661 	fprintf(stderr, "Usage: %s [options] devicename\n", program);
662 	fprintf(stderr,
663 	"-B		blank cd-rw disc before formatting\n"
664 	"-F		format cd-rw disc\n"
665 	"-O		CD-RW formatting 'old-style' for old CD-RW drives\n"
666 	"-M		select MRW format\n"
667 	"-R		restart MRW & DVD+RW format\n"
668 	"-G		grow last CD-RW/DVD-RW session\n"
669 	"-S		grow spare space DVD-RAM/BD-RE\n"
670 	"-s		format DVD+MRW/BD-RE with extra spare space\n"
671 	"-w		wait until completion of background format\n"
672 	"-p		explicitly set packet format\n"
673 	"-c num		media certification for DVD-RAM/BD-RE : "
674 			"0 no, 1 full, 2 quick\n"
675 	"-r		recompile defect list for DVD-RAM (cmplist)\n"
676 	"-h -H -I	help/inquiry formats\n"
677 	"-X format	expert format selector form 'fmt:blks:param' with -c\n"
678 	"-b blockingnr	in sectors (for CD-RW)\n"
679 	"-D 		verbose SCSI command errors\n"
680 	);
681 	return 1;
682 }
683 
684 
685 extern char	*optarg;
686 extern int	 optind;
687 extern int	 optreset;
688 
689 
690 int
691 main(int argc, char *argv[])
692 {
693 	struct uscsi_addr saddr;
694 	uint32_t blks[256], params[256];
695 	uint32_t format_type, format_blks, format_param, blockingnr;
696 	uint8_t  allow[256];
697 	uint8_t caps[512];
698 	uint32_t caps_len = sizeof(caps);
699 	char *progname;
700 	int blank, format, mrw, background;
701 	int inquiry, spare, oldtimer;
702 	int expert;
703 	int restart_format, grow_session, grow_spare, packet_wr;
704 	int mmc_profile, flag, error, display_usage;
705 	int certification, cmplist;
706 	int wait_until_finished;
707 	progname = strdup(argv[0]);
708 	if (argc == 1) {
709 		return usage(progname);
710 	}
711 
712 	blank               = 0;
713 	format              = 0;
714 	mrw                 = 0;
715 	restart_format      = 0;
716 	grow_session        = 0;
717 	grow_spare          = 0;
718 	wait_until_finished = 0;
719 	packet_wr           = 0;
720 	certification       = 1;
721 	cmplist             = 0;
722 	inquiry             = 0;
723 	spare               = 0;
724 	inquiry             = 0;
725 	oldtimer            = 0;
726 	expert              = 0;
727 	display_usage       = 0;
728 	blockingnr          = 32;
729 	uscsilib_verbose    = 0;
730 	while ((flag = getopt(argc, argv, "BFMRGSwpsc:rhHIX:Ob:D")) != -1) {
731 		switch (flag) {
732 			case 'B' :
733 				blank = 1;
734 				break;
735 			case 'F' :
736 				format = 1;
737 				break;
738 			case 'M' :
739 				mrw = 1;
740 				break;
741 			case 'R' :
742 				restart_format = 1;
743 				break;
744 			case 'G' :
745 				grow_session = 1;
746 				break;
747 			case 'S' :
748 				grow_spare = 1;
749 				break;
750 			case 'w' :
751 				wait_until_finished = 1;
752 				break;
753 			case 'p' :
754 				packet_wr = 1;
755 				break;
756 			case 's' :
757 				spare = 1;
758 				break;
759 			case 'c' :
760 				certification = atoi(optarg);
761 				break;
762 			case 'r' :
763 				cmplist = 1;
764 				break;
765 			case 'h' :
766 			case 'H' :
767 				display_usage = 1;
768 				break;
769 			case 'I' :
770 				inquiry = 1;
771 				break;
772 			case 'X' :
773 				/* TODO parse expert mode string */
774 				printf("-X not implemented yet\n");
775 				expert = 1;
776 				exit(1);
777 				break;
778 			case 'O' :
779 				/* oldtimer CD-RW format */
780 				oldtimer = 1;
781 				format   = 1;
782 				break;
783 			case 'b' :
784 				blockingnr = atoi(optarg);
785 				break;
786 			case 'D' :
787 				uscsilib_verbose = 1;
788 				break;
789 			default :
790 				return usage(progname);
791 		}
792 	}
793 	argv += optind;
794 	argc -= optind;
795 
796 	if (!blank && !format && !grow_session && !grow_spare &&
797 	    !expert && !inquiry && !display_usage) {
798 		fprintf(stderr, "%s : at least one of -B, -F, -G, -h, -H -S, "
799 				"-X or -I needs to be specified\n\n", progname);
800 		return usage(progname);
801 	}
802 
803 	if (format + grow_session + grow_spare + expert > 1) {
804 		fprintf(stderr, "%s : at most one of -F, -G, -S or -X "
805 				"needs to be specified\n\n", progname);
806 		return usage(progname);
807 	}
808 
809 	if (argc != 1) return usage(progname);
810 
811 	/* Open the device */
812 	dev.dev_name = strdup(*argv);
813 	printf("Opening device %s\n", dev.dev_name);
814 	error = uscsi_open(&dev);
815 	if (error) {
816 		fprintf(stderr, "Device failed to open : %s\n",
817 			strerror(error));
818 		exit(1);
819 	}
820 
821 	error = uscsi_check_for_scsi(&dev);
822 	if (error) {
823 		fprintf(stderr, "sorry, not a SCSI/ATAPI device : %s\n",
824 			strerror(error));
825 		exit(1);
826 	}
827 
828 	error = uscsi_identify(&dev, &saddr);
829 	if (error) {
830 		fprintf(stderr, "SCSI/ATAPI identify returned : %s\n",
831 			strerror(error));
832 		exit(1);
833 	}
834 
835 	printf("\nDevice identifies itself as : ");
836 
837 	if (saddr.type == USCSI_TYPE_SCSI) {
838 		printf("SCSI   busnum = %d, target = %d, lun = %d\n",
839 			saddr.addr.scsi.scbus, saddr.addr.scsi.target,
840 			saddr.addr.scsi.lun);
841 	} else {
842 		printf("ATAPI  busnum = %d, drive = %d\n",
843 			saddr.addr.atapi.atbus, saddr.addr.atapi.drive);
844 	}
845 
846 	printf("\n");
847 
848 	/* get MMC profile */
849 	error = uscsi_get_mmc_profile(&dev, &mmc_profile);
850 	if (error) {
851 		fprintf(stderr,
852 			"Can't get the disc's MMC profile because of :"
853 			" %s\n", strerror(error));
854 		fprintf(stderr, "aborting\n");
855 		uscsi_close(&dev);
856 		return 1;
857 	}
858 
859 	/* blank disc section */
860 	if (blank) {
861 		printf("\nBlanking disc.... "); fflush(stdout);
862 		error = uscsi_blank_disc(&dev);
863 
864 		if (error) {
865 			printf("fail\n"); fflush(stdout);
866 			fprintf(stderr,
867 				"Blanking failed because of : %s\n",
868 				strerror(error));
869 			uscsi_close(&dev);
870 
871 			return 1;
872 		} else {
873 			printf("success!\n\n");
874 		}
875 	}
876 
877 	/* re-get MMC profile */
878 	error = uscsi_get_mmc_profile(&dev, &mmc_profile);
879 	if (error) {
880 		fprintf(stderr,
881 			"Can't get the disc's MMC profile because of : %s\n",
882 			strerror(error));
883 		fprintf(stderr, "aborting\n");
884 		uscsi_close(&dev);
885 		return 1;
886 	}
887 
888 	error = get_format_capabilities(&dev, caps, &caps_len);
889 	if (error)
890 		exit(1);
891 
892 	process_format_caps(caps, caps_len, inquiry, allow, blks, params);
893 
894 	format_type = 0;
895 	/* expert format section */
896 	if (expert) {
897 	}
898 
899 	if (!format && !grow_spare && !grow_session) {
900 		/* we're done */
901 		if (display_usage)
902 			usage(progname);
903 		uscsi_close(&dev);
904 		exit(0);
905 	}
906 
907 	/* normal format section */
908 	if (format) {
909 		/* get current mmc profile of disc */
910 
911 		if (oldtimer && mmc_profile != 0x0a) {
912 			printf("Oldtimer flag only defined for CD-RW; "
913 				"ignored\n");
914 		}
915 
916 		switch (mmc_profile) {
917 		case 0x12 :	/* DVD-RAM	*/
918 			format_type = 0x00;
919 			break;
920 		case 0x0a :	/* CD-RW	*/
921 			format_type = mrw ? 0x24 : 0x10;
922 			packet_wr   = 1;
923 			break;
924 		case 0x13 :	/* DVD-RW restricted overwrite */
925 		case 0x14 :	/* DVD-RW sequential 		*/
926 			format_type = 0x10;
927 			/*
928 			 * Some drives suddenly stop supporting this format
929 			 * type when packet_wr = 1
930 			 */
931 			packet_wr   = 0;
932 			break;
933 		case 0x1a :	/* DVD+RW	*/
934 			format_type = mrw ? 0x24 : 0x26;
935 			break;
936 		case 0x43 :	/* BD-RE	*/
937 			format_type = spare ? 0x30 : 0x31;
938 			break;
939 		default :
940 			fprintf(stderr, "Can't format discs of type %s\n",
941 				print_mmc_profile(mmc_profile));
942 			uscsi_close(&dev);
943 			exit(1);
944 		}
945 	}
946 
947 	if (grow_spare) {
948 		switch (mmc_profile) {
949 		case 0x12 :	/* DVD-RAM */
950 		case 0x43 :	/* BD-RE   */
951 			format_type = 0x01;
952 			break;
953 		default :
954 			fprintf(stderr,
955 				"Can't grow spare area for discs of type %s\n",
956 				print_mmc_profile(mmc_profile));
957 			uscsi_close(&dev);
958 			exit(1);
959 		}
960 	}
961 
962 	if (grow_session) {
963 		switch (mmc_profile) {
964 		case 0x0a :	/* CD-RW */
965 			format_type = 0x11;
966 			break;
967 		case 0x13 :	/* DVD-RW restricted overwrite */
968 		case 0x14 :	/* DVD-RW sequential ? */
969 			format_type = 0x13;
970 			break;
971 		default :
972 			uscsi_close(&dev);
973 			fprintf(stderr,
974 				"Can't grow session for discs of type %s\n",
975 				print_mmc_profile(mmc_profile));
976 			exit(1);
977 		}
978 	}
979 
980 	/* check if format type is allowed */
981 	format_blks  = blks[format_type];
982 	format_param = params[format_type];
983 	if (!allow[format_type]) {
984 		if (!inquiry)
985 			process_format_caps(caps, caps_len, 1, allow,
986 				blks, params);
987 
988 		printf("\n");
989 		fflush(stdout);
990 		fprintf(stderr,
991 			"Drive indicates it can't format with deduced format "
992 			"type 0x%02x\n", format_type);
993 		uscsi_close(&dev);
994 		exit(1);
995 	}
996 
997 	if (restart_format && !((mmc_profile == 0x1a) || (format_type == 0x24)))
998 	{
999 		fprintf(stderr,
1000 			"Format restarting only for MRW formats or DVD+RW "
1001 			"formats\n");
1002 		uscsi_close(&dev);
1003 		exit(1);
1004 	}
1005 
1006 	if (restart_format && !wait_until_finished) {
1007 		printf( "Warning : format restarting without waiting for it be "
1008 			"finished is prolly not handy\n");
1009 	}
1010 
1011 	/* explicitly select packet write just in case */
1012 	if (packet_wr) {
1013 		printf("Explicitly setting packet type and blocking number\n");
1014 		error = uscsi_set_packet_parameters(&dev, blockingnr);
1015 		if (error) {
1016 			fprintf(stderr,
1017 				"Can't set packet writing and blocking number: "
1018 				"%s\n", strerror(error));
1019 			uscsi_close(&dev);
1020 			exit(1);
1021 		}
1022 	}
1023 
1024 	/* determine if formatting is done in the background */
1025 	background = 0;
1026 	if (format_type == 0x24) background = 1;
1027 	if (format_type == 0x26) background = 1;
1028 
1029 	/* special case format type 0x24 : MRW */
1030 	if (format_type == 0x24) {
1031 		format_blks  = spare ? 0xffff0000 : 0xffffffff;
1032 		format_param = restart_format;
1033 	}
1034 	/* special case format type 0x26 : DVD+RW */
1035 	if (format_type == 0x26) {
1036 		format_param = restart_format;
1037 	}
1038 
1039 	/* verbose to the user */
1040 	DEBUG(
1041 		printf("Actual format selected: "
1042 			"format_type 0x%02x, blks %d, param %d, "
1043 			"certification %d, cmplist %d\n",
1044 			format_type, format_blks, format_param,
1045 			certification, cmplist);
1046 	);
1047 	printf("\nFormatting.... "); fflush(stdout);
1048 
1049 	/* formatting time! */
1050 	if (oldtimer) {
1051 		error = uscsi_format_cdrw_mode7(&dev, format_blks);
1052 		background = 0;
1053 	} else {
1054 		error = uscsi_format_disc(&dev, !background, format_type,
1055 				format_blks, format_param, certification,
1056 				cmplist);
1057 	}
1058 
1059 	/* what now? */
1060 	if (error) {
1061 		printf("fail\n"); fflush(stdout);
1062 		fprintf(stderr, "Formatting failed because of : %s\n",
1063 			strerror(error));
1064 	} else {
1065 		if (background) {
1066 			printf("background formatting in progress\n");
1067 			if (wait_until_finished) {
1068 				printf("Waiting for completion ... ");
1069 				uscsi_waitop(&dev);
1070 			}
1071 			/* explicitly do NOT close disc ... (for now) */
1072 			return 0;
1073 		} else {
1074 			printf("success!\n\n");
1075 		}
1076 	}
1077 
1078 	/* finish up */
1079 	uscsi_close(&dev);
1080 
1081 	return error;
1082 }
1083 
1084