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