1 /*
2  * DVD Media Info utility by Andy Polyakov <appro@fy.chalmers.se>.
3  *
4  * This code is in public domain.
5  */
6 
7 #include <stdio.h>
8 #include <stddef.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include "transport.hxx"
13 
14 #ifdef _WIN32
15 #define LLU "I64u"
16 #else
17 #define LLU "llu"
18 #endif
19 
main(int argc,char * argv[])20 int main(int argc,char *argv[])
21 { Scsi_Command	cmd;
22   unsigned char	header[48],inq[128],*page2A=NULL;
23   char		cmdname[64];
24   int		i,j,ntracks,err,dvd_dash=0,dvd_plus=0,
25 		plus_mediaid_printed=0,page2A_printed=0,dvd_ram_spare=0;
26   unsigned short profile,
27 		dvd_0E=0,dvd_10=0,dvd_11=0,dvd_0A=0,dvd_C0=0,
28 		prf_23=0,prf_24=0,prf_38=0;
29   int		_1x=1385,blu_ray=0;
30 
31     if (argc<2)
32 	fprintf (stderr,"usage: %s /dev/dvd\n",argv[0]),
33 	exit (FATAL_START(EINVAL));
34 
35     if (!cmd.associate(argv[1]))
36 	fprintf (stderr,"%s: unable to open: ",argv[1]),
37 	perror (NULL),
38 	exit (FATAL_START(errno));
39 
40     cmd[0] = 0x12;	// INQUIRY
41     cmd[4] = 36;
42     cmd[5] = 0;
43     if ((err=cmd.transport(READ,inq,36)))
44 	sperror ("INQUIRY",err),
45 	exit (FATAL_START(errno));
46 
47     if ((inq[0]&0x1F) != 5)
48 	fprintf (stderr,":-( not an MMC unit!\n"),
49 	exit (FATAL_START(EINVAL));
50 
51     if (argc>1)
52 	printf ("INQUIRY:                [%.8s][%.16s][%.4s]\n",
53 		inq+8,inq+16,inq+32);
54 
55 #if 0
56     wait_for_unit(cmd);
57 #else
58     // consume sense data, most commonly "MEDIUM MAY HAVE CHANGED"...
59     cmd[0] = 0;		// TEST UNIT READY
60     cmd[5] = 0;
61     if ((err=cmd.transport()) == -1)
62 	sperror ("TEST UNIT READY",err),
63 	exit (FATAL_START(errno));
64 #endif
65 
66     if (argc>2) do
67     { unsigned char *pages;
68       int len,n;
69       static int pagecode=0x3F;
70 
71 	printf ("MODE SENSE[#%02Xh]:\n",pagecode);
72 
73 	cmd[0] = 0x5A;		// MODE SENSE
74 	cmd[1] = 0x08;		// "Disable Block Descriptors"
75 	cmd[2] = pagecode;	// initially "All Pages"
76 	cmd[8] = 12;
77 	cmd[9] = 0;
78 	if ((err=cmd.transport(READ,header,12)))
79 	    sprintf (cmdname,"MODE SENSE#%02X",pagecode),
80 	    sperror (cmdname,err),
81 	    exit(errno);
82 
83 	if (header[6]<<8|header[7])
84 	    fprintf (stderr,":-( \"DBD\" is not respected\n"),
85 	    exit(errno);
86 
87 	len=(header[0]<<8|header[1])+2;
88 
89 	if (len < (8+2+header[9]))
90 	{   if (pagecode == 0x3F)
91 	    {	pagecode = 0x5;
92 		continue;
93 	    }
94 	    len = 8+2+header[9];
95 	}
96 
97 	if (!(pages=(unsigned char *)malloc(len))) exit(1);
98 
99 	cmd[0] = 0x5A;
100 	cmd[1] = 0x08;
101 	cmd[2] = pagecode;
102 	cmd[7] = len>>8;
103 	cmd[8] = len;
104 	cmd[9] = 0;
105 	cmd.transport(READ,pages,len);
106 
107 	for(i=8+(pages[6]<<8|pages[7]);i<len;)
108 	{   if (pages[i] == 0)	break;
109 
110 	    if (pages[i] == 0x2A) page2A_printed=1;
111 	    printf (" %02X:",pages[i++]);
112 	    n=pages[i++];
113 	    for (j=0;j<n;j++)
114 	    {	if (j && j%16==0) printf("\n");
115 		printf ("%c%02x",(j%16)?' ':'\t',pages[i++]);
116 	    }
117 	    printf("\n");
118 	}
119 
120 	if (i<len)
121 	{   printf (" RES:");
122 	    for (len-=i,j=0;j<len;j++)
123 	    {	if (j && j%16==0) printf("\n");
124 		printf ("%c%02x",(j%16)?' ':'\t',pages[i++]);
125 	    }
126 	    printf ("\n");
127 	}
128 
129 	free(pages);
130 
131 	break;
132 
133     } while (1);
134 
135     { int len,n;
136 
137 	page2A=header, len=36;
138 
139 	while (1)
140 	{   cmd[0] = 0x5A;	// MODE SENSE(10)
141 	    cmd[1] = 0x08;	// "Disable Block Descriptors"
142 	    cmd[2] = 0x2A;	// "Capabilities and Mechanical Status"
143 	    cmd[7] = len>>8;
144 	    cmd[8] = len;
145 	    cmd[9] = 0;
146 	    if ((err=cmd.transport(READ,page2A,len)))
147 		sperror ("MODE SENSE#2A",err),
148 		exit (errno);
149 
150 	    if (page2A[6]<<8|page2A[7])
151 		fprintf (stderr,":-( \"DBD\" is not respected\n");
152 	    else if (len<((page2A[0]<<8|page2A[1])+2) || len<(8+2+page2A[9]))
153 	    {	len=(page2A[0]<<8|page2A[1])+2;
154 		if (len < (8+2+page2A[9]))
155 		    len = 8+2+page2A[9];
156 		page2A=(unsigned char *)malloc(len);
157 		continue;
158 	    }
159 
160 	    if (!(page2A[8+(page2A[6]<<8|page2A[7])+2]&8))
161 		fprintf (stderr,":-( not a DVD unit\n"),
162 		exit (FATAL_START(EINVAL));
163 
164 	    if (page2A==header) page2A=NULL;
165 	    else if (argc>2 && !page2A_printed)
166 	    {	printf("MODE SENSE[#2A]:\n");
167 		for(i=8+(page2A[6]<<8|page2A[7]);i<len;)
168 		{   printf (" %02X:",page2A[i++]);
169 		    n=page2A[i++];
170 		    for (j=0;j<n;j++)
171 		    {	if (j && j%16==0) printf("\n");
172 			printf ("%c%02x",(j%16)?' ':'\t',page2A[i++]);
173 		    }
174 		    printf("\n");
175 		}
176 	    }
177 
178 	    break;
179 	}
180     }
181 
182     cmd[0] = 0x46;	// GET CONFIGURATION
183     cmd[1] = 1;
184     cmd[8] = 8;
185     cmd[9] = 0;
186     if ((err=cmd.transport(READ,header,8)))
187     {	if (err!=0x52000)	// "IVALID COMMAND OPERATION CODE"
188 	    sperror ("GET CONFIGURATION",err),
189 	    perror (NULL),
190 	    exit(FATAL_START(errno));
191 	profile=0x10;	// non MMC3 DVD-ROM unit?
192 	goto legacy;
193     }
194 
195     profile=header[6]<<8|header[7];
196 
197     do
198     { unsigned char *profiles;
199       int len,n;
200 
201 	len=header[2]<<8|header[3];
202 	len+=4;
203 
204 	if (!(profiles=(unsigned char *)malloc(len))) exit(1);
205 
206 	cmd[0] = 0x46;
207 	cmd[1] = 1;
208 	cmd[6] = len>>16;
209 	cmd[7] = len>>8;
210 	cmd[8] = len;
211 	cmd[9] = 0;
212 	cmd.transport(READ,profiles,len);
213 
214 	printf ("GET [CURRENT] CONFIGURATION:\n");
215 
216 	for(i=8;i<len;)
217 	{   unsigned short p=profiles[i]<<8|profiles[i+1];
218 	    n=profiles[i+3]; i+=4;
219 	    if      (p==0x23)	prf_23=n+4;
220 	    else if (p==0x24)	prf_24=n+4;
221 	    else if (p==0x38)	prf_38=n+4;
222 	    if (argc<=2)
223 	    {	i+=n; continue;   }
224 	    printf (" %04X%c",p,':');
225 	    for (j=0;j<n;j++)
226 	    {	if (j && j%16==0) printf("\n");
227 		printf ("%c%02x",(j%16)?' ':'\t',profiles[i++]);
228 	    }
229 	    printf("\n");
230 	}
231 
232 	free(profiles);
233     } while(0);
234 
235     if (profile==0)
236 	fprintf (stderr,":-( no media mounted, exiting...\n"),
237 	exit(FATAL_START(ENOMEDIUM));
238     if ((profile&0xF0) != 0x10 && (profile&0xF0) != 0x20 &&
239 	(profile&0xF0) != 0x40)
240 	fprintf (stderr,":-( non-DVD media mounted, exiting...\n"),
241 	exit (FATAL_START(EMEDIUMTYPE));
242 
243     { const char *str;
244 
245 	switch (profile)
246 	{   case 0x10:	str="DVD-ROM";				break;
247 	    case 0x11:	str="DVD-R Sequential";			break;
248 	    case 0x12:	str="DVD-RAM";				break;
249 	    case 0x13:	str="DVD-RW Restricted Overwrite";	break;
250 	    case 0x14:	str="DVD-RW Sequential";		break;
251 	    case 0x15:	str="DVD-R Dual Layer Sequential";	break;
252 	    case 0x16:	str="DVD-R Dual Layer Jump";		break;
253 	    case 0x1A:	str="DVD+RW";				break;
254 	    case 0x1B:	str="DVD+R";				break;
255 	    case 0x2A:	str="DVD+RW Double Layer";		break;
256 	    case 0x2B:	str="DVD+R Double Layer";		break;
257 	    case 0x40:	str="BD-ROM";				break;
258 	    case 0x41:	str=prf_38?"BD-R SRM+POW":"BD-R SRM";	break;
259 	    case 0x42:	str="BD-R RRM";				break;
260 	    case 0x43:	str="BD-RE";				break;
261 	    default:	str="unknown";				break;
262 	}
263 
264 	printf (" Mounted Media:         %Xh, %s\n",profile,str);
265 	if (str[3]=='-' && str[6]!='M')	dvd_dash=0x10;
266 	if (str[3]=='+')		dvd_plus=0x11;
267     }
268 
269     if ((profile&0xF0) == 0x40)	_1x=4495, blu_ray=1;
270 
271     do
272     { unsigned int   len,j;
273       unsigned char *p;
274 
275 	cmd[0] = 0xAD;
276 	cmd[1] = blu_ray;	// Media Type
277 	cmd[7] = 0xFF;
278 	cmd[9] = 4;
279 	cmd[11] = 0;
280 	if ((err=cmd.transport(READ,header,4)))
281 	{   if (argc>2)
282 		sperror (blu_ray?"READ BD STRUCTURE#FF":"READ DVD STRUCTURE#FF",err);
283 	    break;
284 	}
285 
286 	len = (header[0]<<8|header[1]) + 2;
287 	if (!(p = (unsigned char *)malloc(len))) break;
288 
289 	cmd[0] = 0xAD;
290 	cmd[1] = blu_ray;	// Media Type
291 	cmd[7] = 0xFF;
292 	cmd[8] = len>>8;
293 	cmd[9] = len;
294 	cmd[11] = 0;
295 	if ((err=cmd.transport(READ,p,len)))
296 	    break;
297 
298 	for (j=4;j<len;j+=4)
299 	    switch (p[j])
300 	    {	case 0x0A:  dvd_0A=(p[j+2]<<8|p[j+3])+2;    break;
301 		case 0x0E:  dvd_0E=(p[j+2]<<8|p[j+3])+2;    break;
302 		case 0x10:  dvd_10=(p[j+2]<<8|p[j+3])+2;    break;
303 		case 0x11:  dvd_11=(p[j+2]<<8|p[j+3])+2;    break;
304 		case 0xC0:  dvd_C0=(p[j+2]<<8|p[j+3])+2;    break;
305 	    }
306 
307 	if (argc<=2) break;
308 
309 	printf ("READ %s STRUCTURE[#FF]:\n",blu_ray?"BD":"DVD");
310 	for (j=4;j<len;j+=4)
311 	printf(" STRUCTURE#%02x           %02x:%d\n",p[j],p[j+1],p[j+2]<<8|p[j+3]);
312 
313     } while (0);
314 
315     if (blu_ray) do
316     { unsigned char di[128];
317       unsigned int  len;
318       unsigned char format=0;
319 
320 	cmd[0] = 0xAD;
321 	cmd[1] = blu_ray;	// Media Type
322 	cmd[7] = format;
323 	cmd[9] = 4;
324 	cmd[11] = 0;
325 	if ((err=cmd.transport(READ,header,4)))
326 	{   if (argc>2)
327 		sprintf (cmdname,"READ BD STRUCTURE#%02x",format),
328 		sperror (cmdname,cmd.sense());
329 	    break;
330 	}
331 
332 	len = (header[0]<<8|header[1]) + 2;
333 	if (len>sizeof(di)) len=sizeof(di);
334 
335 	cmd[0] = 0xAD;
336 	cmd[1] = blu_ray;	// Media Type
337 	cmd[7] = format;
338 	cmd[8] = len>>8;
339 	cmd[9] = len;
340 	cmd[11] = 0;
341 	if ((err=cmd.transport(READ,&di,len)))
342 	    break;
343 
344 	if (argc>2)
345 	{   printf ("READ BD STRUCTURE[#%02xh]:",format);
346 	    for (j=0;j<(int)len;j++)
347 		printf("%c%02x",j?' ':'\t',di[j]);
348 	    printf("\n");
349 	}
350 
351 	if (di[4+0]=='D' && di[4+1]=='I')
352 	printf (" Media ID:              %6.6s/%-3.3s\n",di+4+100,di+4+106);
353 
354     } while (0);
355     else if (dvd_0E || dvd_11) do
356     { union { unsigned char _e[4+40],_11[4+256]; } dvd;
357       unsigned int  len;
358       unsigned char format=dvd_plus?0x11:0x0E;
359 
360 	cmd[0] = 0xAD;
361 	cmd[7] = format;
362 	cmd[9] = 4;
363 	cmd[11] = 0;
364 	if ((err=cmd.transport(READ,header,4)))
365 	{   if (argc>2)
366 		sprintf (cmdname,"READ DVD STRUCTURE#%02x",format),
367 		sperror (cmdname,cmd.sense());
368 	    break;
369 	}
370 
371 	len = (header[0]<<8|header[1]) + 2;
372 	if (len>sizeof(dvd)) len=sizeof(dvd);
373 
374 	cmd[0] = 0xAD;
375 	cmd[7] = format;
376 	cmd[8] = len>>8;
377 	cmd[9] = len;
378 	cmd[11] = 0;
379 	if ((err=cmd.transport(READ,&dvd,len)))
380 	    break;
381 
382 	if (argc>2)
383 	{   printf ("READ DVD STRUCTURE[#%02xh]:",format);
384 	    for (j=0,i=dvd_dash?sizeof(dvd._e):sizeof(dvd._11);j<i;j++)
385 		printf("%c%02x",j?' ':'\t',dvd._11[j]);
386 	    printf("\n");
387 	}
388 
389 	if (!dvd_plus && dvd_0E && dvd._e[4+16]==3 && dvd._e[4+24]==4)
390 	printf (" Media ID:              %6.6s%-6.6s\n",dvd._e+4+17,dvd._e+4+25);
391 
392 	if (dvd_plus && dvd_11)
393 	printf (" Media ID:              %.8s/%.3s\n",dvd._11+23,dvd._11+31),
394 	plus_mediaid_printed=1;
395 
396     } while (0);
397 
398     if (page2A) do
399     { int len,hlen,v,n=0;
400       unsigned char *p;
401 
402 	len  = (page2A[0]<<8|page2A[1])+2;
403 	hlen = 8+(page2A[6]<<8|page2A[7]);
404 	if (len<(hlen+30)) break;	// no "Current Write Speed"
405 
406         p = page2A+hlen;
407 
408 	v = p[28]<<8|p[29];
409 	// Check Write Speed descriptors for sanity. Some DVD+units
410 	// return CD-R descriptors here, which has no meaning in the
411 	// context of interest.
412 	if (v<1352)	break;
413 	if (len>=(hlen+32))
414 	{   n = (p[30]<<8|p[31])*4;
415 	    for (i=0;i<n;i+=4)
416 		if ((p[32+i+2]<<8|p[32+i+3]) < 1352) break;
417 	    if (i<n) break;
418 	}
419 
420 	printf (" Current Write Speed:   %.1fx%d=%dKB/s\n",(double)v/_1x,_1x,v);
421 
422 	if (len<(hlen+32)) break;	// no "Write Speed Descriptors"
423 
424 	for (i=0;i<n;i+=4)
425 	v = p[32+i+2]<<8|p[32+i+3],
426 	printf (" Write Speed #%d:        %.1fx%d=%dKB/s\n",i/4,(double)v/_1x,_1x,v);
427 
428     } while (0);
429 
430     do
431     { unsigned char d[8+16],*p;
432       unsigned int  len,rv,wv,i;
433 
434 	cmd[0]=0xAC;		// GET PERFORMANCE
435 	cmd[1]=4;		// ask for "Overall Write Performance"
436 	cmd[9]=1;		// start with one descriptor
437 	cmd[10]=0;		// ask for descriptor in effect
438 	cmd[11]=0;
439 	if ((err=cmd.transport(READ,d,sizeof(d))))
440 	{   if (argc>2)
441 		sperror ("GET CURRENT PERFORMACE",err);
442 	    break;
443 	}
444 
445 	len = (d[0]<<24|d[1]<<16|d[2]<<8|d[3])-4;
446 
447 	if (len%16)	// insane length
448 	{   if (argc>2)
449 		fprintf (stderr,":-( insane GET PERFORMANCE length %u\n",len);
450 	    break;
451 	}
452 
453 	len+=8;
454 	if (len < sizeof(d))
455 	{   if (argc>2)
456 		fprintf (stderr,":-( empty GET CURRENT PERFORMACE descriptor\n");
457 	    break;
458 	}
459 	if (len == sizeof(d))
460 	    p = d;
461 	else	// ever happens?
462 	{ unsigned int n=(len-8)/16;
463 
464 	    p = (unsigned char *)malloc(len);	// will leak...
465 
466 	    cmd[0]=0xAC;	// GET PERFORMANCE
467 	    cmd[1]=4;		// ask for "Overall Write Performance"
468 	    cmd[8]=n>>8;
469 	    cmd[9]=n;		// real number of descriptors
470 	    cmd[10]=0;		// ask for descriptor in effect
471 	    cmd[11]=0;
472 	    if ((err=cmd.transport(READ,p,len)))
473 	    {	sperror ("GET CURRENT PERFORMANCE",err);
474 		break;
475 	    }
476 	}
477 
478 	if (argc>2)
479 	{   printf ("GET CURRENT PERFORMANCE:\t");
480 	    for (i=4;i<len;i++) printf ("%02x ",p[i]);
481 	    printf ("\n");
482 	}
483 
484 	if ((p[4]&2) == 0)
485 	    fprintf (stderr,":-( reported write performance might be bogus\n");
486 
487 	if (argc<=2) printf ("GET [CURRENT] PERFORMANCE:\n");
488 
489 	for (p+=8,len-=8,i=0;len;p+=16,len-=16,i++)
490 	{   rv=p[4]<<24 |p[5]<<16 |p[6]<<8 |p[7],
491 	    wv=p[12]<<24|p[13]<<16|p[14]<<8|p[15];
492 	    if (rv==wv)
493 		printf (" %-23s%.1fx%d=%uKB/s@[%u -> %u]\n",
494 			i==0?"Write Performance:":"",
495 			(double)rv/_1x,_1x,rv,
496 			p[0]<<24|p[1]<<16|p[2]<<8 |p[3],
497 			p[8]<<24|p[9]<<16|p[10]<<8|p[11]);
498 	    else
499 		printf (" %-23s%.1fx%d=%uKB/s@%u -> %.1fx%d=%uKB/s@%u\n",
500 			i==0?"Write Performance:":"",
501 			(double)rv/_1x,_1x,rv,p[0]<<24|p[1]<<16|p[2]<<8 |p[3],
502 			(double)wv/_1x,_1x,wv,p[8]<<24|p[9]<<16|p[10]<<8|p[11]);
503 	}
504     } while (0);
505 
506     do
507     { unsigned char d[8+16],*p;
508       unsigned int  len,rv,wv,i;
509 
510 	cmd[0]=0xAC;		// GET PERFORMANCE
511 	cmd[9]=1;		// start with one descriptor
512 	cmd[10]=0x3;		// ask for "Write Speed Descriptor"
513 	cmd[11]=0;
514 	if ((err=cmd.transport(READ,d,sizeof(d))))
515 	{   if (argc>2)
516 		sperror ("GET PERFORMACE",err);
517 	    break;
518 	}
519 
520 	len = (d[0]<<24|d[1]<<16|d[2]<<8|d[3])-4;
521 
522 	if (len%16)	// insage length
523 	{   if (argc>2)
524 		fprintf (stderr,":-( insane GET PERFORMANCE length %u\n",len);
525 	    break;
526 	}
527 
528 	len+=8;
529 	if (len < sizeof(d))
530 	{   fprintf (stderr,":-( empty GET PERFORMACE descriptor\n");
531 	    break;
532 	}
533 	if (len == sizeof(d))
534 	    p = d;
535 	else
536 	{ unsigned int n=(len-8)/16;
537 
538 	    p = (unsigned char *)malloc(len);	// will leak...
539 
540 	    cmd[0]=0xAC;	// GET PERFORMANCE
541 	    cmd[8]=n>>8;
542 	    cmd[9]=n;		// real number of descriptors
543 	    cmd[10]=0x3;	// ask for "Write Speed Descriptor"
544 	    cmd[11]=0;
545 	    if ((err=cmd.transport(READ,p,len)))
546 	    {	sperror ("GET PERFORMANCE",err);
547 		break;
548 	    }
549 	}
550 
551 	if (argc>2)
552 	{   printf ("GET PERFORMANCE:\t");
553 	    for (i=8;i<len;i++) printf ("%02x ",p[i]);
554 	    printf ("\n");
555 	}
556 
557 	for (p+=8,len-=8,i=0;len;p+=16,len-=16,i++)
558 	rv=p[8]<<24 |p[9]<<16 |p[10]<<8|p[11],
559 	wv=p[12]<<24|p[13]<<16|p[14]<<8|p[15],
560 	printf (" Speed Descriptor#%d:    %02x/%u R@%.1fx%d=%uKB/s W@%.1fx%d=%uKB/s\n",
561 		i,p[0],p[4]<<24|p[5]<<16|p[6]<<8|p[7],
562 		(double)rv/_1x,_1x,rv,(double)wv/_1x,_1x,wv);
563     } while (0);
564 
565 legacy:
566 
567     if (!blu_ray) do
568     { unsigned char book=header[4];
569       const char *brand;
570       unsigned int phys_start,phys_end;
571 
572 	header[4]=0;
573 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
574 	cmd[7] = dvd_dash;
575 	cmd[9] = 36;
576 	cmd[11] = 0;
577 	if ((err=cmd.transport(READ,header,36)))
578 	{   if (dvd_dash) { dvd_dash=0; goto legacy; }
579 	    if (err!=0x52400 || argc>2)
580 		sprintf (cmdname,"READ DVD STRUCTURE#%X",dvd_dash),
581 		sperror (cmdname,cmd.sense());
582 	    break;
583 	}
584 
585 	printf("READ DVD STRUCTURE[#%Xh]:",dvd_dash<0?0:dvd_dash);
586 	if (argc>2)
587 	    for (j=0;j<20-4;j++) printf("%c%02x",j?' ':'\t',header[4+j]);
588 	printf("\n");
589 	switch(book&0xF0)
590 	{   case 0x00:	brand="-ROM";	break;
591 	    case 0x10:	brand="-RAM";	break;
592 	    case 0x20:	brand="-R";	break;
593 	    case 0x30:	brand="-RW";	break;
594 	    case 0x90:	brand="+RW";	break;
595 	    case 0xA0:	brand="+R";	break;
596 	    case 0xE0:	brand="+R DL";	break;
597 	    default:	brand=NULL;	break;
598 	}
599 	printf (" Media Book Type:       %02Xh, ",book);
600 	if (brand)  printf ("DVD%s book [revision %d]\n",
601 			    brand,book&0xF);
602 	else	    printf ("unrecognized value\n");
603 
604 	if (header[4+1]&0xF0)	dvd_ram_spare=5120;
605 	else			dvd_ram_spare=12800;
606 
607 	if (dvd_plus && !plus_mediaid_printed)
608 	printf (" Media ID:              %.8s/%.3s\n",header+23,header+31);
609 
610 	phys_start  = header[4+5]<<16,
611 	phys_start |= header[4+6]<<8,
612 	phys_start |= header[4+7];
613 	if ((header[4+2]&0x60)==0)	// single-layer
614 	    phys_end  = header[4+9]<<16,
615 	    phys_end |= header[4+10]<<8,
616 	    phys_end |= header[4+11];
617 	else
618 	    phys_end  = header[4+13]<<16,
619 	    phys_end |= header[4+14]<<8,
620 	    phys_end |= header[4+15];
621 	if (phys_end>0)	phys_end -= phys_start;
622 	if (phys_end>0) phys_end += 1;
623 
624 	printf (" %s    %u*2KB=%" LLU "\n",
625 		dvd_dash>=0?"Legacy lead-out at:":"Last border-out at:",
626 		phys_end,phys_end*2048LL);
627 
628 	if (dvd_dash<=0) break;
629 
630 	cmd[0] = 0xAD;
631 	cmd[7] = 0; dvd_dash=-1;
632 	cmd[9] = 20;
633 	cmd[11] = 0;
634 	if ((err=cmd.transport(READ,header,20)))
635 	{   sperror ("READ DVD-R STRUCTURE",err);
636 	    break;
637 	}
638     } while (1);
639 
640     if (profile==0x10 && (header[4]&0xF0)==0)
641 	printf ("DVD-ROM media detected, exiting...\n"),
642 	exit(0);
643 
644     if (profile==0x2B) do
645     { unsigned char s[4+8];
646 
647 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
648 	cmd[7] = 0x20;	// "DVD+R Double Layer Boundary Information"
649 	cmd[9] = sizeof(s);
650 	cmd[11] = 0;
651 	if ((err=cmd.transport(READ,s,sizeof(s))))
652 	{   sperror ("READ LAYER BOUNDARY INFORMATION",err);
653 	    break;
654 	}
655 
656 	if ((s[0]<<8|s[1]) < 10)
657 	{   fprintf (stderr,":-( insane LBI structure length\n");
658 	    break;
659 	}
660 
661 	printf ("DVD+R DOUBLE LAYER BOUNDARY INFORMATION:\n");
662 	printf (" L0 Data Zone Capacity: %u*2KB, can %s be set\n",
663 					 s[8]<<24|s[9]<<16|s[10]<<8|s[11],
664 					 s[4]&0x80?"no longer":"still");
665     } while (0);
666 
667     if (profile==0x16) do
668     { unsigned char s[4+8];
669 
670 	// Layer Jump specific structures. Note that growisofs doesn't
671 	// perform Layer Jump recordings, see web-page for details...
672 
673 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
674 	cmd[7] = 0x21;	// "DVD-R DL Shifted Middle Area Start Address"
675 	cmd[9] = sizeof(s);
676 	cmd[11] = 0;
677 	if ((err=cmd.transport(READ,s,sizeof(s))))
678 	    argc>2 ? sperror ("READ SHIFTED MIDDLE AREA START ADDRESS",err) : (void)0;
679 	else if ((s[0]<<8|s[1]) < 10)
680 	    fprintf (stderr,":-( insane SMASA structure length\n");
681 	else {
682 	printf ("DVD-R DL SHIFTED MIDDLE AREA START ADDRESS:\n");
683 	printf (" Shifted Middle Area:   %u*2KB, can %s be set\n",
684 					 s[8]<<24|s[9]<<16|s[10]<<8|s[11],
685 					 s[4]&0x80?"no longer":"still");
686 	}
687 
688 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
689 	cmd[7] = 0x22;	// "DVD-R DL Jump Interval Size"
690 	cmd[9] = sizeof(s);
691 	cmd[11] = 0;
692 	if ((err=cmd.transport(READ,s,sizeof(s))))
693 	    argc>2 ? sperror ("READ JUMP INTERVAL SIZE",err) : (void)0;
694 	else if ((s[0]<<8|s[1]) < 10)
695 	    fprintf (stderr,":-( insane JIS structure length\n");
696 	else {
697 	printf ("DVD-R DL JUMP INTERVAL SIZE:\n");
698 	printf (" Jump Interval Size:    %u*2KB\n",
699 					 s[8]<<24|s[9]<<16|s[10]<<8|s[11]);
700 	}
701 
702 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
703 	cmd[7] = 0x23;	// "DVD-R DL Manual Layer Jump Address"
704 	cmd[9] = sizeof(s);
705 	cmd[11] = 0;
706 	if ((err=cmd.transport(READ,s,sizeof(s))))
707 	    argc>2 ? sperror ("READ MANUAL LAYER JUMP ADDRESS",err) : (void)0;
708 	else if ((s[0]<<8|s[1]) < 10)
709 	    fprintf (stderr,":-( insane MLJA structure length\n");
710 	else {
711 	printf ("DVD-R DL MANUAL LAYER JUMP ADDRESS:\n");
712 	printf (" Manual Layer Jump:     %u*2KB\n",
713 					 s[8]<<24|s[9]<<16|s[10]<<8|s[11]);
714 	}
715 
716 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
717 	cmd[7] = 0x24;	// "DVD-R DL Remapping Address"
718 	cmd[9] = sizeof(s);
719 	cmd[11] = 0;
720 	if ((err=cmd.transport(READ,s,sizeof(s))))
721 	    argc>2 ? sperror ("READ REMAPPING ADDRESS",err) : (void)0;
722 	else if ((s[0]<<8|s[1]) < 10)
723 	    fprintf (stderr,":-( insane RA structure length\n");
724 	else {
725 	printf ("DVD-R DL REMAPPING ADDRESS:\n");
726 	printf (" Remapping Jump:        %u*2KB\n",
727 					 s[8]<<24|s[9]<<16|s[10]<<8|s[11]);
728 	}
729     } while (0);
730 
731     if (profile==0x12 && dvd_0A) do
732     { unsigned char s[16];
733       unsigned int  a,b;
734 
735 	if (dvd_0A>sizeof(s))	dvd_0A=sizeof(s);
736 
737 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
738 	cmd[7] = 0x0A;	// "DVD-RAM Spare Area Information"
739 	cmd[8] = dvd_0A>>8;
740 	cmd[9] = dvd_0A;
741 	cmd[11] = 0;
742 	if ((err=cmd.transport(READ,s,dvd_0A)))
743 	{   sperror ("READ DVD-RAM SPARE INFORMATION",err);
744 	    break;
745 	}
746 
747 	if (dvd_0A<8)	break;
748 	printf ("DVD-RAM SPARE AREA INFORMATION:\n");
749 	a = s[ 4]<<24|s[ 5]<<16|s[ 6]<<8|s[ 7];
750 	b = dvd_ram_spare;
751 	printf (" Primary SA:            %u/%u=%.1f%% free\n",a,b,(a*100.0)/b);
752 	if (dvd_0A<16)	break;
753 	a = s[ 8]<<24|s[ 9]<<16|s[10]<<8|s[11];
754 	b = s[12]<<24|s[13]<<16|s[14]<<8|s[15];
755 	printf (" Supplementary SA:      %u/%u=%.1f%% free\n",a,b,(a*100.0)/b);
756     } while (0);
757 
758     if (profile==0x12 && dvd_C0) do
759     { unsigned char s[8];
760 
761 	if (dvd_C0>sizeof(s))	dvd_C0=sizeof(s);
762 
763 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
764 	cmd[7] = 0xC0;	// "Write Protection Status"
765 	cmd[8] = dvd_C0>>8;
766 	cmd[9] = dvd_C0;
767 	cmd[11] = 0;
768 	if ((err=cmd.transport(READ,s,dvd_C0)))
769 	{   sperror ("READ WRITE PROTECTION STATUS",err);
770 	    break;
771 	}
772 
773 	if (dvd_C0<8)	break;
774 	printf ("DVD-RAM WRITE PROTECTION STATUS:\n");
775 	if (s[4]&0x04)
776 	    printf (" Write protect tab on cartridge is set to protected\n");
777 	if (s[4]&0x01)
778 	    printf (" Software Write Protection until Power down is on\n");
779 	printf (" Persistent Write Protection is %s\n",s[4]&0x02?"on":"off");
780     } while (0);
781 
782     if (blu_ray && dvd_0A) do
783     { unsigned char s[16];
784       unsigned int  a,b;
785 
786 	if (dvd_0A>sizeof(s))	dvd_0A=sizeof(s);
787 
788 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
789 	cmd[1] = blu_ray;
790 	cmd[7] = 0x0A;	// "BD Spare Area Information"
791 	cmd[8] = dvd_0A>>8;
792 	cmd[9] = dvd_0A;
793 	cmd[11] = 0;
794 	if ((err=cmd.transport(READ,s,dvd_0A)))
795 	{   if (err!=0x33100 || argc>2) sperror ("READ BD SPARE INFORMATION",err);
796 	    break;
797 	}
798 
799 	a = s[ 8]<<24|s[ 9]<<16|s[10]<<8|s[11];
800 	b = s[12]<<24|s[13]<<16|s[14]<<8|s[15];
801 	if (dvd_0A<16 || b==0)	break;
802 	printf ("BD SPARE AREA INFORMATION:\n");
803 	printf (" Spare Area:            %u/%u=%.1f%% free\n",a,b,(a*100.0)/b);
804     } while (0);
805 
806     if (blu_ray && prf_38) do
807     { unsigned char pow[16];
808 
809 	cmd[0] = 0x51;	// READ DISC INFORMATION
810 	cmd[1] = 0x10;	// "POW Resources Information"
811 	cmd[8] = sizeof(pow);
812 	cmd[9] = 0;
813 	if ((err=cmd.transport(READ,pow,sizeof(pow))))
814 	{   sperror ("READ ROW RESOURCES INFORMATION",err);
815 	    break;
816 	}
817 
818 	printf ("POW RESOURCES INFORMATION:\n");
819 	printf (" Remaining Replacements:%u\n",pow[ 4]<<24|pow[ 5]<<16|pow[ 6]<<8|pow[ 7]);
820 	printf (" Remaining Map Entries: %u\n",pow[ 8]<<24|pow[ 9]<<16|pow[10]<<8|pow[11]);
821 	printf (" Remaining Updates:     %u\n",pow[12]<<24|pow[13]<<16|pow[14]<<8|pow[15]);
822     } while(0);
823 
824     if (blu_ray && argc>2) do
825     { unsigned char *p,*pac,*s;
826       unsigned int   len;
827 
828 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
829 	cmd[1] = blu_ray;
830 	cmd[7] = 0x30;	// "Physical Access Control (PAC)"
831 	cmd[9] = 4;
832 	cmd[11] = 0;
833 	if ((err=cmd.transport(READ,header,4)))
834 	{   if (err!=0x33100 || argc>2) sperror ("READ BD PAC LIST",err);
835 	    break;
836 	}
837 
838 	len = (header[0]<<8|header[1])+2;
839 	if (!(p = (unsigned char *)malloc(len))) break;
840 
841 	cmd[0] = 0xAD;	// READ DVD STRUCTURE
842 	cmd[1] = blu_ray;
843 	cmd[7] = 0x30;	// "Physical Access Control (PAC)"
844 	cmd[8] = len>>8;
845 	cmd[9] = len;
846 	cmd[11] = 0;
847 	if ((err=cmd.transport(READ,p,len)))
848 	{   sperror ("READ BD PAC LIST",err);
849 	    break;
850 	}
851 
852 	printf ("BD WRITTEN PAC HEADERS:\n");
853 	for (pac=p+4;pac<p+len;pac+=384)
854 	{   printf (" %3.3s.%02x:                ",pac,pac[3]);
855 	    for (i=4;i<16;i++) printf ("%02x ",pac[i]);
856 	    for (i=0,s=pac+16;i<pac[15];i++,s+=8)
857 		printf ("%u:%u ",s[0]<<24|s[1]<<16|s[2]<<8|s[3],
858 				 s[4]<<24|s[5]<<16|s[6]<<8|s[7]);
859 	    printf ("\n");
860 	}
861     } while (0);
862 
863     cmd[0] = 0x51;	// READ DISC INFORMATION
864     cmd[8] = 32;
865     cmd[9] = 0;
866     if ((err=cmd.transport(READ,header,32)))
867 	sperror ("READ DISC INFORMATION",cmd.sense()),
868 	exit (errno);
869 
870     { static const char
871 		   *session_state[]={	"empty",	"incomplete",
872 					"reserved/damaged","complete"	},
873 		   *disc_status[]={	"blank",	"appendable",
874 					"complete",	"other"		},
875 		   *bg_format[]={	"blank",	"suspended",
876 					"in progress",	"complete"	};
877 
878 	printf ("READ DISC INFORMATION:");
879 	if (argc>2)
880 	    for (j=0;j<16;j++) printf("%c%02x",j?' ':'\t',header[j]);
881 	printf ("\n");
882 	printf (" Disc status:           %s\n",disc_status[header[2]&3]);
883 	printf (" Number of Sessions:    %d\n",header[9]<<8|header[4]);
884 	printf (" State of Last Session: %s\n",session_state[(header[2]>>2)&3]);
885 	if ((header[2]&3)!=2)
886 	printf (" \"Next\" Track:          %d\n",header[10]<<8|header[5]);
887 	printf (" Number of Tracks:      %d",ntracks=header[11]<<8|header[6]);
888 	if (header[7]&3)
889 	printf ("\n BG Format Status:      %s",bg_format[header[7]&3]);
890 	if ((header[7]&3) == 2)
891 	{ unsigned char sense[18];
892 
893 	    cmd[0] = 0x03;	// REQUEST SENSE
894 	    cmd[4] = sizeof(sense);
895 	    cmd[5] = 0;
896 	    if (!cmd.transport (READ,sense,sizeof(sense)) && sense[15]&0x80)
897 		printf (", %.1f%% complete",(sense[16]<<8|sense[17])/655.36);
898 	}
899 	printf ("\n");
900 
901 	while (prf_23 || profile==0x12 || (header[2]&0x10 && argc>2))
902 	{ unsigned char formats[260];
903 	  int len;
904 	  unsigned int capacity,blocksize;
905 	  static const char *type[] = {	"reserved",	"unformatted",
906 					"formatted",	"no media"	};
907 
908 	    printf ("READ FORMAT CAPACITIES:\n");
909 
910 	    cmd[0] = 0x23;		// READ FORMAT CAPACITIES
911 	    cmd[8] = 12;
912 	    cmd[9] = 0;
913 	    if ((err=cmd.transport(READ,formats,12)))
914 	    {	sperror ("READ FORMAT CAPACITIES",err);
915 		break;
916 	    }
917 
918 	    len = formats[3];
919 	    if (len&7 || len<16)
920 	    {	fprintf (stderr,":-( allocation length isn't sane %d\n",len);
921 		break;
922 	    }
923 
924 	    cmd[0] = 0x23;		// READ FORMAT CAPACITIES
925 	    cmd[7] = (4+len)>>8;	// now with real length...
926 	    cmd[8] = (4+len)&0xFF;
927 	    cmd[9] = 0;
928 	    if ((err=cmd.transport(READ,formats,4+len)))
929 	    {	sperror ("READ FORMAT CAPACITIES",err);
930 		break;
931 	    }
932 
933 	    if (len != formats[3])
934 	    {	fprintf (stderr,":-( parameter length inconsistency\n");
935 		break;
936 	    }
937 
938 	    if (blu_ray)    blocksize=2048;
939 	    else	    blocksize=formats[9]<<16|formats[10]<<8|formats[11];
940 
941 	    printf(" %s:\t\t%u*%u=",type[formats[8]&3],
942 	        capacity=formats[4]<<24|formats[5]<<16|formats[6]<<8|formats[7],
943 		blocksize);
944 	    printf("%" LLU "\n",(unsigned long long)capacity*blocksize);
945 
946 	    for(i=12;i<len;i+=8)
947 	    {	printf(" %02Xh(%x):\t\t%u*%u=",formats[i+4]>>2,
948 			formats[i+5]<<16|formats[i+6]<<8|formats[i+7],
949 	    		capacity=formats[i]<<24|formats[i+1]<<16|formats[i+2]<<8|formats[i+3],
950 			blocksize);
951 		printf("%" LLU "\n",(unsigned long long)capacity*blocksize);
952 	    }
953 	    break;
954 	}
955     }
956 
957     if (argc<=2 && profile==0x12) ntracks=0;
958 
959     for (i=1;i<=ntracks;i++)
960     { static const char *dash_track_state[]={
961 				"complete",	"complete incremental",
962 				"blank",	"invisible incremental",
963 				"partial",	"partial incremental",
964 				"reserved",	"reserved incremental"};
965       static const char *plus_track_state[]={
966 				"invisible",		"blank",
967 				"partial/complete",	"reserved" };
968       int len=32;
969 
970 	while (len)
971 	{   cmd[0] = 0x52;	// READ TRACK INFORMATION
972 	    cmd[1] = 1;
973 	    cmd[2] = i>>24;
974 	    cmd[3] = i>>16;
975 	    cmd[4] = i>>8;
976 	    cmd[5] = i;
977 	    cmd[8] = len;
978 	    cmd[9] = 0;
979 	    if ((err=cmd.transport(READ,header,len)))
980 	    {	if (i<ntracks) { i=ntracks; continue; }
981 		sperror ("READ TRACK INFORMATION",err),
982 		exit (errno);
983 	    }
984 
985 	    if ((profile==0x1B || profile==0x2B) &&
986 		(header[6]&0x80)==0 && (header[0]<<8|header[1])>=38)
987 	    {	if (len==32)	{ len=40; continue; }
988 		else		{ break;            }
989 	    }
990 	    else if ((profile==0x15 || profile==0x16) &&
991 		(header[5]&0xC0) && (header[0]<<8|header[1])>=46)
992 	    {	if (len==32)	{ len=48; continue; }
993 		else		{ break;	    }
994 	    }
995 
996 	    len = 0;
997 	}
998 
999 	printf ("READ TRACK INFORMATION[#%d]:",i);
1000 	if (argc>2)
1001 	    for (j=0;j<16;j++) printf("%c%02x",j?' ':'\t',header[j]);
1002 	printf ("\n");
1003 	if ((profile&0x0F)==0x0B)	// DVD+R
1004 	{
1005 	printf (" Track State:           %s\n",
1006 					 plus_track_state[header[6]>>6]);
1007 	}
1008 	else
1009 	{
1010 	printf (" Track State:           %s%s",
1011 					 ((header[6]>>5)==1 && (header[7]&1))?"in":"",
1012 					 dash_track_state[header[6]>>5]);
1013 	if (header[5]&0x20)	printf(",damaged");
1014 	printf ("\n");
1015 	}
1016 	printf (" Track Start Address:   %u*2KB\n",
1017 		header[8]<<24|header[9]<<16|header[10]<<8|header[11]);
1018 	if (header[7]&1)
1019 	printf (" Next Writable Address: %u*2KB\n",
1020 		header[12]<<24|header[13]<<16|header[14]<<8|header[15]);
1021 	printf (" Free Blocks:           %u*2KB\n",
1022 		header[16]<<24|header[17]<<16|header[18]<<8|header[19]);
1023 	if (header[6]&0x10)
1024 	printf (" Fixed Packet Size:     %u*2KB\n",
1025 		header[20]<<24|header[21]<<16|header[22]<<8|header[23]);
1026 	printf (header[5]&0x40?		// check upon LJ bit
1027 		" Zone End Address:      %u*2KB\n":
1028 		" Track Size:            %u*2KB\n",
1029 		header[24]<<24|header[25]<<16|header[26]<<8|header[27]);
1030 	if (header[7]&2)
1031 	printf (" Last Recorded Address: %u*2KB\n",
1032 		header[28]<<24|header[29]<<16|header[30]<<8|header[31]);
1033 	if (len>=40)
1034 	printf (" ROM Compatibility LBA: %u\n",
1035 		header[36]<<24|header[37]<<16|header[38]<<8|header[39]);
1036 	if (len>=48 && header[5]&0xC0)	// check upon LJRS bits
1037 	printf (" LJRS field:            %u\n",header[5]>>6),
1038 	printf (" Next Layer Jump:       %u\n",
1039 		header[40]<<24|header[41]<<16|header[42]<<8|header[43]),
1040 	printf (" Last Layer Jump:       %u\n",
1041 		header[44]<<24|header[45]<<16|header[46]<<8|header[47]);
1042     }
1043 
1044     do
1045     {	unsigned char *toc,*p;
1046 	int   len,sony;
1047 
1048 	cmd[0] = 0x43;	// READ TOC
1049 	cmd[6] = 1;
1050 	cmd[8] = 12;
1051 	cmd[9] = 0;
1052 	if ((err=cmd.transport (READ,header,12)))
1053 	{   if (argc>2)
1054 		sperror ("READ TOC",err);
1055 	    break;
1056 	}
1057 
1058 	len = (header[0]<<8|header[1])+2;
1059 	toc = (unsigned char *)malloc (len);
1060 
1061 	printf ("FABRICATED TOC:");
1062 	if (argc>2)
1063 	    printf ("\t\t%u %x %x",header[0]<<8|header[1],header[2],header[3]);
1064 	printf ("\n");
1065 
1066 	cmd[0] = 0x43;		// READ TOC
1067 	cmd[6] = header[2];	// "First Track Number"
1068 	cmd[7] = len>>8;
1069 	cmd[8] = len;
1070 	cmd[9] = 0;
1071 	if ((err=cmd.transport (READ,toc,len)))
1072 	{   sperror ("READ TOC",err);
1073 	    break;
1074 	}
1075 
1076 	for (p=toc+4,i=4;i<len-8;i+=8,p+=8)
1077 	printf (" Track#%-3u:             %02x@%u\n",
1078 		p[2],p[1],p[4]<<24|p[5]<<16|p[6]<<8|p[7]);
1079 	printf (" Track#%-2X :             %02x@%u\n",
1080 		p[2],p[1],p[4]<<24|p[5]<<16|p[6]<<8|p[7]);
1081 
1082 	cmd[0] = 0x43;	// READ TOC
1083 	cmd[2] = 1;	// "Session info"
1084 	cmd[8] = 12;
1085 	cmd[9] = 0;
1086 	if ((err=cmd.transport (READ,header,12)))
1087 	{   if (argc>2)
1088 		sperror ("GET SESSION INFO",err);
1089 	    break;
1090 	}
1091 
1092 	len = header[4+4]<<24|header[4+5]<<16|header[4+6]<<8|header[4+7];
1093 	printf (" Multi-session Info:    #%u@%u\n",header[4+2],len);
1094 
1095 	cmd[0] = 0x43;	// READ TOC
1096 	cmd[8] = 12;
1097 	cmd[9] = 0x40;	// "SONY Vendor bit"
1098 	if ((err=cmd.transport (READ,header,12)))
1099 	{   if (argc>2)
1100 		sperror ("GET SONY SESSION INFO",err);
1101 	    break;
1102 	}
1103 
1104 	sony = header[4+4]<<24|header[4+5]<<16|header[4+6]<<8|header[4+7];
1105 	if (len != sony)
1106 	printf (" SONY Session Info:     #%u@%u\n",header[4+2],sony);
1107 
1108     } while (0);
1109 
1110     do {
1111 	unsigned int ccity,bsize;
1112 	cmd[0] = 0x25;	// READ CAPACITY
1113 	cmd[9] = 0;
1114 	if ((err=cmd.transport (READ,header,8)))
1115 	{   if (argc>2)
1116 		sperror ("READ CAPACITY",err);
1117 	    break;
1118 	}
1119 
1120 	ccity = header[0]<<24|header[1]<<16|header[2]<<8|header[3];
1121 	if (ccity) ccity++;
1122 	bsize = header[4]<<24|header[5]<<16|header[6]<<8|header[7];
1123 
1124 	printf ("READ CAPACITY:          %u*%u=%" LLU "\n",
1125 					ccity,bsize,
1126 					(unsigned long long)ccity*bsize);
1127     } while (0);
1128 
1129   return 0;
1130 }
1131