/* -*- tab-width: 8; c-basic-offset: 4 -*- */ /* Main file for CD-ROM support * * Copyright 1994 Martin Ayotte * Copyright 1999, 2001, 2003 Eric Pouech * Copyright 2000 Andreas Mohr * Copyright 2005 Ivan Leo Puoti * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include #include #include #include #ifdef HAVE_IO_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #ifdef HAVE_SYS_STAT_H # include #endif #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SCSI_SG_H # include #endif #ifdef HAVE_SCSI_SCSI_H # include # undef REASSIGN_BLOCKS /* avoid conflict with winioctl.h */ # undef FAILED /* avoid conflict with winerror.h */ #endif #ifdef HAVE_SCSI_SCSI_IOCTL_H # include #endif #ifdef HAVE_LINUX_MAJOR_H # include #endif #ifdef HAVE_LINUX_HDREG_H # include #endif #ifdef HAVE_LINUX_PARAM_H # include #endif #ifdef HAVE_LINUX_CDROM_H # include #endif #ifdef HAVE_LINUX_UCDROM_H # include #endif #ifdef HAVE_SYS_CDIO_H # include #endif #ifdef HAVE_SYS_SCSIIO_H # include #endif #ifdef HAVE_IOKIT_IOKITLIB_H # include # include # include # include # include # include # include # include # define SENSEBUFLEN kSenseDefaultSize typedef struct { uint32_t attribute; uint32_t timeout; uint32_t response; uint32_t status; uint8_t direction; uint8_t cdbSize; uint8_t reserved0144[2]; uint8_t cdb[16]; void* buffer; uint64_t bufferSize; void* sense; uint64_t senseLen; } dk_scsi_command_t; #define DKIOCSCSICOMMAND _IOWR('d', 253, dk_scsi_command_t) #endif #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winternl.h" #include "winioctl.h" #include "ntddstor.h" #include "ntddcdrm.h" #include "ddk/ntddcdvd.h" #include "ntddscsi.h" #include "ntdll_misc.h" #include "wine/server.h" #include "wine/library.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(cdrom); /* Non-Linux systems do not have linux/cdrom.h and the like, and thus lack the following constants. */ #ifndef CD_SECS # define CD_SECS 60 /* seconds per minute */ #endif #ifndef CD_FRAMES # define CD_FRAMES 75 /* frames per second */ #endif static const struct iocodexs { DWORD code; const char *codex; } iocodextable[] = { #define X(x) { x, #x }, X(IOCTL_CDROM_CHECK_VERIFY) X(IOCTL_CDROM_CURRENT_POSITION) X(IOCTL_CDROM_DISK_TYPE) X(IOCTL_CDROM_GET_CONTROL) X(IOCTL_CDROM_GET_DRIVE_GEOMETRY) X(IOCTL_CDROM_GET_VOLUME) X(IOCTL_CDROM_LOAD_MEDIA) X(IOCTL_CDROM_MEDIA_CATALOG) X(IOCTL_CDROM_MEDIA_REMOVAL) X(IOCTL_CDROM_PAUSE_AUDIO) X(IOCTL_CDROM_PLAY_AUDIO_MSF) X(IOCTL_CDROM_RAW_READ) X(IOCTL_CDROM_READ_Q_CHANNEL) X(IOCTL_CDROM_READ_TOC) X(IOCTL_CDROM_RESUME_AUDIO) X(IOCTL_CDROM_SEEK_AUDIO_MSF) X(IOCTL_CDROM_SET_VOLUME) X(IOCTL_CDROM_STOP_AUDIO) X(IOCTL_CDROM_TRACK_ISRC) X(IOCTL_DISK_MEDIA_REMOVAL) X(IOCTL_DVD_END_SESSION) X(IOCTL_DVD_GET_REGION) X(IOCTL_DVD_READ_KEY) X(IOCTL_DVD_READ_STRUCTURE) X(IOCTL_DVD_SEND_KEY) X(IOCTL_DVD_START_SESSION) X(IOCTL_SCSI_GET_ADDRESS) X(IOCTL_SCSI_GET_CAPABILITIES) X(IOCTL_SCSI_GET_INQUIRY_DATA) X(IOCTL_SCSI_PASS_THROUGH) X(IOCTL_SCSI_PASS_THROUGH_DIRECT) X(IOCTL_STORAGE_CHECK_VERIFY) X(IOCTL_STORAGE_EJECTION_CONTROL) X(IOCTL_STORAGE_EJECT_MEDIA) X(IOCTL_STORAGE_GET_DEVICE_NUMBER) X(IOCTL_STORAGE_GET_MEDIA_TYPES) X(IOCTL_STORAGE_GET_MEDIA_TYPES_EX) X(IOCTL_STORAGE_LOAD_MEDIA) X(IOCTL_STORAGE_MEDIA_REMOVAL) X(IOCTL_STORAGE_RESET_DEVICE) #undef X }; static const char *iocodex(DWORD code) { unsigned int i; static char buffer[25]; for(i=0; i= MAX_CACHE_ENTRIES) return 0; if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr) == -1 ) return 0; if ( memcmp(&hdr, &cdrom_cache[dev].hdr, sizeof(struct cdrom_tochdr)) ) return 1; for (i=hdr.cdth_trk0; i<=hdr.cdth_trk1+1; i++) { if (i == hdr.cdth_trk1 + 1) { entry.cdte_track = CDROM_LEADOUT; } else { entry.cdte_track = i; } entry.cdte_format = CDROM_MSF; if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry) == -1) return 0; if ( memcmp(&entry, cdrom_cache[dev].entry+i-hdr.cdth_trk0, sizeof(struct cdrom_tocentry)) ) return 1; } return 0; } #endif /****************************************************************** * get_parent_device * * On Mac OS, get the device for the whole disk from a fd that points to a partition. * This is ugly and inefficient, but we have no choice since the partition fd doesn't * support the eject ioctl. */ #ifdef __APPLE__ static NTSTATUS get_parent_device( int fd, char *name, size_t len ) { NTSTATUS status = STATUS_NO_SUCH_FILE; struct stat st; int i; io_service_t service; CFMutableDictionaryRef dict; CFTypeRef val; if (fstat( fd, &st ) == -1) return FILE_GetNtStatus(); if (!S_ISCHR( st.st_mode )) return STATUS_OBJECT_TYPE_MISMATCH; /* create a dictionary with the right major/minor numbers */ if (!(dict = IOServiceMatching( kIOMediaClass ))) return STATUS_NO_MEMORY; i = major( st.st_rdev ); val = CFNumberCreate( NULL, kCFNumberIntType, &i ); CFDictionaryAddValue( dict, CFSTR( "BSD Major" ), val ); CFRelease( val ); i = minor( st.st_rdev ); val = CFNumberCreate( NULL, kCFNumberIntType, &i ); CFDictionaryAddValue( dict, CFSTR( "BSD Minor" ), val ); CFRelease( val ); CFDictionaryAddValue( dict, CFSTR("Removable"), kCFBooleanTrue ); service = IOServiceGetMatchingService( kIOMasterPortDefault, dict ); /* now look for the parent that has the "Whole" attribute set to TRUE */ while (service) { io_service_t parent = 0; CFBooleanRef whole; CFStringRef str; int ok; if (!IOObjectConformsTo( service, kIOMediaClass )) goto next; if (!(whole = IORegistryEntryCreateCFProperty( service, CFSTR("Whole"), NULL, 0 ))) goto next; ok = (whole == kCFBooleanTrue); CFRelease( whole ); if (!ok) goto next; if ((str = IORegistryEntryCreateCFProperty( service, CFSTR("BSD Name"), NULL, 0 ))) { strcpy( name, "/dev/r" ); CFStringGetCString( str, name + 6, len - 6, kCFStringEncodingUTF8 ); CFRelease( str ); status = STATUS_SUCCESS; } IOObjectRelease( service ); break; next: IORegistryEntryGetParentEntry( service, kIOServicePlane, &parent ); IOObjectRelease( service ); service = parent; } return status; } #endif /****************************************************************** * CDROM_SyncCache [internal] * * Read the TOC in and store it in the cdrom_cache structure. * Further requests for the TOC will be copied from the cache * unless certain events like disk ejection is detected, in which * case the cache will be cleared, causing it to be resynced. * The cache section must be held by caller. */ static NTSTATUS CDROM_SyncCache(int dev, int fd) { #ifdef linux int i, tsz; struct cdrom_tochdr hdr; struct cdrom_tocentry entry; CDROM_TOC *toc = &cdrom_cache[dev].toc; cdrom_cache[dev].toc_good = 0; if (ioctl(fd, CDROMREADTOCHDR, &hdr) == -1) { WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno)); return FILE_GetNtStatus(); } toc->FirstTrack = hdr.cdth_trk0; toc->LastTrack = hdr.cdth_trk1; tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack) + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2); toc->Length[0] = tsz >> 8; toc->Length[1] = tsz; TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack ); for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++) { if (i == toc->LastTrack + 1) entry.cdte_track = CDROM_LEADOUT; else entry.cdte_track = i; entry.cdte_format = CDROM_MSF; if (ioctl(fd, CDROMREADTOCENTRY, &entry) == -1) { WARN("error read entry (%s)\n", strerror(errno)); return FILE_GetNtStatus(); } toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl; toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr; /* marking last track with leadout value as index */ toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track; toc->TrackData[i - toc->FirstTrack].Address[0] = 0; toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute; toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second; toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame; } cdrom_cache[dev].toc_good = 1; return STATUS_SUCCESS; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) int i, tsz; struct ioc_toc_header hdr; struct ioc_read_toc_entry entry; struct cd_toc_entry toc_buffer; CDROM_TOC *toc = &cdrom_cache[dev].toc; cdrom_cache[dev].toc_good = 0; if (ioctl(fd, CDIOREADTOCHEADER, &hdr) == -1) { WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno)); return FILE_GetNtStatus(); } toc->FirstTrack = hdr.starting_track; toc->LastTrack = hdr.ending_track; tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack) + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2); toc->Length[0] = tsz >> 8; toc->Length[1] = tsz; TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack ); for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++) { if (i == toc->LastTrack + 1) { #define LEADOUT 0xaa entry.starting_track = LEADOUT; } else { entry.starting_track = i; } memset(&toc_buffer, 0, sizeof(toc_buffer)); entry.address_format = CD_MSF_FORMAT; entry.data_len = sizeof(toc_buffer); entry.data = &toc_buffer; if (ioctl(fd, CDIOREADTOCENTRYS, &entry) == -1) { WARN("error read entry (%s)\n", strerror(errno)); return FILE_GetNtStatus(); } toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control; toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type; /* marking last track with leadout value as index */ toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track; toc->TrackData[i - toc->FirstTrack].Address[0] = 0; toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute; toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second; toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame; } cdrom_cache[dev].toc_good = 1; return STATUS_SUCCESS; #elif defined(__APPLE__) int i; dk_cd_read_toc_t hdr; CDROM_TOC *toc = &cdrom_cache[dev].toc; cdrom_cache[dev].toc_good = 0; memset( &hdr, 0, sizeof(hdr) ); hdr.buffer = toc; hdr.bufferLength = sizeof(*toc); if (ioctl(fd, DKIOCCDREADTOC, &hdr) == -1) { WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno)); return FILE_GetNtStatus(); } for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++) { /* convert address format */ TRACK_DATA *data = &toc->TrackData[i - toc->FirstTrack]; DWORD frame = (((DWORD)data->Address[0] << 24) | ((DWORD)data->Address[1] << 16) | ((DWORD)data->Address[2] << 8) | data->Address[3]); MSF_OF_FRAME( data->Address[1], frame ); data->Address[0] = 0; } cdrom_cache[dev].toc_good = 1; return STATUS_SUCCESS; #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } static void CDROM_ClearCacheEntry(int dev) { RtlEnterCriticalSection( &cache_section ); cdrom_cache[dev].toc_good = 0; RtlLeaveCriticalSection( &cache_section ); } /****************************************************************** * CDROM_GetInterfaceInfo * * Determines the ide interface (the number after the ide), and the * number of the device on that interface for ide cdroms (*iface <= 1). * Determines the scsi information for scsi cdroms (*iface >= 2). * Returns false if the info cannot not be obtained. */ static int CDROM_GetInterfaceInfo(int fd, UCHAR* iface, UCHAR* port, UCHAR* device, UCHAR* lun) { #if defined(linux) struct stat st; if ( fstat(fd, &st) == -1 || ! S_ISBLK(st.st_mode)) return 0; *port = 0; *iface = 0; *device = 0; *lun = 0; switch (major(st.st_rdev)) { case IDE0_MAJOR: *iface = 0; break; case IDE1_MAJOR: *iface = 1; break; case IDE2_MAJOR: *iface = 2; break; case IDE3_MAJOR: *iface = 3; break; case IDE4_MAJOR: *iface = 4; break; case IDE5_MAJOR: *iface = 5; break; case IDE6_MAJOR: *iface = 6; break; case IDE7_MAJOR: *iface = 7; break; default: *port = 1; break; } if (*port == 0) *device = (minor(st.st_rdev) >> 6); else { #ifdef SCSI_IOCTL_GET_IDLUN UINT32 idlun[2]; if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, idlun) != -1) { *port = (idlun[0] >> 24) & 0xff; *iface = ((idlun[0] >> 16) & 0xff) + 2; *device = idlun[0] & 0xff; *lun = (idlun[0] >> 8) & 0xff; } else #endif { WARN("CD-ROM device (%d, %d) not supported\n", major(st.st_rdev), minor(st.st_rdev)); return 0; } } return 1; #elif defined(__NetBSD__) struct scsi_addr addr; if (ioctl(fd, SCIOCIDENTIFY, &addr) != -1) { switch (addr.type) { case TYPE_SCSI: *port = 1; *iface = addr.addr.scsi.scbus; *device = addr.addr.scsi.target; *lun = addr.addr.scsi.lun; return 1; case TYPE_ATAPI: *port = 0; *iface = addr.addr.atapi.atbus; *device = addr.addr.atapi.drive; *lun = 0; return 1; } } return 0; #else FIXME("not implemented on this O/S\n"); return 0; #endif } /****************************************************************** * CDROM_Open * */ static NTSTATUS CDROM_Open(int fd, int* dev) { struct stat st; NTSTATUS ret = STATUS_SUCCESS; int empty = -1; fstat(fd, &st); RtlEnterCriticalSection( &cache_section ); for (*dev = 0; *dev < MAX_CACHE_ENTRIES; (*dev)++) { if (empty == -1 && cdrom_cache[*dev].device == 0 && cdrom_cache[*dev].inode == 0) empty = *dev; else if (cdrom_cache[*dev].device == st.st_dev && cdrom_cache[*dev].inode == st.st_ino) break; } if (*dev == MAX_CACHE_ENTRIES) { if (empty == -1) ret = STATUS_NOT_IMPLEMENTED; else { *dev = empty; cdrom_cache[*dev].device = st.st_dev; cdrom_cache[*dev].inode = st.st_ino; } } RtlLeaveCriticalSection( &cache_section ); TRACE("%d, %d\n", *dev, fd); return ret; } /****************************************************************** * CDROM_GetStatusCode * * */ static NTSTATUS CDROM_GetStatusCode(int io) { if (io == 0) return STATUS_SUCCESS; return FILE_GetNtStatus(); } /****************************************************************** * CDROM_GetControl * */ static NTSTATUS CDROM_GetControl(int dev, int fd, CDROM_AUDIO_CONTROL* cac) { #ifdef __APPLE__ uint16_t speed; int io = ioctl( fd, DKIOCCDGETSPEED, &speed ); if (io != 0) return CDROM_GetStatusCode( io ); /* DKIOCCDGETSPEED returns the speed in kilobytes per second, * so convert to logical blocks (assumed to be ~2 KB). */ cac->LogicalBlocksPerSecond = speed/2; #else cac->LogicalBlocksPerSecond = 1; /* FIXME */ #endif cac->LbaFormat = 0; /* FIXME */ return STATUS_NOT_SUPPORTED; } /****************************************************************** * CDROM_GetDeviceNumber * */ static NTSTATUS CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum) { FIXME( "stub\n" ); devnum->DeviceType = FILE_DEVICE_DISK; devnum->DeviceNumber = 1; devnum->PartitionNumber = 1; return STATUS_SUCCESS; } /****************************************************************** * CDROM_GetDriveGeometry * */ static NTSTATUS CDROM_GetDriveGeometry(int dev, int fd, DISK_GEOMETRY* dg) { CDROM_TOC toc; NTSTATUS ret = 0; int fsize = 0; if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret; fsize = FRAME_OF_TOC(toc, toc.LastTrack+1) - FRAME_OF_TOC(toc, 1); /* Total size in frames */ dg->Cylinders.u.LowPart = fsize / (64 * 32); dg->Cylinders.u.HighPart = 0; dg->MediaType = RemovableMedia; dg->TracksPerCylinder = 64; dg->SectorsPerTrack = 32; dg->BytesPerSector= 2048; return ret; } /****************************************************************** * CDROM_GetMediaType * */ static NTSTATUS CDROM_GetMediaType(int dev, GET_MEDIA_TYPES* medtype) { FIXME(": faking success\n"); medtype->DeviceType = FILE_DEVICE_CD_ROM; medtype->MediaInfoCount = 0; return STATUS_SUCCESS; } /************************************************************************** * CDROM_Reset [internal] */ static NTSTATUS CDROM_ResetAudio(int fd) { #if defined(linux) return CDROM_GetStatusCode(ioctl(fd, CDROMRESET)); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) return CDROM_GetStatusCode(ioctl(fd, CDIOCRESET, NULL)); #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_SetTray * * */ static NTSTATUS CDROM_SetTray(int fd, BOOL doEject) { #if defined(linux) return CDROM_GetStatusCode(ioctl(fd, doEject ? CDROMEJECT : CDROMCLOSETRAY)); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) return CDROM_GetStatusCode((ioctl(fd, CDIOCALLOW, NULL)) || (ioctl(fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) || (ioctl(fd, CDIOCPREVENT, NULL))); #elif defined(__APPLE__) if (doEject) return CDROM_GetStatusCode( ioctl( fd, DKIOCEJECT, NULL ) ); else return STATUS_NOT_SUPPORTED; #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_ControlEjection * * */ static NTSTATUS CDROM_ControlEjection(int fd, const PREVENT_MEDIA_REMOVAL* rmv) { #if defined(linux) return CDROM_GetStatusCode(ioctl(fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval)); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) return CDROM_GetStatusCode(ioctl(fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL)); #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_ReadTOC * * */ static NTSTATUS CDROM_ReadTOC(int dev, int fd, CDROM_TOC* toc) { NTSTATUS ret = STATUS_NOT_SUPPORTED; if (dev < 0 || dev >= MAX_CACHE_ENTRIES) return STATUS_INVALID_PARAMETER; RtlEnterCriticalSection( &cache_section ); if (cdrom_cache[dev].toc_good || !(ret = CDROM_SyncCache(dev, fd))) { *toc = cdrom_cache[dev].toc; ret = STATUS_SUCCESS; } RtlLeaveCriticalSection( &cache_section ); return ret; } /****************************************************************** * CDROM_GetDiskData * * */ static NTSTATUS CDROM_GetDiskData(int dev, int fd, CDROM_DISK_DATA* data) { CDROM_TOC toc; NTSTATUS ret; int i; if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret; data->DiskData = 0; for (i = toc.FirstTrack; i <= toc.LastTrack; i++) { if (toc.TrackData[i-toc.FirstTrack].Control & 0x04) data->DiskData |= CDROM_DISK_DATA_TRACK; else data->DiskData |= CDROM_DISK_AUDIO_TRACK; } return STATUS_SUCCESS; } /****************************************************************** * CDROM_ReadQChannel * * */ static NTSTATUS CDROM_ReadQChannel(int dev, int fd, const CDROM_SUB_Q_DATA_FORMAT* fmt, SUB_Q_CHANNEL_DATA* data) { NTSTATUS ret = STATUS_NOT_SUPPORTED; #ifdef linux SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data; int io; struct cdrom_subchnl sc; sc.cdsc_format = CDROM_MSF; io = ioctl(fd, CDROMSUBCHNL, &sc); if (io == -1) { TRACE("opened or no_media (%s)!\n", strerror(errno)); hdr->AudioStatus = AUDIO_STATUS_NO_STATUS; CDROM_ClearCacheEntry(dev); goto end; } hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED; switch (sc.cdsc_audiostatus) { case CDROM_AUDIO_INVALID: CDROM_ClearCacheEntry(dev); hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED; break; case CDROM_AUDIO_NO_STATUS: CDROM_ClearCacheEntry(dev); hdr->AudioStatus = AUDIO_STATUS_NO_STATUS; break; case CDROM_AUDIO_PLAY: hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS; break; case CDROM_AUDIO_PAUSED: hdr->AudioStatus = AUDIO_STATUS_PAUSED; break; case CDROM_AUDIO_COMPLETED: hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE; break; case CDROM_AUDIO_ERROR: hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR; break; default: TRACE("status=%02X !\n", sc.cdsc_audiostatus); break; } switch (fmt->Format) { case IOCTL_CDROM_CURRENT_POSITION: RtlEnterCriticalSection( &cache_section ); if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) { data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION; data->CurrentPosition.Control = sc.cdsc_ctrl; data->CurrentPosition.ADR = sc.cdsc_adr; data->CurrentPosition.TrackNumber = sc.cdsc_trk; data->CurrentPosition.IndexNumber = sc.cdsc_ind; data->CurrentPosition.AbsoluteAddress[0] = 0; data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute; data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second; data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame; data->CurrentPosition.TrackRelativeAddress[0] = 0; data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute; data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second; data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame; cdrom_cache[dev].CurrentPosition = data->CurrentPosition; } else /* not playing */ { cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */ data->CurrentPosition = cdrom_cache[dev].CurrentPosition; } RtlLeaveCriticalSection( &cache_section ); break; case IOCTL_CDROM_MEDIA_CATALOG: data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG; { struct cdrom_mcn mcn; if ((io = ioctl(fd, CDROM_GET_MCN, &mcn)) == -1) goto end; data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG; data->MediaCatalog.Mcval = 0; /* FIXME */ memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14); data->MediaCatalog.MediaCatalog[14] = 0; } break; case IOCTL_CDROM_TRACK_ISRC: FIXME("TrackIsrc: NIY on linux\n"); data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC; data->TrackIsrc.Tcval = 0; io = 0; break; } end: ret = CDROM_GetStatusCode(io); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data; int io; struct ioc_read_subchannel read_sc; struct cd_sub_channel_info sc; read_sc.address_format = CD_MSF_FORMAT; read_sc.track = 0; read_sc.data_len = sizeof(sc); read_sc.data = ≻ switch (fmt->Format) { case IOCTL_CDROM_CURRENT_POSITION: read_sc.data_format = CD_CURRENT_POSITION; break; case IOCTL_CDROM_MEDIA_CATALOG: read_sc.data_format = CD_MEDIA_CATALOG; break; case IOCTL_CDROM_TRACK_ISRC: read_sc.data_format = CD_TRACK_INFO; sc.what.track_info.track_number = data->TrackIsrc.Track; break; } io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc); if (io == -1) { TRACE("opened or no_media (%s)!\n", strerror(errno)); CDROM_ClearCacheEntry(dev); hdr->AudioStatus = AUDIO_STATUS_NO_STATUS; goto end; } hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED; switch (sc.header.audio_status) { case CD_AS_AUDIO_INVALID: CDROM_ClearCacheEntry(dev); hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED; break; case CD_AS_NO_STATUS: CDROM_ClearCacheEntry(dev); hdr->AudioStatus = AUDIO_STATUS_NO_STATUS; break; case CD_AS_PLAY_IN_PROGRESS: hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS; break; case CD_AS_PLAY_PAUSED: hdr->AudioStatus = AUDIO_STATUS_PAUSED; break; case CD_AS_PLAY_COMPLETED: hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE; break; case CD_AS_PLAY_ERROR: hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR; break; default: TRACE("status=%02X !\n", sc.header.audio_status); } switch (fmt->Format) { case IOCTL_CDROM_CURRENT_POSITION: RtlEnterCriticalSection( &cache_section ); if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) { data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION; data->CurrentPosition.Control = sc.what.position.control; data->CurrentPosition.ADR = sc.what.position.addr_type; data->CurrentPosition.TrackNumber = sc.what.position.track_number; data->CurrentPosition.IndexNumber = sc.what.position.index_number; data->CurrentPosition.AbsoluteAddress[0] = 0; data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute; data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second; data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame; data->CurrentPosition.TrackRelativeAddress[0] = 0; data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute; data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second; data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame; cdrom_cache[dev].CurrentPosition = data->CurrentPosition; } else { /* not playing */ cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */ data->CurrentPosition = cdrom_cache[dev].CurrentPosition; } RtlLeaveCriticalSection( &cache_section ); break; case IOCTL_CDROM_MEDIA_CATALOG: data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG; data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid; memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15); break; case IOCTL_CDROM_TRACK_ISRC: data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC; data->TrackIsrc.Tcval = sc.what.track_info.ti_valid; memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15); break; } end: ret = CDROM_GetStatusCode(io); #elif defined(__APPLE__) SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data; int io = 0; union { dk_cd_read_mcn_t mcn; dk_cd_read_isrc_t isrc; } ioc; /* We need IOCDAudioControl for IOCTL_CDROM_CURRENT_POSITION */ if (fmt->Format == IOCTL_CDROM_CURRENT_POSITION) { FIXME("NIY\n"); return STATUS_NOT_SUPPORTED; } /* No IOCDAudioControl support; just set the audio status to none */ hdr->AudioStatus = AUDIO_STATUS_NO_STATUS; switch(fmt->Format) { case IOCTL_CDROM_MEDIA_CATALOG: if ((io = ioctl(fd, DKIOCCDREADMCN, &ioc.mcn)) == -1) break; memcpy(data->MediaCatalog.MediaCatalog, ioc.mcn.mcn, kCDMCNMaxLength); data->MediaCatalog.Mcval = 1; break; case IOCTL_CDROM_TRACK_ISRC: ioc.isrc.track = fmt->Track; if ((io = ioctl(fd, DKIOCCDREADISRC, &ioc.isrc)) == -1) break; memcpy(data->TrackIsrc.TrackIsrc, ioc.isrc.isrc, kCDISRCMaxLength); data->TrackIsrc.Tcval = 1; data->TrackIsrc.Track = ioc.isrc.track; } ret = CDROM_GetStatusCode(io); #endif return ret; } /****************************************************************** * CDROM_Verify * Implements: IOCTL_STORAGE_CHECK_VERIFY * IOCTL_CDROM_CHECK_VERIFY * IOCTL_DISK_CHECK_VERIFY * */ static NTSTATUS CDROM_Verify(int dev, int fd) { #if defined(linux) int ret; ret = ioctl(fd, CDROM_DRIVE_STATUS, NULL); if(ret == -1) { TRACE("ioctl CDROM_DRIVE_STATUS failed(%s)!\n", strerror(errno)); return CDROM_GetStatusCode(ret); } if(ret == CDS_DISC_OK) return STATUS_SUCCESS; else return STATUS_NO_MEDIA_IN_DEVICE; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) int ret; ret = ioctl(fd, CDIOCSTART, NULL); if(ret == 0) return STATUS_SUCCESS; else return STATUS_NO_MEDIA_IN_DEVICE; #elif defined(__APPLE__) /* At this point, we know that we have media, because in Mac OS X, the * device file is only created when media is present. */ return STATUS_SUCCESS; #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_PlayAudioMSF * * */ static NTSTATUS CDROM_PlayAudioMSF(int fd, const CDROM_PLAY_AUDIO_MSF* audio_msf) { NTSTATUS ret = STATUS_NOT_SUPPORTED; #ifdef linux struct cdrom_msf msf; int io; msf.cdmsf_min0 = audio_msf->StartingM; msf.cdmsf_sec0 = audio_msf->StartingS; msf.cdmsf_frame0 = audio_msf->StartingF; msf.cdmsf_min1 = audio_msf->EndingM; msf.cdmsf_sec1 = audio_msf->EndingS; msf.cdmsf_frame1 = audio_msf->EndingF; io = ioctl(fd, CDROMSTART); if (io == -1) { WARN("motor doesn't start !\n"); goto end; } io = ioctl(fd, CDROMPLAYMSF, &msf); if (io == -1) { WARN("device doesn't play !\n"); goto end; } TRACE("msf = %d:%d:%d %d:%d:%d\n", msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0, msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1); end: ret = CDROM_GetStatusCode(io); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) struct ioc_play_msf msf; int io; msf.start_m = audio_msf->StartingM; msf.start_s = audio_msf->StartingS; msf.start_f = audio_msf->StartingF; msf.end_m = audio_msf->EndingM; msf.end_s = audio_msf->EndingS; msf.end_f = audio_msf->EndingF; io = ioctl(fd, CDIOCSTART, NULL); if (io == -1) { WARN("motor doesn't start !\n"); goto end; } io = ioctl(fd, CDIOCPLAYMSF, &msf); if (io == -1) { WARN("device doesn't play !\n"); goto end; } TRACE("msf = %d:%d:%d %d:%d:%d\n", msf.start_m, msf.start_s, msf.start_f, msf.end_m, msf.end_s, msf.end_f); end: ret = CDROM_GetStatusCode(io); #endif return ret; } /****************************************************************** * CDROM_SeekAudioMSF * * */ static NTSTATUS CDROM_SeekAudioMSF(int dev, int fd, const CDROM_SEEK_AUDIO_MSF* audio_msf) { CDROM_TOC toc; int i, io, frame; SUB_Q_CURRENT_POSITION *cp; #if defined(linux) struct cdrom_msf0 msf; struct cdrom_subchnl sc; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) struct ioc_play_msf msf; struct ioc_read_subchannel read_sc; struct cd_sub_channel_info sc; int final_frame; #endif /* Use the information on the TOC to compute the new current * position, which is shadowed on the cache. [Portable]. */ frame = FRAME_OF_MSF(*audio_msf); if ((io = CDROM_ReadTOC(dev, fd, &toc)) != 0) return io; for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++) if (FRAME_OF_TOC(toc,i)>frame) break; if (i <= toc.FirstTrack || i > toc.LastTrack+1) return STATUS_INVALID_PARAMETER; i--; RtlEnterCriticalSection( &cache_section ); cp = &cdrom_cache[dev].CurrentPosition; cp->FormatCode = IOCTL_CDROM_CURRENT_POSITION; cp->Control = toc.TrackData[i-toc.FirstTrack].Control; cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr; cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber; cp->IndexNumber = 0; /* FIXME: where do they keep these? */ cp->AbsoluteAddress[0] = 0; cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1]; cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2]; cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3]; frame -= FRAME_OF_TOC(toc,i); cp->TrackRelativeAddress[0] = 0; MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame); RtlLeaveCriticalSection( &cache_section ); /* If playing, then issue a seek command, otherwise do nothing */ #ifdef linux sc.cdsc_format = CDROM_MSF; io = ioctl(fd, CDROMSUBCHNL, &sc); if (io == -1) { TRACE("opened or no_media (%s)!\n", strerror(errno)); CDROM_ClearCacheEntry(dev); return CDROM_GetStatusCode(io); } if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY) { msf.minute = audio_msf->M; msf.second = audio_msf->S; msf.frame = audio_msf->F; return CDROM_GetStatusCode(ioctl(fd, CDROMSEEK, &msf)); } return STATUS_SUCCESS; #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) read_sc.address_format = CD_MSF_FORMAT; read_sc.track = 0; read_sc.data_len = sizeof(sc); read_sc.data = ≻ read_sc.data_format = CD_CURRENT_POSITION; io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc); if (io == -1) { TRACE("opened or no_media (%s)!\n", strerror(errno)); CDROM_ClearCacheEntry(dev); return CDROM_GetStatusCode(io); } if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS) { msf.start_m = audio_msf->M; msf.start_s = audio_msf->S; msf.start_f = audio_msf->F; final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1; MSF_OF_FRAME(msf.end_m, final_frame); return CDROM_GetStatusCode(ioctl(fd, CDIOCPLAYMSF, &msf)); } return STATUS_SUCCESS; #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_PauseAudio * * */ static NTSTATUS CDROM_PauseAudio(int fd) { #if defined(linux) return CDROM_GetStatusCode(ioctl(fd, CDROMPAUSE)); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) return CDROM_GetStatusCode(ioctl(fd, CDIOCPAUSE, NULL)); #else FIXME(": not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_ResumeAudio * * */ static NTSTATUS CDROM_ResumeAudio(int fd) { #if defined(linux) return CDROM_GetStatusCode(ioctl(fd, CDROMRESUME)); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) return CDROM_GetStatusCode(ioctl(fd, CDIOCRESUME, NULL)); #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_StopAudio * * */ static NTSTATUS CDROM_StopAudio(int fd) { #if defined(linux) return CDROM_GetStatusCode(ioctl(fd, CDROMSTOP)); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) return CDROM_GetStatusCode(ioctl(fd, CDIOCSTOP, NULL)); #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_GetVolume * * */ static NTSTATUS CDROM_GetVolume(int fd, VOLUME_CONTROL* vc) { #if defined(linux) struct cdrom_volctrl volc; int io; io = ioctl(fd, CDROMVOLREAD, &volc); if (io != -1) { vc->PortVolume[0] = volc.channel0; vc->PortVolume[1] = volc.channel1; vc->PortVolume[2] = volc.channel2; vc->PortVolume[3] = volc.channel3; } return CDROM_GetStatusCode(io); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) struct ioc_vol volc; int io; io = ioctl(fd, CDIOCGETVOL, &volc); if (io != -1) { vc->PortVolume[0] = volc.vol[0]; vc->PortVolume[1] = volc.vol[1]; vc->PortVolume[2] = volc.vol[2]; vc->PortVolume[3] = volc.vol[3]; } return CDROM_GetStatusCode(io); #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_SetVolume * * */ static NTSTATUS CDROM_SetVolume(int fd, const VOLUME_CONTROL* vc) { #if defined(linux) struct cdrom_volctrl volc; volc.channel0 = vc->PortVolume[0]; volc.channel1 = vc->PortVolume[1]; volc.channel2 = vc->PortVolume[2]; volc.channel3 = vc->PortVolume[3]; return CDROM_GetStatusCode(ioctl(fd, CDROMVOLCTRL, &volc)); #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) struct ioc_vol volc; volc.vol[0] = vc->PortVolume[0]; volc.vol[1] = vc->PortVolume[1]; volc.vol[2] = vc->PortVolume[2]; volc.vol[3] = vc->PortVolume[3]; return CDROM_GetStatusCode(ioctl(fd, CDIOCSETVOL, &volc)); #else FIXME(": not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_RawRead * * Some features of this IOCTL are rather poorly documented and * not really intuitive either: * * 1. Although the DiskOffset parameter is meant to be a * byte offset into the disk, it is in fact the sector * number multiplied by 2048 regardless of the actual * sector size. * * 2. The least significant 11 bits of DiskOffset are ignored. * * 3. The TrackMode parameter has no effect on the sector * size. The entire raw sector (i.e. 2352 bytes of data) * is always returned. IMO the TrackMode is only used * to check the correct sector type. * */ static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz) { int ret = STATUS_NOT_SUPPORTED; int io = -1; #ifdef __APPLE__ dk_cd_read_t cdrd; #endif TRACE("RAW_READ_INFO: DiskOffset=%i,%i SectorCount=%i TrackMode=%i\n buffer=%p len=%i sz=%p\n", raw->DiskOffset.u.HighPart, raw->DiskOffset.u.LowPart, raw->SectorCount, raw->TrackMode, buffer, len, sz); if (len < raw->SectorCount * 2352) return STATUS_BUFFER_TOO_SMALL; #if defined(linux) if (raw->DiskOffset.u.HighPart & ~2047) { WARN("DiskOffset points to a sector >= 2**32\n"); return ret; } switch (raw->TrackMode) { case YellowMode2: case XAForm2: { DWORD lba = raw->DiskOffset.QuadPart >> 11; struct cdrom_msf* msf; PBYTE *bp; /* current buffer pointer */ DWORD i; if ((lba + raw->SectorCount) > ((1 << 8*sizeof(msf->cdmsf_min0)) * CD_SECS * CD_FRAMES - CD_MSF_OFFSET)) { WARN("DiskOffset not accessible with MSF\n"); return ret; } /* Linux reads only one sector at a time. * ioctl CDROMREADRAW takes struct cdrom_msf as an argument * on the contrary to what header comments state. */ lba += CD_MSF_OFFSET; for (i = 0, bp = buffer; i < raw->SectorCount; i++, lba++, bp += 2352) { msf = (struct cdrom_msf*)bp; msf->cdmsf_min0 = lba / CD_FRAMES / CD_SECS; msf->cdmsf_sec0 = lba / CD_FRAMES % CD_SECS; msf->cdmsf_frame0 = lba % CD_FRAMES; io = ioctl(fd, CDROMREADRAW, msf); if (io != 0) { *sz = 2352 * i; return CDROM_GetStatusCode(io); } } break; } case CDDA: { struct cdrom_read_audio cdra; cdra.addr.lba = raw->DiskOffset.QuadPart >> 11; TRACE("reading at %u\n", cdra.addr.lba); cdra.addr_format = CDROM_LBA; cdra.nframes = raw->SectorCount; cdra.buf = buffer; io = ioctl(fd, CDROMREADAUDIO, &cdra); break; } default: FIXME("NIY: %d\n", raw->TrackMode); return STATUS_INVALID_PARAMETER; } #elif defined(__APPLE__) /* Mac OS lets us read multiple parts of the sector at a time. * We can read all the sectors in at once, unlike Linux. */ memset(&cdrd, 0, sizeof(cdrd)); cdrd.offset = (raw->DiskOffset.QuadPart >> 11) * kCDSectorSizeWhole; cdrd.buffer = buffer; cdrd.bufferLength = raw->SectorCount * kCDSectorSizeWhole; switch (raw->TrackMode) { case YellowMode2: cdrd.sectorType = kCDSectorTypeMode2; cdrd.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader | kCDSectorAreaUser; break; case XAForm2: cdrd.sectorType = kCDSectorTypeMode2Form2; cdrd.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader | kCDSectorAreaSubHeader | kCDSectorAreaUser; break; case CDDA: cdrd.sectorType = kCDSectorTypeCDDA; cdrd.sectorArea = kCDSectorAreaUser; break; default: FIXME("NIY: %d\n", raw->TrackMode); return STATUS_INVALID_PARAMETER; } io = ioctl(fd, DKIOCCDREAD, &cdrd); if (io != 0) { *sz = cdrd.bufferLength; return CDROM_GetStatusCode(io); } #else switch (raw->TrackMode) { case YellowMode2: FIXME("YellowMode2: NIY\n"); return ret; case XAForm2: FIXME("XAForm2: NIY\n"); return ret; case CDDA: FIXME("CDDA: NIY\n"); return ret; default: FIXME("NIY: %d\n", raw->TrackMode); return STATUS_INVALID_PARAMETER; } #endif *sz = 2352 * raw->SectorCount; ret = CDROM_GetStatusCode(io); return ret; } /****************************************************************** * CDROM_ScsiPassThroughDirect * Implements IOCTL_SCSI_PASS_THROUGH_DIRECT * */ static NTSTATUS CDROM_ScsiPassThroughDirect(int fd, PSCSI_PASS_THROUGH_DIRECT pPacket) { int ret = STATUS_NOT_SUPPORTED; #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID sg_io_hdr_t cmd; int io; #elif defined HAVE_SCSIREQ_T_CMD scsireq_t cmd; int io; #elif defined __APPLE__ dk_scsi_command_t cmd; int io; #endif if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT)) return STATUS_BUFFER_TOO_SMALL; if (pPacket->CdbLength > 16) return STATUS_INVALID_PARAMETER; #ifdef SENSEBUFLEN if (pPacket->SenseInfoLength > SENSEBUFLEN) return STATUS_INVALID_PARAMETER; #elif defined HAVE_REQUEST_SENSE if (pPacket->SenseInfoLength > sizeof(struct request_sense)) return STATUS_INVALID_PARAMETER; #endif if (pPacket->DataTransferLength > 0 && !pPacket->DataBuffer) return STATUS_INVALID_PARAMETER; #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID RtlZeroMemory(&cmd, sizeof(cmd)); cmd.interface_id = 'S'; cmd.cmd_len = pPacket->CdbLength; cmd.mx_sb_len = pPacket->SenseInfoLength; cmd.dxfer_len = pPacket->DataTransferLength; cmd.dxferp = pPacket->DataBuffer; cmd.cmdp = pPacket->Cdb; cmd.sbp = (unsigned char*)pPacket + pPacket->SenseInfoOffset; cmd.timeout = pPacket->TimeOutValue*1000; switch (pPacket->DataIn) { case SCSI_IOCTL_DATA_IN: cmd.dxfer_direction = SG_DXFER_FROM_DEV; break; case SCSI_IOCTL_DATA_OUT: cmd.dxfer_direction = SG_DXFER_TO_DEV; break; case SCSI_IOCTL_DATA_UNSPECIFIED: cmd.dxfer_direction = SG_DXFER_NONE; break; default: return STATUS_INVALID_PARAMETER; } io = ioctl(fd, SG_IO, &cmd); pPacket->ScsiStatus = cmd.status; pPacket->DataTransferLength = cmd.resid; pPacket->SenseInfoLength = cmd.sb_len_wr; ret = CDROM_GetStatusCode(io); #elif defined HAVE_SCSIREQ_T_CMD memset(&cmd, 0, sizeof(cmd)); memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength); cmd.cmdlen = pPacket->CdbLength; cmd.databuf = pPacket->DataBuffer; cmd.datalen = pPacket->DataTransferLength; cmd.senselen = pPacket->SenseInfoLength; cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */ switch (pPacket->DataIn) { case SCSI_IOCTL_DATA_OUT: cmd.flags |= SCCMD_WRITE; break; case SCSI_IOCTL_DATA_IN: cmd.flags |= SCCMD_READ; break; case SCSI_IOCTL_DATA_UNSPECIFIED: cmd.flags = 0; break; default: return STATUS_INVALID_PARAMETER; } io = ioctl(fd, SCIOCCOMMAND, &cmd); switch (cmd.retsts) { case SCCMD_OK: break; case SCCMD_TIMEOUT: return STATUS_TIMEOUT; break; case SCCMD_BUSY: return STATUS_DEVICE_BUSY; break; case SCCMD_SENSE: break; case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL; break; } if (pPacket->SenseInfoLength != 0) { memcpy((char*)pPacket + pPacket->SenseInfoOffset, cmd.sense, pPacket->SenseInfoLength); } pPacket->ScsiStatus = cmd.status; ret = CDROM_GetStatusCode(io); #elif defined(__APPLE__) memset(&cmd, 0, sizeof(cmd)); memcpy(cmd.cdb, pPacket->Cdb, pPacket->CdbLength); cmd.cdbSize = pPacket->CdbLength; cmd.buffer = pPacket->DataBuffer; cmd.bufferSize = pPacket->DataTransferLength; cmd.sense = (char*)pPacket + pPacket->SenseInfoOffset; cmd.senseLen = pPacket->SenseInfoLength; cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */ switch (pPacket->DataIn) { case SCSI_IOCTL_DATA_OUT: cmd.direction = kSCSIDataTransfer_FromInitiatorToTarget; break; case SCSI_IOCTL_DATA_IN: cmd.direction = kSCSIDataTransfer_FromTargetToInitiator; break; case SCSI_IOCTL_DATA_UNSPECIFIED: cmd.direction = kSCSIDataTransfer_NoDataTransfer; break; default: return STATUS_INVALID_PARAMETER; } io = ioctl(fd, DKIOCSCSICOMMAND, &cmd); if (cmd.response == kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE) { /* Command failed */ switch (cmd.status) { case kSCSITaskStatus_TaskTimeoutOccurred: return STATUS_TIMEOUT; break; case kSCSITaskStatus_ProtocolTimeoutOccurred: return STATUS_IO_TIMEOUT; break; case kSCSITaskStatus_DeviceNotResponding: return STATUS_DEVICE_BUSY; break; case kSCSITaskStatus_DeviceNotPresent: return STATUS_NO_SUCH_DEVICE; break; case kSCSITaskStatus_DeliveryFailure: return STATUS_DEVICE_PROTOCOL_ERROR; break; case kSCSITaskStatus_No_Status: default: return STATUS_UNSUCCESSFUL; break; } } if (cmd.status != kSCSITaskStatus_No_Status) pPacket->ScsiStatus = cmd.status; ret = CDROM_GetStatusCode(io); #endif return ret; } /****************************************************************** * CDROM_ScsiPassThrough * Implements IOCTL_SCSI_PASS_THROUGH * */ static NTSTATUS CDROM_ScsiPassThrough(int fd, PSCSI_PASS_THROUGH pPacket) { int ret = STATUS_NOT_SUPPORTED; #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID sg_io_hdr_t cmd; int io; #elif defined HAVE_SCSIREQ_T_CMD scsireq_t cmd; int io; #elif defined __APPLE__ dk_scsi_command_t cmd; int io; #endif if (pPacket->Length < sizeof(SCSI_PASS_THROUGH)) return STATUS_BUFFER_TOO_SMALL; if (pPacket->CdbLength > 16) return STATUS_INVALID_PARAMETER; #ifdef SENSEBUFLEN if (pPacket->SenseInfoLength > SENSEBUFLEN) return STATUS_INVALID_PARAMETER; #elif defined HAVE_REQUEST_SENSE if (pPacket->SenseInfoLength > sizeof(struct request_sense)) return STATUS_INVALID_PARAMETER; #endif if (pPacket->DataTransferLength > 0 && pPacket->DataBufferOffset < sizeof(SCSI_PASS_THROUGH)) return STATUS_INVALID_PARAMETER; #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID RtlZeroMemory(&cmd, sizeof(cmd)); cmd.interface_id = 'S'; cmd.dxfer_len = pPacket->DataTransferLength; cmd.dxferp = (char*)pPacket + pPacket->DataBufferOffset; cmd.cmd_len = pPacket->CdbLength; cmd.cmdp = pPacket->Cdb; cmd.mx_sb_len = pPacket->SenseInfoLength; cmd.timeout = pPacket->TimeOutValue*1000; if(cmd.mx_sb_len > 0) cmd.sbp = (unsigned char*)pPacket + pPacket->SenseInfoOffset; switch (pPacket->DataIn) { case SCSI_IOCTL_DATA_IN: cmd.dxfer_direction = SG_DXFER_FROM_DEV; break; case SCSI_IOCTL_DATA_OUT: cmd.dxfer_direction = SG_DXFER_TO_DEV; break; case SCSI_IOCTL_DATA_UNSPECIFIED: cmd.dxfer_direction = SG_DXFER_NONE; break; default: return STATUS_INVALID_PARAMETER; } io = ioctl(fd, SG_IO, &cmd); pPacket->ScsiStatus = cmd.status; pPacket->DataTransferLength = cmd.resid; pPacket->SenseInfoLength = cmd.sb_len_wr; ret = CDROM_GetStatusCode(io); #elif defined HAVE_SCSIREQ_T_CMD memset(&cmd, 0, sizeof(cmd)); memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength); if ( pPacket->DataBufferOffset > 0x1000 ) { cmd.databuf = (void*)pPacket->DataBufferOffset; } else { cmd.databuf = (char*)pPacket + pPacket->DataBufferOffset; } cmd.cmdlen = pPacket->CdbLength; cmd.datalen = pPacket->DataTransferLength; cmd.senselen = pPacket->SenseInfoLength; cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */ switch (pPacket->DataIn) { case SCSI_IOCTL_DATA_OUT: cmd.flags |= SCCMD_WRITE; break; case SCSI_IOCTL_DATA_IN: cmd.flags |= SCCMD_READ; break; case SCSI_IOCTL_DATA_UNSPECIFIED: cmd.flags = 0; break; default: return STATUS_INVALID_PARAMETER; } io = ioctl(fd, SCIOCCOMMAND, &cmd); switch (cmd.retsts) { case SCCMD_OK: break; case SCCMD_TIMEOUT: return STATUS_TIMEOUT; break; case SCCMD_BUSY: return STATUS_DEVICE_BUSY; break; case SCCMD_SENSE: break; case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL; break; } if (pPacket->SenseInfoLength != 0) { memcpy((char*)pPacket + pPacket->SenseInfoOffset, cmd.sense, pPacket->SenseInfoLength); } pPacket->ScsiStatus = cmd.status; ret = CDROM_GetStatusCode(io); #elif defined(__APPLE__) memset(&cmd, 0, sizeof(cmd)); memcpy(cmd.cdb, pPacket->Cdb, pPacket->CdbLength); cmd.cdbSize = pPacket->CdbLength; cmd.buffer = (char*)pPacket + pPacket->DataBufferOffset; cmd.bufferSize = pPacket->DataTransferLength; cmd.sense = (char*)pPacket + pPacket->SenseInfoOffset; cmd.senseLen = pPacket->SenseInfoLength; cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */ switch (pPacket->DataIn) { case SCSI_IOCTL_DATA_OUT: cmd.direction = kSCSIDataTransfer_FromInitiatorToTarget; break; case SCSI_IOCTL_DATA_IN: cmd.direction = kSCSIDataTransfer_FromTargetToInitiator; break; case SCSI_IOCTL_DATA_UNSPECIFIED: cmd.direction = kSCSIDataTransfer_NoDataTransfer; break; default: return STATUS_INVALID_PARAMETER; } io = ioctl(fd, DKIOCSCSICOMMAND, &cmd); if (cmd.response == kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE) { /* Command failed */ switch (cmd.status) { case kSCSITaskStatus_TaskTimeoutOccurred: return STATUS_TIMEOUT; break; case kSCSITaskStatus_ProtocolTimeoutOccurred: return STATUS_IO_TIMEOUT; break; case kSCSITaskStatus_DeviceNotResponding: return STATUS_DEVICE_BUSY; break; case kSCSITaskStatus_DeviceNotPresent: return STATUS_NO_SUCH_DEVICE; break; case kSCSITaskStatus_DeliveryFailure: return STATUS_DEVICE_PROTOCOL_ERROR; break; case kSCSITaskStatus_No_Status: default: return STATUS_UNSUCCESSFUL; break; } } if (cmd.status != kSCSITaskStatus_No_Status) pPacket->ScsiStatus = cmd.status; ret = CDROM_GetStatusCode(io); #endif return ret; } /****************************************************************** * CDROM_ScsiGetCaps * * */ static NTSTATUS CDROM_ScsiGetCaps(int fd, PIO_SCSI_CAPABILITIES caps) { NTSTATUS ret = STATUS_NOT_IMPLEMENTED; #ifdef SG_SCATTER_SZ caps->Length = sizeof(*caps); caps->MaximumTransferLength = SG_SCATTER_SZ; /* FIXME */ caps->MaximumPhysicalPages = SG_SCATTER_SZ / getpagesize(); caps->SupportedAsynchronousEvents = TRUE; caps->AlignmentMask = getpagesize(); caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */ caps->AdapterScansDown = FALSE; /* FIXME ? */ caps->AdapterUsesPio = FALSE; /* FIXME ? */ ret = STATUS_SUCCESS; #elif defined __APPLE__ uint64_t bytesr, bytesw, align; int io = ioctl(fd, DKIOCGETMAXBYTECOUNTREAD, &bytesr); if (io != 0) return CDROM_GetStatusCode(io); io = ioctl(fd, DKIOCGETMAXBYTECOUNTWRITE, &bytesw); if (io != 0) return CDROM_GetStatusCode(io); io = ioctl(fd, DKIOCGETMINSEGMENTALIGNMENTBYTECOUNT, &align); if (io != 0) return CDROM_GetStatusCode(io); caps->Length = sizeof(*caps); caps->MaximumTransferLength = bytesr < bytesw ? bytesr : bytesw; caps->MaximumPhysicalPages = caps->MaximumTransferLength / getpagesize(); caps->SupportedAsynchronousEvents = TRUE; caps->AlignmentMask = align-1; caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */ caps->AdapterScansDown = FALSE; /* FIXME ? */ caps->AdapterUsesPio = FALSE; /* FIXME ? */ ret = STATUS_SUCCESS; #else FIXME("Unimplemented\n"); #endif return ret; } /****************************************************************** * CDROM_GetAddress * * implements IOCTL_SCSI_GET_ADDRESS */ static NTSTATUS CDROM_GetAddress(int fd, SCSI_ADDRESS* address) { UCHAR portnum, busid, targetid, lun; address->Length = sizeof(SCSI_ADDRESS); if ( ! CDROM_GetInterfaceInfo(fd, &portnum, &busid, &targetid, &lun)) return STATUS_NOT_SUPPORTED; address->PortNumber = portnum; /* primary=0 secondary=1 for ide */ address->PathId = busid; /* always 0 for ide */ address->TargetId = targetid; /* master=0 slave=1 for ide */ address->Lun = lun; return STATUS_SUCCESS; } /****************************************************************** * DVD_StartSession * * */ static NTSTATUS DVD_StartSession(int fd, const DVD_SESSION_ID *sid_in, PDVD_SESSION_ID sid_out) { #if defined(linux) NTSTATUS ret = STATUS_NOT_SUPPORTED; dvd_authinfo auth_info; memset( &auth_info, 0, sizeof( auth_info ) ); auth_info.type = DVD_LU_SEND_AGID; if (sid_in) auth_info.lsa.agid = *(const int*)sid_in; /* ?*/ TRACE("fd 0x%08x\n",fd); ret =CDROM_GetStatusCode(ioctl(fd, DVD_AUTH, &auth_info)); *sid_out = auth_info.lsa.agid; return ret; #elif defined(__APPLE__) NTSTATUS ret = STATUS_NOT_SUPPORTED; dk_dvd_report_key_t dvdrk; DVDAuthenticationGrantIDInfo agid_info; dvdrk.format = kDVDKeyFormatAGID_CSS; dvdrk.keyClass = kDVDKeyClassCSS_CPPM_CPRM; if(sid_in) dvdrk.grantID = *(uint8_t*)sid_in; /* ? */ dvdrk.bufferLength = sizeof(DVDAuthenticationGrantIDInfo); dvdrk.buffer = &agid_info; ret = CDROM_GetStatusCode(ioctl(fd, DKIOCDVDREPORTKEY, &dvdrk)); *sid_out = agid_info.grantID; return ret; #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * DVD_EndSession * * */ static NTSTATUS DVD_EndSession(int fd, const DVD_SESSION_ID *sid) { #if defined(linux) dvd_authinfo auth_info; memset( &auth_info, 0, sizeof( auth_info ) ); auth_info.type = DVD_INVALIDATE_AGID; auth_info.lsa.agid = *(const int*)sid; TRACE("\n"); return CDROM_GetStatusCode(ioctl(fd, DVD_AUTH, &auth_info)); #elif defined(__APPLE__) dk_dvd_send_key_t dvdsk; dvdsk.format = kDVDKeyFormatAGID_Invalidate; dvdsk.keyClass = kDVDKeyClassCSS_CPPM_CPRM; dvdsk.grantID = (uint8_t)*sid; return CDROM_GetStatusCode(ioctl(fd, DKIOCDVDSENDKEY, &dvdsk)); #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * DVD_SendKey * * */ static NTSTATUS DVD_SendKey(int fd, const DVD_COPY_PROTECT_KEY *key) { #if defined(linux) NTSTATUS ret = STATUS_NOT_SUPPORTED; dvd_authinfo auth_info; memset( &auth_info, 0, sizeof( auth_info ) ); switch (key->KeyType) { case DvdChallengeKey: auth_info.type = DVD_HOST_SEND_CHALLENGE; auth_info.hsc.agid = (int)key->SessionId; TRACE("DvdChallengeKey ioc 0x%x\n", DVD_AUTH ); memcpy( auth_info.hsc.chal, key->KeyData, DVD_CHALLENGE_SIZE ); ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info )); break; case DvdBusKey2: auth_info.type = DVD_HOST_SEND_KEY2; auth_info.hsk.agid = (int)key->SessionId; memcpy( auth_info.hsk.key, key->KeyData, DVD_KEY_SIZE ); TRACE("DvdBusKey2\n"); ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info )); break; default: FIXME("Unknown Keytype 0x%x\n",key->KeyType); } return ret; #elif defined(__APPLE__) dk_dvd_send_key_t dvdsk; union { DVDChallengeKeyInfo chal; DVDKey2Info key2; } key_desc; switch(key->KeyType) { case DvdChallengeKey: dvdsk.format = kDVDKeyFormatChallengeKey; dvdsk.bufferLength = sizeof(key_desc.chal); dvdsk.buffer = &key_desc.chal; OSWriteBigInt16(key_desc.chal.dataLength, 0, key->KeyLength); memcpy(key_desc.chal.challengeKeyValue, key->KeyData, key->KeyLength); break; case DvdBusKey2: dvdsk.format = kDVDKeyFormatKey2; dvdsk.bufferLength = sizeof(key_desc.key2); dvdsk.buffer = &key_desc.key2; OSWriteBigInt16(key_desc.key2.dataLength, 0, key->KeyLength); memcpy(key_desc.key2.key2Value, key->KeyData, key->KeyLength); break; case DvdInvalidateAGID: dvdsk.format = kDVDKeyFormatAGID_Invalidate; break; case DvdBusKey1: case DvdTitleKey: case DvdAsf: case DvdGetRpcKey: case DvdDiskKey: ERR("attempted to write read-only key type 0x%x\n", key->KeyType); return STATUS_NOT_SUPPORTED; case DvdSetRpcKey: FIXME("DvdSetRpcKey NIY\n"); return STATUS_NOT_SUPPORTED; default: FIXME("got unknown key type 0x%x\n", key->KeyType); return STATUS_NOT_SUPPORTED; } dvdsk.keyClass = kDVDKeyClassCSS_CPPM_CPRM; dvdsk.grantID = (uint8_t)key->SessionId; return CDROM_GetStatusCode(ioctl(fd, DKIOCDVDSENDKEY, &dvdsk)); #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * DVD_ReadKey * * */ static NTSTATUS DVD_ReadKey(int fd, PDVD_COPY_PROTECT_KEY key) { #if defined(linux) NTSTATUS ret = STATUS_NOT_SUPPORTED; dvd_struct dvd; dvd_authinfo auth_info; memset( &dvd, 0, sizeof( dvd_struct ) ); memset( &auth_info, 0, sizeof( auth_info ) ); switch (key->KeyType) { case DvdDiskKey: dvd.type = DVD_STRUCT_DISCKEY; dvd.disckey.agid = (int)key->SessionId; memset( dvd.disckey.value, 0, DVD_DISCKEY_SIZE ); TRACE("DvdDiskKey\n"); ret = CDROM_GetStatusCode(ioctl( fd, DVD_READ_STRUCT, &dvd )); if (ret == STATUS_SUCCESS) memcpy(key->KeyData,dvd.disckey.value,DVD_DISCKEY_SIZE); break; case DvdTitleKey: auth_info.type = DVD_LU_SEND_TITLE_KEY; auth_info.lstk.agid = (int)key->SessionId; auth_info.lstk.lba = (int)(key->Parameters.TitleOffset.QuadPart>>11); TRACE("DvdTitleKey session %d Quadpart 0x%08lx offset 0x%08x\n", (int)key->SessionId, (long)key->Parameters.TitleOffset.QuadPart, auth_info.lstk.lba); ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info )); if (ret == STATUS_SUCCESS) memcpy(key->KeyData, auth_info.lstk.title_key, DVD_KEY_SIZE ); break; case DvdChallengeKey: auth_info.type = DVD_LU_SEND_CHALLENGE; auth_info.lsc.agid = (int)key->SessionId; TRACE("DvdChallengeKey\n"); ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info )); if (ret == STATUS_SUCCESS) memcpy( key->KeyData, auth_info.lsc.chal, DVD_CHALLENGE_SIZE ); break; case DvdAsf: auth_info.type = DVD_LU_SEND_ASF; TRACE("DvdAsf\n"); auth_info.lsasf.asf=((PDVD_ASF)key->KeyData)->SuccessFlag; ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info )); ((PDVD_ASF)key->KeyData)->SuccessFlag = auth_info.lsasf.asf; break; case DvdBusKey1: auth_info.type = DVD_LU_SEND_KEY1; auth_info.lsk.agid = (int)key->SessionId; TRACE("DvdBusKey1\n"); ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info )); if (ret == STATUS_SUCCESS) memcpy( key->KeyData, auth_info.lsk.key, DVD_KEY_SIZE ); break; case DvdGetRpcKey: auth_info.type = DVD_LU_SEND_RPC_STATE; TRACE("DvdGetRpcKey\n"); ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info )); if (ret == STATUS_SUCCESS) { ((PDVD_RPC_KEY)key->KeyData)->TypeCode = auth_info.lrpcs.type; ((PDVD_RPC_KEY)key->KeyData)->RegionMask = auth_info.lrpcs.region_mask; ((PDVD_RPC_KEY)key->KeyData)->RpcScheme = auth_info.lrpcs.rpc_scheme; ((PDVD_RPC_KEY)key->KeyData)->UserResetsAvailable = auth_info.lrpcs.ucca; ((PDVD_RPC_KEY)key->KeyData)->ManufacturerResetsAvailable = auth_info.lrpcs.vra; } break; default: FIXME("Unknown keytype 0x%x\n",key->KeyType); } return ret; #elif defined(__APPLE__) union { dk_dvd_report_key_t key; dk_dvd_read_structure_t disk_key; } ioc; union { DVDDiscKeyInfo disk_key; DVDChallengeKeyInfo chal; DVDKey1Info key1; DVDTitleKeyInfo title; DVDAuthenticationSuccessFlagInfo asf; DVDRegionPlaybackControlInfo rpc; } desc; NTSTATUS ret = STATUS_NOT_SUPPORTED; switch(key->KeyType) { case DvdChallengeKey: ioc.key.format = kDVDKeyFormatChallengeKey; ioc.key.grantID = (uint8_t)key->SessionId; ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM; ioc.key.bufferLength = sizeof(desc.chal); ioc.key.buffer = &desc.chal; OSWriteBigInt16(desc.chal.dataLength, 0, key->KeyLength); break; case DvdBusKey1: ioc.key.format = kDVDKeyFormatKey1; ioc.key.grantID = (uint8_t)key->SessionId; ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM; ioc.key.bufferLength = sizeof(desc.key1); ioc.key.buffer = &desc.key1; OSWriteBigInt16(desc.key1.dataLength, 0, key->KeyLength); break; case DvdTitleKey: ioc.key.format = kDVDKeyFormatTitleKey; ioc.key.grantID = (uint8_t)key->SessionId; ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM; ioc.key.bufferLength = sizeof(desc.title); ioc.key.buffer = &desc.title; ioc.key.address = (uint32_t)(key->Parameters.TitleOffset.QuadPart>>11); OSWriteBigInt16(desc.title.dataLength, 0, key->KeyLength); break; case DvdAsf: ioc.key.format = kDVDKeyFormatASF; ioc.key.grantID = (uint8_t)key->SessionId; ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM; ioc.key.bufferLength = sizeof(desc.asf); ioc.key.buffer = &desc.asf; OSWriteBigInt16(desc.asf.dataLength, 0, key->KeyLength); break; case DvdGetRpcKey: ioc.key.format = kDVDKeyFormatRegionState; ioc.key.grantID = (uint8_t)key->SessionId; ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM; ioc.key.bufferLength = sizeof(desc.rpc); ioc.key.buffer = &desc.rpc; OSWriteBigInt16(desc.rpc.dataLength, 0, key->KeyLength); break; case DvdDiskKey: ioc.disk_key.format = kDVDStructureFormatDiscKeyInfo; ioc.disk_key.grantID = (uint8_t)key->SessionId; ioc.disk_key.bufferLength = sizeof(desc.disk_key); ioc.disk_key.buffer = &desc.disk_key; break; case DvdInvalidateAGID: ioc.key.format = kDVDKeyFormatAGID_Invalidate; ioc.key.grantID = (uint8_t)key->SessionId; ioc.key.keyClass = kDVDKeyClassCSS_CPPM_CPRM; break; case DvdBusKey2: case DvdSetRpcKey: ERR("attempted to read write-only key type 0x%x\n", key->KeyType); return STATUS_NOT_SUPPORTED; default: FIXME("got unknown key type 0x%x\n", key->KeyType); return STATUS_NOT_SUPPORTED; } ret = CDROM_GetStatusCode(ioctl(fd, (key->KeyType == DvdDiskKey ? DKIOCDVDREADSTRUCTURE : DKIOCDVDREPORTKEY), &ioc)); if (ret == STATUS_SUCCESS) { switch(key->KeyType) { case DvdChallengeKey: key->KeyLength = OSReadBigInt16(desc.chal.dataLength, 0); memcpy(key->KeyData, desc.chal.challengeKeyValue, key->KeyLength); break; case DvdBusKey1: key->KeyLength = OSReadBigInt16(desc.key1.dataLength, 0); memcpy(key->KeyData, desc.key1.key1Value, key->KeyLength); break; case DvdTitleKey: key->KeyLength = OSReadBigInt16(desc.title.dataLength, 0); memcpy(key->KeyData, desc.title.titleKeyValue, key->KeyLength); key->KeyFlags = 0; if (desc.title.CPM) { /*key->KeyFlags |= DVD_COPYRIGHTED;*/ if (desc.title.CP_SEC) key->KeyFlags |= DVD_SECTOR_PROTECTED; /*else key->KeyFlags |= DVD_SECTOR_NOT_PROTECTED;*/ #if 0 switch (desc.title.CGMS) { case 0: key->KeyFlags |= DVD_CGMS_COPY_PERMITTED; break; case 2: key->KeyFlags |= DVD_CGMS_COPY_ONCE; break; case 3: key->KeyFlags |= DVD_CGMS_NO_COPY; break; } #endif } /*else key->KeyFlags |= DVD_NOT_COPYRIGHTED;*/ break; case DvdAsf: key->KeyLength = OSReadBigInt16(desc.title.dataLength, 0); ((PDVD_ASF)key->KeyData)->SuccessFlag = desc.asf.successFlag; break; case DvdGetRpcKey: key->KeyLength = OSReadBigInt16(desc.rpc.dataLength, 0); ((PDVD_RPC_KEY)key->KeyData)->UserResetsAvailable = desc.rpc.numberUserResets; ((PDVD_RPC_KEY)key->KeyData)->ManufacturerResetsAvailable = desc.rpc.numberVendorResets; ((PDVD_RPC_KEY)key->KeyData)->TypeCode = desc.rpc.typeCode; ((PDVD_RPC_KEY)key->KeyData)->RegionMask = desc.rpc.driveRegion; ((PDVD_RPC_KEY)key->KeyData)->RpcScheme = desc.rpc.rpcScheme; case DvdDiskKey: key->KeyLength = OSReadBigInt16(desc.disk_key.dataLength, 0); memcpy(key->KeyData, desc.disk_key.discKeyStructures, key->KeyLength); break; case DvdBusKey2: case DvdSetRpcKey: case DvdInvalidateAGID: default: /* Silence warning */ ; } } return ret; #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * DVD_GetRegion * * This IOCTL combines information from both IOCTL_DVD_READ_KEY * with key type DvdGetRpcKey and IOCTL_DVD_READ_STRUCTURE with * structure type DvdCopyrightInformation into one structure. */ static NTSTATUS DVD_GetRegion(int fd, PDVD_REGION region) { #if defined(linux) NTSTATUS ret = STATUS_NOT_SUPPORTED; dvd_struct dvd; dvd_authinfo auth_info; dvd.type = DVD_STRUCT_COPYRIGHT; dvd.copyright.layer_num = 0; auth_info.type = DVD_LU_SEND_RPC_STATE; ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info )); if (ret == STATUS_SUCCESS) { ret = CDROM_GetStatusCode(ioctl( fd, DVD_READ_STRUCT, &dvd )); if (ret == STATUS_SUCCESS) { region->CopySystem = dvd.copyright.cpst; region->RegionData = dvd.copyright.rmi; region->SystemRegion = auth_info.lrpcs.region_mask; region->ResetCount = auth_info.lrpcs.ucca; } } return ret; #elif defined(__APPLE__) dk_dvd_report_key_t key; dk_dvd_read_structure_t dvd; DVDRegionPlaybackControlInfo rpc; DVDCopyrightInfo copy; NTSTATUS ret = STATUS_NOT_SUPPORTED; key.format = kDVDKeyFormatRegionState; key.keyClass = kDVDKeyClassCSS_CPPM_CPRM; key.bufferLength = sizeof(rpc); key.buffer = &rpc; dvd.format = kDVDStructureFormatCopyrightInfo; dvd.bufferLength = sizeof(copy); dvd.buffer = © ret = CDROM_GetStatusCode(ioctl( fd, DKIOCDVDREPORTKEY, &key )); if (ret == STATUS_SUCCESS) { ret = CDROM_GetStatusCode(ioctl( fd, DKIOCDVDREADSTRUCTURE, &dvd )); if (ret == STATUS_SUCCESS) { region->CopySystem = copy.copyrightProtectionSystemType; region->RegionData = copy.regionMask; region->SystemRegion = rpc.driveRegion; region->ResetCount = rpc.numberUserResets; } } return ret; #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * DVD_ReadStructure * * */ static NTSTATUS DVD_ReadStructure(int dev, const DVD_READ_STRUCTURE *structure, PDVD_LAYER_DESCRIPTOR layer) { #ifdef DVD_READ_STRUCT /* dvd_struct is not defined consistently across platforms */ union { struct dvd_physical physical; struct dvd_copyright copyright; struct dvd_disckey disckey; struct dvd_bca bca; struct dvd_manufact manufact; } s; if (structure->BlockByteOffset.u.HighPart || structure->BlockByteOffset.u.LowPart) FIXME(": BlockByteOffset is not handled\n"); switch (structure->Format) { case DvdPhysicalDescriptor: s.physical.type = DVD_STRUCT_PHYSICAL; s.physical.layer_num = structure->LayerNumber; break; case DvdCopyrightDescriptor: s.copyright.type = DVD_STRUCT_COPYRIGHT; s.copyright.layer_num = structure->LayerNumber; break; case DvdDiskKeyDescriptor: s.disckey.type = DVD_STRUCT_DISCKEY; s.disckey.agid = structure->SessionId; break; case DvdBCADescriptor: s.bca.type = DVD_STRUCT_BCA; break; case DvdManufacturerDescriptor: s.manufact.type = DVD_STRUCT_MANUFACT; s.manufact.layer_num = structure->LayerNumber; break; case DvdMaxDescriptor: /* This is not a real request, no matter what MSDN says! */ default: return STATUS_INVALID_PARAMETER; } if (ioctl(dev, DVD_READ_STRUCT, &s) < 0) return STATUS_INVALID_PARAMETER; switch (structure->Format) { case DvdPhysicalDescriptor: { internal_dvd_layer_descriptor *p = (internal_dvd_layer_descriptor *) layer; struct dvd_layer *l = &s.physical.layer[s.physical.layer_num]; p->DataLength[0] = 2; p->DataLength[1] = 8; p->Reserved0[0] = 0; p->Reserved0[1] = 0; p->BookVersion = l->book_version; p->BookType = l->book_type; p->MinimumRate = l->min_rate; p->DiskSize = l->disc_size; p->LayerType = l->layer_type; p->TrackPath = l->track_path; p->NumberOfLayers = l->nlayers; p->Reserved1 = 0; p->TrackDensity = l->track_density; p->LinearDensity = l->linear_density; p->StartingDataSector = l->start_sector; p->EndDataSector = l->end_sector; p->EndLayerZeroSector = l->end_sector_l0; p->Reserved5 = 0; p->BCAFlag = l->bca; p->Reserved6 = 0; } break; case DvdCopyrightDescriptor: { PDVD_COPYRIGHT_DESCRIPTOR p = (PDVD_COPYRIGHT_DESCRIPTOR) layer; p->CopyrightProtectionType = s.copyright.cpst; p->RegionManagementInformation = s.copyright.rmi; p->Reserved = 0; } break; case DvdDiskKeyDescriptor: { PDVD_DISK_KEY_DESCRIPTOR p = (PDVD_DISK_KEY_DESCRIPTOR) layer; memcpy(p->DiskKeyData, s.disckey.value, 2048); } break; case DvdBCADescriptor: { PDVD_BCA_DESCRIPTOR p = (PDVD_BCA_DESCRIPTOR) layer; memcpy(p->BCAInformation, s.bca.value, s.bca.len); } break; case DvdManufacturerDescriptor: { PDVD_MANUFACTURER_DESCRIPTOR p = (PDVD_MANUFACTURER_DESCRIPTOR) layer; memcpy(p->ManufacturingInformation, s.manufact.value, 2048); } break; case DvdMaxDescriptor: /* Suppress warning */ break; } #elif defined(__APPLE__) NTSTATUS ret = STATUS_NOT_SUPPORTED; dk_dvd_read_structure_t dvdrs; union { DVDPhysicalFormatInfo phys; DVDCopyrightInfo copy; DVDDiscKeyInfo disk_key; DVDManufacturingInfo manf; } desc; union { PDVD_LAYER_DESCRIPTOR layer; internal_dvd_layer_descriptor *xlayer; PDVD_COPYRIGHT_DESCRIPTOR copy; PDVD_DISK_KEY_DESCRIPTOR disk_key; PDVD_MANUFACTURER_DESCRIPTOR manf; } nt_desc; nt_desc.layer = layer; dvdrs.address = (uint32_t)(structure->BlockByteOffset.QuadPart>>11); dvdrs.grantID = (uint8_t)structure->SessionId; dvdrs.layer = structure->LayerNumber; switch(structure->Format) { case DvdPhysicalDescriptor: dvdrs.format = kDVDStructureFormatPhysicalFormatInfo; dvdrs.bufferLength = sizeof(desc.phys); dvdrs.buffer = &desc.phys; break; case DvdCopyrightDescriptor: dvdrs.format = kDVDStructureFormatCopyrightInfo; dvdrs.bufferLength = sizeof(desc.copy); dvdrs.buffer = &desc.copy; break; case DvdDiskKeyDescriptor: dvdrs.format = kDVDStructureFormatDiscKeyInfo; dvdrs.bufferLength = sizeof(desc.disk_key); dvdrs.buffer = &desc.disk_key; break; case DvdBCADescriptor: FIXME("DvdBCADescriptor NIY\n"); return STATUS_NOT_SUPPORTED; case DvdManufacturerDescriptor: dvdrs.format = kDVDStructureFormatManufacturingInfo; dvdrs.bufferLength = sizeof(desc.manf); dvdrs.buffer = &desc.manf; break; case DvdMaxDescriptor: default: FIXME("got unknown structure type 0x%x\n", structure->Format); return STATUS_NOT_SUPPORTED; } ret = CDROM_GetStatusCode(ioctl(dev, DKIOCDVDREADSTRUCTURE, &dvdrs)); if(ret == STATUS_SUCCESS) { switch(structure->Format) { case DvdPhysicalDescriptor: nt_desc.xlayer->DataLength[0] = 2; nt_desc.xlayer->DataLength[1] = 8; nt_desc.xlayer->Reserved0[0] = 0; nt_desc.xlayer->Reserved0[1] = 0; nt_desc.xlayer->BookVersion = desc.phys.partVersion; nt_desc.xlayer->BookType = desc.phys.bookType; nt_desc.xlayer->MinimumRate = desc.phys.minimumRate; nt_desc.xlayer->DiskSize = desc.phys.discSize; nt_desc.xlayer->LayerType = desc.phys.layerType; nt_desc.xlayer->TrackPath = desc.phys.trackPath; nt_desc.xlayer->NumberOfLayers = desc.phys.numberOfLayers; nt_desc.xlayer->Reserved1 = 0; nt_desc.xlayer->TrackDensity = desc.phys.trackDensity; nt_desc.xlayer->LinearDensity = desc.phys.linearDensity; nt_desc.xlayer->BCAFlag = desc.phys.bcaFlag; nt_desc.xlayer->StartingDataSector = OSReadBigInt32(&desc.phys.zero1, 0); nt_desc.xlayer->EndDataSector = OSReadBigInt32(&desc.phys.zero2, 0); nt_desc.xlayer->EndLayerZeroSector = OSReadBigInt32(&desc.phys.zero3, 0); nt_desc.xlayer->Reserved5 = 0; nt_desc.xlayer->Reserved6 = 0; break; case DvdCopyrightDescriptor: nt_desc.copy->CopyrightProtectionType = desc.copy.copyrightProtectionSystemType; nt_desc.copy->RegionManagementInformation = desc.copy.regionMask; nt_desc.copy->Reserved = 0; break; case DvdDiskKeyDescriptor: memcpy( nt_desc.disk_key->DiskKeyData, desc.disk_key.discKeyStructures, 2048); break; case DvdManufacturerDescriptor: memcpy( nt_desc.manf->ManufacturingInformation, desc.manf.discManufacturingInfo, 2048); break; case DvdBCADescriptor: case DvdMaxDescriptor: default: /* Silence warning */ break; } } return ret; #else FIXME("\n"); #endif return STATUS_SUCCESS; } /****************************************************************** * GetInquiryData * Implements the IOCTL_GET_INQUIRY_DATA ioctl. * Returns Inquiry data for all devices on the specified scsi bus * Returns STATUS_BUFFER_TOO_SMALL if the output buffer is too small, * STATUS_INVALID_DEVICE_REQUEST if the given handle isn't to a SCSI device, * or STATUS_NOT_SUPPORTED if the OS driver is too old */ static NTSTATUS GetInquiryData(int fd, PSCSI_ADAPTER_BUS_INFO BufferOut, DWORD OutBufferSize) { #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID PSCSI_INQUIRY_DATA pInquiryData = NULL; UCHAR sense_buffer[32]; int iochk, version; sg_io_hdr_t iocmd; UCHAR inquiry[INQ_CMD_LEN] = {INQUIRY, 0, 0, 0, INQ_REPLY_LEN, 0}; /* Check we have a SCSI device and a supported driver */ if(ioctl(fd, SG_GET_VERSION_NUM, &version) != 0) { WARN("IOCTL_SCSI_GET_INQUIRY_DATA sg driver is not loaded\n"); return STATUS_INVALID_DEVICE_REQUEST; } if(version < 30000 ) return STATUS_NOT_SUPPORTED; /* FIXME: Enumerate devices on the bus */ BufferOut->NumberOfBuses = 1; BufferOut->BusData[0].NumberOfLogicalUnits = 1; BufferOut->BusData[0].InquiryDataOffset = sizeof(SCSI_ADAPTER_BUS_INFO); pInquiryData = (PSCSI_INQUIRY_DATA)(BufferOut + 1); RtlZeroMemory(&iocmd, sizeof(iocmd)); iocmd.interface_id = 'S'; iocmd.cmd_len = sizeof(inquiry); iocmd.mx_sb_len = sizeof(sense_buffer); iocmd.dxfer_direction = SG_DXFER_FROM_DEV; iocmd.dxfer_len = INQ_REPLY_LEN; iocmd.dxferp = pInquiryData->InquiryData; iocmd.cmdp = inquiry; iocmd.sbp = sense_buffer; iocmd.timeout = 1000; iochk = ioctl(fd, SG_IO, &iocmd); if(iochk != 0) WARN("ioctl SG_IO returned %d, error (%s)\n", iochk, strerror(errno)); CDROM_GetInterfaceInfo(fd, &BufferOut->BusData[0].InitiatorBusId, &pInquiryData->PathId, &pInquiryData->TargetId, &pInquiryData->Lun); pInquiryData->DeviceClaimed = TRUE; pInquiryData->InquiryDataLength = INQ_REPLY_LEN; pInquiryData->NextInquiryDataOffset = 0; return STATUS_SUCCESS; #else FIXME("not supported on this O/S\n"); return STATUS_NOT_SUPPORTED; #endif } /****************************************************************** * CDROM_DeviceIoControl * * */ NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice, HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine, PVOID UserApcContext, PIO_STATUS_BLOCK piosb, ULONG dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize) { DWORD sz = 0; NTSTATUS status = STATUS_SUCCESS; int fd, needs_close, dev; TRACE("%p %s %p %d %p %d %p\n", hDevice, iocodex(dwIoControlCode), lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, piosb); piosb->Information = 0; if ((status = server_get_unix_fd( hDevice, 0, &fd, &needs_close, NULL, NULL ))) { if (status == STATUS_BAD_DEVICE_TYPE) return status; /* no associated fd */ goto error; } if ((status = CDROM_Open(fd, &dev))) { if (needs_close) close( fd ); goto error; } #ifdef __APPLE__ { char name[100]; /* This is ugly as hell, but Mac OS is unable to do anything from the * partition fd, it wants an fd for the whole device, and it sometimes * also requires the device fd to be closed first, so we have to close * the handle that the caller gave us. * Also for some reason it wants the fd to be closed before we even * open the parent if we're trying to eject the disk. */ if ((status = get_parent_device( fd, name, sizeof(name) ))) goto error; if (dwIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) NtClose( hDevice ); if (needs_close) close( fd ); TRACE("opening parent %s\n", name ); if ((fd = open( name, O_RDONLY )) == -1) { status = FILE_GetNtStatus(); goto error; } needs_close = 1; } #endif switch (dwIoControlCode) { case IOCTL_STORAGE_CHECK_VERIFY: case IOCTL_CDROM_CHECK_VERIFY: case IOCTL_DISK_CHECK_VERIFY: sz = 0; CDROM_ClearCacheEntry(dev); if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else status = CDROM_Verify(dev, fd); break; /* EPP case IOCTL_STORAGE_CHECK_VERIFY2: */ /* EPP case IOCTL_STORAGE_FIND_NEW_DEVICES: */ /* EPP case IOCTL_CDROM_FIND_NEW_DEVICES: */ case IOCTL_STORAGE_LOAD_MEDIA: case IOCTL_CDROM_LOAD_MEDIA: sz = 0; CDROM_ClearCacheEntry(dev); if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else status = CDROM_SetTray(fd, FALSE); break; case IOCTL_STORAGE_EJECT_MEDIA: sz = 0; CDROM_ClearCacheEntry(dev); if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else status = CDROM_SetTray(fd, TRUE); break; case IOCTL_CDROM_MEDIA_REMOVAL: case IOCTL_DISK_MEDIA_REMOVAL: case IOCTL_STORAGE_MEDIA_REMOVAL: case IOCTL_STORAGE_EJECTION_CONTROL: /* FIXME the last ioctl:s is not the same as the two others... * lockcount/owner should be handled */ sz = 0; CDROM_ClearCacheEntry(dev); if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_ControlEjection(fd, lpInBuffer); break; case IOCTL_STORAGE_GET_MEDIA_TYPES: case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: sz = sizeof(GET_MEDIA_TYPES); if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_GetMediaType(dev, lpOutBuffer); break; case IOCTL_STORAGE_GET_DEVICE_NUMBER: sz = sizeof(STORAGE_DEVICE_NUMBER); if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_GetDeviceNumber(dev, lpOutBuffer); break; case IOCTL_STORAGE_RESET_DEVICE: sz = 0; CDROM_ClearCacheEntry(dev); if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else status = CDROM_ResetAudio(fd); break; case IOCTL_CDROM_GET_CONTROL: sz = sizeof(CDROM_AUDIO_CONTROL); if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_GetControl(dev, fd, lpOutBuffer); break; case IOCTL_CDROM_GET_DRIVE_GEOMETRY: sz = sizeof(DISK_GEOMETRY); if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_GetDriveGeometry(dev, fd, lpOutBuffer); break; case IOCTL_CDROM_DISK_TYPE: sz = sizeof(CDROM_DISK_DATA); /* CDROM_ClearCacheEntry(dev); */ if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_GetDiskData(dev, fd, lpOutBuffer); break; /* EPP case IOCTL_CDROM_GET_LAST_SESSION: */ case IOCTL_CDROM_READ_Q_CHANNEL: sz = sizeof(SUB_Q_CHANNEL_DATA); if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT)) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_ReadQChannel(dev, fd, lpInBuffer, lpOutBuffer); break; case IOCTL_CDROM_READ_TOC: sz = sizeof(CDROM_TOC); if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_ReadTOC(dev, fd, lpOutBuffer); break; /* EPP case IOCTL_CDROM_READ_TOC_EX: */ case IOCTL_CDROM_PAUSE_AUDIO: sz = 0; if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else status = CDROM_PauseAudio(fd); break; case IOCTL_CDROM_PLAY_AUDIO_MSF: sz = 0; if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_PlayAudioMSF(fd, lpInBuffer); break; case IOCTL_CDROM_RESUME_AUDIO: sz = 0; if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else status = CDROM_ResumeAudio(fd); break; case IOCTL_CDROM_SEEK_AUDIO_MSF: sz = 0; if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_SeekAudioMSF(dev, fd, lpInBuffer); break; case IOCTL_CDROM_STOP_AUDIO: sz = 0; CDROM_ClearCacheEntry(dev); /* Maybe intention is to change media */ if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER; else status = CDROM_StopAudio(fd); break; case IOCTL_CDROM_GET_VOLUME: sz = sizeof(VOLUME_CONTROL); if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_GetVolume(fd, lpOutBuffer); break; case IOCTL_CDROM_SET_VOLUME: sz = 0; CDROM_ClearCacheEntry(dev); if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL) status = STATUS_INVALID_PARAMETER; else status = CDROM_SetVolume(fd, lpInBuffer); break; case IOCTL_CDROM_RAW_READ: sz = 0; if (nInBufferSize < sizeof(RAW_READ_INFO)) status = STATUS_INVALID_PARAMETER; else if (lpOutBuffer == NULL) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_RawRead(fd, lpInBuffer, lpOutBuffer, nOutBufferSize, &sz); break; case IOCTL_SCSI_GET_ADDRESS: sz = sizeof(SCSI_ADDRESS); if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_GetAddress(fd, lpOutBuffer); break; case IOCTL_SCSI_PASS_THROUGH_DIRECT: sz = sizeof(SCSI_PASS_THROUGH_DIRECT); if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_ScsiPassThroughDirect(fd, lpOutBuffer); break; case IOCTL_SCSI_PASS_THROUGH: sz = sizeof(SCSI_PASS_THROUGH); if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_ScsiPassThrough(fd, lpOutBuffer); break; case IOCTL_SCSI_GET_CAPABILITIES: sz = sizeof(IO_SCSI_CAPABILITIES); if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sizeof(IO_SCSI_CAPABILITIES)) status = STATUS_BUFFER_TOO_SMALL; else status = CDROM_ScsiGetCaps(fd, lpOutBuffer); break; case IOCTL_DVD_START_SESSION: sz = sizeof(DVD_SESSION_ID); if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else { TRACE("before in 0x%08x out 0x%08x\n",(lpInBuffer)?*(PDVD_SESSION_ID)lpInBuffer:0, *(PDVD_SESSION_ID)lpOutBuffer); status = DVD_StartSession(fd, lpInBuffer, lpOutBuffer); TRACE("before in 0x%08x out 0x%08x\n",(lpInBuffer)?*(PDVD_SESSION_ID)lpInBuffer:0, *(PDVD_SESSION_ID)lpOutBuffer); } break; case IOCTL_DVD_END_SESSION: sz = sizeof(DVD_SESSION_ID); if ((lpInBuffer == NULL) || (nInBufferSize < sz))status = STATUS_INVALID_PARAMETER; else status = DVD_EndSession(fd, lpInBuffer); break; case IOCTL_DVD_SEND_KEY: sz = 0; if (!lpInBuffer || (((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength != nInBufferSize)) status = STATUS_INVALID_PARAMETER; else { TRACE("doing DVD_SendKey\n"); status = DVD_SendKey(fd, lpInBuffer); } break; case IOCTL_DVD_READ_KEY: if (!lpInBuffer || (((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength != nInBufferSize)) status = STATUS_INVALID_PARAMETER; else if (lpInBuffer !=lpOutBuffer) status = STATUS_BUFFER_TOO_SMALL; else { TRACE("doing DVD_READ_KEY\n"); sz = ((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength; status = DVD_ReadKey(fd, lpInBuffer); } break; case IOCTL_DVD_GET_REGION: sz = sizeof(DVD_REGION); if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; TRACE("doing DVD_Get_REGION\n"); status = DVD_GetRegion(fd, lpOutBuffer); break; case IOCTL_DVD_READ_STRUCTURE: sz = sizeof(DVD_LAYER_DESCRIPTOR); if (lpInBuffer == NULL || nInBufferSize != sizeof(DVD_READ_STRUCTURE)) status = STATUS_INVALID_PARAMETER; else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL; else { TRACE("doing DVD_READ_STRUCTURE\n"); status = DVD_ReadStructure(fd, lpInBuffer, lpOutBuffer); } break; case IOCTL_SCSI_GET_INQUIRY_DATA: sz = INQ_REPLY_LEN; status = GetInquiryData(fd, lpOutBuffer, nOutBufferSize); break; default: if (needs_close) close( fd ); return STATUS_NOT_SUPPORTED; } if (needs_close) close( fd ); error: piosb->u.Status = status; piosb->Information = sz; if (hEvent) NtSetEvent(hEvent, NULL); return status; }