📄 growisofs_mmc.cpp
字号:
perf = (unsigned char *)malloc(len+=8); if (perf == NULL) fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM)); if (len == sizeof(d)) memcpy (perf,d,sizeof(d)); else { unsigned int n=(len-8)/16; cmd[0]=0xAC; // GET PERFORMANCE cmd[8]=n>>8; cmd[9]=n; // real number of descriptors cmd[10]=0x3; // ask for "Write Speed Descriptor" cmd[11]=0; if ((err=cmd.transport(READ,perf,len))) sperror ("GET PERFORMANCE",err), exit (FATAL_START(errno)); } int targetv=0,errp=0; do { memset (d,0,sizeof(d)); cmd[0]=0xAC; // GET PERFORMANCE cmd[1]=4; // ask for "Overall Write performance" cmd[9]=1; cmd[10]=0; // ask for descriptor in effect cmd[11]=0; if (errp || (errp=cmd.transport(READ,d,sizeof(d))))#if 0 sperror ("GET CURRENT PERFORMANCE",errp), exit (FATAL_START(errno)); // well, if it passed above, we // expect it to pass this too...#else // Pioneer doesn't report current speed through GET PERFORMANCE:-( { emulated_err: if (page2A == NULL) return -1; unsigned int plen,hlen; plen = (page2A[0]<<8|page2A[1]) + 2; hlen = 8 + (page2A[6]<<8|page2A[7]); unsigned char * const p = page2A + hlen; if (plen<(hlen+32) || p[1]<(32-2)) return -1; // well, SET CD SPEED wouldn't work... velocity = p[28]<<8|p[29]; }#endif else if ((errp = pull_velocity (cmd,d)) < 0) goto emulated_err; if (speed_factor != 0.0) { int i,j=len-8,v,v0,v1,minv,closesti,closestv=0; unsigned char *wsdp=perf+8; for (minv=0x7fffffff,i=0;i<j;i+=16) { v=wsdp[i+12]<<24|wsdp[i+13]<<16|wsdp[i+14]<<8|wsdp[i+15]; if (v<minv) minv=v; } // minv will be treated as 1x // New generation units offer minimum velocities other than 1x if (minv>(15*_1x)) speed_factor /= 16.0; else if (minv>(11*_1x)) speed_factor /= 12.0; else if (minv>(7*_1x)) speed_factor /= 8.0; else if (minv>(5*_1x)) speed_factor /= 6.0; else if (minv>(3*_1x)) speed_factor /= 4.0; else if (dvdplus && minv>(2*DVD_1X)) // 2.4x is like 1x for DVD+, but it turned out that there're // units, most notably "DVDRW IDE1004," burning DVD+ at // lower speed, so I have to watch out for it... speed_factor /= 2.4; else if (minv>=(2*_1x)) speed_factor /= 2.0; v0=(int)(speed_factor*minv + 0.5); for (closesti=0,minv=0x7fffffff,i=0;i<j;i+=16) { v1=wsdp[i+12]<<24|wsdp[i+13]<<16|wsdp[i+14]<<8|wsdp[i+15]; v=abs(v0-v1); if (v<minv) minv=v, closesti=i, closestv=v1; } // currenti is index of descriptor with *closest* velocity targetv=closestv; speed_factor=0.0; if (velocity==targetv) break; // already in shape, nothing to do... unsigned char pd[28]; memset (pd,0,sizeof(pd)); // setup "Performance Descriptor" pd[0]=*(wsdp+closesti); // copy "WRC" and other flags#if 0 memset(pd+8,0xFF,4);#else // I might have to copy the value from current descriptor... unsigned int cap=get_2k_capacity(cmd)-1; pd[8]=cap>>24; pd[9]=cap>>16; pd[10]=cap>>8; pd[11]=cap;#endif memcpy(pd+12,wsdp+closesti+8,4); // copy "Read Speed" memcpy(pd+20,wsdp+closesti+12,4); // copy "Write Speed" pd[18]=pd[26]=1000>>8; // set both "Read Time" and pd[19]=pd[27]=1000&0xFF; // "Write Time" to 1000ms cmd[0]=0xB6; // SET STREAMING cmd[10]=sizeof(pd); cmd[11]=0; if ((err=cmd.transport (WRITE,pd,sizeof(pd)))) sperror ("SET STREAMING",err), exit (FATAL_START(errno)); // I pull the Page 2A in either case, because unit might // have provided irrelevant information (Plextor). Not to // mention cases when it simply failed to reply to GET // CURRENT PERFORMANCE (Pioneer, LG). unsigned int plen = (page2A[0]<<8|page2A[1]) + 2; cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x2A; // "Capabilities and Mechanical Status" cmd[7] = plen>>8; cmd[8] = plen; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page2A,plen))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); } else if (targetv) { if (targetv!=velocity) { if (errp==0) // check Page 2A for speed then... { errp=-1; continue; } if (velocity==0 || velocity>=_1x) { fprintf(stderr,":-? Failed to change write speed: %d->%d\n", (int)velocity,targetv); if (velocity<targetv) velocity=targetv; } else { fprintf(stderr,":-( Failed to change write speed: %d->%d\n", (int)velocity,targetv); exit (FATAL_START(EINVAL)); } } break; } } while (targetv); return targetv;}static int set_speed_BBh (Scsi_Command &cmd,unsigned char *page2A){ unsigned int plen,hlen; int err; plen = (page2A[0]<<8|page2A[1]) + 2; hlen = 8 + (page2A[6]<<8|page2A[7]); unsigned char * const p = page2A + hlen; if (plen<(hlen+32) || p[1]<(32-2)) return -1; int targetv=0; do { velocity = p[28]<<8|p[29]; if (speed_factor != 0.0) { int i,j,v,v0,v1,minv,closesti,closestv=0; unsigned char *wsdp=p+32; j=(p[30]<<8|p[31])*4; for (minv=0x7fffffff,i=0;i<j;i+=4) { v=wsdp[i+2]<<8|wsdp[i+3]; if (v<minv) minv=v; } // minv will be treated as 1x v0=(int)(speed_factor*minv + 0.5); for (closesti=0,minv=0x7fffffff,i=0;i<j;i+=4) { v1=wsdp[i+2]<<8|wsdp[i+3]; v=abs(v0-v1); if (v<minv) minv=v, closesti=i, closestv=v1; } // currenti is index of descriptor with *closest* velocity targetv=closestv; speed_factor=0.0; if (velocity==targetv) break; // already in shape, nothing to do... cmd[0]=0xBB; // SET CD SPEED cmd[1]=wsdp[closesti+1]; // Rotation Control cmd[2]=cmd[3]=0xff; // Read Speed cmd[4]=wsdp[closesti+2]; // Write Speed cmd[5]=wsdp[closesti+3]; cmd[11]=0; if ((err=cmd.transport())) sperror ("SET CD SPEED",err), exit (FATAL_START(errno)); cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x2A; // "Capabilities and Mechanical Status" cmd[7] = plen>>8; cmd[8] = plen; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page2A,plen))) sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno)); } else if (targetv) { if (targetv!=velocity) fprintf (stderr,":-( Failed to change write speed: %d->%d\n", (int)velocity,targetv), exit (FATAL_START(EINVAL)); break; } } while (targetv); return targetv;}static int plusminus_pages_setup (Scsi_Command &cmd,int profile){ unsigned int plen,hlen,dvddash=0,dvdplus=0; unsigned char header[16],p32; int err; class autofree page2A; switch (profile) { case 0x1A: // DVD+RW p32 = 0; dvdplus=1; break; case 0x1B: // DVD+R // // Even though MMC-4 draft explicitely exempts DVD+RW/+R media // from those being affected by Page 05h settings, some // firmwares apparently pay attention to Multi-session flag // when finalizing DVD+R media. Well, we probably can't blame // vendors as specification is still a work in progress, not to // mention that it was published after DVD+R was introduced to // the market. // p32 = dvd_compat?0x02:0xC0; dvdplus=1; break; case 0x13: // DVD-RW Restricted Overwrite p32 = 0xC0; // NB! Test Write in not option here dvddash = 1; break; case 0x11: // DVD-R Sequential case 0x14: // DVD-RW Sequential case 0x15: // DVD-R Dual Layer Sequential p32 = 0xC0; if (next_track==1) do { if (dvd_compat >= (profile==0x14?2:256)) { is_dao = 1, fprintf (stderr,"%s: engaging DVD-%s DAO upon user request...\n", ioctl_device,profile==0x14?"RW":"R"); break; } // Try to figure out if we have to go for DAO... cmd[0] = 0x46; // GET CONFIGURATION cmd[1] = 2; // ask for the only feature... cmd[3] = 0x21; // the "Incremental Streaming Writable" one cmd[8] = 16; // The feature should be there, right? cmd[9] = 0; if ((err=cmd.transport (READ,header,16))) sperror ("GET FEATURE 21h",err), exit(FATAL_START(errno)); hlen = header[0]<<24|header[1]<<16|header[2]<<8|header[3]; // See if Feature 21h is "current," if not, engage DAO... if (hlen>=12 && (header[8+2]&1)==0) { is_dao = dvd_compat = 1; fprintf (stderr,"%s: FEATURE 21h is not on, engaging DAO...\n", ioctl_device); break; } } while (0); if (is_dao) p32 |=2; // DAO if (profile==0x15 || // DVD-R DL Seq has no notion of multi-session dvd_compat) p32 &= 0x3F; // Single-session if (test_write) p32 |= 0x10; // Test Write for debugging purposes dvddash=1; break; case 0x16: // DVD-R Dual Layer Jump p32 = 0xC0; p32 |= 4; // Jump if (test_write) p32 |= 0x10; // Test Write for debugging purposes dvddash=1; break; default: p32 = 0; break; } if (dvddash || p32) page05_setup (cmd,profile,p32); page2A = pull_page2A (cmd); plen = (page2A[0]<<8|page2A[1]) + 2; hlen = 8 + (page2A[6]<<8|page2A[7]); unsigned char * const p = page2A + hlen; if (plen<(hlen+14) || p[1]<(14-2)) // no "Buffer Size Supported" buf_size = 512; // bogus value else buf_size = (p[12]<<8|p[13]); if (buf_size<512) // bogus value buf_size = 512; // GET PERFORMANCE/SET STREAMING are listed as mandatory, so that // this is actually the most likely path... if (set_speed_B6h (cmd,dvdplus,page2A) >= 0) goto opc; if (plen<(hlen+30) || p[1]<(30-2)) // no "Current Write Speed" present { if (dvddash) { fprintf (stderr,":-( \"Current Write Speed\" descriptor " "is not present, faking 1x...\n"), velocity = _1x; speed_factor = 0.0; } else velocity = 0; } else do { velocity = p[28]<<8|p[29]; // Some units, most notably NEC and derivatives, were observed // to report CD-R descriptors... if (velocity < DVD_1X) // must be bogus { velocity=0; break; } if (plen<(hlen+32) || p[1]<(32-2)) // no write descriptors break; int n = p[30]<<8|p[31],minv=1<<16; unsigned char *p_= p+32; for (;n--;p_+=4) { int v=p_[2]<<8|p_[3]; if (v==0) continue; // Lite-On returns zeros here... if (v<_1x) // must be bogus velocity=0; if (v < minv) minv = v; } if (dvdplus && minv>2*_1x) // See corresponding comment in set_speed_B6h() speed_factor /= 2.4; } while (0); if (speed_factor != 0.0 && (velocity==0 || set_speed_BBh (cmd,page2A)<0) ) fprintf (stderr,":-( can't control writing velocity, " "-speed option is ignored\n");opc: if (!dvddash || test_write || no_opc) return 0; // See if OPC is required... cmd[0] = 0x51; // READ DISC INFORMATION cmd[8] = 8; cmd[9] = 0; if ((err=cmd.transport (READ,header,8))) sperror ("READ DISC INFORMATION",err), exit(FATAL_START(errno)); if ((header[0]<<8|header[1]) <= 0x20) { cmd[0] = 0x54; // SEND OPC INFORMATION cmd[1] = 1; // "Perform OPC" cmd[9] = 0; cmd.timeout(120); // NEC units can be slooo...w if ((err=cmd.transport())) do { if (err==0x17301) // "POWER CALIBRATION AREA ALMOST FULL" { fprintf (stderr,":-! WARNING: Power Calibration Area " "is almost full\n"); break; } sperror ("PERFORM OPC",err); if ((err&0x0FF00)==0x07300) exit(FATAL_START(errno)); /* * Th rest of errors are ignored, most notably observed: * 0x52000, "INVALID COMMAND" Panasonic LD-F321 * 0x52700, "WRITE PROTECTED" Plextor PX-712A * 0x52C00, "COMMAND SEQUENCE ERROR" Plextor PX-716UF * 0x53002, "MEDIA NOT SUPPORTED" Toshiba TS-H542A */ } while (0); } handle_events(cmd); return 0;}static int minus_rw_quickgrow (Scsi_Command &cmd,off64_t size){ unsigned char format[12]; unsigned int lead_out,blocks,type; int i,len,err; type = formats[4+4]&3; if (type==2 && size!=0) { blocks = size/2048; blocks += 15, blocks &= ~15; lead_out = 0; lead_out |= formats[4+0], lead_out <<= 8; lead_out |= formats[4+1], lead_out <<= 8; lead_out |= formats[4+2], lead_out <<= 8; lead_out |= formats[4+3]; if (blocks<=lead_out) // no need to grow the session... return 0; } // look for Quick Grow descriptor... for (i=8,len=formats[3];i<len;i+=8) if ((formats [4+i+4]>>2) == 0x13) break; if (i==len) // no Quick Grow descriptor { if (type != 2) quickgrown=1; // in reality quick formatted... return 0; } else { blocks = 0; blocks |= formats[i+0], blocks <<= 8; blocks |= formats[i+1], blocks <<= 8; blocks |= formats[i+2], blocks <<= 8; blocks |= formats[i+3]; if (type==2 && blocks==0) // nowhere no grow... return 0; } quickgrown=1; fprintf (stderr,"%s: \"Quick Grow\" session...\n",ioctl_device); memset (format,0,sizeof(format)); format [1] = 2; // "IMMED" format [3] = 8; // "Length" format [8] = 0x13<<2; // "Quick Grow" format [11] = 16; cmd[0] = 0x4; // FORMAT UNIT cmd[1] = 0x11; cmd[5] = 0; if ((err=cmd.transport (WRITE,format,sizeof(format)))) { sperror ("QUICK GROW",err); return 1; } return wait_for_unit (cmd);}static int minus_r_reserve_track (Scsi_Command &cmd,off64_t size){ int err; unsigned int blocks; blocks = size/2048; if (is_dao) dao_blocks = blocks; else blocks += 15, blocks &= ~15; fprintf (stderr,"%s: reserving %u blocks",ioctl_device,blocks); if (is_dao && blocks<380000) fprintf (stderr,"\b, warning for short DAO recording"), poll (NULL,0,5000); fprintf (stderr,"\n"); cmd[0] = 0x53; // RESERVE TRACK cmd[5] = blocks>>24; cmd[6] = blocks>>16; cmd[7] = blocks>>8; cmd[8] = blocks; cmd[9] = 0; if ((err=cmd.transport ())) { sperror ("RESERVE TRACK",err); return 1; } return 0;}static void plus_r_dl_split (Scsi_Command &cmd,off64_t size){ int err; unsigned int blocks,split; unsigned char dvd_20[4+8]; cmd[0] = 0xAD; // READ DVD STRUCTURE cmd[7] = 0x20; // "DVD+R Double Layer Boundary Information" cmd[9] = sizeof(dvd_20); cmd[11] = 0; if ((err=cmd.transport(READ,dvd_20,sizeof(dvd_20)))) sperror ("READ DVD+R DL BOUNDARY INFORMATION",err), exit (FATAL_START(errno)); if ((dvd_20[0]<<8|dvd_20[1]) < 10) fprintf (stderr,":-( insane DVD+R DL BI structure length\n"), exit (FATAL_START(EINVAL)); if (dvd_20[4]&0x80) { fprintf (stderr,":-? L0 Data Zone Capacity is set already\n"); return; } split = dvd_20[8]<<24|dvd_20[9]<<16|dvd_20[10]<<8|dvd_20[11]; blocks = size/2048; blocks += 15, blocks &= ~15; if (blocks <= split) fprintf (stderr,":-( more than 50%% of space will be *wasted*!\n" " use single layer media for this recording\n"), exit (FATAL_START(EMEDIUMTYPE)); blocks /= 16; blocks += 1; blocks /= 2; blocks *= 16; fprintf (stderr,"%s: splitting layers at %u blocks\n", ioctl_device,blocks); memset (dvd_20,0,sizeof(dvd_20)); dvd_20[1] = sizeof(dvd_20)-2; dvd_20[8] = blocks>>24; dvd_20[9] = blocks>>16;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -