📄 growisofs_mmc.cpp
字号:
#ifndef _LARGEFILE_SOURCE#define _LARGEFILE_SOURCE#endif#ifndef _LARGEFILE64_SOURCE#define _LARGEFILE64_SOURCE#endif#ifndef _FILE_OFFSET_BITS#define _FILE_OFFSET_BITS 64#endif#if defined(__linux)/* ... and "engage" glibc large file support */#ifndef _GNU_SOURCE#define _GNU_SOURCE#endif#endif#include "transport.hxx"#include <time.h>#include <sys/types.h>#define SIGS_BLOCK 1#define SIGS_UNBLOCK 0extern "C" void sigs_mask (int);extern "C" void atsignals (void (*)(void));#define DVD_1X 1352 // 1385 * 1000 / 1024#define BD_1X 4390 // 4495.5 * 1000 / 1024static int media_written=0,next_track=1, is_dao=0,quickgrown=0,do_reload=1, _1x=DVD_1X,_64k=0,bdr_plus_pow=0;static unsigned int stdnap=(256*1000)/DVD_1X,// measured in milliseconds buf_size=512; // measured in KBsstatic class __velocity__ { private: int value; public: int operator=(int v) { if (v>=_1x) stdnap=(buf_size*500)/v; else stdnap=(buf_size*500)/_1x; return value = v; } operator int() { return value; }} velocity;static void *ioctl_handle=(void *)-1;#ifndef ioctl_fd#define ioctl_fd ((long)ioctl_handle)#endifstatic unsigned int next_wr_addr=1; // it starts as booleanstatic unsigned int dao_blocks=0;static unsigned int cap2kstart=0;static unsigned char formats[260],disc_info[32];extern "C" {extern int dvd_compat,test_write,no_reload,mmc_profile,_argc, wrvfy,no_opc,spare;extern double speed_factor;extern char *ioctl_device,**_argv;}extern "C"int __1x (void) { return _1x; }extern "C"int fumount (int fd){ Scsi_Command cmd; return cmd.umount(fd);}extern "C"int media_reload (char *name=NULL,struct stat *sb=NULL,unsigned int cap2k=0){ if (name==NULL) { Scsi_Command cmd(ioctl_handle); pioneer_stop (cmd);#if defined(RELOAD_NEVER_NEEDED)#undef RELOAD_NEVER_NEEDED#define RELOAD_NEVER_NEEDED 1#else#define RELOAD_NEVER_NEEDED 0#endif if (RELOAD_NEVER_NEEDED || no_reload>0) { cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL cmd[5] = 0; cmd.transport (); return (errno=0); }#if !RELOAD_NEVER_NEEDED char fdstr[12],cap2kstr[12]; int n; if ((n=fcntl (ioctl_fd,F_GETFD))<0) n=0; fcntl (ioctl_fd,F_SETFD,n&~FD_CLOEXEC); sprintf (fdstr,"%ld",ioctl_fd); sprintf (cap2kstr,"%u",cap2kstart); execlp(_argv[0],no_reload<0?"-eject":"-reload", fdstr,ioctl_device,cap2kstr,(void *)NULL); } else { { Scsi_Command cmd; unsigned char c[8]; unsigned int cap2kend; if (!cmd.associate (name,sb)) return 1; cmd[0] = 0x25; // READ CAPACITY cmd[9] = 0; if (!cmd.transport (READ,c,sizeof(c))) cap2kend = c[0]<<24|c[1]<<16|c[2]<<8|c[3]; else cap2kend = (unsigned int)-1; if (cmd.is_reload_needed(cap2k==cap2kend)) { fprintf (stderr,"%s: reloading tray\n",name); cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL cmd[5] = 0; if (cmd.transport ()) return 1; while (1) // Pioneer DVR-x05 needs this... { cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0x2; // "Eject" cmd[5] = 0; if (cmd.transport() == 0x20407) // "OP IN PROGRESS" { poll (NULL,0,333); continue; } break; } // yes, once again, non-"IMMED"... cmd[0] = 0x1B; // START/STOP UNIT cmd[4] = 0x2; // "Eject" cmd[5] = 0; if (cmd.transport()) return 1; }#if defined(__sun) || defined(sun) else if (volmgt_running()) { setuid(getuid()); execl("/usr/bin/volrmmount","volrmmount","-i",name,(void*)NULL); return 0; // not normally reached }#endif else return 0; // m-m-m-m! patched kernel:-) } if (no_reload>=0) { Scsi_Command cmd; if (cmd.associate (name,sb)) { cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0x3; // "Load" cmd[5] = 0; cmd.transport (); } errno=0; // ignore all errors on load } return 0;#endif } return 1;}extern "C"int get_mmc_profile (void *fd){ Scsi_Command cmd(fd); unsigned char buf[8],inq[128]; int profile=0,once=1,blank=0,err; unsigned int len; #if defined(__APPLE__) && defined(__MACH__) // The reason for Mac OS X specific MMCIO appearing here and there // is that raw SCSI requires exclusive access, while I'm not ready // to give it up yet... // I don't call INQUIRY, because kernel ensures that // MMCDeviceInterface is attached to an MMC device... if ((err=MMCIO(fd,GetConfiguration,0,0,buf,sizeof(buf)))) sperror ("GET CONFIGURATION",err), exit (FATAL_START(errno)); profile = buf[6]<<8|buf[7];#else // INQUIRY is considered to be "non-intrusive" in a sense that // it won't interfere with any other operation nor clear sense // data, which might be important to retain for security reasons. cmd[0] = 0x12; // INQUIRY cmd[4] = 36; cmd[5] = 0; if ((err=cmd.transport(READ,inq,36))) sperror ("INQUIRY",err), exit (FATAL_START(errno)); // make sure we're talking to MMC unit, for security reasons... if ((inq[0]&0x1F) != 5) fprintf (stderr,":-( not an MMC unit!\n"), exit (FATAL_START(EINVAL)); do { cmd[0] = 0x46; cmd[8] = sizeof(buf); cmd[9] = 0; if ((err=cmd.transport(READ,buf,sizeof(buf)))) sperror ("GET CONFIGURATION",err), fprintf (stderr,":-( non-MMC unit?\n"), exit (FATAL_START(errno)); if ((profile = buf[6]<<8|buf[7]) || !once) break; // no media? cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((cmd.transport()&0xFFF00) != 0x23A00) break; // try to load tray... cmd[0] = 0x1B; // START/STOP UNIT cmd[4] = 0x3; // "Load" cmd[5] = 0; if ((err=cmd.transport ())) sperror ("LOAD TRAY",err), exit (FATAL_START(errno));#if 1 wait_for_unit (cmd);#else // consume sense data, most likely "MEDIA MAY HAVE CHANGED" cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((err=cmd.transport ()) == -1) sperror ("TEST UNIT READY",err), exit (FATAL_START(errno));#endif } while (once--);#endif // !Mac OS X if (profile==0 || (profile&0x70)==0) // no or non-DVD/BD media... return profile; if ((profile&0xF0) == 0x40) { unsigned char header[16]; _1x = BD_1X; _64k = 1; if (profile==0x41) // BD-R SRM { // Check for POW feature...#if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,GetConfiguration,2,0x38,header,16);#else cmd[0] = 0x46; // GET CONFIGURATION cmd[1] = 2; // ask for the only feature... cmd[3] = 0x38; // the "BD-R Pseudo-Overwrite" one cmd[8] = 16; // The feature should be there, right? cmd[9] = 0; err = cmd.transport (READ,header,16);#endif if (err==0 && (header[0]<<24|header[1]<<16|header[2]<<8|header[3])>=12 && (header[8+2]&1)==1) bdr_plus_pow=1; } }#if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,ReadDiscInformation,disc_info,sizeof(disc_info));#else cmd[0] = 0x51; // READ DISC INFORMATION cmd[8] = sizeof(disc_info); cmd[9] = 0; err = cmd.transport (READ,disc_info,sizeof(disc_info));#endif if (err) sperror ("READ DISC INFORMATION",err), exit (FATAL_START(errno)); // see if it's blank media if ((disc_info[2]&3) == 0) blank=0x10000; if (profile != 0x1A && profile != 0x2A && // DVD+RW profile != 0x13 && profile != 0x12 && // DVD-R[AM] profile != 0x43 && !(profile == 0x41 && blank)) // BD-R[E] return blank|profile;#if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,ReadFormatCapacities,formats,12);#else cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[8] = 12; cmd[9] = 0; err = cmd.transport (READ,formats,12);#endif if (err) sperror ("READ FORMAT CAPACITIES",err), exit (FATAL_START(errno)); len = formats[3]; if (len&7 || len<16) fprintf (stderr,":-( FORMAT allocaion length isn't sane"), exit (FATAL_START(EINVAL));#if defined(__APPLE__) && defined(__MACH__) err = MMCIO(fd,ReadFormatCapacities,formats,4+len);#else cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[7] = (4+len)>>8; cmd[8] = (4+len)&0xFF; cmd[9] = 0; err = cmd.transport (READ,formats,4+len);#endif if (err) sperror ("READ FORMAT CAPACITIES",err), exit (FATAL_START(errno)); if (len != formats[3]) fprintf (stderr,":-( parameter length inconsistency\n"), exit(FATAL_START(EINVAL)); // see if it's not formatted if ((formats[8]&3) != 2) blank = 0x10000; return blank|profile;}static unsigned int get_2k_capacity (Scsi_Command &cmd,void *fd=NULL){ unsigned char buf[32]; unsigned int ret=0; unsigned int nwa,free_blocks; int i,obligatory,len,err; obligatory=0x00; switch (mmc_profile&0xFFFF) { case 0x1A: // DVD+RW obligatory=0x26; case 0x13: // DVD-RW Restricted Overwrite for (i=8,len=formats[3];i<len;i+=8) if ((formats [4+i+4]>>2) == obligatory) break; if (i==len) { fprintf (stderr,":-( can't locate obligatory format descriptor\n"); return 0; } ret = formats[4+i+0]<<24; ret |= formats[4+i+1]<<16; ret |= formats[4+i+2]<<8; ret |= formats[4+i+3]; nwa = formats[4+5]<<16|formats[4+6]<<8|formats[4+7]; if (nwa>2048) ret *= nwa/2048; else if (nwa<2048) ret /= 2048/nwa; break; case 0x12: // DVD-RAM // As for the moment of this writing I don't format DVD-RAM. // Therefore I just pull formatted capacity for now... ret = formats[4+0]<<24; ret |= formats[4+1]<<16; ret |= formats[4+2]<<8; ret |= formats[4+3]; nwa = formats[4+5]<<16|formats[4+6]<<8|formats[4+7]; if (nwa>2048) ret *= nwa/2048; else if (nwa<2048) ret /= 2048/nwa; break; case 0x11: // DVD-R case 0x14: // DVD-RW Sequential case 0x15: // DVD-R Dual Layer Sequential case 0x16: // DVD-R Dual Layer Jump case 0x1B: // DVD+R case 0x2B: // DVD+R Double Layer case 0x41: // BD-R SRM#if defined(__APPLE__) && defined(__MACH__) if (fd) err = MMCIO(fd,ReadTrackInformation,1,next_track,buf,sizeof(buf)); else#endif { cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; cmd[4] = next_track>>8; cmd[5] = next_track&0xFF; // last track, set up earlier cmd[8] = sizeof(buf); cmd[9] = 0; err = cmd.transport (READ,buf,sizeof(buf)); } if (err) { sperror ("READ TRACK INFORMATION",err); return 0; } nwa = 0; if (buf[7]&1 && !bdr_plus_pow) // NWA_V { nwa = buf[12]<<24; nwa |= buf[13]<<16; nwa |= buf[14]<<8; nwa |= buf[15]; } free_blocks = buf[16]<<24; free_blocks |= buf[17]<<16; free_blocks |= buf[18]<<8; free_blocks |= buf[19]; ret = nwa + free_blocks; break; case 0x43: // BD-RE // just pull formatted capacity for now... ret = formats[4+0]<<24; ret |= formats[4+1]<<16; ret |= formats[4+2]<<8; ret |= formats[4+3]; break; default: break; } return ret;}extern "C"off64_t get_capacity (void *fd){ Scsi_Command cmd(fd); return (off64_t)get_2k_capacity(cmd,fd)*2048;}extern "C"float get_buffer_stats (void *fd){ Scsi_Command cmd(fd); unsigned char bcap[12]; unsigned int bsize,bfree,err; cmd[0] = 0x5C; // READ BUFFER CAPACITY cmd[8] = sizeof(bcap); cmd[9] = 0; if ((err=cmd.transport (READ,bcap,sizeof(bcap)))) return -err; bsize = bcap[4]<<24|bcap[5]<<16|bcap[6]<<8|bcap[7]; bfree = bcap[8]<<24|bcap[9]<<16|bcap[10]<<8|bcap[11]; return bsize ? (1.0 - (float)bfree/(float)bsize) : 0.0;}ssize_t poor_mans_pwrite64 (int fd,const void *_buff,size_t size,off64_t foff){ Scsi_Command cmd(ioctl_handle); /* screw first argument */ unsigned char bcap[12]; const unsigned char *buff=(const unsigned char *)_buff; unsigned int lba,nbl,bsize,bfree; int retries=0,errcode; static int dao_toggle=-1; if (foff&0x7FFF || size&0x7FFF) // 32K block size return (errno=EINVAL,-1); lba = foff>>11; nbl = size>>11; if (!media_written && next_wr_addr) { if ((lba+nbl) <= next_wr_addr) return size; else if (next_wr_addr > lba) nbl -= (next_wr_addr-lba), size -= (next_wr_addr-lba)<<11, buff += (next_wr_addr-lba)<<11, lba = next_wr_addr; }#if defined(__sun) || defined(sun) else next_wr_addr = lba;#endif if (dao_toggle<0) dao_toggle=is_dao; { static unsigned int first_wr_addr=0; if (!media_written) { sigs_mask (SIGS_BLOCK); first_wr_addr = lba; } else if (lba >= (first_wr_addr+buf_size/2)) // measured in 2KBs! { sigs_mask (SIGS_UNBLOCK); } } if (dao_blocks!=0 && (lba+nbl)>dao_blocks) nbl = dao_blocks-lba; while (1) { cmd[0] = wrvfy?0x2E:0x2A; // WRITE [AND VERIFY] (10) cmd[2] = (lba>>24)&0xff; // Logical Block Addrss cmd[3] = (lba>>16)&0xff; cmd[4] = (lba>>8)&0xff; cmd[5] = lba&0xff; cmd[7] = (nbl>>8)&0xff; cmd[8] = nbl&0xff; cmd[9] = 0;#if 0 cmd[0] = 0xAA; // WRITE(12) cmd[2] = (lba>>24)&0xff; // Logical Block Addrss cmd[3] = (lba>>16)&0xff; cmd[4] = (lba>>8)&0xff; cmd[5] = lba&0xff; cmd[8] = (nbl>>8)&0xff; cmd[9] = nbl&0xff; cmd[10] = 0x80; // "Streaming" cmd[11] = 0;#endif // // First writes can be long, especially in DAO mode... // I wish I could complement this with "if (lba==0)," // but some units might choose to fill the buffer before // they take the first nap... // cmd.timeout(dao_toggle?180:60); // // It should also be noted that under Linux these values // (if actually respected by kernel!) can turn out bogus. // The problem is that I scale them to milliseconds as // documentation requires/implies, while kernel treats // them as "jiffies." I could/should have used HZ macro // (or sysconf(_SC_CLK_TCK)), but recent kernels maintain // own higher HZ value and disrespects the user-land one. // Sending them down as milliseconds is just safer... // if (!(errcode=cmd.transport (WRITE,(void *)buff,size))) break; //--- WRITE failed ---//#if defined(__sun) || defined(sun) // // Solaris can slice USB WRITEs to multiple ones. Here I try // to find out which slice has failed. I expect we get here // only when we re-enter the loop... // if (lba==next_wr_addr && errcode==0x52102) // "INVALID ADDRESS FOR WRITE" { unsigned char track[32]; cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; cmd[4] = next_track>>8; cmd[5] = next_track&0xFF; cmd[8] = sizeof(track); cmd[9] = 0; if (!cmd.transport (READ,track,sizeof(track)))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -