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