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