📄 transport.hxx
字号:
req.ds_senselen = sizeof(_sense); req.ds_flags = DSRQ_SENSE; req.ds_time = 60*1000; } req.ds_cmdlen = i+1; return cdb[i]; } unsigned char &operator()(size_t i) { return _sense[i]; } unsigned char *sense() { return _sense; } void timeout(int i) { req.ds_time=i*1000; } size_t residue() { return req.ds_datalen-req.ds_datasent; } int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { int ret=0,retries=3; req.ds_databuf = (caddr_t)buf; req.ds_datalen = sz; req.ds_flags |= dir; // I personally don't understand why do we need to loop, but // /usr/share/src/irix/examples/scsi/lib/src/dslib.c is looping... while (retries--) { if (ioctl(fd,DS_ENTER,&req) < 0) return -1; if (req.ds_status==STA_GOOD) return 0; if (req.ds_ret==DSRT_NOSEL) continue; if (req.ds_status==STA_BUSY || req.ds_status==STA_RESERV) { poll(NULL,0,500); continue; } break; } errno=EIO; ret=-1; if (req.ds_status==STA_CHECK && req.ds_sensesent>=14) { ret = ERRCODE(_sense); if (ret==0) ret=-1; else CREAM_ON_ERRNO(_sense); } return ret; } // mimics umount(2), therefore inconsistent return values int umount(int f=-1) { struct stat fsb,msb; struct mntent *mb; FILE *fp; pid_t pid,rpid; int ret=0,rval; char hw_path[MAXPATHLEN]; int hw_len=sizeof(hw_path)-1; if (f==-1) f=fd; if (fstat (f,&fsb) < 0) return -1; if (!getenv("MEDIAD_GOT_EXCLUSIVEUSE")) { if (attr_getf (f,"_devname",hw_path,&hw_len,0)) return -1; if (hw_len>=sizeof(hw_path)) hw_len=sizeof(hw_path)-1;// paranoia hw_path[hw_len]='\0'; // mediad even unmounts removable volumes. However! The // locks are "granted" even for unmanaged devices, so // it's not possible to tell if device is ignored through // /etc/config/mediad.config or actually managed. Therefore // I have to pass through own unmount code in either case... mediad_get_exclusiveuse(hw_path,"dvd+rw-tools"); switch (mediad_last_error()) { case RMED_NOERROR: break; case RMED_EACCESS: case RMED_ECANTUMOUNT: errno=EBUSY; return -1; case RMED_ENOMEDIAD: break; case -1: if(errno==ECONNREFUSED) break; // no mediad... else return -1; default: errno=ENOTTY; return -1; } } if ((fp=setmntent (MOUNTED,"r"))==NULL) return -1; while ((mb=getmntent (fp))!=NULL) { if (stat (mb->mnt_fsname,&msb) < 0) continue; // corrupted line? // Following effectively catches only /dev/rdsk/dksXdYvol, // which is sufficient for iso9660 volumes, but not for e.g. // EFS formatted media. I mean code might have to be more // versatile... Wait for feedback... if (msb.st_rdev == fsb.st_rdev) { ret = -1; if ((pid = fork()) == (pid_t)-1) break; if (pid == 0) execl ("/sbin/umount","umount",mb->mnt_dir,NULL); while (1) { rpid = waitpid (pid,&rval,0); if (rpid == (pid_t)-1) { if (errno==EINTR) continue; else break; } else if (rpid != pid) { errno = ECHILD; break; } if (WIFEXITED(rval)) { if (WEXITSTATUS(rval) == 0) ret=0; else errno=EBUSY; // most likely break; } else if (WIFSTOPPED(rval) || WIFCONTINUED(rval)) continue; else { errno = ENOLINK; // some phony errno break; } } break; } } endmntent (fp); return ret; }#if 0 // for now just an idea to test...#define RELOAD_NEVER_NEEDED int is_reload_needed () { return 0; }#else int is_reload_needed () { return 1; }#endif};#elif defined(_WIN32)#if defined(__MINGW32__)#include <ddk/ntddscsi.h>#else#include <devioctl.h>#include <ntddscsi.h>#endiftypedef enum { NONE=SCSI_IOCTL_DATA_UNSPECIFIED, READ=SCSI_IOCTL_DATA_IN, WRITE=SCSI_IOCTL_DATA_OUT } Direction;typedef struct { SCSI_PASS_THROUGH_DIRECT spt; unsigned char sense[18];} SPKG;class Scsi_Command {private: HANDLE fd; SPKG p;public: Scsi_Command() { fd=INVALID_HANDLE_VALUE; } ~Scsi_Command() { if (fd!=INVALID_HANDLE_VALUE) CloseHandle (fd); } int associate (const char *file) { char dev[32]; sprintf(dev,"\\\\.\\%.*s",sizeof(dev)-5,file); fd=CreateFile (dev,GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL); return fd!=INVALID_HANDLE_VALUE; } unsigned char &operator[] (size_t i) { if (i==0) { memset(&p,0,sizeof(p)); p.spt.Length = sizeof(p.spt); p.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; p.spt.TimeOutValue = 30; p.spt.SenseInfoLength = sizeof(p.sense); p.spt.SenseInfoOffset = offsetof(SPKG,sense); } p.spt.CdbLength = i+1; return p.spt.Cdb[i]; } unsigned char &operator()(size_t i) { return p.sense[i]; } unsigned char *sense() { return p.sense; } void timeout(int i) { p.spt.TimeOutValue=i; } size_t residue() { return 0; } // bogus int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0) { DWORD bytes; int ret=0; p.spt.DataBuffer = buf; p.spt.DataTransferLength = sz; p.spt.DataIn = dir; if (DeviceIoControl (fd,IOCTL_SCSI_PASS_THROUGH_DIRECT, &p,sizeof(p.spt), &p,sizeof(p), &bytes,FALSE) == 0) return -1; if (p.sense[0]&0x70) { SetLastError (ERROR_GEN_FAILURE); ret = ERRCODE(p.sense); if (ret==0) ret=-1; else CREAM_ON_ERRNO(p.sense); } return ret; } int umount () { return 0; } // bogus int is_reload_needed () { return 0; } // bogus};#else#error "Unsupported OS"#endif#define DUMP_EVENTS 0static int handle_events (Scsi_Command &cmd){ unsigned char event[8]; unsigned short profile=0,started=0; int err,ret=0; unsigned int descr; static unsigned char events=0xFF; // "All events" while (events) { cmd[0] = 0x4A; // GET EVENT cmd[1] = 1; // "Polled" cmd[4] = events; cmd[8] = sizeof(event); cmd[9] = 0; if ((err=cmd.transport(READ,event,sizeof(event)))) { events=0; sperror ("GET EVENT",err); return ret; } events = event[3]; if ((event[2]&7) == 0 || (event[0]<<8|event[1]) == 2 || (event[4]&0xF) == 0 ) // No Changes return ret; descr = event[4]<<24|event[5]<<16|event[6]<<8|event[7];#if DUMP_EVENTS fprintf(stderr,"< %d[%08x],%x >\n",event[2],descr,events);#endif switch(event[2]&7) { case 0: return ret; // No [supported] events case 1: ret |= 1<<1; // Operational Change if ((descr&0xFFFF) < 3) goto read_profile; start_unit: if (!started) { cmd[0]=0x1B; // START STOP UNIT cmd[4]=1; // "Start" cmd[5]=0; if ((err=cmd.transport()) && err!=0x62800) sperror ("START UNIT",err); started=1, profile=0; } read_profile: if (!profile) { cmd[0] = 0x46; // GET CONFIGURATION cmd[8] = sizeof(event); cmd[9] = 0; if (!cmd.transport(READ,event,sizeof(event))) profile=event[6]<<8|event[7]; } break; case 2: ret |= 1<<2; // Power Management if (event[5]>1) // State is other than Active goto start_unit; break; case 3: ret |= 1<<3; break; // External Request case 4: ret |= 1<<4; // Media if (event[5]&2) // Media in goto start_unit; break; case 5: ret |= 1<<5; break; // Multiple Initiators case 6: // Device Busy if ((event[4]&0xF)==1) // Timeout occured { poll(NULL,0,(descr&0xFFFF)*100+100); cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((err=cmd.transport())) sperror("TEST UNIT READY",err); ret |= 1<<6; } break; case 7: ret |= 1<<7; break; // Reserved } } return ret;}#undef DUMP_EVENTSstatic int wait_for_unit (Scsi_Command &cmd,volatile int *progress=NULL){ unsigned char *sense=cmd.sense(),sensebuf[18]; int err; long msecs=1000; while (1) { if (msecs > 0) poll(NULL,0,msecs); msecs = getmsecs(); cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if (!(err=cmd.transport ())) break; // I wish I could test just for sense.valid, but (at least) // hp dvd100i returns 0 in valid bit at this point:-( So I // check for all bits... if ((sense[0]&0x70)==0) { perror (":-( unable to TEST UNIT READY"); return -1; } else if (sense[12]==0x3A) // doesn't get any further than "no media" return err; while (progress) { if (sense[15]&0x80) { *progress = sense[16]<<8|sense[17]; break; } // MMC-3 (draft) specification says that the unit should // return progress indicator in key specific bytes even // in reply to TEST UNIT READY. I.e. as above! But (at // least) hp dvd100i doesn't do that and I have to fetch // it separately:-( cmd[0] = 0x03; // REQUEST SENSE cmd[4] = sizeof(sensebuf); cmd[5] = 0; if ((err=cmd.transport (READ,sensebuf,sizeof(sensebuf)))) { sperror ("REQUEST SENSE",err); return err; } if (sensebuf[15]&0x80) *progress = sensebuf[16]<<8|sensebuf[17]; break; } msecs = 1000 - (getmsecs() - msecs); } return 0;}#define FEATURE21_BROKEN 1static void page05_setup (Scsi_Command &cmd, unsigned short profile=0, unsigned char p32=0xC0) // 5 least significant bits of p32 go to p[2], Test Write&Write Type // 2 most significant bits go to p[3], Multi-session field // 0xC0 means "Multi-session, no Test Write, Incremental"{ unsigned int len,bdlen; unsigned char header[12],track[32],*p;#if !FEATURE21_BROKEN unsigned char feature21[24];#endif int err; class autofree page05; if (profile==0) { unsigned char prof[8]; cmd[0] = 0x46; // GET CONFIGURATION cmd[8] = sizeof(prof); cmd[9] = 0; if ((err=cmd.transport(READ,prof,sizeof(prof)))) sperror ("GET CONFIGURATION",err), exit(FATAL_START(errno)); profile = prof[6]<<8|prof[7]; }#if !FEATURE21_BROKEN if (profile==0x11 || profile==0x14) { cmd[0] = 0x46; // GET CONFIGURATION cmd[1] = 2; // ask for the only feature... cmd[3] = 0x21; // the "Incremental Streaming Writable" one cmd[8] = 8; // read the header only to start with cmd[9] = 0; if ((err=cmd.transport(READ,feature21,8))) sperror ("GET CONFIGURATION",err), exit(FATAL_START(errno)); len = feature21[0]<<24|feature21[1]<<16|feature21[2]<<8|feature21[3]; len += 4; if (len>sizeof(feature21)) len = sizeof(feature21); else if (len<(8+8)) fprintf (stderr,":-( READ FEATURE DESCRIPTOR 0021h: insane length\n"), exit(FATAL_START(EINVAL)); cmd[0] = 0x46; // GET CONFIGURATION cmd[1] = 2; // ask for the only feature... cmd[3] = 0x21; // the "Incremental Streaming Writable" one cmd[8] = len; // this time with real length cmd[9] = 0; if ((err=cmd.transport(READ,feature21,len))) sperror ("READ FEATURE DESCRIPTOR 0021h",err), exit(FATAL_START(errno)); if ((feature21[8+2]&1)==0) fprintf (stderr,":-( FEATURE 0021h is not in effect\n"), exit(FATAL_START(EMEDIUMTYPE)); } else feature21[8+2]=0;#endif cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; // TRACK INFORMATION cmd[5] = 1; // track#1, in DVD context it's safe to assume // that all tracks are in the same mode cmd[8] = sizeof(track); cmd[9] = 0; if ((err=cmd.transport(READ,track,sizeof(track)))) sperror ("READ TRACK INFORMATION",err), exit(FATAL_START(errno)); // WRITE PAGE SETUP // cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x05; // "Write Page" cmd[8] = sizeof(header); // header only to start with cmd[9] = 0; if ((err=cmd.transport(READ,header,sizeof(header)))) sperror ("MODE SENSE",err), exit(FATAL_START(errno)); len = (header[0]<<8|header[1])+2; bdlen = header[6]<<8|header[7]; if (bdlen) // should never happen as we set "DBD" above { if (len <= (8+bdlen+14)) fprintf (stderr,":-( LUN is impossible to bear with...\n"), exit(FATAL_START(EINVAL)); } else if (len < (8+2+(unsigned int)header[9]))// SANYO does this. len = 8+2+header[9]; page05 = (unsigned char *)malloc(len); if (page05 == NULL) fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM)); cmd[0] = 0x5A; // MODE SENSE cmd[1] = 0x08; // "Disable Block Descriptors" cmd[2] = 0x05; // "Write Page" cmd[7] = len>>8; cmd[8] = len; // real length this time cmd[9] = 0; if ((err=cmd.transport(READ,page05,len))) sperror("MODE SENSE",err), exit(FATAL_START(errno)); len -= 2; if (len < ((unsigned int)page05[0]<<8|page05[1])) // paranoia:-) page05[0] = len>>8, page05[1] = len; len = (page05[0]<<8|page05[1])+2; bdlen = page05[6]<<8|page05[7]; len -= bdlen; if (len < (8+14)) fprintf (stderr,":-( LUN is impossible to bear with...\n"), exit(FATAL_START(EINVAL)); p = page05 + 8 + bdlen; memset (p-8,0,8); p[0] &= 0x7F; // copy "Test Write" and "Write Type" from p32 p[2] &= ~0x1F, p[2] |= p32&0x1F; p[2] |= 0x40; // insist on BUFE on // setup Preferred Link Size#if !FEATURE21_BROKEN if (feature21[8+2]&1) { if (feature21[8+7]) p[2] |= 0x20, p[5] = feature21[8+8]; else p[2] &= ~0x20, p[5] = 0; }#else // At least Pioneer DVR-104 returns some bogus data in // Preferred Link Size... if (profile==0x11 || profile==0x14) // Sequential recordings... p[2] |= 0x20, p[5] = 0x10;#endif else p[2] &= ~0x20, p[5] = 0; // copy Track Mode from TRACK INFORMATION // [some DVD-R units (most notably Panasonic LF-D310), insist // on Track Mode 5, even though it's effectively ignored] p[3] &= ~0x0F, p[3] |= profile==0x11?5:(track[5]&0x0F); // copy "Multi-session" bits from p32 p[3] &= ~0xC0, p[3] |= p32&0xC0; if (profile == 0x13) // DVD-RW Restricted Overwrite p[3] &= 0x3F; // always Single-session? // setup Data Block Type if ((track[6]&0x0F)==1) p[4] = 8; else fprintf (stderr,":-( none Mode 1 track\n"), exit(FATAL_START(EMEDIUMTYPE)); // setup Packet Size // [some DVD-R units (most notably Panasonic LF-D310), insist // on fixed Packet Size of 16 blocks, even though it's effectively // ignored] p[3] |= 0x20, memset (p+10,0,4), p[13] = 0x10; if (track[6]&0x10) memcpy (p+10,track+20,4); // Fixed else if (profile != 0x11) p[3] &= ~0x20, p[13] = 0; // Variable switch (profile) { case 0x13: // DVD-RW Restricted Overwrite if (!(track[6]&0x10)) fprintf (stderr,":-( track is not formatted for fixed packet size\n"), exit(FATAL_START(EMEDIUMTYPE)); break; case 0x14: // DVD-RW Sequential Recording case 0x11: // DVD-R Sequential Recording if (track[6]&0x10) fprintf (stderr,":-( track is formatted for fixed packet size\n"), exit(FATAL_START(EMEDIUMTYPE)); break; default:#if 0 fprintf (stderr,":-( invalid profile %04xh\n",profile); exit(FATAL_START(EMEDIUMTYPE));#endif break; } p[8] = 0; // "Session Format" should be ignored, but // I reset it just in case... cmd[0] = 0x55; // MODE SELECT cmd[1] = 0x10; // conformant cmd[7] = len>>8; cmd[8] = len; cmd[9] = 0; if ((err=cmd.transport(WRITE,p-8,len))) sperror ("MODE SELECT",err), exit(FATAL_START(errno)); // END OF WRITE PAGE SETUP //}#undef FEATURE21_BROKEN#undef ERRCODE#undef CREAM_ON_ERRNO#undef CREAM_ON_ERRNO_NAKED
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -