Home | History | Annotate | Line # | Download | only in mmcformat
mmcformat.c revision 1.1
      1 /* $NetBSD: mmcformat.c,v 1.1 2008/05/14 16:49:48 reinoud 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      blk_len = 10000, val_len;
    214 	uint8_t  res[blk_len], *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 caps_len = 512;
    697 	uint32_t blks[256], params[256];
    698 	uint32_t format_type, format_blks, format_param, blockingnr;
    699 	uint8_t  allow[256];
    700 	uint8_t caps[caps_len];
    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