1/* $NetBSD: mmcformat.c,v 1.2 2008/05/18 13:08:58 tron Exp $ */
2
3/*
4 * Copyright (c) 2006, 2008 Reinoud Zandijk
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29#include <stdio.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <errno.h>
34#include <string.h>
35#include <strings.h>
36#include <assert.h>
37#include <limits.h>
38#include <sys/types.h>
39#include <sys/time.h>
40#include <inttypes.h>
41
42#include "uscsilib.h"
43
44
45/* globals */
46struct uscsi_dev dev;
47extern int scsilib_verbose;
48
49/* #define DEBUG(a) {a;} */
50#define DEBUG(a) ;
51
52
53static uint64_t
54getmtime(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
63static void
64print_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
84static void
85uscsi_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
151static char const *
152print_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
187static int
188uscsi_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
209static int
210uscsi_set_packet_parameters(struct uscsi_dev *mydev, int blockingnr)
211{
212	scsicmd  cmd;
213	int      val_len;
214	uint8_t  res[10000], *pos;
215	int      error;
216
217	/* Set up CD/DVD recording parameters */
218	DEBUG(printf("Setting device's recording parameters\n"));
219
220	val_len = 0x32+2+8;
221	bzero(res, val_len);
222
223	pos = res + 8;
224
225	bzero(cmd, SCSI_CMD_LEN);
226	pos[ 0] = 0x05;		/* page code 5 : cd writing		*/
227	pos[ 1] = 0x32;		/* length in bytes			*/
228	pos[ 2] = 0;		/* write type 0 : packet/incremental	*/
229
230	/* next session OK, data packet, rec. incr. fixed packets	*/
231	pos[ 3] = (3<<6) | 32 | 5;
232	pos[ 4] = 10;		/* ISO mode 2; XA form 1		*/
233	pos[ 8] = 0x20;		/* CD-ROM XA disc or DDCD disc		*/
234	pos[10] = (blockingnr >> 24) & 0xff;	/* MSB packet size 	*/
235	pos[11] = (blockingnr >> 16) & 0xff;
236	pos[12] = (blockingnr >>  8) & 0xff;
237	pos[13] = (blockingnr      ) & 0xff;	/* LSB packet size 	*/
238
239	bzero(cmd, SCSI_CMD_LEN);
240	cmd[0] = 0x55;			/* MODE SELECT (10)		*/
241	cmd[1] = 16;			/* PF format			*/
242	cmd[7] = val_len >> 8;		/* length of blob		*/
243	cmd[8] = val_len & 0xff;
244	cmd[9] = 0;			/* control			*/
245
246	error = uscsi_command(SCSI_WRITECMD, mydev,
247			cmd, 10, res, val_len, 30000, NULL);
248	if (error) {
249		perror("While WRTITING parameter page 5");
250		return error;
251	}
252
253	/* flag OK */
254	return 0;
255}
256
257
258static int
259get_format_capabilities(struct uscsi_dev *mydev, uint8_t *buf, uint32_t *len)
260{
261	scsicmd		cmd;
262	int		list_length;
263	int		trans_len;
264	size_t		buf_len = 512;
265	int		error;
266
267	assert(*len >= buf_len);
268	bzero(buf, buf_len);
269
270	trans_len = 12;				/* only fixed header first */
271	bzero(cmd, SCSI_CMD_LEN);
272	cmd[0] = 0x23;				/* Read format capabilities */
273	cmd[7] = trans_len >> 8;		/* MSB allocation length */
274	cmd[8] = trans_len & 0xff;		/* LSB allocation length */
275	cmd[9] = 0;				/* control */
276	error = uscsi_command(SCSI_READCMD, mydev,
277			cmd, 10, buf, trans_len, 30000, NULL);
278	if (error) {
279		fprintf(stderr, "While reading format capabilities : %s\n",
280			strerror(error));
281		return error;
282	}
283
284	list_length = buf[ 3];
285
286	if (list_length % 8) {
287		printf( "\t\tWarning: violating SCSI spec,"
288			"capacity list length ought to be multiple of 8\n");
289		printf("\t\tInterpreting as including header of 4 bytes\n");
290		assert(list_length % 8 == 4);
291		list_length -= 4;
292	}
293
294	/* read in full capacity list */
295	trans_len = 12 + list_length;		/* complete structure */
296	bzero(cmd, SCSI_CMD_LEN);
297	cmd[0] = 0x23;				/* Read format capabilities */
298	cmd[7] = trans_len >> 8;		/* MSB allocation length */
299	cmd[8] = trans_len & 0xff;		/* LSB allocation length */
300	cmd[9] = 0;				/* control */
301	error = uscsi_command(SCSI_READCMD, mydev,
302			cmd, 10, buf, trans_len, 30000, NULL);
303	if (error) {
304		fprintf(stderr, "While reading format capabilities : %s\n",
305			strerror(error));
306		return error;
307	}
308
309	*len = list_length;
310	return 0;
311}
312
313
314static void
315print_format(int format_tp, uint32_t num_blks, uint32_t param,
316	int dscr_type, int verbose, int *supported)
317{
318	char const *format_str, *nblks_str, *param_str, *user_spec;
319
320	format_str = nblks_str = param_str = "reserved";
321	user_spec = "";
322	*supported = 1;
323
324	switch (format_tp) {
325	case  0x00 :
326		format_str = "full format capacity";
327		nblks_str  = "sectors";
328		param_str  = "block length in bytes";
329		user_spec  = "'-F [-b blockingnr]'";
330		break;
331	case  0x01 :
332		format_str = "spare area expansion";
333		nblks_str  = "extension in blocks";
334		param_str  = "block length in bytes";
335		user_spec  = "'-S'";
336		break;
337	/* 0x02 - 0x03 reserved */
338	case  0x04 :
339		format_str = "variable length zone'd format";
340		nblks_str  = "zone length";
341		param_str  = "zone number";
342		*supported = 0;
343		break;
344	case  0x05 :
345		format_str = "fixed length zone'd format";
346		nblks_str  = "zone lenght";
347		param_str  = "last zone number";
348		*supported = 0;
349		break;
350	/* 0x06 - 0x0f reserved */
351	case  0x10 :
352		format_str = "CD-RW/DVD-RW full packet format";
353		nblks_str  = "adressable blocks";
354		param_str  = "fixed packet size/ECC blocksize in sectors";
355		user_spec  = "'-F -p [-b blockingnr]'";
356		break;
357	case  0x11 :
358		format_str = "CD-RW/DVD-RW grow session";
359		nblks_str  = "adressable blocks";
360		param_str  = "fixed packet size/ECC blocksize in sectors";
361		user_spec  = "'-G'";
362		break;
363	case  0x12 :
364		format_str = "CD-RW/DVD-RW add session";
365		nblks_str  = "adressable blocks";
366		param_str  = "maximum fixed packet size/ECC blocksize "
367			     "in sectors";
368		*supported = 0;
369		break;
370	case  0x13 :
371		format_str = "DVD-RW max growth of last complete session";
372		nblks_str  = "adressable blocks";
373		param_str  = "ECC blocksize in sectors";
374		user_spec  = "'-G'";
375		break;
376	case  0x14 :
377		format_str = "DVD-RW quick grow last session";
378		nblks_str  = "adressable blocks";
379		param_str  = "ECC blocksize in sectors";
380		*supported = 0;
381		break;
382	case  0x15 :
383		format_str = "DVD-RW quick full format";
384		nblks_str  = "adressable blocks";
385		param_str  = "ECC blocksize in sectors";
386		*supported = 0;
387		break;
388	/* 0x16 - 0x23 reserved */
389	case  0x24 :
390		format_str = "background MRW format";
391		nblks_str  = "Defect Management Area blocks";
392		param_str  = "not used";
393		user_spec  = "'[-R] [-s] [-w] -F -M [-b blockingnr]'";
394		break;
395	/* 0x25 reserved */
396	case  0x26 :
397		format_str = "background DVD+RW full format";
398		nblks_str  = "sectors";
399		param_str  = "not used";
400		user_spec  = "'[-R] [-w] -F'";
401		break;
402	/* 0x27 - 0x2f reserved */
403	case  0x30 :
404		format_str = "BD-RE full format with spare area";
405		nblks_str  = "blocks";
406		param_str  = "total spare area size in clusters";
407		user_spec  = "'[-s] -F'";
408		break;
409	case  0x31 :
410		format_str = "BD-RE full format without spare area";
411		nblks_str  = "blocks";
412		param_str  = "block length in bytes";
413		user_spec  = "'-F'";
414		break;
415	/* 0x32 - 0x3f reserved */
416	default :
417		break;
418	}
419
420	if (verbose) {
421		printf("\n\tFormat type 0x%02x : %s\n", format_tp, format_str);
422
423		switch (dscr_type) {
424		case  1 :
425			printf( "\t\tUnformatted media,"
426				"maximum formatted capacity\n");
427			break;
428		case  2 :
429			printf( "\t\tFormatted media,"
430				"current formatted capacity\n");
431			break;
432		case  3 :
433			printf( "\t\tNo media present or incomplete session, "
434				"maximum formatted capacity\n");
435			break;
436		default :
437			printf("\t\tUnspecified descriptor type\n");
438			break;
439		}
440
441		printf("\t\tNumber of blocks : %12d\t(%s)\n",
442			num_blks, nblks_str);
443		printf("\t\tParameter        : %12d\t(%s)\n",
444			param, param_str);
445
446		if (format_tp == 0x24) {
447			printf( "\t\tExpert select    : "
448				"'-X 0x%02x:0xffffff:0' or "
449				"'-X 0x%02x:0xffff0000:0'\n",
450				format_tp, format_tp);
451		} else {
452			printf( "\t\tExpert select    : "
453				"'-X 0x%02x:%d:%d'\n",
454				format_tp, num_blks, param);
455		}
456		if (*supported) {
457			printf("\t\tmmc_format arg   : %s\n", user_spec);
458		} else {
459			printf("\t\t** not supported **\n");
460		}
461	}
462}
463
464
465static void
466process_format_caps(uint8_t *buf, int list_length, int verbose,
467	uint8_t *allow, uint32_t *blks, uint32_t *params)
468{
469	uint32_t	num_blks, param;
470	uint8_t	       *fcd;
471	int		dscr_type, format_tp;
472	int		supported;
473
474	bzero(allow,  255);
475	bzero(blks,   255*4);
476	bzero(params, 255*4);
477
478	fcd = buf + 4;
479	list_length -= 4;		/* strip header */
480
481	if (verbose)
482		printf("\tCurrent/max capacity followed by additional capacity,"
483			"reported length of %d bytes (8/entry)\n", list_length);
484
485	while (list_length > 0) {
486		num_blks    = fcd[ 3]        | (fcd[ 2] << 8) |
487			     (fcd[ 1] << 16) | (fcd[ 0] << 24);
488		dscr_type   = fcd[ 4] & 3;
489		format_tp   = fcd[ 4] >> 2;
490		param       = fcd[ 7] | (fcd[ 6] << 8) |  (fcd[ 5] << 16);
491
492		print_format(format_tp, num_blks, param, dscr_type, verbose,
493			&supported);
494
495		 allow[format_tp] = 1;	/* TODO = supported? */
496		  blks[format_tp] = num_blks;
497		params[format_tp] = param;
498
499		fcd += 8;
500		list_length-=8;
501	}
502}
503
504
505
506/* format a CD-RW disc */
507/* old style format 7 */
508static int
509uscsi_format_cdrw_mode7(struct uscsi_dev *mydev, uint32_t blocks)
510{
511	scsicmd cmd;
512	struct uscsi_sense sense;
513	uint32_t param;
514	uint8_t  buffer[16];
515	int cnt, error;
516
517	param = cnt = 0;
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
573static int
574uscsi_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
634static int
635uscsi_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
660static int
661usage(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
688extern char	*optarg;
689extern int	 optind;
690extern int	 optreset;
691
692
693int
694main(int argc, char *argv[])
695{
696	struct uscsi_addr saddr;
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[512];
701	uint32_t caps_len = sizeof(caps);
702	char *progname;
703	int blank, format, mrw, background;
704	int inquiry, spare, oldtimer;
705	int expert;
706	int restart_format, grow_session, grow_spare, packet_wr;
707	int mmc_profile, flag, error, display_usage;
708	int certification, cmplist;
709	int wait_until_finished;
710	progname = strdup(argv[0]);
711	if (argc == 1) {
712		return usage(progname);
713	}
714
715	blank               = 0;
716	format              = 0;
717	mrw                 = 0;
718	restart_format      = 0;
719	grow_session        = 0;
720	grow_spare          = 0;
721	wait_until_finished = 0;
722	packet_wr           = 0;
723	certification       = 1;
724	cmplist             = 0;
725	inquiry             = 0;
726	spare               = 0;
727	inquiry             = 0;
728	oldtimer            = 0;
729	expert              = 0;
730	display_usage       = 0;
731	blockingnr          = 32;
732	uscsilib_verbose    = 0;
733	while ((flag = getopt(argc, argv, "BFMRGSwpsc:rhHIX:Ob:D")) != -1) {
734		switch (flag) {
735			case 'B' :
736				blank = 1;
737				break;
738			case 'F' :
739				format = 1;
740				break;
741			case 'M' :
742				mrw = 1;
743				break;
744			case 'R' :
745				restart_format = 1;
746				break;
747			case 'G' :
748				grow_session = 1;
749				break;
750			case 'S' :
751				grow_spare = 1;
752				break;
753			case 'w' :
754				wait_until_finished = 1;
755				break;
756			case 'p' :
757				packet_wr = 1;
758				break;
759			case 's' :
760				spare = 1;
761				break;
762			case 'c' :
763				certification = atoi(optarg);
764				break;
765			case 'r' :
766				cmplist = 1;
767				break;
768			case 'h' :
769			case 'H' :
770				display_usage = 1;
771			case 'I' :
772				inquiry = 1;
773				break;
774			case 'X' :
775				/* TODO parse expert mode string */
776				printf("-X not implemented yet\n");
777				expert = 1;
778				exit(1);
779				break;
780			case 'O' :
781				/* oldtimer CD-RW format */
782				oldtimer = 1;
783				format   = 1;
784				break;
785			case 'b' :
786				blockingnr = atoi(optarg);
787				break;
788			case 'D' :
789				uscsilib_verbose = 1;
790				break;
791			default :
792				return usage(progname);
793		}
794	}
795	argv += optind;
796	argc -= optind;
797
798	if ((!blank && !format && !grow_session && !grow_spare) &&
799	    (!expert && !inquiry)) {
800		fprintf(stderr, "%s : at least one of -B, -F, -G, -S, -X or -I "
801				"needs to be specified\n\n", progname);
802		return usage(progname);
803	}
804
805	if (format + grow_session + grow_spare + expert > 1) {
806		fprintf(stderr, "%s : at most one of -F, -G, -S or -X "
807				"needs to be specified\n\n", progname);
808		return usage(progname);
809	}
810
811	if (argc != 1) return usage(progname);
812
813	/* Open the device */
814	dev.dev_name = strdup(*argv);
815	printf("Opening device %s\n", dev.dev_name);
816	error = uscsi_open(&dev);
817	if (error) {
818		fprintf(stderr, "Device failed to open : %s\n",
819			strerror(error));
820		exit(1);
821	}
822
823	error = uscsi_check_for_scsi(&dev);
824	if (error) {
825		fprintf(stderr, "sorry, not a SCSI/ATAPI device : %s\n",
826			strerror(error));
827		exit(1);
828	}
829
830	error = uscsi_identify(&dev, &saddr);
831	if (error) {
832		fprintf(stderr, "SCSI/ATAPI identify returned : %s\n",
833			strerror(error));
834		exit(1);
835	}
836
837	printf("\nDevice identifies itself as : ");
838
839	if (saddr.type == USCSI_TYPE_SCSI) {
840		printf("SCSI   busnum = %d, target = %d, lun = %d\n",
841			saddr.addr.scsi.scbus, saddr.addr.scsi.target,
842			saddr.addr.scsi.lun);
843	} else {
844		printf("ATAPI  busnum = %d, drive = %d\n",
845			saddr.addr.atapi.atbus, saddr.addr.atapi.drive);
846	}
847
848	printf("\n");
849
850	/* get MMC profile */
851	error = uscsi_get_mmc_profile(&dev, &mmc_profile);
852	if (error) {
853		fprintf(stderr,
854			"Can't get the disc's MMC profile because of :"
855			" %s\n", strerror(error));
856		fprintf(stderr, "aborting\n");
857		uscsi_close(&dev);
858		return 1;
859	}
860
861	/* blank disc section */
862	if (blank) {
863		printf("\nBlanking disc.... "); fflush(stdout);
864		error = uscsi_blank_disc(&dev);
865
866		if (error) {
867			printf("fail\n"); fflush(stdout);
868			fprintf(stderr,
869				"Blanking failed because of : %s\n",
870				strerror(error));
871			uscsi_close(&dev);
872
873			return 1;
874		} else {
875			printf("success!\n\n");
876		}
877	}
878
879	/* re-get MMC profile */
880	error = uscsi_get_mmc_profile(&dev, &mmc_profile);
881	if (error) {
882		fprintf(stderr,
883			"Can't get the disc's MMC profile because of : %s\n",
884			strerror(error));
885		fprintf(stderr, "aborting\n");
886		uscsi_close(&dev);
887		return 1;
888	}
889
890	error = get_format_capabilities(&dev, caps, &caps_len);
891	if (error)
892		exit(1);
893
894	process_format_caps(caps, caps_len, inquiry, allow, blks, params);
895
896	format_type = 0;
897	/* expert format section */
898	if (expert) {
899	}
900
901	if (!format && !grow_spare && !grow_session) {
902		/* we're done */
903		if (display_usage)
904			usage(progname);
905		uscsi_close(&dev);
906		exit(0);
907	}
908
909	/* normal format section */
910	if (format) {
911		/* get current mmc profile of disc */
912
913		if (oldtimer && mmc_profile != 0x0a) {
914			printf("Oldtimer flag only defined for CD-RW; "
915				"ignored\n");
916		}
917
918		switch (mmc_profile) {
919		case 0x12 :	/* DVD-RAM	*/
920			format_type = 0x00;
921			break;
922		case 0x0a :	/* CD-RW	*/
923			format_type = mrw ? 0x24 : 0x10;
924			packet_wr   = 1;
925			break;
926		case 0x13 :	/* DVD-RW restricted overwrite */
927		case 0x14 :	/* DVD-RW sequential 		*/
928			format_type = 0x10;
929			/*
930			 * Some drives suddenly stop supporting this format
931			 * type when packet_wr = 1
932			 */
933			packet_wr   = 0;
934			break;
935		case 0x1a :	/* DVD+RW	*/
936			format_type = mrw ? 0x24 : 0x26;
937			break;
938		case 0x43 :	/* BD-RE	*/
939			format_type = spare ? 0x30 : 0x31;
940			break;
941		default :
942			fprintf(stderr, "Can't format discs of type %s\n",
943				print_mmc_profile(mmc_profile));
944			uscsi_close(&dev);
945			exit(1);
946		}
947	}
948
949	if (grow_spare) {
950		switch (mmc_profile) {
951		case 0x12 :	/* DVD-RAM */
952		case 0x43 :	/* BD-RE   */
953			format_type = 0x01;
954			break;
955		default :
956			fprintf(stderr,
957				"Can't grow spare area for discs of type %s\n",
958				print_mmc_profile(mmc_profile));
959			uscsi_close(&dev);
960			exit(1);
961		}
962	}
963
964	if (grow_session) {
965		switch (mmc_profile) {
966		case 0x0a :	/* CD-RW */
967			format_type = 0x11;
968			break;
969		case 0x13 :	/* DVD-RW restricted overwrite */
970		case 0x14 :	/* DVD-RW sequential ? */
971			format_type = 0x13;
972			break;
973		default :
974			uscsi_close(&dev);
975			fprintf(stderr,
976				"Can't grow session for discs of type %s\n",
977				print_mmc_profile(mmc_profile));
978			exit(1);
979		}
980	}
981
982	/* check if format type is allowed */
983	format_blks  = blks[format_type];
984	format_param = params[format_type];
985	if (!allow[format_type]) {
986		if (!inquiry)
987			process_format_caps(caps, caps_len, 1, allow,
988				blks, params);
989
990		printf("\n");
991		fflush(stdout);
992		fprintf(stderr,
993			"Drive indicates it can't format with deduced format "
994			"type 0x%02x\n", format_type);
995		uscsi_close(&dev);
996		exit(1);
997	}
998
999	if (restart_format && !((mmc_profile == 0x1a) || (format_type == 0x24)))
1000	{
1001		fprintf(stderr,
1002			"Format restarting only for MRW formats or DVD+RW "
1003			"formats\n");
1004		uscsi_close(&dev);
1005		exit(1);
1006	}
1007
1008	if (restart_format && !wait_until_finished) {
1009		printf( "Warning : format restarting without waiting for it be "
1010			"finished is prolly not handy\n");
1011	}
1012
1013	/* explicitly select packet write just in case */
1014	if (packet_wr) {
1015		printf("Explicitly setting packet type and blocking number\n");
1016		error = uscsi_set_packet_parameters(&dev, blockingnr);
1017		if (error) {
1018			fprintf(stderr,
1019				"Can't set packet writing and blocking number: "
1020				"%s\n", strerror(error));
1021			uscsi_close(&dev);
1022			exit(1);
1023		}
1024	}
1025
1026	/* determine if formatting is done in the background */
1027	background = 0;
1028	if (format_type == 0x24) background = 1;
1029	if (format_type == 0x26) background = 1;
1030
1031	/* special case format type 0x24 : MRW */
1032	if (format_type == 0x24) {
1033		format_blks  = spare ? 0xffff0000 : 0xffffffff;
1034		format_param = restart_format;
1035	}
1036	/* special case format type 0x26 : DVD+RW */
1037	if (format_type == 0x26) {
1038		format_param = restart_format;
1039	}
1040
1041	/* verbose to the user */
1042	DEBUG(
1043		printf("Actual format selected: "
1044			"format_type 0x%02x, blks %d, param %d, "
1045			"certification %d, cmplist %d\n",
1046			format_type, format_blks, format_param,
1047			certification, cmplist);
1048	);
1049	printf("\nFormatting.... "); fflush(stdout);
1050
1051	/* formatting time! */
1052	if (oldtimer) {
1053		error = uscsi_format_cdrw_mode7(&dev, format_blks);
1054		background = 0;
1055	} else {
1056		error = uscsi_format_disc(&dev, !background, format_type,
1057				format_blks, format_param, certification,
1058				cmplist);
1059	}
1060
1061	/* what now? */
1062	if (error) {
1063		printf("fail\n"); fflush(stdout);
1064		fprintf(stderr, "Formatting failed because of : %s\n",
1065			strerror(error));
1066	} else {
1067		if (background) {
1068			printf("background formatting in progress\n");
1069			if (wait_until_finished) {
1070				printf("Waiting for completion ... ");
1071				uscsi_waitop(&dev);
1072			}
1073			/* explicitly do NOT close disc ... (for now) */
1074			return 0;
1075		} else {
1076			printf("success!\n\n");
1077		}
1078	}
1079
1080	/* finish up */
1081	uscsi_close(&dev);
1082
1083	return error;
1084}
1085
1086