1 /*
2  * BD/DVD�RW/-RAM format 7.1 by Andy Polyakov <appro@fy.chalmers.se>.
3  *
4  * Use-it-on-your-own-risk, GPL bless...
5  *
6  * For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/.
7  *
8  * Revision history:
9  *
10  * 2.0:
11  * - deploy "IMMED" bit in "FORMAT UNIT";
12  * 2.1:
13  * - LP64 fix;
14  * - USB workaround;
15  * 3.0:
16  * - C++-fication for better portability;
17  * - SYSV signals for better portability;
18  * - -lead-out option for improved DVD+RW compatibility;
19  * - tested with SONY DRU-500A;
20  * 4.0:
21  * - support for DVD-RW formatting and blanking, tool name becomes
22  *   overloaded...
23  * 4.1:
24  * - re-make it work under Linux 2.2 kernel;
25  * 4.2:
26  * - attempt to make DVD-RW Quick Format truly quick, upon release
27  *   is verified to work with Pioneer DVR-x05;
28  * - media reload is moved to growisofs where is actually belongs;
29  * 4.3:
30  * - -blank to imply -force;
31  * - reject -blank in DVD+RW context and -lead-out in DVD-RW;
32  * 4.4:
33  * - support for -force=full in DVD-RW context;
34  * - ask unit to perform OPC if READ DISC INFORMATION doesn't return
35  *   any OPC descriptors;
36  * 4.5:
37  * - increase timeout for OPC, NEC multi-format derivatives might
38  *   require more time to fulfill the OPC procedure;
39  * 4.6:
40  * - -force to ignore error from READ DISC INFORMATION;
41  * - -force was failing under FreeBSD with 'unable to unmount';
42  * - undocumented -gui flag to ease progress indicator parsing for
43  *   GUI front-ends;
44  * 4.7:
45  * - when formatting DVD+RW, Pioneer DVR-x06 remained unaccessible for
46  *   over 2 minutes after dvd+rw-format exited and user was frustrated
47  *   to poll the unit himself, now dvd+rw-format does it for user;
48  * 4.8:
49  * - DVD-RW format fails if preceeded by dummy recording;
50  * - make sure we talk to MMC unit, be less intrusive;
51  * - unify error reporting;
52  * - permit for -lead-out even for blank DVD+RW, needed(?) for SANYO
53  *   derivatives;
54  * 4.9:
55  * - permit for DVD-RW blank even if format descriptors are not present;
56  * 4.10:
57  * - add support for DVD-RAM;
58  * 6.0:
59  * - versioning harmonization;
60  * - WIN32 port;
61  * - Initial DVD+RW Double Layer support;
62  * - Ignore "WRITE PROTECTED" error in OPC;
63  * 6.1:
64  * - � localization;
65  * - Treat only x73xx OPC errors as fatal;
66  * 7.0:
67  * - Blu-ray Disc support;
68  * - Mac OS X 10>=2 support;
69  * 7.1:
70  * - refine x73xx error treatment;
71  */
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 
76 #if defined(__unix) || defined(__unix__)
77 #include <sys/types.h>
78 #include <sys/stat.h>
79 #include <fcntl.h>
80 #include <unistd.h>
81 #include <sys/mman.h>
82 #include <signal.h>
83 #include <setjmp.h>
84 #include <sys/wait.h>
85 #endif
86 
87 #include "transport.hxx"
88 
usage(char * prog)89 static void usage (char *prog)
90 { fprintf (stderr,"- usage: %s [-force[=full]] [-lead-out|-blank[=full]]\n"
91 		  "         [-ssa[=none|default|max|XXXm]] /dev/dvd\n",prog),
92   exit(1);
93 }
94 
95 #ifdef _WIN32
96 #include <process.h>
97 
98 # if defined(__GNUC__)
99 #  define __shared__	__attribute__((section(".shared"),shared))
100 # elif defined(_MSC_VER)
101 #  define __shared__
102 #  pragma data_seg(".shared")
103 #  pragma comment(linker,"/section:.shared,rws")
104 # endif
105 static volatile int shared_progress_indicator __shared__ = 0;
106 # if defined(_MSC_VER)
107 #  pragma data_seg()
108 # endif
109 
110 static volatile int *progress = &shared_progress_indicator;
111 static HANDLE Process    = NULL;
112 
GoBackground(DWORD code)113 BOOL WINAPI GoBackground (DWORD code)
114 {   if (*progress)
115     {	FreeConsole();	/* detach from console upon ctrl-c or window close */
116 	return TRUE;
117     }
118   return FALSE;
119 }
120 
KillForeground(void)121 void KillForeground (void)
122 {   fprintf(stderr,"\n");
123     TerminateProcess(Process,0);
124 }
125 #else
126 static volatile int *progress;
127 #endif
128 
129 static const char   *gui_action=NULL;
130 static const char   *str = "|/-\\",*backspaces="\b\b\b\b\b\b\b\b\b\b";
131 
132 #if defined(__unix) || defined(__unix__)
alarm_handler(int no)133 extern "C" void alarm_handler (int no)
134 { static int         i=0,len=1,old_progress=0;
135   int new_progress = *progress;
136 
137     if (gui_action)
138     {	fprintf (stderr,"* %s %.1f%%\n",gui_action,
139 			100.0*new_progress/65536.0);
140 	alarm(3);
141 	return;
142     }
143 
144     if (new_progress != old_progress)
145         len = fprintf (stderr,"%.*s%.1f%%",len,backspaces,
146 				100.0*new_progress/65536.0) - len,
147 	old_progress = new_progress;
148     else
149         fprintf (stderr,"\b%c",str[i]),
150 	i++, i&=3;
151 
152     alarm(1);
153 }
154 #endif
155 
main(int argc,char * argv[])156 int main (int argc, char *argv[])
157 { unsigned char	formats[260],dinfo[32],inq[128];
158   char		*dev=NULL,*p;
159   unsigned int	capacity,lead_out,mmc_profile,err;
160   int		len,i;
161   int		force=0,full=0,compat=0,blank=0,ssa=0,do_opc=0,gui=0,
162 		blu_ray=0,not_pow=0;
163 #ifdef _WIN32
164   DWORD		ppid;
165   char		filename[MAX_PATH],*progname;
166 
167     /*
168      * Foreground process spawns itself and simply waits for shared
169      * progress indicator to increment...
170      */
171     if (sscanf(argv[0],":%u:",&ppid) != 1)
172     { int  i=0,len=1,old_progress=0,new_progress;
173 
174 	sprintf (filename,":%u:",GetCurrentProcessId());
175 	progname = argv[0];
176 	argv[0]  = filename;
177 	Process  = (HANDLE)_spawnv (_P_NOWAIT,progname,argv);
178 	if (Process == (HANDLE)-1)
179 	    perror("_spawnv"), ExitProcess(errno);
180 
181 	while(1)
182 	{   if (WaitForSingleObject(Process,1000) == WAIT_OBJECT_0)
183 	    {	ppid = 0; /* borrow DWORD variable */
184 		GetExitCodeProcess(Process,&ppid);
185 		ExitProcess(ppid);
186 	    }
187 
188 	    new_progress = *progress;
189 	    if (new_progress != old_progress)
190 		len = fprintf (stderr,"%.*s%.1f%%",len,backspaces,
191 					100.0*new_progress/65536.0) - len,
192 		old_progress = new_progress;
193 	    else if (new_progress)
194 		fprintf (stderr,"\b%c",str[i]),
195 		i++, i&=3;
196 	}
197     }
198 
199     /*
200      * ... while background process does *all* the job...
201      */
202     Process = OpenProcess (PROCESS_TERMINATE,FALSE,ppid);
203     if (Process == NULL)
204 	perror("OpenProcess"), ExitProcess(errno);
205 
206     atexit (KillForeground);
207     SetConsoleCtrlHandler (GoBackground,TRUE);
208 
209     GetModuleFileName (NULL,filename,sizeof(filename));
210     progname = strrchr(filename,'\\');
211     if (progname)	argv[0] = progname+1;
212     else		argv[0] = filename;
213 #elif defined(__unix) || defined(__unix__)
214   pid_t		pid;
215 
216     { int fd;
217       char *s;
218 
219 	if ((fd=mkstemp (s=strdup("/tmp/dvd+rw-format.XXXXXX"))) < 0)
220 	    fprintf (stderr,":-( unable to mkstemp(\"%s\")",s),
221 	    exit(1);
222 
223 	ftruncate(fd,sizeof(*progress));
224 	unlink(s);
225 
226 	progress = (int *)mmap(NULL,sizeof(*progress),PROT_READ|PROT_WRITE,
227 				MAP_SHARED,fd,0);
228 	close (fd);
229 	if (progress == MAP_FAILED)
230             perror (":-( unable to mmap anonymously"),
231 	    exit(1);
232     }
233     *progress = 0;
234 
235     if ((pid=fork()) == (pid_t)-1)
236 	perror (":-( unable to fork()"),
237 	exit(1);
238 
239     if (pid)
240     { struct sigaction sa;
241 
242 	sigaction (SIGALRM,NULL,&sa);
243 	sa.sa_flags &= ~SA_RESETHAND;
244 	sa.sa_flags |= SA_RESTART;
245 	sa.sa_handler = alarm_handler;
246 	sigaction (SIGALRM,&sa,NULL);
247 	alarm(1);
248 	while ((waitpid(pid,&i,0) != pid) && !WIFEXITED(i)) ;
249 	if (WEXITSTATUS(i) == 0) fprintf (stderr,"\n");
250 	exit (0);
251     }
252 #endif
253 
254     fprintf (stderr,"* BD/DVD%sRW/-RAM format utility by <appro@fy.chalmers.se>, "
255 		    "version 7.1.\n",plusminus_locale());
256 
257     for (i=1;i<argc;i++) {
258 	if (*argv[i] == '-')
259 	    if (argv[i][1] == 'f')	// -format|-force
260 	    {	force = 1;
261 		if ((p=strchr(argv[i],'=')) && p[1]=='f') full=1;
262 	    }
263 	    else if (argv[i][1] == 'l')	// -lead-out
264 		force = compat = 1;
265 	    else if (argv[i][1] == 'b')	// -blank
266 	    {	blank=0x11;
267 		if ((p=strchr(argv[i],'=')) && p[1]=='f') blank=0x10;
268 		force=1;
269 	    }
270 	    else if (argv[i][1] == 'p')	// -pow
271 	    {	not_pow=1;		// minus pow
272 	    }
273 	    else if (argv[i][1] == 's')	// -ssa|-spare
274 	    {	force=ssa=1;
275 		if ((p=strchr(argv[i],'=')))
276 		{   if (p[1]=='n')	ssa=-1;	// =none
277 		    else if (p[3]=='n') ssa=1;	// =min
278 		    else if (p[1]=='d')	ssa=2;	// =default
279 		    else if (p[3]=='x')	ssa=3;	// =max
280 		    else if (p[1]=='.' || (p[1]>='0' && p[1]<='9'))
281 		    { char  *s=NULL;
282 		      double a=strtod(p+1,&s);
283 
284 			if (s)
285 			{   if      (*s=='g' || *s=='G')    a *= 1e9;
286 			    else if (*s=='m' || *s=='M')    a *= 1e6;
287 			    else if (*s=='k' || *s=='K')    a *= 1e3;
288 			}
289 			ssa = (int)(a/2e3);	// amount of 2K
290 		    }
291 		    else		ssa=0;	// invalid?
292 		}
293 	    }
294 	    else if (argv[i][1] == 'g')	gui=1;
295 	    else			usage(argv[0]);
296 #ifdef _WIN32
297 	else if (argv[i][1] == ':')
298 #else
299 	else if (*argv[i] == '/')
300 #endif
301 	    dev = argv[i];
302 	else
303 	    usage (argv[0]);
304     }
305 
306     if (dev==NULL) usage (argv[0]);
307 
308     Scsi_Command	cmd;
309 
310     if (!cmd.associate(dev))
311 	fprintf (stderr,":-( unable to open(\"%s\"): ",dev), perror (NULL),
312 	exit(1);
313 
314     cmd[0] = 0x12;	// INQUIRY
315     cmd[4] = 36;
316     cmd[5] = 0;
317     if ((err=cmd.transport(READ,inq,36)))
318 	sperror ("INQUIRY",err), exit (1);
319 
320     if ((inq[0]&0x1F) != 5)
321 	fprintf (stderr,":-( not an MMC unit!\n"),
322 	exit (1);
323 
324     cmd[0] = 0x46;		// GET CONFIGURATION
325     cmd[8] = 8;
326     cmd[9] = 0;
327     if ((err=cmd.transport(READ,formats,8)))
328 	sperror ("GET CONFIGURATION",err), exit (1);
329 
330     mmc_profile = formats[6]<<8|formats[7];
331 
332     blu_ray = ((mmc_profile&0xF0)==0x40);
333 
334     if (mmc_profile!=0x1A && mmc_profile!=0x2A
335 	&& mmc_profile!=0x14 && mmc_profile!=0x13
336 	&& mmc_profile!=0x12
337 	&& !blu_ray)
338 	fprintf (stderr,":-( mounted media doesn't appear to be "
339 			"DVD%sRW, DVD-RAM or Blu-ray\n",plusminus_locale()),
340 	exit (1);
341 
342     /*
343      * First figure out how long the actual list is. Problem here is
344      * that (at least Linux) USB units absolutely insist on accurate
345      * cgc.buflen and you can't just set buflen to arbitrary value
346      * larger than actual transfer length.
347      */
348     int once=1;
349     do
350     {	cmd[0] = 0x23;		// READ FORMAT CAPACITIES
351 	cmd[8] = 4;
352 	cmd[9] = 0;
353 	if ((err=cmd.transport(READ,formats,4)))
354 	{   if (err==0x62800 && once)	// "MEDIUM MAY HAVE CHANGED"
355 	    {	cmd[0] = 0;		// TEST UNIT READY
356 		cmd[5] = 0;
357 		cmd.transport();	// just swallow it...
358 		continue;
359 	    }
360 	    sperror ("READ FORMAT CAPACITIES",err), exit (1);
361 	}
362     } while (once--);
363 
364     len = formats[3];
365     if (len&7 || len<8)
366 	fprintf (stderr,":-( allocation length isn't sane\n"),
367 	exit(1);
368 
369     cmd[0] = 0x23;		// READ FORMAT CAPACITIES
370     cmd[7] = (4+len)>>8;	// now with real length...
371     cmd[8] = (4+len)&0xFF;
372     cmd[9] = 0;
373     if ((err=cmd.transport(READ,formats,4+len)))
374 	sperror ("READ FORMAT CAPACITIES",err), exit (1);
375 
376     if (len != formats[3])
377 	fprintf (stderr,":-( parameter length inconsistency\n"),
378 	exit(1);
379 
380     if (mmc_profile==0x1A || mmc_profile==0x2A)	// DVD+RW
381     {	for (i=8;i<len;i+=8)	// look for "DVD+RW Full" descriptor
382 	    if ((formats [4+i+4]>>2) == 0x26) break;
383     }
384     else if (mmc_profile==0x12)	// DVD-RAM
385     { unsigned int   v,ref;
386       unsigned char *f,descr=0x01;
387       int j;
388 
389 	switch (ssa)
390 	{   case -1:	// no ssa
391 		for (ref=0,i=len,j=8;j<len;j+=8)
392 		{   f = formats+4+j;
393 		    if ((f[4]>>2) == 0x00)
394 		    {	v=f[0]<<24|f[1]<<16|f[2]<<8|f[3];
395 			if (v>ref) ref=v,i=j;
396 		    }
397 		}
398 		break;
399 	    case 1:	// first ssa
400 		for (i=8;i<len;i+=8)
401 		    if ((formats[4+i+4]>>2) == 0x01) break;
402 		break;
403 	    case 2:	// default ssa
404 		descr=0x00;
405 	    case 3:	// max ssa
406 		for (ref=0xFFFFFFFF,i=len,j=8;j<len;j+=8)
407 		{   f = formats+4+j;
408 		    if ((f[4]>>2) == descr)
409 		    {	v=f[0]<<24|f[1]<<16|f[2]<<8|f[3];
410 			if (v<ref) ref=v,i=j;
411 		    }
412 		}
413 		break;
414 	    default:
415 		i=8;	// just grab the first descriptor?
416 		break;
417 	}
418     }
419     else if (mmc_profile==0x41)		// BD-R
420     { unsigned int   max,min,cap;
421       unsigned char *f;
422       int j;
423 
424 	switch (ssa)
425 	{   case -1:	// no spare -> nothing to do
426 	    case 0:
427 		exit (0);
428 		break;
429 	    case 1:	// min spare <- max capacity
430 		i=len;
431 		for (max=0,j=8;j<len;j+=8)
432 		{   f = formats+4+j;
433 		    if ((f[4]>>2) == 0x32)
434 		    {	cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3];
435 			if (max < cap) max=cap,i=j;
436 		    }
437 		}
438 		break;
439 	    case 2:	// default ssa
440 		i=8;	// just grab first descriptor
441 		break;
442 	    case 3:	// max, ~10GB, is too big to trust user
443 		fprintf (stderr,"- -ssa=max is not supported for BD-R, "
444 				"specify explicit size with -ssa=XXXG\n");
445 		exit (1);
446 	    default:
447 		i=len;
448 		for (max=0,min=0xffffffff,j=8;j<len;j+=8)
449 		{   f = formats+4+j;
450 		    if ((f[4]>>2) == 0x32)
451 		    {	cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3];
452 			if (max < cap) max=cap;
453 			if (min > cap) min=cap;
454 		    }
455 		}
456 		if (max==0) break;
457 
458 		// for simplicity adjust spare size according to DL(!) rules
459 		ssa += 8192, ssa &= ~16383;	// i.e. in 32MB increments
460 
461 		f = formats+4;
462 		capacity = f[0]<<24|f[1]<<16|f[2]<<8|f[3];
463 
464 		cap = capacity - ssa;
465 		// place it within given boundaries
466 		if      (cap < min)	cap = min;
467 		else if (cap > max)	cap = max;
468 
469 		i = 8;
470 		f = formats+4+i;
471 		f[0] = cap>>24;
472 		f[1] = cap>>16;
473 		f[2] = cap>>8;
474 		f[3] = cap;
475 		f[4] = 0x32<<2 | not_pow;
476 		f[5] = 0;
477 		f[6] = 0;
478 		f[7] = 0;
479 		break;
480 	}
481 
482 	if (i<len)
483 	{   f = formats+4+i;
484 	    f[4] &= ~3;		// it's either SRM+POW ...
485 	    f[4] |= not_pow;	// ... or SRM-POW
486 	}
487     }
488     else if (mmc_profile==0x43)		// BD-RE
489     { unsigned int   max,min,cap;
490       unsigned char *f;
491       int j;
492 
493 	switch (ssa)
494 	{   case -1:	// no spare
495 		for (i=8;i<len;i+=8)	// look for descriptor 0x31
496 		    if ((formats [4+i+4]>>2) == 0x31) break;
497 		break;
498 	    case 0:
499 		i = 8;
500 		if ((formats[4+4]&3)==2)// same capacity for already formatted
501 		{   f = formats+4+i;
502 		    memcpy (f,formats+4,4);
503 		    f[4] = 0x30<<2;
504 		    f[5] = 0;
505 		    f[6] = 0;
506 		    f[7] = 0;
507 		}
508 		break;
509 	    case 1:	// min spare <- max capacity
510 		i=len;
511 		for (max=0,j=8;j<len;j+=8)
512 		{   f = formats+4+j;
513 		    if ((f[4]>>2) == 0x30)
514 		    {	cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3];
515 			if (max < cap) max=cap,i=j;
516 		    }
517 		}
518 		break;
519 	    case 2:	// default ssa
520 		i=8;	// just grab first descriptor
521 		break;
522 	    case 3:	// max spare <- min capacity
523 		i=len;
524 		for (min=0xffffffff,j=8;j<len;j+=8)
525 		{   f = formats+4+j;
526 		    if ((f[4]>>2) == 0x30)
527 		    {	cap=f[0]<<24|f[1]<<16|f[2]<<8|f[3];
528 			if (min > cap) min=cap,i=j;
529 		    }
530 		}
531 		break;
532 	    default:
533 		i=len;
534 		capacity=0;
535 		for (max=0,min=0xffffffff,j=8;j<len;j+=8)
536 		{   f = formats+4+j;
537 		    if ((f[4]>>2) == 0x30)
538 		    {	cap = f[0]<<24|f[1]<<16|f[2]<<8|f[3];
539 			if (max < cap) max=cap;
540 			if (min > cap) min=cap;
541 		    }
542 		    else if ((f[4]>>2) == 0x31)
543 			capacity = f[0]<<24|f[1]<<16|f[2]<<8|f[3];
544 		}
545 		if (max==0 || capacity==0) break;
546 
547 		// for simplicity adjust spare size according to DL(!) rules
548 		ssa += 8192, ssa &= ~16383;	// i.e. in 32MB increments
549 
550 		cap = capacity - ssa;
551 		// place it within given boundaries
552 		if      (cap < min)	cap = min;
553 		else if (cap > max)	cap = max;
554 
555 		i = 8;
556 		f = formats+4+i;
557 		f[0] = cap>>24;
558 		f[1] = cap>>16;
559 		f[2] = cap>>8;
560 		f[3] = cap;
561 		f[4] = 0x30<<2;
562 		f[5] = 0;
563 		f[6] = 0;
564 		f[7] = 0;
565 		break;
566 	}
567 
568 	if (i<len)
569 	{   f = formats+4+i;
570 	    f[4] &= ~3;
571 	    if ((f[4]>>2)==0x30)
572 		f[4] |= full?2:3;	// "Full" or "Quick Certification"
573 	}
574     }
575     else			// DVD-RW
576     { int descr=full?0x10:0x15;
577 
578 	for (i=8;i<len;i+=8)	// look for "DVD-RW Quick" descriptor
579 	    if ((formats [4+i+4]>>2) == descr) break;
580 	if (descr==0x15 && i==len)
581 	{   fprintf (stderr,":-( failed to locate \"Quick Format\" descriptor.\n");
582 	    for (i=8;i<len;i+=8)// ... alternatively for "DVD-RW Full"
583 		if ((formats [4+i+4]>>2) == 0x10) break;
584 	}
585     }
586 
587     if (i==len)
588     {	fprintf (stderr,":-( can't locate appropriate format descriptor\n");
589 	if (blank)	i=0;
590 	else		exit(1);
591     }
592 
593     capacity = 0;
594     if (blu_ray)
595     {	capacity |= formats[4+0], capacity <<= 8;
596 	capacity |= formats[4+1], capacity <<= 8;
597 	capacity |= formats[4+2], capacity <<= 8;
598 	capacity |= formats[4+3];
599     }
600     else
601     {	capacity |= formats[4+i+0], capacity <<= 8;
602 	capacity |= formats[4+i+1], capacity <<= 8;
603 	capacity |= formats[4+i+2], capacity <<= 8;
604 	capacity |= formats[4+i+3];
605     }
606 
607     if (mmc_profile==0x1A || mmc_profile==0x2A)	// DVD+RW
608 	fprintf (stderr,"* %.1fGB DVD+RW media detected.\n",
609 			2048.0*capacity/1e9);
610     else if (mmc_profile==0x12)			// DVD-RAM
611 	fprintf (stderr,"* %.1fGB DVD-RAM media detected.\n",
612 			2048.0*capacity/1e9);
613     else if (blu_ray)				// BD
614 	fprintf (stderr,"* %.1fGB BD media detected.\n",
615 			2048.0*capacity/1e9);
616     else					// DVD-RW
617 	fprintf (stderr,"* %.1fGB DVD-RW media in %s mode detected.\n",
618 			2048.0*capacity/1e9,
619 			mmc_profile==0x13?"Restricted Overwrite":"Sequential");
620 
621     lead_out = 0;
622     lead_out |= formats[4+0], lead_out <<= 8;
623     lead_out |= formats[4+1], lead_out <<= 8;
624     lead_out |= formats[4+2], lead_out <<= 8;
625     lead_out |= formats[4+3];
626 
627     cmd[0] = 0x51;		// READ DISC INFORMATION
628     cmd[8] = sizeof(dinfo);
629     cmd[9] = 0;
630     if ((err=cmd.transport(READ,dinfo,sizeof(dinfo))))
631     {	sperror ("READ DISC INFORMATION",err);
632 	if (!force) exit(1);
633 	memset (dinfo,0xff,sizeof(dinfo));
634 	cmd[0] = 0x35;
635 	cmd[9] = 0;
636 	cmd.transport();
637     }
638 
639     do_opc = ((dinfo[0]<<8|dinfo[1])<=0x20);
640 
641     if (dinfo[2]&3)		// non-blank media
642     {	if (!force)
643 	{   if (mmc_profile==0x1A || mmc_profile==0x2A || mmc_profile==0x13 || mmc_profile==0x12)
644 		fprintf (stderr,"- media is already formatted, lead-out is currently at\n"
645 				"  %d KiB which is %.1f%% of total capacity.\n",
646 				lead_out*2,(100.0*lead_out)/capacity);
647 	    else
648 		fprintf (stderr,"- media is not blank\n");
649 	offer_options:
650 	    if (mmc_profile == 0x1A || mmc_profile == 0x2A)
651 		fprintf (stderr,"- you have the option to re-run %s with:\n"
652 				"  -lead-out  to elicit lead-out relocation for better\n"
653 				"             DVD-ROM compatibility, data is not affected;\n"
654 				"  -force     to enforce new format (not recommended)\n"
655 				"             and wipe the data.\n",
656 				argv[0]);
657 	    else if (mmc_profile == 0x12)
658 		fprintf (stderr,"- you have the option to re-run %s with:\n"
659 				"  -format=full  to perform full (lengthy) reformat;\n"
660 				"  -ssa[=none|default|max]\n"
661 				"                to grow, eliminate, reset to default or\n"
662 				"                maximize Supplementary Spare Area.\n",
663 				argv[0]);
664 	    else if (mmc_profile == 0x43)
665 		fprintf (stderr,"- you have the option to re-run %s with:\n"
666 				"  -format=full  to perform (lengthy) Full Certification;\n"
667 				"  -ssa[=none|default|max|XXX[GM]]\n"
668 				"                to eliminate or adjust Spare Area.\n",
669 				argv[0]);
670 	    else if (blu_ray)	// BD-R
671 		fprintf (stderr,"- BD-R can be pre-formatted only once\n");
672 	    else
673 	    {	fprintf (stderr,"- you have the option to re-run %s with:\n",
674 				argv[0]);
675 		if (i) fprintf (stderr,
676 				"  -force[=full] to enforce new format or mode transition\n"
677 				"                and wipe the data;\n");
678 		fprintf (stderr,"  -blank[=full] to change to Sequential mode.\n");
679 	    }
680 	    exit (0);
681 	}
682 	else if (cmd.umount())
683 	    perror (errno==EBUSY ? ":-( unable to proceed with format" :
684 				   ":-( unable to umount"),
685 	    exit (1);
686     }
687     else
688     {	if (mmc_profile==0x14 && blank==0 && !force)
689 	{   fprintf (stderr,"- media is blank\n");
690 	    fprintf (stderr,"- given the time to apply full blanking procedure I've chosen\n"
691 			    "  to insist on -force option upon mode transition.\n");
692 	    exit (0);
693 	}
694 	else if (mmc_profile==041 && !force)
695 	{   fprintf (stderr,"- media is blank\n");
696 	    fprintf (stderr,"- as BD-R can be pre-formance only once, I've chosen\n"
697 			    "  to insist on -force option.\n");
698 	}
699 	force = 0;
700     }
701 
702     if (((mmc_profile == 0x1A || mmc_profile == 0x2A) && blank)
703 	|| (mmc_profile != 0x1A && compat)
704 	|| (mmc_profile != 0x12 && mmc_profile != 0x43 && ssa) )
705     {	fprintf (stderr,"- illegal command-line option for this media.\n");
706 	goto offer_options;
707     }
708     else if ((mmc_profile == 0x1A || mmc_profile == 0x2A) && full)
709     {	fprintf (stderr,"- unimplemented command-line option for this media.\n");
710 	goto offer_options;
711     }
712 
713 #if defined(__unix) || defined(__unix__)
714     /*
715      * You can suspend, terminate, etc. the parent. We will keep on
716      * working in background...
717      */
718     setsid();
719     signal(SIGHUP,SIG_IGN);
720     signal(SIGINT,SIG_IGN);
721     signal(SIGTERM,SIG_IGN);
722 #endif
723 
724     if (compat && force)	str="relocating lead-out";
725     else if (blank)		str="blanking";
726     else			str="formatting";
727     if (gui)			gui_action=str;
728     else			fprintf (stderr,"* %s .",str);
729 
730     *progress = 1;
731 
732     // formats[i] becomes "Format Unit Parameter List"
733     formats[i+0] = 0;		// "Reserved"
734     formats[i+1] = 2;		// "IMMED" flag
735     formats[i+2] = 0;		// "Descriptor Length" (MSB)
736     formats[i+3] = 8;		// "Descriptor Length" (LSB)
737 
738     handle_events(cmd);
739 
740     if (mmc_profile==0x1A || mmc_profile==0x2A)	// DVD+RW
741     {	if (compat && force && (dinfo[2]&3))
742 	    formats[i+4+0]=formats[i+4+1]=formats[i+4+2]=formats[i+4+3]=0,
743 	    formats[i+4+7] = 1;	// "Restart format"
744 
745 	cmd[0] = 0x04;		// FORMAT UNIT
746 	cmd[1] = 0x11;		// "FmtData" and "Format Code"
747 	cmd[5] = 0;
748 	if ((err=cmd.transport(WRITE,formats+i,12)))
749 	    sperror ("FORMAT UNIT",err), exit(1);
750 
751 	if (wait_for_unit (cmd,progress)) exit (1);
752 
753 	if (!compat)
754 	{   cmd[0] = 0x5B;	// CLOSE TRACK/SESSION
755 	    cmd[1] = 1;		// "IMMED" flag on
756 	    cmd[2] = 0;		// "Stop De-Icing"
757 	    cmd[9] = 0;
758 	    if ((err=cmd.transport()))
759 		sperror ("STOP DE-ICING",err), exit(1);
760 
761 	    if (wait_for_unit (cmd,progress)) exit (1);
762 	}
763 
764 	cmd[0] = 0x5B;		// CLOSE TRACK/SESSION
765 	cmd[1] = 1;		// "IMMED" flag on
766 	cmd[2] = 2;		// "Close Session"
767 	cmd[9] = 0;
768 	if ((err=cmd.transport()))
769 	    sperror ("CLOSE SESSION",err), exit(1);
770 
771 	if (wait_for_unit (cmd,progress)) exit (1);
772     }
773     else if (mmc_profile==0x12)	// DVD-RAM
774     {	cmd[0] = 0x04;		// FORMAT UNIT
775 	cmd[1] = 0x11;		// "FmtData"|"Format Code"
776 	cmd[5] = 0;
777 	formats[i+1] = 0x82;	// "FOV"|"IMMED"
778 	if ((formats[i+4+4]>>2) != 0x01 && !full)
779 	    formats[i+1] |= 0x20,// |"DCRT"
780 	    cmd[1] |= 0x08;	// |"CmpLst"
781 	if ((err=cmd.transport(WRITE,formats+i,12)))
782 	    sperror ("FORMAT UNIT",err), exit(1);
783 
784 	if (wait_for_unit (cmd,progress)) exit(1);
785     }
786     else if (mmc_profile==0x43)	// BD-RE
787     {	cmd[0] = 0x04;		// FORMAT UNIT
788 	cmd[1] = 0x11;		// "FmtData"|"Format Code"
789 	cmd[5] = 0;
790 	formats[i+1] = 0x82;	// "FOV"|"IMMED"
791 	if (full && (formats[i+4+4]>>2)!=0x31)
792 	    formats[i+4+4] |= 2;// "Full Certificaton"
793 	else if ((formats[i+4+4]>>2)==0x30)
794 	    formats[i+4+4] |= 3;// "Quick Certification"
795 	if ((err=cmd.transport(WRITE,formats+i,12)))
796 	    sperror ("FORMAT UNIT",err), exit(1);
797 
798 	if (wait_for_unit (cmd,progress)) exit(1);
799     }
800     else if (mmc_profile==0x41)	// BD-R
801     {	cmd[0] = 0x04;		// FORMAT UNIT
802 	cmd[1] = 0x11;		// "FmtData"|"Format Code"
803 	cmd[5] = 0;
804 	formats[i+1] = 0x2;	// IMMED"
805 	if ((err=cmd.transport(WRITE,formats+i,12)))
806 	    sperror ("FORMAT UNIT",err), exit(1);
807 
808 	if (wait_for_unit (cmd,progress)) exit(1);
809     }
810     else			// DVD-RW
811     {	page05_setup (cmd,mmc_profile);
812 
813 	if (do_opc)
814 	{   cmd[0] = 0x54;	// SEND OPC INFORMATION
815 	    cmd[1] = 1;		// "Perform OPC"
816 	    cmd[9] = 0;
817 	    cmd.timeout(120);	// NEC units can be slooo...w
818 	    if ((err=cmd.transport()))
819 	    {	if (err==0x17301)	// "POWER CALIBRATION AREA ALMOST FULL"
820 		    fprintf (stderr,":-! WARNING: Power Calibration Area "
821 					"is almost full\n");
822 		else
823 		{   sperror ("PERFORM OPC",err);
824 		    if ((err&0x0FF00)==0x07300 && (err&0xF0000)!=0x10000)
825 			exit (1);
826 		    /* The rest of errors are ignored, see groisofs_mmc.cpp
827 		     * for further details... */
828 		}
829 	    }
830 	}
831 
832 	if (blank)		// DVD-RW blanking procedure
833     	{   cmd[0] = 0xA1;	// BLANK
834 	    cmd[1] = blank;
835 	    cmd[11] = 0;
836 	    if ((err=cmd.transport()))
837 		sperror ("BLANK",err), exit(1);
838 
839 	    if (wait_for_unit (cmd,progress)) exit (1);
840 	}
841 	else			// DVD-RW format
842 	{   if ((formats[i+4+4]>>2)==0x15)	// make it really quick
843 		formats[i+4+0]=formats[i+4+1]=formats[i+4+2]=formats[i+4+3]=0;
844 
845 	    cmd[0] = 0x04;	// FORMAT UNIT
846 	    cmd[1] = 0x11;	// "FmtData" and "Format Code"
847 	    cmd[5] = 0;
848 	    if ((err=cmd.transport(WRITE,formats+i,12)))
849 		sperror ("FORMAT UNIT",err), exit(1);
850 
851 	    if (wait_for_unit (cmd,progress)) exit (1);
852 
853 	    cmd[0] = 0x35;	// FLUSH CACHE
854 	    cmd[9] = 0;
855 	    cmd.transport ();
856 	}
857     }
858 
859     pioneer_stop (cmd,progress);
860 
861 #if 0
862     cmd[0] = 0x1E;	// ALLOW MEDIA REMOVAL
863     cmd[5] = 0;
864     if (cmd.transport ()) return 1;
865 
866     cmd[0] = 0x1B;	// START/STOP UNIT
867     cmd[4] = 0x2;	// "Eject"
868     cmd[5] = 0;
869     if (cmd.transport()) return 1;
870 
871     cmd[0] = 0x1B;	// START/STOP UNIT
872     cmd[1] = 0x1;	// "IMMED"
873     cmd[4] = 0x3;	// "Load"
874     cmd[5] = 0;
875     cmd.transport ();
876 #endif
877 
878   return 0;
879 }
880