1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /* Main file for CD-ROM support
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1999, 2001, 2003 Eric Pouech
6 * Copyright 2000 Andreas Mohr
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "wine/port.h"
38 #include <sys/types.h>
40 #ifdef HAVE_SYS_IOCTL_H
41 #include <sys/ioctl.h>
46 #ifdef HAVE_SCSI_SCSI_H
47 # include <scsi/scsi.h>
48 # undef REASSIGN_BLOCKS /* avoid conflict with winioctl.h */
50 #ifdef HAVE_SCSI_SCSI_IOCTL_H
51 # include <scsi/scsi_ioctl.h>
53 #ifdef HAVE_LINUX_MAJOR_H
54 # include <linux/major.h>
56 #ifdef HAVE_LINUX_HDREG_H
57 # include <linux/hdreg.h>
59 #ifdef HAVE_LINUX_PARAM_H
60 # include <linux/param.h>
62 #ifdef HAVE_LINUX_CDROM_H
63 # include <linux/cdrom.h>
65 #ifdef HAVE_LINUX_UCDROM_H
66 # include <linux/ucdrom.h>
68 #ifdef HAVE_SYS_CDIO_H
69 # include <sys/cdio.h>
71 #ifdef HAVE_SYS_SCSIIO_H
72 # include <sys/scsiio.h>
75 #define NONAMELESSUNION
76 #define NONAMELESSSTRUCT
86 #include "ntdll_misc.h"
87 #include "wine/server.h"
88 #include "wine/library.h"
89 #include "wine/debug.h"
91 /* Non-Linux systems do not have linux/cdrom.h and the like, and thus
92 lack the following constants. */
95 #define CD_SECS 60 /* seconds per minute */
98 #define CD_FRAMES 75 /* frames per second */
101 static const struct iocodexs
106 {IOCTL_CDROM_UNLOAD_DRIVER, "IOCTL_CDROM_UNLOAD_DRIVER"},
107 {IOCTL_CDROM_READ_TOC, "IOCTL_CDROM_READ_TOC"},
108 {IOCTL_CDROM_GET_CONTROL, "IOCTL_CDROM_GET_CONTROL"},
109 {IOCTL_CDROM_PLAY_AUDIO_MSF, "IOCTL_CDROM_PLAY_AUDIO_MSF"},
110 {IOCTL_CDROM_SEEK_AUDIO_MSF, "IOCTL_CDROM_SEEK_AUDIO_MSF"},
111 {IOCTL_CDROM_STOP_AUDIO, "IOCTL_CDROM_STOP_AUDIO"},
112 {IOCTL_CDROM_PAUSE_AUDIO, "IOCTL_CDROM_PAUSE_AUDIO"},
113 {IOCTL_CDROM_RESUME_AUDIO, "IOCTL_CDROM_RESUME_AUDIO"},
114 {IOCTL_CDROM_GET_VOLUME, "IOCTL_CDROM_GET_VOLUME"},
115 {IOCTL_CDROM_SET_VOLUME, "IOCTL_CDROM_SET_VOLUME"},
116 {IOCTL_CDROM_READ_Q_CHANNEL, "IOCTL_CDROM_READ_Q_CHANNEL"},
117 {IOCTL_CDROM_GET_LAST_SESSION, "IOCTL_CDROM_GET_LAST_SESSION"},
118 {IOCTL_CDROM_RAW_READ, "IOCTL_CDROM_RAW_READ"},
119 {IOCTL_CDROM_DISK_TYPE, "IOCTL_CDROM_DISK_TYPE"},
120 {IOCTL_CDROM_GET_DRIVE_GEOMETRY, "IOCTL_CDROM_GET_DRIVE_GEOMETRY"},
121 {IOCTL_CDROM_CHECK_VERIFY, "IOCTL_CDROM_CHECK_VERIFY"},
122 {IOCTL_CDROM_MEDIA_REMOVAL, "IOCTL_CDROM_MEDIA_REMOVAL"},
123 {IOCTL_CDROM_EJECT_MEDIA, "IOCTL_CDROM_EJECT_MEDIA"},
124 {IOCTL_CDROM_LOAD_MEDIA, "IOCTL_CDROM_LOAD_MEDIA"},
125 {IOCTL_CDROM_RESERVE, "IOCTL_CDROM_RESERVE"},
126 {IOCTL_CDROM_RELEASE, "IOCTL_CDROM_RELEASE"},
127 {IOCTL_CDROM_FIND_NEW_DEVICES, "IOCTL_CDROM_FIND_NEW_DEVICES"}
129 static const char *iocodex(DWORD code)
132 static char buffer[25];
133 for(i=0; i<sizeof(iocodextable)/sizeof(struct iocodexs); i++)
134 if (code==iocodextable[i].code)
135 return iocodextable[i].codex;
136 sprintf(buffer, "IOCTL_CODE_%x", (int)code);
140 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
142 #define FRAME_OF_ADDR(a) (((int)(a)[1] * CD_SECS + (a)[2]) * CD_FRAMES + (a)[3])
143 #define FRAME_OF_MSF(a) (((int)(a).M * CD_SECS + (a).S) * CD_FRAMES + (a).F)
144 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
145 #define MSF_OF_FRAME(m,fr) {int f=(fr); ((UCHAR *)&(m))[2]=f%CD_FRAMES;f/=CD_FRAMES;((UCHAR *)&(m))[1]=f%CD_SECS;((UCHAR *)&(m))[0]=f/CD_SECS;}
147 static NTSTATUS CDROM_ReadTOC(int, int, CDROM_TOC*);
148 static NTSTATUS CDROM_GetStatusCode(int);
154 # define IDE6_MAJOR 88
157 # define IDE7_MAJOR 89
160 # ifdef CDROM_SEND_PACKET
161 /* structure for CDROM_PACKET_COMMAND ioctl */
162 /* not all Linux versions have all the fields, so we define the
163 * structure ourselves to make sure */
164 struct linux_cdrom_generic_command
166 unsigned char cmd[CDROM_PACKET_SIZE];
167 unsigned char *buffer;
170 struct request_sense *sense;
171 unsigned char data_direction;
176 # endif /* CDROM_SEND_PACKET */
180 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
181 * this should be removed when a proper device interface is implemented
183 * (WS) We need this to keep track of current position and to safely
184 * detect media changes. Besides this should provide a great speed up
190 char toc_good; /* if false, will reread TOC from disk */
192 SUB_Q_CURRENT_POSITION CurrentPosition;
194 /* who has more than 5 cdroms on his/her machine ?? */
195 /* FIXME: this should grow depending on the number of cdroms we install/configure
198 #define MAX_CACHE_ENTRIES 5
199 static struct cdrom_cache cdrom_cache[MAX_CACHE_ENTRIES];
201 static CRITICAL_SECTION cache_section;
202 static CRITICAL_SECTION_DEBUG critsect_debug =
204 0, 0, &cache_section,
205 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
206 0, 0, { 0, (DWORD)(__FILE__ ": cache_section") }
208 static CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
210 /* Proposed media change function: not really needed at this time */
211 /* This is a 1 or 0 type of function */
213 static int CDROM_MediaChanged(int dev)
217 struct cdrom_tochdr hdr;
218 struct cdrom_tocentry entry;
220 if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
222 if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr) == -1 )
225 if ( memcmp(&hdr, &cdrom_cache[dev].hdr, sizeof(struct cdrom_tochdr)) )
228 for (i=hdr.cdth_trk0; i<=hdr.cdth_trk1+1; i++)
230 if (i == hdr.cdth_trk1 + 1)
232 entry.cdte_track = CDROM_LEADOUT;
234 entry.cdte_track = i;
236 entry.cdte_format = CDROM_MSF;
237 if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry) == -1)
239 if ( memcmp(&entry, cdrom_cache[dev].entry+i-hdr.cdth_trk0,
240 sizeof(struct cdrom_tocentry)) )
247 /******************************************************************
248 * CDROM_SyncCache [internal]
250 * Read the TOC in and store it in the cdrom_cache structure.
251 * Further requests for the TOC will be copied from the cache
252 * unless certain events like disk ejection is detected, in which
253 * case the cache will be cleared, causing it to be resynced.
254 * The cache section must be held by caller.
256 static int CDROM_SyncCache(int dev, int fd)
260 struct cdrom_tochdr hdr;
261 struct cdrom_tocentry entry;
262 #elif defined(__FreeBSD__) || defined(__NetBSD__)
263 struct ioc_toc_header hdr;
264 struct ioc_read_toc_entry entry;
265 struct cd_toc_entry toc_buffer;
267 CDROM_TOC *toc = &cdrom_cache[dev].toc;
268 cdrom_cache[dev].toc_good = 0;
272 io = ioctl(fd, CDROMREADTOCHDR, &hdr);
275 WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
279 TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
281 toc->FirstTrack = hdr.cdth_trk0;
282 toc->LastTrack = hdr.cdth_trk1;
283 tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
284 + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
285 toc->Length[0] = tsz >> 8;
286 toc->Length[1] = tsz;
288 for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
290 if (i == toc->LastTrack + 1)
291 entry.cdte_track = CDROM_LEADOUT;
293 entry.cdte_track = i;
294 entry.cdte_format = CDROM_MSF;
295 io = ioctl(fd, CDROMREADTOCENTRY, &entry);
297 WARN("error read entry (%s)\n", strerror(errno));
300 toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
301 toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
302 /* marking last track with leadout value as index */
303 toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
304 toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
305 toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
306 toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
307 toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
309 cdrom_cache[dev].toc_good = 1;
311 #elif defined(__FreeBSD__) || defined(__NetBSD__)
313 io = ioctl(fd, CDIOREADTOCHEADER, &hdr);
316 WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
319 toc->FirstTrack = hdr.starting_track;
320 toc->LastTrack = hdr.ending_track;
321 tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
322 + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
323 toc->Length[0] = tsz >> 8;
324 toc->Length[1] = tsz;
326 TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
328 for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
330 if (i == toc->LastTrack + 1)
333 entry.starting_track = LEADOUT;
335 entry.starting_track = i;
337 memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
338 entry.address_format = CD_MSF_FORMAT;
339 entry.data_len = sizeof(toc_buffer);
340 entry.data = &toc_buffer;
341 io = ioctl(fd, CDIOREADTOCENTRYS, &entry);
343 WARN("error read entry (%s)\n", strerror(errno));
346 toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
347 toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
348 /* marking last track with leadout value as index */
349 toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
350 toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
351 toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
352 toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
353 toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
355 cdrom_cache[dev].toc_good = 1;
358 return STATUS_NOT_SUPPORTED;
361 return CDROM_GetStatusCode(io);
364 static void CDROM_ClearCacheEntry(int dev)
366 RtlEnterCriticalSection( &cache_section );
367 cdrom_cache[dev].toc_good = 0;
368 RtlLeaveCriticalSection( &cache_section );
373 /******************************************************************
374 * CDROM_GetInterfaceInfo
376 * Determines the ide interface (the number after the ide), and the
377 * number of the device on that interface for ide cdroms (*port == 0).
378 * Determines the scsi information for scsi cdroms (*port >= 1).
379 * Returns false if the info cannot not be obtained.
381 * NOTE: this function is used in CDROM_InitRegistry and CDROM_GetAddress
383 static int CDROM_GetInterfaceInfo(int fd, int* port, int* iface, int* device,int* lun)
387 if ( fstat(fd, &st) == -1 || ! S_ISBLK(st.st_mode))
389 FIXME("cdrom not a block device!!!\n");
396 switch (major(st.st_rdev)) {
397 case IDE0_MAJOR: *iface = 0; break;
398 case IDE1_MAJOR: *iface = 1; break;
399 case IDE2_MAJOR: *iface = 2; break;
400 case IDE3_MAJOR: *iface = 3; break;
401 case IDE4_MAJOR: *iface = 4; break;
402 case IDE5_MAJOR: *iface = 5; break;
403 case IDE6_MAJOR: *iface = 6; break;
404 case IDE7_MAJOR: *iface = 7; break;
405 default: *port = 1; break;
409 *device = (minor(st.st_rdev) >> 6);
412 #ifdef SCSI_IOCTL_GET_IDLUN
414 if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun) != -1)
416 *port = ((idlun[0] >> 24) & 0xff) + 1;
417 *iface = (idlun[0] >> 16) & 0xff;
418 *device = idlun[0] & 0xff;
419 *lun = (idlun[0] >> 8) & 0xff;
424 FIXME("CD-ROM device (%d, %d) not supported\n",
425 major(st.st_rdev), minor(st.st_rdev));
430 #elif defined(__NetBSD__)
431 struct scsi_addr addr;
432 if (ioctl(fd, SCIOCIDENTIFY, &addr) != -1)
436 case TYPE_SCSI: *port = 1;
437 *iface = addr.addr.scsi.scbus;
438 *device = addr.addr.scsi.target;
439 *lun = addr.addr.scsi.lun;
441 case TYPE_ATAPI: *port = 0;
442 *iface = addr.addr.atapi.atbus;
443 *device = addr.addr.atapi.drive;
450 #elif defined(__FreeBSD__)
451 FIXME("not implemented for BSD\n");
454 FIXME("not implemented for nonlinux\n");
460 /******************************************************************
463 * Initializes registry to contain scsi info about the cdrom in NT.
464 * All devices (even not real scsi ones) have this info in NT.
465 * TODO: for now it only works for non scsi devices
466 * NOTE: programs usually read these registry entries after sending the
467 * IOCTL_SCSI_GET_ADDRESS ioctl to the cdrom
469 void CDROM_InitRegistry(int fd)
471 int portnum, busid, targetid, lun;
472 OBJECT_ATTRIBUTES attr;
473 UNICODE_STRING nameW;
485 attr.Length = sizeof(attr);
486 attr.RootDirectory = 0;
487 attr.ObjectName = &nameW;
489 attr.SecurityDescriptor = NULL;
490 attr.SecurityQualityOfService = NULL;
492 if (!CDROM_GetInterfaceInfo(fd, &portnum, &busid, &targetid, &lun))
495 /* Ensure there is Scsi key */
496 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\HARDWARE\\DEVICEMAP\\Scsi" ) ||
497 NtCreateKey( &scsiKey, KEY_ALL_ACCESS, &attr, 0,
498 NULL, REG_OPTION_VOLATILE, &disp ))
500 ERR("Cannot create DEVICEMAP\\Scsi registry key\n" );
503 RtlFreeUnicodeString( &nameW );
505 snprintf(buffer,sizeof(buffer),"Scsi Port %d",portnum);
506 attr.RootDirectory = scsiKey;
507 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
508 NtCreateKey( &portKey, KEY_ALL_ACCESS, &attr, 0,
509 NULL, REG_OPTION_VOLATILE, &disp ))
511 ERR("Cannot create DEVICEMAP\\Scsi Port registry key\n" );
514 RtlFreeUnicodeString( &nameW );
516 RtlCreateUnicodeStringFromAsciiz( &nameW, "Driver" );
518 RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
519 NtSetValueKey( portKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
520 RtlFreeUnicodeString( &nameW );
522 RtlCreateUnicodeStringFromAsciiz( &nameW, "FirstBusTimeScanInMs" );
523 NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
524 RtlFreeUnicodeString( &nameW );
529 if (ioctl(fd,HDIO_GET_DMA, &dma) != -1) {
531 TRACE("setting dma to %lx\n", value);
535 RtlCreateUnicodeStringFromAsciiz( &nameW, "DMAEnabled" );
536 NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
537 RtlFreeUnicodeString( &nameW );
539 snprintf(buffer,40,"Scsi Bus %d", busid);
540 attr.RootDirectory = portKey;
541 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
542 NtCreateKey( &busKey, KEY_ALL_ACCESS, &attr, 0,
543 NULL, REG_OPTION_VOLATILE, &disp ))
545 ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus registry key\n" );
548 RtlFreeUnicodeString( &nameW );
550 attr.RootDirectory = busKey;
551 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Initiator Id 255" ) ||
552 NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
553 NULL, REG_OPTION_VOLATILE, &disp ))
555 ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus\\Initiator Id 255 registry key\n" );
558 RtlFreeUnicodeString( &nameW );
559 NtClose( targetKey );
561 snprintf(buffer,40,"Target Id %d", targetid);
562 attr.RootDirectory = busKey;
563 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
564 NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
565 NULL, REG_OPTION_VOLATILE, &disp ))
567 ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus 0\\Target Id registry key\n" );
570 RtlFreeUnicodeString( &nameW );
572 RtlCreateUnicodeStringFromAsciiz( &nameW, "Type" );
573 data = "CdRomPeripheral";
574 RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
575 NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
576 RtlFreeUnicodeString( &nameW );
577 /* FIXME - maybe read the real identifier?? */
578 RtlCreateUnicodeStringFromAsciiz( &nameW, "Identifier" );
580 RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
581 NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
582 RtlFreeUnicodeString( &nameW );
583 /* FIXME - we always use Cdrom0 - do not know about the nt behaviour */
584 RtlCreateUnicodeStringFromAsciiz( &nameW, "DeviceName" );
586 RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
587 NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
588 RtlFreeUnicodeString( &nameW );
590 NtClose( targetKey );
597 /******************************************************************
601 static NTSTATUS CDROM_Open(int fd, int* dev)
604 NTSTATUS ret = STATUS_SUCCESS;
609 RtlEnterCriticalSection( &cache_section );
610 for (*dev = 0; *dev < MAX_CACHE_ENTRIES; (*dev)++)
613 cdrom_cache[*dev].device == 0 &&
614 cdrom_cache[*dev].inode == 0)
616 else if (cdrom_cache[*dev].device == st.st_dev &&
617 cdrom_cache[*dev].inode == st.st_ino)
620 if (*dev == MAX_CACHE_ENTRIES)
622 if (empty == -1) ret = STATUS_NOT_IMPLEMENTED;
626 cdrom_cache[*dev].device = st.st_dev;
627 cdrom_cache[*dev].inode = st.st_ino;
630 RtlLeaveCriticalSection( &cache_section );
632 TRACE("%d, %d\n", *dev, fd);
636 /******************************************************************
637 * CDROM_GetStatusCode
641 static NTSTATUS CDROM_GetStatusCode(int io)
643 if (io == 0) return STATUS_SUCCESS;
650 return STATUS_NO_MEDIA_IN_DEVICE;
652 return STATUS_ACCESS_DENIED;
654 return STATUS_INVALID_PARAMETER;
655 /* case EBADF: Bad file descriptor */
657 return STATUS_NOT_SUPPORTED;
659 FIXME("Unmapped error code %d: %s\n", errno, strerror(errno));
660 return STATUS_IO_DEVICE_ERROR;
663 /******************************************************************
667 static NTSTATUS CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
669 cac->LbaFormat = 0; /* FIXME */
670 cac->LogicalBlocksPerSecond = 1; /* FIXME */
671 return STATUS_NOT_SUPPORTED;
674 /******************************************************************
675 * CDROM_GetDeviceNumber
678 static NTSTATUS CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
680 return STATUS_NOT_SUPPORTED;
683 /******************************************************************
684 * CDROM_GetDriveGeometry
687 static NTSTATUS CDROM_GetDriveGeometry(int dev, int fd, DISK_GEOMETRY* dg)
693 if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
695 fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
696 - FRAME_OF_TOC(toc, 1); /* Total size in frames */
698 dg->Cylinders.u.LowPart = fsize / (64 * 32);
699 dg->Cylinders.u.HighPart = 0;
700 dg->MediaType = RemovableMedia;
701 dg->TracksPerCylinder = 64;
702 dg->SectorsPerTrack = 32;
703 dg->BytesPerSector= 2048;
707 /**************************************************************************
708 * CDROM_Reset [internal]
710 static NTSTATUS CDROM_ResetAudio(int fd)
713 return CDROM_GetStatusCode(ioctl(fd, CDROMRESET));
714 #elif defined(__FreeBSD__) || defined(__NetBSD__)
715 return CDROM_GetStatusCode(ioctl(fd, CDIOCRESET, NULL));
717 return STATUS_NOT_SUPPORTED;
721 /******************************************************************
726 static NTSTATUS CDROM_SetTray(int fd, BOOL doEject)
729 return CDROM_GetStatusCode(ioctl(fd, doEject ? CDROMEJECT : CDROMCLOSETRAY));
730 #elif defined(__FreeBSD__) || defined(__NetBSD__)
731 return CDROM_GetStatusCode((ioctl(fd, CDIOCALLOW, NULL)) ||
732 (ioctl(fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
733 (ioctl(fd, CDIOCPREVENT, NULL)));
735 return STATUS_NOT_SUPPORTED;
739 /******************************************************************
740 * CDROM_ControlEjection
744 static NTSTATUS CDROM_ControlEjection(int fd, const PREVENT_MEDIA_REMOVAL* rmv)
747 return CDROM_GetStatusCode(ioctl(fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
748 #elif defined(__FreeBSD__) || defined(__NetBSD__)
749 return CDROM_GetStatusCode(ioctl(fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
751 return STATUS_NOT_SUPPORTED;
755 /******************************************************************
760 static NTSTATUS CDROM_ReadTOC(int dev, int fd, CDROM_TOC* toc)
762 NTSTATUS ret = STATUS_NOT_SUPPORTED;
764 if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
765 return STATUS_INVALID_PARAMETER;
767 RtlEnterCriticalSection( &cache_section );
768 if (cdrom_cache[dev].toc_good || !(ret = CDROM_SyncCache(dev, fd)))
770 *toc = cdrom_cache[dev].toc;
771 ret = STATUS_SUCCESS;
773 RtlLeaveCriticalSection( &cache_section );
777 /******************************************************************
782 static NTSTATUS CDROM_GetDiskData(int dev, int fd, CDROM_DISK_DATA* data)
788 if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
790 for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
791 if (toc.TrackData[i-toc.FirstTrack].Control & 0x04)
792 data->DiskData |= CDROM_DISK_DATA_TRACK;
794 data->DiskData |= CDROM_DISK_AUDIO_TRACK;
796 return STATUS_SUCCESS;
799 /******************************************************************
804 static NTSTATUS CDROM_ReadQChannel(int dev, int fd, const CDROM_SUB_Q_DATA_FORMAT* fmt,
805 SUB_Q_CHANNEL_DATA* data)
807 NTSTATUS ret = STATUS_NOT_SUPPORTED;
810 SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data;
812 struct cdrom_subchnl sc;
813 sc.cdsc_format = CDROM_MSF;
815 io = ioctl(fd, CDROMSUBCHNL, &sc);
818 TRACE("opened or no_media (%s)!\n", strerror(errno));
819 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
820 CDROM_ClearCacheEntry(dev);
824 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
826 switch (sc.cdsc_audiostatus) {
827 case CDROM_AUDIO_INVALID:
828 CDROM_ClearCacheEntry(dev);
829 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
831 case CDROM_AUDIO_NO_STATUS:
832 CDROM_ClearCacheEntry(dev);
833 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
835 case CDROM_AUDIO_PLAY:
836 hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
838 case CDROM_AUDIO_PAUSED:
839 hdr->AudioStatus = AUDIO_STATUS_PAUSED;
841 case CDROM_AUDIO_COMPLETED:
842 hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
844 case CDROM_AUDIO_ERROR:
845 hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
848 TRACE("status=%02X !\n", sc.cdsc_audiostatus);
853 case IOCTL_CDROM_CURRENT_POSITION:
854 size = sizeof(SUB_Q_CURRENT_POSITION);
855 RtlEnterCriticalSection( &cache_section );
856 if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
857 data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
858 data->CurrentPosition.Control = sc.cdsc_ctrl;
859 data->CurrentPosition.ADR = sc.cdsc_adr;
860 data->CurrentPosition.TrackNumber = sc.cdsc_trk;
861 data->CurrentPosition.IndexNumber = sc.cdsc_ind;
863 data->CurrentPosition.AbsoluteAddress[0] = 0;
864 data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute;
865 data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
866 data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
868 data->CurrentPosition.TrackRelativeAddress[0] = 0;
869 data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute;
870 data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
871 data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
873 cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
875 else /* not playing */
877 cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
878 data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
880 RtlLeaveCriticalSection( &cache_section );
882 case IOCTL_CDROM_MEDIA_CATALOG:
883 size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
884 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
886 struct cdrom_mcn mcn;
887 if ((io = ioctl(fd, CDROM_GET_MCN, &mcn)) == -1) goto end;
889 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
890 data->MediaCatalog.Mcval = 0; /* FIXME */
891 memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
892 data->MediaCatalog.MediaCatalog[14] = 0;
895 case IOCTL_CDROM_TRACK_ISRC:
896 size = sizeof(SUB_Q_CURRENT_POSITION);
897 FIXME("TrackIsrc: NIY on linux\n");
898 data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
899 data->TrackIsrc.Tcval = 0;
905 ret = CDROM_GetStatusCode(io);
906 #elif defined(__FreeBSD__) || defined(__NetBSD__)
908 SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data;
910 struct ioc_read_subchannel read_sc;
911 struct cd_sub_channel_info sc;
913 read_sc.address_format = CD_MSF_FORMAT;
915 read_sc.data_len = sizeof(sc);
919 case IOCTL_CDROM_CURRENT_POSITION:
920 read_sc.data_format = CD_CURRENT_POSITION;
922 case IOCTL_CDROM_MEDIA_CATALOG:
923 read_sc.data_format = CD_MEDIA_CATALOG;
925 case IOCTL_CDROM_TRACK_ISRC:
926 read_sc.data_format = CD_TRACK_INFO;
927 sc.what.track_info.track_number = data->TrackIsrc.Track;
930 io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
933 TRACE("opened or no_media (%s)!\n", strerror(errno));
934 CDROM_ClearCacheEntry(dev);
935 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
939 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
941 switch (sc.header.audio_status) {
942 case CD_AS_AUDIO_INVALID:
943 CDROM_ClearCacheEntry(dev);
944 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
946 case CD_AS_NO_STATUS:
947 CDROM_ClearCacheEntry(dev);
948 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
950 case CD_AS_PLAY_IN_PROGRESS:
951 hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
953 case CD_AS_PLAY_PAUSED:
954 hdr->AudioStatus = AUDIO_STATUS_PAUSED;
956 case CD_AS_PLAY_COMPLETED:
957 hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
959 case CD_AS_PLAY_ERROR:
960 hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
963 TRACE("status=%02X !\n", sc.header.audio_status);
967 case IOCTL_CDROM_CURRENT_POSITION:
968 size = sizeof(SUB_Q_CURRENT_POSITION);
969 RtlEnterCriticalSection( &cache_section );
970 if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
971 data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
972 data->CurrentPosition.Control = sc.what.position.control;
973 data->CurrentPosition.ADR = sc.what.position.addr_type;
974 data->CurrentPosition.TrackNumber = sc.what.position.track_number;
975 data->CurrentPosition.IndexNumber = sc.what.position.index_number;
977 data->CurrentPosition.AbsoluteAddress[0] = 0;
978 data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
979 data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
980 data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
981 data->CurrentPosition.TrackRelativeAddress[0] = 0;
982 data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
983 data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
984 data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
985 cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
987 else { /* not playing */
988 cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
989 data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
991 RtlLeaveCriticalSection( &cache_section );
993 case IOCTL_CDROM_MEDIA_CATALOG:
994 size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
995 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
996 data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
997 memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
999 case IOCTL_CDROM_TRACK_ISRC:
1000 size = sizeof(SUB_Q_CURRENT_POSITION);
1001 data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
1002 data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
1003 memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
1008 ret = CDROM_GetStatusCode(io);
1013 /******************************************************************
1018 static NTSTATUS CDROM_Verify(int dev, int fd)
1020 /* quick implementation */
1021 CDROM_SUB_Q_DATA_FORMAT fmt;
1022 SUB_Q_CHANNEL_DATA data;
1024 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
1025 return CDROM_ReadQChannel(dev, fd, &fmt, &data) ? 1 : 0;
1028 /******************************************************************
1029 * CDROM_PlayAudioMSF
1033 static NTSTATUS CDROM_PlayAudioMSF(int fd, const CDROM_PLAY_AUDIO_MSF* audio_msf)
1035 NTSTATUS ret = STATUS_NOT_SUPPORTED;
1037 struct cdrom_msf msf;
1040 msf.cdmsf_min0 = audio_msf->StartingM;
1041 msf.cdmsf_sec0 = audio_msf->StartingS;
1042 msf.cdmsf_frame0 = audio_msf->StartingF;
1043 msf.cdmsf_min1 = audio_msf->EndingM;
1044 msf.cdmsf_sec1 = audio_msf->EndingS;
1045 msf.cdmsf_frame1 = audio_msf->EndingF;
1047 io = ioctl(fd, CDROMSTART);
1050 WARN("motor doesn't start !\n");
1053 io = ioctl(fd, CDROMPLAYMSF, &msf);
1056 WARN("device doesn't play !\n");
1059 TRACE("msf = %d:%d:%d %d:%d:%d\n",
1060 msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
1061 msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
1063 ret = CDROM_GetStatusCode(io);
1064 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1065 struct ioc_play_msf msf;
1068 msf.start_m = audio_msf->StartingM;
1069 msf.start_s = audio_msf->StartingS;
1070 msf.start_f = audio_msf->StartingF;
1071 msf.end_m = audio_msf->EndingM;
1072 msf.end_s = audio_msf->EndingS;
1073 msf.end_f = audio_msf->EndingF;
1075 io = ioctl(fd, CDIOCSTART, NULL);
1078 WARN("motor doesn't start !\n");
1081 io = ioctl(fd, CDIOCPLAYMSF, &msf);
1084 WARN("device doesn't play !\n");
1087 TRACE("msf = %d:%d:%d %d:%d:%d\n",
1088 msf.start_m, msf.start_s, msf.start_f,
1089 msf.end_m, msf.end_s, msf.end_f);
1091 ret = CDROM_GetStatusCode(io);
1096 /******************************************************************
1097 * CDROM_SeekAudioMSF
1101 static NTSTATUS CDROM_SeekAudioMSF(int dev, int fd, const CDROM_SEEK_AUDIO_MSF* audio_msf)
1105 SUB_Q_CURRENT_POSITION *cp;
1107 struct cdrom_msf0 msf;
1108 struct cdrom_subchnl sc;
1109 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1110 struct ioc_play_msf msf;
1111 struct ioc_read_subchannel read_sc;
1112 struct cd_sub_channel_info sc;
1116 /* Use the information on the TOC to compute the new current
1117 * position, which is shadowed on the cache. [Portable]. */
1118 frame = FRAME_OF_MSF(*audio_msf);
1120 if ((io = CDROM_ReadTOC(dev, fd, &toc)) != 0) return io;
1122 for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
1123 if (FRAME_OF_TOC(toc,i)>frame) break;
1124 if (i <= toc.FirstTrack || i > toc.LastTrack+1)
1125 return STATUS_INVALID_PARAMETER;
1127 RtlEnterCriticalSection( &cache_section );
1128 cp = &cdrom_cache[dev].CurrentPosition;
1129 cp->FormatCode = IOCTL_CDROM_CURRENT_POSITION;
1130 cp->Control = toc.TrackData[i-toc.FirstTrack].Control;
1131 cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr;
1132 cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber;
1133 cp->IndexNumber = 0; /* FIXME: where do they keep these? */
1134 cp->AbsoluteAddress[0] = 0;
1135 cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1];
1136 cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2];
1137 cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3];
1138 frame -= FRAME_OF_TOC(toc,i);
1139 cp->TrackRelativeAddress[0] = 0;
1140 MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame);
1141 RtlLeaveCriticalSection( &cache_section );
1143 /* If playing, then issue a seek command, otherwise do nothing */
1145 sc.cdsc_format = CDROM_MSF;
1147 io = ioctl(fd, CDROMSUBCHNL, &sc);
1150 TRACE("opened or no_media (%s)!\n", strerror(errno));
1151 CDROM_ClearCacheEntry(dev);
1152 return CDROM_GetStatusCode(io);
1154 if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY)
1156 msf.minute = audio_msf->M;
1157 msf.second = audio_msf->S;
1158 msf.frame = audio_msf->F;
1159 return CDROM_GetStatusCode(ioctl(fd, CDROMSEEK, &msf));
1161 return STATUS_SUCCESS;
1162 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1163 read_sc.address_format = CD_MSF_FORMAT;
1165 read_sc.data_len = sizeof(sc);
1167 read_sc.data_format = CD_CURRENT_POSITION;
1169 io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
1172 TRACE("opened or no_media (%s)!\n", strerror(errno));
1173 CDROM_ClearCacheEntry(dev);
1174 return CDROM_GetStatusCode(io);
1176 if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS)
1179 msf.start_m = audio_msf->M;
1180 msf.start_s = audio_msf->S;
1181 msf.start_f = audio_msf->F;
1182 final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1;
1183 MSF_OF_FRAME(msf.end_m, final_frame);
1185 return CDROM_GetStatusCode(ioctl(fd, CDIOCPLAYMSF, &msf));
1187 return STATUS_SUCCESS;
1189 return STATUS_NOT_SUPPORTED;
1193 /******************************************************************
1198 static NTSTATUS CDROM_PauseAudio(int fd)
1201 return CDROM_GetStatusCode(ioctl(fd, CDROMPAUSE));
1202 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1203 return CDROM_GetStatusCode(ioctl(fd, CDIOCPAUSE, NULL));
1205 return STATUS_NOT_SUPPORTED;
1209 /******************************************************************
1214 static NTSTATUS CDROM_ResumeAudio(int fd)
1217 return CDROM_GetStatusCode(ioctl(fd, CDROMRESUME));
1218 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1219 return CDROM_GetStatusCode(ioctl(fd, CDIOCRESUME, NULL));
1221 return STATUS_NOT_SUPPORTED;
1225 /******************************************************************
1230 static NTSTATUS CDROM_StopAudio(int fd)
1233 return CDROM_GetStatusCode(ioctl(fd, CDROMSTOP));
1234 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1235 return CDROM_GetStatusCode(ioctl(fd, CDIOCSTOP, NULL));
1237 return STATUS_NOT_SUPPORTED;
1241 /******************************************************************
1246 static NTSTATUS CDROM_GetVolume(int fd, VOLUME_CONTROL* vc)
1249 struct cdrom_volctrl volc;
1252 io = ioctl(fd, CDROMVOLREAD, &volc);
1255 vc->PortVolume[0] = volc.channel0;
1256 vc->PortVolume[1] = volc.channel1;
1257 vc->PortVolume[2] = volc.channel2;
1258 vc->PortVolume[3] = volc.channel3;
1260 return CDROM_GetStatusCode(io);
1261 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1262 struct ioc_vol volc;
1265 io = ioctl(fd, CDIOCGETVOL, &volc);
1268 vc->PortVolume[0] = volc.vol[0];
1269 vc->PortVolume[1] = volc.vol[1];
1270 vc->PortVolume[2] = volc.vol[2];
1271 vc->PortVolume[3] = volc.vol[3];
1273 return CDROM_GetStatusCode(io);
1275 return STATUS_NOT_SUPPORTED;
1279 /******************************************************************
1284 static NTSTATUS CDROM_SetVolume(int fd, const VOLUME_CONTROL* vc)
1287 struct cdrom_volctrl volc;
1289 volc.channel0 = vc->PortVolume[0];
1290 volc.channel1 = vc->PortVolume[1];
1291 volc.channel2 = vc->PortVolume[2];
1292 volc.channel3 = vc->PortVolume[3];
1294 return CDROM_GetStatusCode(ioctl(fd, CDROMVOLCTRL, &volc));
1295 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1296 struct ioc_vol volc;
1298 volc.vol[0] = vc->PortVolume[0];
1299 volc.vol[1] = vc->PortVolume[1];
1300 volc.vol[2] = vc->PortVolume[2];
1301 volc.vol[3] = vc->PortVolume[3];
1303 return CDROM_GetStatusCode(ioctl(fd, CDIOCSETVOL, &volc));
1305 return STATUS_NOT_SUPPORTED;
1309 /******************************************************************
1314 static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
1316 int ret = STATUS_NOT_SUPPORTED;
1320 switch (raw->TrackMode)
1322 case YellowMode2: sectSize = 2336; break;
1323 case XAForm2: sectSize = 2328; break;
1324 case CDDA: sectSize = 2352; break;
1325 default: return STATUS_INVALID_PARAMETER;
1327 if (len < raw->SectorCount * sectSize) return STATUS_BUFFER_TOO_SMALL;
1328 /* strangely enough, it seems that sector offsets are always indicated with a size of 2048,
1329 * even if a larger size if read...
1333 struct cdrom_read cdr;
1334 struct cdrom_read_audio cdra;
1336 switch (raw->TrackMode)
1339 if (raw->DiskOffset.u.HighPart) FIXME("Unsupported value\n");
1340 cdr.cdread_lba = raw->DiskOffset.u.LowPart; /* FIXME ? */
1341 cdr.cdread_bufaddr = buffer;
1342 cdr.cdread_buflen = raw->SectorCount * sectSize;
1343 io = ioctl(fd, CDROMREADMODE2, &cdr);
1346 FIXME("XAForm2: NIY\n");
1349 /* FIXME: the output doesn't seem 100% correct... in fact output is shifted
1350 * between by NT2K box and this... should check on the same drive...
1351 * otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
1353 * Anyway, that's not critical at all. We're talking of 16/32 bytes, we're
1354 * talking of 0.2 ms of sound
1356 /* 2048 = 2 ** 11 */
1357 if (raw->DiskOffset.u.HighPart & ~2047) FIXME("Unsupported value\n");
1358 cdra.addr.lba = ((raw->DiskOffset.u.LowPart >> 11) |
1359 (raw->DiskOffset.u.HighPart << (32 - 11))) - 1;
1360 FIXME("reading at %u\n", cdra.addr.lba);
1361 cdra.addr_format = CDROM_LBA;
1362 cdra.nframes = raw->SectorCount;
1364 io = ioctl(fd, CDROMREADAUDIO, &cdra);
1367 FIXME("NIY: %d\n", raw->TrackMode);
1373 switch (raw->TrackMode)
1376 FIXME("YellowMode2: NIY\n");
1379 FIXME("XAForm2: NIY\n");
1382 FIXME("CDDA: NIY\n");
1388 *sz = sectSize * raw->SectorCount;
1389 ret = CDROM_GetStatusCode(io);
1393 /******************************************************************
1394 * CDROM_ScsiPassThroughDirect
1398 static NTSTATUS CDROM_ScsiPassThroughDirect(int fd, PSCSI_PASS_THROUGH_DIRECT pPacket)
1400 int ret = STATUS_NOT_SUPPORTED;
1401 #if defined(linux) && defined(CDROM_SEND_PACKET)
1402 struct linux_cdrom_generic_command cmd;
1403 struct request_sense sense;
1406 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1407 return STATUS_BUFFER_TOO_SMALL;
1409 if (pPacket->CdbLength > 12)
1410 return STATUS_INVALID_PARAMETER;
1412 if (pPacket->SenseInfoLength > sizeof(sense))
1413 return STATUS_INVALID_PARAMETER;
1415 memset(&cmd, 0, sizeof(cmd));
1416 memset(&sense, 0, sizeof(sense));
1418 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1420 cmd.buffer = pPacket->DataBuffer;
1421 cmd.buflen = pPacket->DataTransferLength;
1424 cmd.timeout = pPacket->TimeOutValue*HZ;
1426 switch (pPacket->DataIn)
1428 case SCSI_IOCTL_DATA_OUT:
1429 cmd.data_direction = CGC_DATA_WRITE;
1431 case SCSI_IOCTL_DATA_IN:
1432 cmd.data_direction = CGC_DATA_READ;
1434 case SCSI_IOCTL_DATA_UNSPECIFIED:
1435 cmd.data_direction = CGC_DATA_NONE;
1438 return STATUS_INVALID_PARAMETER;
1441 io = ioctl(fd, CDROM_SEND_PACKET, &cmd);
1443 if (pPacket->SenseInfoLength != 0)
1445 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1446 &sense, pPacket->SenseInfoLength);
1449 pPacket->ScsiStatus = cmd.stat;
1451 ret = CDROM_GetStatusCode(io);
1453 #elif defined(__NetBSD__)
1457 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1458 return STATUS_BUFFER_TOO_SMALL;
1460 if (pPacket->CdbLength > 12)
1461 return STATUS_INVALID_PARAMETER;
1463 if (pPacket->SenseInfoLength > SENSEBUFLEN)
1464 return STATUS_INVALID_PARAMETER;
1466 memset(&cmd, 0, sizeof(cmd));
1467 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1469 cmd.cmdlen = pPacket->CdbLength;
1470 cmd.databuf = pPacket->DataBuffer;
1471 cmd.datalen = pPacket->DataTransferLength;
1472 cmd.senselen = pPacket->SenseInfoLength;
1473 cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */
1475 switch (pPacket->DataIn)
1477 case SCSI_IOCTL_DATA_OUT:
1478 cmd.flags |= SCCMD_WRITE;
1480 case SCSI_IOCTL_DATA_IN:
1481 cmd.flags |= SCCMD_READ;
1483 case SCSI_IOCTL_DATA_UNSPECIFIED:
1487 return STATUS_INVALID_PARAMETER;
1490 io = ioctl(fd, SCIOCCOMMAND, &cmd);
1494 case SCCMD_OK: break;
1495 case SCCMD_TIMEOUT: return STATUS_TIMEOUT;
1497 case SCCMD_BUSY: return STATUS_DEVICE_BUSY;
1499 case SCCMD_SENSE: break;
1500 case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL;
1504 if (pPacket->SenseInfoLength != 0)
1506 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1507 cmd.sense, pPacket->SenseInfoLength);
1510 pPacket->ScsiStatus = cmd.status;
1512 ret = CDROM_GetStatusCode(io);
1517 /******************************************************************
1518 * CDROM_ScsiPassThrough
1522 static NTSTATUS CDROM_ScsiPassThrough(int fd, PSCSI_PASS_THROUGH pPacket)
1524 int ret = STATUS_NOT_SUPPORTED;
1525 #if defined(linux) && defined(CDROM_SEND_PACKET)
1526 struct linux_cdrom_generic_command cmd;
1527 struct request_sense sense;
1530 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1531 return STATUS_BUFFER_TOO_SMALL;
1533 if (pPacket->CdbLength > 12)
1534 return STATUS_INVALID_PARAMETER;
1536 if (pPacket->SenseInfoLength > sizeof(sense))
1537 return STATUS_INVALID_PARAMETER;
1539 memset(&cmd, 0, sizeof(cmd));
1540 memset(&sense, 0, sizeof(sense));
1542 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1544 if ( pPacket->DataBufferOffset > 0x1000 )
1546 cmd.buffer = (void*)pPacket->DataBufferOffset;
1550 cmd.buffer = (char*)pPacket + pPacket->DataBufferOffset;
1552 cmd.buflen = pPacket->DataTransferLength;
1555 cmd.timeout = pPacket->TimeOutValue*HZ;
1557 switch (pPacket->DataIn)
1559 case SCSI_IOCTL_DATA_OUT:
1560 cmd.data_direction = CGC_DATA_WRITE;
1562 case SCSI_IOCTL_DATA_IN:
1563 cmd.data_direction = CGC_DATA_READ;
1565 case SCSI_IOCTL_DATA_UNSPECIFIED:
1566 cmd.data_direction = CGC_DATA_NONE;
1569 return STATUS_INVALID_PARAMETER;
1572 io = ioctl(fd, CDROM_SEND_PACKET, &cmd);
1574 if (pPacket->SenseInfoLength != 0)
1576 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1577 &sense, pPacket->SenseInfoLength);
1580 pPacket->ScsiStatus = cmd.stat;
1582 ret = CDROM_GetStatusCode(io);
1584 #elif defined(__NetBSD__)
1588 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1589 return STATUS_BUFFER_TOO_SMALL;
1591 if (pPacket->CdbLength > 12)
1592 return STATUS_INVALID_PARAMETER;
1594 if (pPacket->SenseInfoLength > SENSEBUFLEN)
1595 return STATUS_INVALID_PARAMETER;
1597 memset(&cmd, 0, sizeof(cmd));
1598 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1600 if ( pPacket->DataBufferOffset > 0x1000 )
1602 cmd.databuf = (void*)pPacket->DataBufferOffset;
1606 cmd.databuf = (char*)pPacket + pPacket->DataBufferOffset;
1609 cmd.cmdlen = pPacket->CdbLength;
1610 cmd.datalen = pPacket->DataTransferLength;
1611 cmd.senselen = pPacket->SenseInfoLength;
1612 cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */
1614 switch (pPacket->DataIn)
1616 case SCSI_IOCTL_DATA_OUT:
1617 cmd.flags |= SCCMD_WRITE;
1619 case SCSI_IOCTL_DATA_IN:
1620 cmd.flags |= SCCMD_READ;
1622 case SCSI_IOCTL_DATA_UNSPECIFIED:
1626 return STATUS_INVALID_PARAMETER;
1629 io = ioctl(fd, SCIOCCOMMAND, &cmd);
1633 case SCCMD_OK: break;
1634 case SCCMD_TIMEOUT: return STATUS_TIMEOUT;
1636 case SCCMD_BUSY: return STATUS_DEVICE_BUSY;
1638 case SCCMD_SENSE: break;
1639 case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL;
1643 if (pPacket->SenseInfoLength != 0)
1645 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1646 cmd.sense, pPacket->SenseInfoLength);
1649 pPacket->ScsiStatus = cmd.status;
1651 ret = CDROM_GetStatusCode(io);
1656 /******************************************************************
1661 static NTSTATUS CDROM_ScsiGetCaps(PIO_SCSI_CAPABILITIES caps)
1663 NTSTATUS ret = STATUS_NOT_IMPLEMENTED;
1665 caps->Length = sizeof(*caps);
1667 caps->MaximumTransferLength = SG_SCATTER_SZ; /* FIXME */
1668 caps->MaximumPhysicalPages = SG_SCATTER_SZ / getpagesize();
1669 caps->SupportedAsynchronousEvents = TRUE;
1670 caps->AlignmentMask = getpagesize();
1671 caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */
1672 caps->AdapterScansDown = FALSE; /* FIXME ? */
1673 caps->AdapterUsesPio = FALSE; /* FIXME ? */
1674 ret = STATUS_SUCCESS;
1676 FIXME("Unimplemented\n");
1681 /******************************************************************
1684 * implements IOCTL_SCSI_GET_ADDRESS
1686 static NTSTATUS CDROM_GetAddress(int fd, SCSI_ADDRESS* address)
1688 int portnum, busid, targetid, lun;
1690 address->Length = sizeof(SCSI_ADDRESS);
1691 if ( ! CDROM_GetInterfaceInfo(fd, &portnum, &busid, &targetid, &lun))
1692 return STATUS_NOT_SUPPORTED;
1694 address->PortNumber = portnum;
1695 address->PathId = busid; /* bus number */
1696 address->TargetId = targetid;
1698 return STATUS_SUCCESS;
1701 /******************************************************************
1702 * CDROM_DeviceIoControl
1706 NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice,
1707 HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1708 PVOID UserApcContext,
1709 PIO_STATUS_BLOCK piosb,
1710 ULONG dwIoControlCode,
1711 LPVOID lpInBuffer, DWORD nInBufferSize,
1712 LPVOID lpOutBuffer, DWORD nOutBufferSize)
1715 NTSTATUS status = STATUS_SUCCESS;
1718 TRACE("%lx %s %lx %ld %lx %ld %p\n",
1719 (DWORD)hDevice, iocodex(dwIoControlCode), (DWORD)lpInBuffer, nInBufferSize,
1720 (DWORD)lpOutBuffer, nOutBufferSize, piosb);
1722 piosb->Information = 0;
1724 if ((status = wine_server_handle_to_fd( hDevice, GENERIC_READ, &fd, NULL, NULL ))) goto error;
1725 if ((status = CDROM_Open(fd, &dev)))
1727 wine_server_release_fd( hDevice, fd );
1731 switch (dwIoControlCode)
1733 case IOCTL_STORAGE_CHECK_VERIFY:
1734 case IOCTL_CDROM_CHECK_VERIFY:
1736 CDROM_ClearCacheEntry(dev);
1737 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1738 status = STATUS_INVALID_PARAMETER;
1739 else status = CDROM_Verify(dev, fd);
1742 /* EPP case IOCTL_STORAGE_CHECK_VERIFY2: */
1744 /* EPP case IOCTL_STORAGE_FIND_NEW_DEVICES: */
1745 /* EPP case IOCTL_CDROM_FIND_NEW_DEVICES: */
1747 case IOCTL_STORAGE_LOAD_MEDIA:
1748 case IOCTL_CDROM_LOAD_MEDIA:
1750 CDROM_ClearCacheEntry(dev);
1751 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1752 status = STATUS_INVALID_PARAMETER;
1753 else status = CDROM_SetTray(fd, FALSE);
1755 case IOCTL_STORAGE_EJECT_MEDIA:
1757 CDROM_ClearCacheEntry(dev);
1758 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1759 status = STATUS_INVALID_PARAMETER;
1760 else status = CDROM_SetTray(fd, TRUE);
1763 case IOCTL_CDROM_MEDIA_REMOVAL:
1764 case IOCTL_DISK_MEDIA_REMOVAL:
1765 case IOCTL_STORAGE_MEDIA_REMOVAL:
1766 case IOCTL_STORAGE_EJECTION_CONTROL:
1767 /* FIXME the last ioctl:s is not the same as the two others...
1768 * lockcount/owner should be handled */
1770 CDROM_ClearCacheEntry(dev);
1771 if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1772 else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) status = STATUS_BUFFER_TOO_SMALL;
1773 else status = CDROM_ControlEjection(fd, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
1776 /* EPP case IOCTL_STORAGE_GET_MEDIA_TYPES: */
1778 case IOCTL_STORAGE_GET_DEVICE_NUMBER:
1779 sz = sizeof(STORAGE_DEVICE_NUMBER);
1780 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1781 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1782 else status = CDROM_GetDeviceNumber(dev, (STORAGE_DEVICE_NUMBER*)lpOutBuffer);
1785 case IOCTL_STORAGE_RESET_DEVICE:
1787 CDROM_ClearCacheEntry(dev);
1788 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1789 status = STATUS_INVALID_PARAMETER;
1790 else status = CDROM_ResetAudio(fd);
1793 case IOCTL_CDROM_GET_CONTROL:
1794 sz = sizeof(CDROM_AUDIO_CONTROL);
1795 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1796 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1797 else status = CDROM_GetControl(dev, (CDROM_AUDIO_CONTROL*)lpOutBuffer);
1800 case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
1801 sz = sizeof(DISK_GEOMETRY);
1802 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1803 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1804 else status = CDROM_GetDriveGeometry(dev, fd, (DISK_GEOMETRY*)lpOutBuffer);
1807 case IOCTL_CDROM_DISK_TYPE:
1808 sz = sizeof(CDROM_DISK_DATA);
1809 /* CDROM_ClearCacheEntry(dev); */
1810 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1811 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1812 else status = CDROM_GetDiskData(dev, fd, (CDROM_DISK_DATA*)lpOutBuffer);
1815 /* EPP case IOCTL_CDROM_GET_LAST_SESSION: */
1817 case IOCTL_CDROM_READ_Q_CHANNEL:
1818 sz = sizeof(SUB_Q_CHANNEL_DATA);
1819 if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT))
1820 status = STATUS_INVALID_PARAMETER;
1821 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1822 else status = CDROM_ReadQChannel(dev, fd, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
1823 (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
1826 case IOCTL_CDROM_READ_TOC:
1827 sz = sizeof(CDROM_TOC);
1828 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1829 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1830 else status = CDROM_ReadTOC(dev, fd, (CDROM_TOC*)lpOutBuffer);
1833 /* EPP case IOCTL_CDROM_READ_TOC_EX: */
1835 case IOCTL_CDROM_PAUSE_AUDIO:
1837 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1838 status = STATUS_INVALID_PARAMETER;
1839 else status = CDROM_PauseAudio(fd);
1841 case IOCTL_CDROM_PLAY_AUDIO_MSF:
1843 if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1844 else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
1845 else status = CDROM_PlayAudioMSF(fd, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
1847 case IOCTL_CDROM_RESUME_AUDIO:
1849 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1850 status = STATUS_INVALID_PARAMETER;
1851 else status = CDROM_ResumeAudio(fd);
1853 case IOCTL_CDROM_SEEK_AUDIO_MSF:
1855 if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1856 else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
1857 else status = CDROM_SeekAudioMSF(dev, fd, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
1859 case IOCTL_CDROM_STOP_AUDIO:
1861 CDROM_ClearCacheEntry(dev); /* Maybe intention is to change media */
1862 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1863 status = STATUS_INVALID_PARAMETER;
1864 else status = CDROM_StopAudio(fd);
1866 case IOCTL_CDROM_GET_VOLUME:
1867 sz = sizeof(VOLUME_CONTROL);
1868 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1869 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1870 else status = CDROM_GetVolume(fd, (VOLUME_CONTROL*)lpOutBuffer);
1872 case IOCTL_CDROM_SET_VOLUME:
1874 CDROM_ClearCacheEntry(dev);
1875 if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
1876 status = STATUS_INVALID_PARAMETER;
1877 else status = CDROM_SetVolume(fd, (const VOLUME_CONTROL*)lpInBuffer);
1879 case IOCTL_CDROM_RAW_READ:
1881 if (nInBufferSize < sizeof(RAW_READ_INFO)) status = STATUS_INVALID_PARAMETER;
1882 else if (lpOutBuffer == NULL) status = STATUS_BUFFER_TOO_SMALL;
1883 else status = CDROM_RawRead(fd, (const RAW_READ_INFO*)lpInBuffer,
1884 lpOutBuffer, nOutBufferSize, &sz);
1886 case IOCTL_SCSI_GET_ADDRESS:
1887 sz = sizeof(SCSI_ADDRESS);
1888 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
1889 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
1890 else status = CDROM_GetAddress(fd, (SCSI_ADDRESS*)lpOutBuffer);
1892 case IOCTL_SCSI_PASS_THROUGH_DIRECT:
1893 sz = sizeof(SCSI_PASS_THROUGH_DIRECT);
1894 if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
1895 else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) status = STATUS_BUFFER_TOO_SMALL;
1896 else status = CDROM_ScsiPassThroughDirect(fd, (PSCSI_PASS_THROUGH_DIRECT)lpOutBuffer);
1898 case IOCTL_SCSI_PASS_THROUGH:
1899 sz = sizeof(SCSI_PASS_THROUGH);
1900 if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
1901 else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) status = STATUS_BUFFER_TOO_SMALL;
1902 else status = CDROM_ScsiPassThrough(fd, (PSCSI_PASS_THROUGH)lpOutBuffer);
1904 case IOCTL_SCSI_GET_CAPABILITIES:
1905 sz = sizeof(IO_SCSI_CAPABILITIES);
1906 if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
1907 else if (nOutBufferSize < sizeof(IO_SCSI_CAPABILITIES)) status = STATUS_BUFFER_TOO_SMALL;
1908 else status = CDROM_ScsiGetCaps((PIO_SCSI_CAPABILITIES)lpOutBuffer);
1911 FIXME("Unsupported IOCTL %lx (type=%lx access=%lx func=%lx meth=%lx)\n",
1912 dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
1913 (dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
1915 status = STATUS_INVALID_PARAMETER;
1918 wine_server_release_fd( hDevice, fd );
1920 piosb->u.Status = status;
1921 piosb->Information = sz;
1922 if (hEvent) NtSetEvent(hEvent, NULL);