Avoid closing the cdrom if we failed to open it.
[wine/multimedia.git] / dlls / ntdll / cdrom.c
blob0c46dc1f75ce97bae000169860d4f98e041bfd34
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /* Main file for CD-ROM support
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1999, 2001 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
23 #include "config.h"
24 #include "wine/port.h"
26 #include <errno.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #ifdef HAVE_IO_H
31 # include <io.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #include <fcntl.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
40 #ifdef HAVE_SYS_IOCTL_H
41 #include <sys/ioctl.h>
42 #endif
43 #ifdef HAVE_SCSI_SG_H
44 # include <scsi/sg.h>
45 #endif
46 #ifdef HAVE_SCSI_SCSI_IOCTL_H
47 # include <scsi/scsi_ioctl.h>
48 #endif
49 #ifdef HAVE_LINUX_MAJOR_H
50 # include <linux/major.h>
51 #endif
52 #ifdef HAVE_LINUX_HDREG_H
53 # include <linux/hdreg.h>
54 #endif
55 #ifdef HAVE_LINUX_PARAM_H
56 # include <linux/param.h>
57 #endif
58 #ifdef HAVE_LINUX_CDROM_H
59 # include <linux/cdrom.h>
60 #endif
61 #ifdef HAVE_LINUX_UCDROM_H
62 # include <linux/ucdrom.h>
63 #endif
64 #ifdef HAVE_SYS_CDIO_H
65 # include <sys/cdio.h>
66 #endif
67 #ifdef HAVE_SYS_SCSIIO_H
68 # include <sys/scsiio.h>
69 #endif
71 #define NONAMELESSUNION
72 #define NONAMELESSSTRUCT
73 #include "ntstatus.h"
74 #include "windef.h"
75 #include "winbase.h"
76 #include "winreg.h"
77 #include "winternl.h"
78 #include "winioctl.h"
79 #include "ntddstor.h"
80 #include "ntddcdrm.h"
81 #include "ntddscsi.h"
82 #include "drive.h"
83 #include "file.h"
84 #include "wine/debug.h"
86 /* Non-Linux systems do not have linux/cdrom.h and the like, and thus
87 lack the following constants. */
89 #ifndef CD_SECS
90 #define CD_SECS 60 /* seconds per minute */
91 #endif
92 #ifndef CD_FRAMES
93 #define CD_FRAMES 75 /* frames per second */
94 #endif
96 static const struct iocodexs
98 DWORD code;
99 const char *codex;
100 } iocodextable[] = {
101 {IOCTL_CDROM_UNLOAD_DRIVER, "IOCTL_CDROM_UNLOAD_DRIVER"},
102 {IOCTL_CDROM_READ_TOC, "IOCTL_CDROM_READ_TOC"},
103 {IOCTL_CDROM_GET_CONTROL, "IOCTL_CDROM_GET_CONTROL"},
104 {IOCTL_CDROM_PLAY_AUDIO_MSF, "IOCTL_CDROM_PLAY_AUDIO_MSF"},
105 {IOCTL_CDROM_SEEK_AUDIO_MSF, "IOCTL_CDROM_SEEK_AUDIO_MSF"},
106 {IOCTL_CDROM_STOP_AUDIO, "IOCTL_CDROM_STOP_AUDIO"},
107 {IOCTL_CDROM_PAUSE_AUDIO, "IOCTL_CDROM_PAUSE_AUDIO"},
108 {IOCTL_CDROM_RESUME_AUDIO, "IOCTL_CDROM_RESUME_AUDIO"},
109 {IOCTL_CDROM_GET_VOLUME, "IOCTL_CDROM_GET_VOLUME"},
110 {IOCTL_CDROM_SET_VOLUME, "IOCTL_CDROM_SET_VOLUME"},
111 {IOCTL_CDROM_READ_Q_CHANNEL, "IOCTL_CDROM_READ_Q_CHANNEL"},
112 {IOCTL_CDROM_GET_LAST_SESSION, "IOCTL_CDROM_GET_LAST_SESSION"},
113 {IOCTL_CDROM_RAW_READ, "IOCTL_CDROM_RAW_READ"},
114 {IOCTL_CDROM_DISK_TYPE, "IOCTL_CDROM_DISK_TYPE"},
115 {IOCTL_CDROM_GET_DRIVE_GEOMETRY, "IOCTL_CDROM_GET_DRIVE_GEOMETRY"},
116 {IOCTL_CDROM_CHECK_VERIFY, "IOCTL_CDROM_CHECK_VERIFY"},
117 {IOCTL_CDROM_MEDIA_REMOVAL, "IOCTL_CDROM_MEDIA_REMOVAL"},
118 {IOCTL_CDROM_EJECT_MEDIA, "IOCTL_CDROM_EJECT_MEDIA"},
119 {IOCTL_CDROM_LOAD_MEDIA, "IOCTL_CDROM_LOAD_MEDIA"},
120 {IOCTL_CDROM_RESERVE, "IOCTL_CDROM_RESERVE"},
121 {IOCTL_CDROM_RELEASE, "IOCTL_CDROM_RELEASE"},
122 {IOCTL_CDROM_FIND_NEW_DEVICES, "IOCTL_CDROM_FIND_NEW_DEVICES"}
124 static const char *iocodex(DWORD code)
126 int i;
127 static char buffer[25];
128 for(i=0; i<sizeof(iocodextable)/sizeof(struct iocodexs); i++)
129 if (code==iocodextable[i].code)
130 return iocodextable[i].codex;
131 sprintf(buffer, "IOCTL_CODE_%x", (int)code);
132 return buffer;
135 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
137 #define FRAME_OF_ADDR(a) (((int)(a)[1] * CD_SECS + (a)[2]) * CD_FRAMES + (a)[3])
138 #define FRAME_OF_MSF(a) (((int)(a).M * CD_SECS + (a).S) * CD_FRAMES + (a).F)
139 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
140 #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;}
142 static NTSTATUS CDROM_ReadTOC(int, CDROM_TOC*);
143 static NTSTATUS CDROM_GetStatusCode(int);
146 #ifdef linux
148 # ifndef IDE6_MAJOR
149 # define IDE6_MAJOR 88
150 # endif
151 # ifndef IDE7_MAJOR
152 # define IDE7_MAJOR 89
153 # endif
155 # ifdef CDROM_SEND_PACKET
156 /* structure for CDROM_PACKET_COMMAND ioctl */
157 /* not all Linux versions have all the fields, so we define the
158 * structure ourselves to make sure */
159 struct linux_cdrom_generic_command
161 unsigned char cmd[CDROM_PACKET_SIZE];
162 unsigned char *buffer;
163 unsigned int buflen;
164 int stat;
165 struct request_sense *sense;
166 unsigned char data_direction;
167 int quiet;
168 int timeout;
169 void *reserved[1];
171 # endif /* CDROM_SEND_PACKET */
173 #endif /* linux */
175 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
176 * this should be removed when a proper device interface is implemented
178 * (WS) We need this to keep track of current position and to safely
179 * detect media changes. Besides this should provide a great speed up
180 * for toc inquiries.
182 struct cdrom_cache {
183 int fd;
184 int count;
185 char toc_good; /* if false, will reread TOC from disk */
186 CDROM_TOC toc;
187 SUB_Q_CURRENT_POSITION CurrentPosition;
189 static struct cdrom_cache cdrom_cache[26];
191 /* Proposed media change function: not really needed at this time */
192 /* This is a 1 or 0 type of function */
193 #if 0
194 static int CDROM_MediaChanged(int dev)
196 int i;
198 struct cdrom_tochdr hdr;
199 struct cdrom_tocentry entry;
201 if (dev < 0 || dev >= 26)
202 return 0;
203 if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr) == -1 )
204 return 0;
206 if ( memcmp(&hdr, &cdrom_cache[dev].hdr, sizeof(struct cdrom_tochdr)) )
207 return 1;
209 for (i=hdr.cdth_trk0; i<=hdr.cdth_trk1+1; i++)
211 if (i == hdr.cdth_trk1 + 1)
213 entry.cdte_track = CDROM_LEADOUT;
214 } else {
215 entry.cdte_track = i;
217 entry.cdte_format = CDROM_MSF;
218 if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry) == -1)
219 return 0;
220 if ( memcmp(&entry, cdrom_cache[dev].entry+i-hdr.cdth_trk0,
221 sizeof(struct cdrom_tocentry)) )
222 return 1;
224 return 0;
226 #endif
228 /******************************************************************
229 * CDROM_SyncCache [internal]
231 * Read the TOC in and store it in the cdrom_cache structure.
232 * Further requests for the TOC will be copied from the cache
233 * unless certain events like disk ejection is detected, in which
234 * case the cache will be cleared, causing it to be resynced.
237 static int CDROM_SyncCache(int dev)
239 int i, io = 0, tsz;
240 #ifdef linux
241 struct cdrom_tochdr hdr;
242 struct cdrom_tocentry entry;
243 #elif defined(__FreeBSD__) || defined(__NetBSD__)
244 struct ioc_toc_header hdr;
245 struct ioc_read_toc_entry entry;
246 struct cd_toc_entry toc_buffer;
247 #endif
248 CDROM_TOC *toc = &cdrom_cache[dev].toc;
249 cdrom_cache[dev].toc_good = 0;
251 #ifdef linux
253 io = ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr);
254 if (io == -1)
256 WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
257 goto end;
260 TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
262 toc->FirstTrack = hdr.cdth_trk0;
263 toc->LastTrack = hdr.cdth_trk1;
264 tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
265 + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
266 toc->Length[0] = tsz >> 8;
267 toc->Length[1] = tsz;
269 for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
271 if (i == toc->LastTrack + 1)
272 entry.cdte_track = CDROM_LEADOUT;
273 else
274 entry.cdte_track = i;
275 entry.cdte_format = CDROM_MSF;
276 io = ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry);
277 if (io == -1) {
278 WARN("error read entry (%s)\n", strerror(errno));
279 goto end;
281 toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
282 toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
283 /* marking last track with leadout value as index */
284 toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
285 toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
286 toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
287 toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
288 toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
290 cdrom_cache[dev].toc_good = 1;
291 io = 0;
292 #elif defined(__FreeBSD__) || defined(__NetBSD__)
294 io = ioctl(cdrom_cache[dev].fd, CDIOREADTOCHEADER, &hdr);
295 if (io == -1)
297 WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
298 goto end;
300 toc->FirstTrack = hdr.starting_track;
301 toc->LastTrack = hdr.ending_track;
302 tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
303 + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
304 toc->Length[0] = tsz >> 8;
305 toc->Length[1] = tsz;
307 TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
309 for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
311 if (i == toc->LastTrack + 1)
313 #define LEADOUT 0xaa
314 entry.starting_track = LEADOUT;
315 } else {
316 entry.starting_track = i;
318 memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
319 entry.address_format = CD_MSF_FORMAT;
320 entry.data_len = sizeof(toc_buffer);
321 entry.data = &toc_buffer;
322 io = ioctl(cdrom_cache[dev].fd, CDIOREADTOCENTRYS, &entry);
323 if (io == -1) {
324 WARN("error read entry (%s)\n", strerror(errno));
325 goto end;
327 toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
328 toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
329 /* marking last track with leadout value as index */
330 toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
331 toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
332 toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
333 toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
334 toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
336 cdrom_cache[dev].toc_good = 1;
337 io = 0;
338 #else
339 return STATUS_NOT_SUPPORTED;
340 #endif
341 end:
342 return CDROM_GetStatusCode(io);
345 static void CDROM_ClearCacheEntry(int dev)
347 cdrom_cache[dev].toc_good = 0;
352 /******************************************************************
353 * CDROM_GetInterfaceInfo
355 * Determines the ide interface (the number after the ide), and the
356 * number of the device on that interface for ide cdroms (*port == 0).
357 * Determines the scsi information for scsi cdroms (*port >= 1).
358 * Returns false if the info cannot not be obtained.
360 * NOTE: this function is used in CDROM_InitRegistry and CDROM_GetAddress
362 static int CDROM_GetInterfaceInfo(int fd, int* port, int* iface, int* device,int* lun)
364 #if defined(linux)
366 struct stat st;
367 if ( fstat(fd, &st) == -1 || ! S_ISBLK(st.st_mode)) {
368 FIXME("cdrom not a block device!!!\n");
369 return 0;
371 *port = 0;
372 *iface = 0;
373 *device = 0;
374 *lun = 0;
375 switch (major(st.st_rdev)) {
376 case IDE0_MAJOR: *iface = 0; break;
377 case IDE1_MAJOR: *iface = 1; break;
378 case IDE2_MAJOR: *iface = 2; break;
379 case IDE3_MAJOR: *iface = 3; break;
380 case IDE4_MAJOR: *iface = 4; break;
381 case IDE5_MAJOR: *iface = 5; break;
382 case IDE6_MAJOR: *iface = 6; break;
383 case IDE7_MAJOR: *iface = 7; break;
384 default: *port = 1; break;
387 if (*port == 0)
388 *device = (minor(st.st_rdev) >> 6);
389 else
391 #ifdef SCSI_IOCTL_GET_IDLUN
392 UINT32 idlun[2];
393 if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun) != -1)
395 *port = ((idlun[0] >> 24) & 0xff) + 1;
396 *iface = (idlun[0] >> 16) & 0xff;
397 *device = idlun[0] & 0xff;
398 *lun = (idlun[0] >> 8) & 0xff;
400 else
401 #endif
403 FIXME("CD-ROM device (%d, %d) not supported\n",
404 major(st.st_rdev), minor(st.st_rdev));
405 return 0;
408 return 1;
410 #elif defined(__NetBSD__)
412 struct scsi_addr addr;
413 if (ioctl(fd, SCIOCIDENTIFY, &addr) != -1) {
414 switch (addr.type) {
415 case TYPE_SCSI: *port = 1;
416 *iface = addr.addr.scsi.scbus;
417 *device = addr.addr.scsi.target;
418 *lun = addr.addr.scsi.lun;
419 break;
420 case TYPE_ATAPI: *port = 0;
421 *iface = addr.addr.atapi.atbus;
422 *device = addr.addr.atapi.drive;
423 *lun = 0;
424 break;
426 return 1;
428 return 0;
430 #elif defined(__FreeBSD__)
431 FIXME("not implemented for BSD\n");
432 return 0;
433 #else
434 FIXME("not implemented for nonlinux\n");
435 return 0;
436 #endif
440 /******************************************************************
441 * CDROM_InitRegistry
443 * Initializes registry to contain scsi info about the cdrom in NT.
444 * All devices (even not real scsi ones) have this info in NT.
445 * TODO: for now it only works for non scsi devices
446 * NOTE: programs usually read these registry entries after sending the
447 * IOCTL_SCSI_GET_ADDRESS ioctl to the cdrom
449 void CDROM_InitRegistry(int fd)
451 int portnum, busid, targetid, lun;
452 OBJECT_ATTRIBUTES attr;
453 UNICODE_STRING nameW;
454 WCHAR dataW[50];
455 DWORD lenW;
456 char buffer[40];
457 DWORD value;
458 const char *data;
459 HKEY scsiKey;
460 HKEY portKey;
461 HKEY busKey;
462 HKEY targetKey;
463 DWORD disp;
465 attr.Length = sizeof(attr);
466 attr.RootDirectory = 0;
467 attr.ObjectName = &nameW;
468 attr.Attributes = 0;
469 attr.SecurityDescriptor = NULL;
470 attr.SecurityQualityOfService = NULL;
472 if ( ! CDROM_GetInterfaceInfo(fd, &portnum, &busid, &targetid, &lun))
473 return;
475 /* Ensure there is Scsi key */
476 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\HARDWARE\\DEVICEMAP\\Scsi" ) ||
477 NtCreateKey( &scsiKey, KEY_ALL_ACCESS, &attr, 0,
478 NULL, REG_OPTION_VOLATILE, &disp ))
480 ERR("Cannot create DEVICEMAP\\Scsi registry key\n" );
481 return;
483 RtlFreeUnicodeString( &nameW );
485 snprintf(buffer,sizeof(buffer),"Scsi Port %d",portnum);
486 attr.RootDirectory = scsiKey;
487 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
488 NtCreateKey( &portKey, KEY_ALL_ACCESS, &attr, 0,
489 NULL, REG_OPTION_VOLATILE, &disp ))
491 ERR("Cannot create DEVICEMAP\\Scsi Port registry key\n" );
492 return;
494 RtlFreeUnicodeString( &nameW );
496 RtlCreateUnicodeStringFromAsciiz( &nameW, "Driver" );
497 data = "atapi";
498 RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
499 NtSetValueKey( portKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
500 RtlFreeUnicodeString( &nameW );
501 value = 10;
502 RtlCreateUnicodeStringFromAsciiz( &nameW, "FirstBusTimeScanInMs" );
503 NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
504 RtlFreeUnicodeString( &nameW );
505 value = 0;
506 #ifdef HDIO_GET_DMA
508 int dma;
509 if (ioctl(fd,HDIO_GET_DMA, &dma) != -1) {
510 value = dma;
511 TRACE("setting dma to %lx\n", value);
514 #endif
515 RtlCreateUnicodeStringFromAsciiz( &nameW, "DMAEnabled" );
516 NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
517 RtlFreeUnicodeString( &nameW );
519 snprintf(buffer,40,"Scsi Bus %d", busid);
520 attr.RootDirectory = portKey;
521 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
522 NtCreateKey( &busKey, KEY_ALL_ACCESS, &attr, 0,
523 NULL, REG_OPTION_VOLATILE, &disp ))
525 ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus registry key\n" );
526 return;
528 RtlFreeUnicodeString( &nameW );
530 attr.RootDirectory = busKey;
531 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Initiator Id 255" ) ||
532 NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
533 NULL, REG_OPTION_VOLATILE, &disp ))
535 ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus\\Initiator Id 255 registry key\n" );
536 return;
538 RtlFreeUnicodeString( &nameW );
539 NtClose( targetKey );
541 snprintf(buffer,40,"Target Id %d", targetid);
542 attr.RootDirectory = busKey;
543 if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
544 NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
545 NULL, REG_OPTION_VOLATILE, &disp ))
547 ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus 0\\Target Id registry key\n" );
548 return;
550 RtlFreeUnicodeString( &nameW );
552 RtlCreateUnicodeStringFromAsciiz( &nameW, "Type" );
553 data = "CdRomPeripheral";
554 RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
555 NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
556 RtlFreeUnicodeString( &nameW );
557 /* FIXME - maybe read the real identifier?? */
558 RtlCreateUnicodeStringFromAsciiz( &nameW, "Identifier" );
559 data = "Wine CDROM";
560 RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
561 NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
562 RtlFreeUnicodeString( &nameW );
563 /* FIXME - we always use Cdrom0 - do not know about the nt behaviour */
564 RtlCreateUnicodeStringFromAsciiz( &nameW, "DeviceName" );
565 data = "Cdrom0";
566 RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data));
567 NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
568 RtlFreeUnicodeString( &nameW );
570 NtClose( targetKey );
571 NtClose( busKey );
572 NtClose( portKey );
573 NtClose( scsiKey );
577 /******************************************************************
578 * CDROM_Open
581 static NTSTATUS CDROM_Open(HANDLE hDevice, DWORD clientID, int* dev)
583 *dev = LOWORD(clientID);
585 if (*dev >= 26) return STATUS_NO_SUCH_DEVICE;
587 if (!cdrom_cache[*dev].count)
589 char root[4];
590 const char *device;
592 strcpy(root, "A:\\");
593 root[0] += *dev;
594 if (GetDriveTypeA(root) != DRIVE_CDROM) return STATUS_NO_SUCH_DEVICE;
595 if (!(device = DRIVE_GetDevice(*dev))) return STATUS_NO_SUCH_DEVICE;
596 cdrom_cache[*dev].fd = open(device, O_RDONLY|O_NONBLOCK);
597 if (cdrom_cache[*dev].fd == -1)
599 FIXME("Can't open configured CD-ROM drive at %s (device %s): %s\n",
600 root, DRIVE_GetDevice(*dev), strerror(errno));
601 return STATUS_NO_SUCH_DEVICE;
604 cdrom_cache[*dev].count++;
605 TRACE("%d, %d, %d\n", *dev, cdrom_cache[*dev].fd, cdrom_cache[*dev].count);
606 return STATUS_SUCCESS;
609 /******************************************************************
610 * CDROM_Close
614 static void CDROM_Close(DWORD clientID)
616 int dev = LOWORD(clientID);
618 if (dev >= 26 /*|| fd != cdrom_cache[dev].fd*/) FIXME("how come\n");
619 if (--cdrom_cache[dev].count == 0)
621 close(cdrom_cache[dev].fd);
622 cdrom_cache[dev].fd = -1;
626 /******************************************************************
627 * CDROM_GetStatusCode
631 static NTSTATUS CDROM_GetStatusCode(int io)
633 if (io == 0) return STATUS_SUCCESS;
634 switch (errno)
636 case EIO:
637 #ifdef ENOMEDIUM
638 case ENOMEDIUM:
639 #endif
640 return STATUS_NO_MEDIA_IN_DEVICE;
641 case EPERM:
642 return STATUS_ACCESS_DENIED;
643 case EINVAL:
644 return STATUS_INVALID_PARAMETER;
645 /* case EBADF: Bad file descriptor */
646 case EOPNOTSUPP:
647 return STATUS_NOT_SUPPORTED;
649 FIXME("Unmapped error code %d: %s\n", errno, strerror(errno));
650 return STATUS_IO_DEVICE_ERROR;
653 /******************************************************************
654 * CDROM_GetControl
657 static NTSTATUS CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
659 cac->LbaFormat = 0; /* FIXME */
660 cac->LogicalBlocksPerSecond = 1; /* FIXME */
661 return STATUS_NOT_SUPPORTED;
664 /******************************************************************
665 * CDROM_GetDeviceNumber
668 static NTSTATUS CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
670 return STATUS_NOT_SUPPORTED;
673 /******************************************************************
674 * CDROM_GetDriveGeometry
677 static NTSTATUS CDROM_GetDriveGeometry(int dev, DISK_GEOMETRY* dg)
679 CDROM_TOC toc;
680 NTSTATUS ret = 0;
681 int fsize = 0;
683 if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
685 fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
686 - FRAME_OF_TOC(toc, 1); /* Total size in frames */
688 dg->Cylinders.s.LowPart = fsize / (64 * 32);
689 dg->Cylinders.s.HighPart = 0;
690 dg->MediaType = RemovableMedia;
691 dg->TracksPerCylinder = 64;
692 dg->SectorsPerTrack = 32;
693 dg->BytesPerSector= 2048;
694 return ret;
697 /**************************************************************************
698 * CDROM_Reset [internal]
700 static NTSTATUS CDROM_ResetAudio(int dev)
702 #if defined(linux)
703 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMRESET));
704 #elif defined(__FreeBSD__) || defined(__NetBSD__)
705 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCRESET, NULL));
706 #else
707 return STATUS_NOT_SUPPORTED;
708 #endif
711 /******************************************************************
712 * CDROM_SetTray
716 static NTSTATUS CDROM_SetTray(int dev, BOOL doEject)
718 #if defined(linux)
719 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, doEject ? CDROMEJECT : CDROMCLOSETRAY));
720 #elif defined(__FreeBSD__) || defined(__NetBSD__)
721 return CDROM_GetStatusCode((ioctl(cdrom_cache[dev].fd, CDIOCALLOW, NULL)) ||
722 (ioctl(cdrom_cache[dev].fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
723 (ioctl(cdrom_cache[dev].fd, CDIOCPREVENT, NULL)));
724 #else
725 return STATUS_NOT_SUPPORTED;
726 #endif
729 /******************************************************************
730 * CDROM_ControlEjection
734 static NTSTATUS CDROM_ControlEjection(int dev, const PREVENT_MEDIA_REMOVAL* rmv)
736 #if defined(linux)
737 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
738 #elif defined(__FreeBSD__) || defined(__NetBSD__)
739 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
740 #else
741 return STATUS_NOT_SUPPORTED;
742 #endif
745 /******************************************************************
746 * CDROM_ReadTOC
750 static NTSTATUS CDROM_ReadTOC(int dev, CDROM_TOC* toc)
752 NTSTATUS ret = STATUS_NOT_SUPPORTED;
754 if (dev < 0 || dev >= 26)
755 return STATUS_INVALID_PARAMETER;
756 if ( !cdrom_cache[dev].toc_good ) {
757 ret = CDROM_SyncCache(dev);
758 if ( ret )
759 return ret;
761 *toc = cdrom_cache[dev].toc;
762 return STATUS_SUCCESS;
765 /******************************************************************
766 * CDROM_GetDiskData
770 static NTSTATUS CDROM_GetDiskData(int dev, CDROM_DISK_DATA* data)
772 CDROM_TOC toc;
773 NTSTATUS ret;
774 int i;
776 if ((ret = CDROM_ReadTOC(dev, &toc)) != 0) return ret;
777 data->DiskData = 0;
778 for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
779 if (toc.TrackData[i-toc.FirstTrack].Control & 0x04)
780 data->DiskData |= CDROM_DISK_DATA_TRACK;
781 else
782 data->DiskData |= CDROM_DISK_AUDIO_TRACK;
784 return STATUS_SUCCESS;
787 /******************************************************************
788 * CDROM_ReadQChannel
792 static NTSTATUS CDROM_ReadQChannel(int dev, const CDROM_SUB_Q_DATA_FORMAT* fmt,
793 SUB_Q_CHANNEL_DATA* data)
795 NTSTATUS ret = STATUS_NOT_SUPPORTED;
796 #ifdef linux
797 unsigned size;
798 SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data;
799 int io;
800 struct cdrom_subchnl sc;
801 sc.cdsc_format = CDROM_MSF;
803 io = ioctl(cdrom_cache[dev].fd, CDROMSUBCHNL, &sc);
804 if (io == -1)
806 TRACE("opened or no_media (%s)!\n", strerror(errno));
807 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
808 CDROM_ClearCacheEntry(dev);
809 goto end;
812 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
814 switch (sc.cdsc_audiostatus) {
815 case CDROM_AUDIO_INVALID:
816 CDROM_ClearCacheEntry(dev);
817 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
818 break;
819 case CDROM_AUDIO_NO_STATUS:
820 CDROM_ClearCacheEntry(dev);
821 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
822 break;
823 case CDROM_AUDIO_PLAY:
824 hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
825 break;
826 case CDROM_AUDIO_PAUSED:
827 hdr->AudioStatus = AUDIO_STATUS_PAUSED;
828 break;
829 case CDROM_AUDIO_COMPLETED:
830 hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
831 break;
832 case CDROM_AUDIO_ERROR:
833 hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
834 break;
835 default:
836 TRACE("status=%02X !\n", sc.cdsc_audiostatus);
837 break;
839 switch (fmt->Format)
841 case IOCTL_CDROM_CURRENT_POSITION:
842 size = sizeof(SUB_Q_CURRENT_POSITION);
843 if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
844 data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
845 data->CurrentPosition.Control = sc.cdsc_ctrl;
846 data->CurrentPosition.ADR = sc.cdsc_adr;
847 data->CurrentPosition.TrackNumber = sc.cdsc_trk;
848 data->CurrentPosition.IndexNumber = sc.cdsc_ind;
850 data->CurrentPosition.AbsoluteAddress[0] = 0;
851 data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute;
852 data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
853 data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
855 data->CurrentPosition.TrackRelativeAddress[0] = 0;
856 data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute;
857 data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
858 data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
860 cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
862 else /* not playing */
864 cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
865 data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
867 break;
868 case IOCTL_CDROM_MEDIA_CATALOG:
869 size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
870 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
872 struct cdrom_mcn mcn;
873 if ((io = ioctl(cdrom_cache[dev].fd, CDROM_GET_MCN, &mcn)) == -1) goto end;
875 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
876 data->MediaCatalog.Mcval = 0; /* FIXME */
877 memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
878 data->MediaCatalog.MediaCatalog[14] = 0;
880 break;
881 case IOCTL_CDROM_TRACK_ISRC:
882 size = sizeof(SUB_Q_CURRENT_POSITION);
883 FIXME("TrackIsrc: NIY on linux\n");
884 data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
885 data->TrackIsrc.Tcval = 0;
886 io = 0;
887 break;
890 end:
891 ret = CDROM_GetStatusCode(io);
892 #elif defined(__FreeBSD__) || defined(__NetBSD__)
893 unsigned size;
894 SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data;
895 int io;
896 struct ioc_read_subchannel read_sc;
897 struct cd_sub_channel_info sc;
899 read_sc.address_format = CD_MSF_FORMAT;
900 read_sc.track = 0;
901 read_sc.data_len = sizeof(sc);
902 read_sc.data = &sc;
903 switch (fmt->Format)
905 case IOCTL_CDROM_CURRENT_POSITION:
906 read_sc.data_format = CD_CURRENT_POSITION;
907 break;
908 case IOCTL_CDROM_MEDIA_CATALOG:
909 read_sc.data_format = CD_MEDIA_CATALOG;
910 break;
911 case IOCTL_CDROM_TRACK_ISRC:
912 read_sc.data_format = CD_TRACK_INFO;
913 sc.what.track_info.track_number = data->TrackIsrc.Track;
914 break;
916 io = ioctl(cdrom_cache[dev].fd, CDIOCREADSUBCHANNEL, &read_sc);
917 if (io == -1)
919 TRACE("opened or no_media (%s)!\n", strerror(errno));
920 CDROM_ClearCacheEntry(dev);
921 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
922 goto end;
925 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
927 switch (sc.header.audio_status) {
928 case CD_AS_AUDIO_INVALID:
929 CDROM_ClearCacheEntry(dev);
930 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
931 break;
932 case CD_AS_NO_STATUS:
933 CDROM_ClearCacheEntry(dev);
934 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
935 break;
936 case CD_AS_PLAY_IN_PROGRESS:
937 hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
938 break;
939 case CD_AS_PLAY_PAUSED:
940 hdr->AudioStatus = AUDIO_STATUS_PAUSED;
941 break;
942 case CD_AS_PLAY_COMPLETED:
943 hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
944 break;
945 case CD_AS_PLAY_ERROR:
946 hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
947 break;
948 default:
949 TRACE("status=%02X !\n", sc.header.audio_status);
951 switch (fmt->Format)
953 case IOCTL_CDROM_CURRENT_POSITION:
954 size = sizeof(SUB_Q_CURRENT_POSITION);
955 if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
956 data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
957 data->CurrentPosition.Control = sc.what.position.control;
958 data->CurrentPosition.ADR = sc.what.position.addr_type;
959 data->CurrentPosition.TrackNumber = sc.what.position.track_number;
960 data->CurrentPosition.IndexNumber = sc.what.position.index_number;
962 data->CurrentPosition.AbsoluteAddress[0] = 0;
963 data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
964 data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
965 data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
966 data->CurrentPosition.TrackRelativeAddress[0] = 0;
967 data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
968 data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
969 data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
970 cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
972 else { /* not playing */
973 cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
974 data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
976 break;
977 case IOCTL_CDROM_MEDIA_CATALOG:
978 size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
979 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
980 data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
981 memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
982 break;
983 case IOCTL_CDROM_TRACK_ISRC:
984 size = sizeof(SUB_Q_CURRENT_POSITION);
985 data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
986 data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
987 memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
988 break;
991 end:
992 ret = CDROM_GetStatusCode(io);
993 #endif
994 return ret;
997 /******************************************************************
998 * CDROM_Verify
1002 static NTSTATUS CDROM_Verify(int dev)
1004 /* quick implementation */
1005 CDROM_SUB_Q_DATA_FORMAT fmt;
1006 SUB_Q_CHANNEL_DATA data;
1008 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
1009 return CDROM_ReadQChannel(dev, &fmt, &data) ? 1 : 0;
1012 /******************************************************************
1013 * CDROM_PlayAudioMSF
1017 static NTSTATUS CDROM_PlayAudioMSF(int dev, const CDROM_PLAY_AUDIO_MSF* audio_msf)
1019 NTSTATUS ret = STATUS_NOT_SUPPORTED;
1020 #ifdef linux
1021 struct cdrom_msf msf;
1022 int io;
1024 msf.cdmsf_min0 = audio_msf->StartingM;
1025 msf.cdmsf_sec0 = audio_msf->StartingS;
1026 msf.cdmsf_frame0 = audio_msf->StartingF;
1027 msf.cdmsf_min1 = audio_msf->EndingM;
1028 msf.cdmsf_sec1 = audio_msf->EndingS;
1029 msf.cdmsf_frame1 = audio_msf->EndingF;
1031 io = ioctl(cdrom_cache[dev].fd, CDROMSTART);
1032 if (io == -1)
1034 WARN("motor doesn't start !\n");
1035 goto end;
1037 io = ioctl(cdrom_cache[dev].fd, CDROMPLAYMSF, &msf);
1038 if (io == -1)
1040 WARN("device doesn't play !\n");
1041 goto end;
1043 TRACE("msf = %d:%d:%d %d:%d:%d\n",
1044 msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
1045 msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
1046 end:
1047 ret = CDROM_GetStatusCode(io);
1048 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1049 struct ioc_play_msf msf;
1050 int io;
1052 msf.start_m = audio_msf->StartingM;
1053 msf.start_s = audio_msf->StartingS;
1054 msf.start_f = audio_msf->StartingF;
1055 msf.end_m = audio_msf->EndingM;
1056 msf.end_s = audio_msf->EndingS;
1057 msf.end_f = audio_msf->EndingF;
1059 io = ioctl(cdrom_cache[dev].fd, CDIOCSTART, NULL);
1060 if (io == -1)
1062 WARN("motor doesn't start !\n");
1063 goto end;
1065 io = ioctl(cdrom_cache[dev].fd, CDIOCPLAYMSF, &msf);
1066 if (io == -1)
1068 WARN("device doesn't play !\n");
1069 goto end;
1071 TRACE("msf = %d:%d:%d %d:%d:%d\n",
1072 msf.start_m, msf.start_s, msf.start_f,
1073 msf.end_m, msf.end_s, msf.end_f);
1074 end:
1075 ret = CDROM_GetStatusCode(io);
1076 #endif
1077 return ret;
1080 /******************************************************************
1081 * CDROM_SeekAudioMSF
1085 static NTSTATUS CDROM_SeekAudioMSF(int dev, const CDROM_SEEK_AUDIO_MSF* audio_msf)
1087 CDROM_TOC toc;
1088 int i, io, frame;
1089 SUB_Q_CURRENT_POSITION *cp;
1090 #if defined(linux)
1091 struct cdrom_msf0 msf;
1092 struct cdrom_subchnl sc;
1093 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1094 struct ioc_play_msf msf;
1095 struct ioc_read_subchannel read_sc;
1096 struct cd_sub_channel_info sc;
1097 int final_frame;
1098 #endif
1100 /* Use the information on the TOC to compute the new current
1101 * position, which is shadowed on the cache. [Portable]. */
1102 frame = FRAME_OF_MSF(*audio_msf);
1103 cp = &cdrom_cache[dev].CurrentPosition;
1104 if ((io = CDROM_ReadTOC(dev, &toc)) != 0) return io;
1106 for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
1107 if (FRAME_OF_TOC(toc,i)>frame) break;
1108 if (i <= toc.FirstTrack || i > toc.LastTrack+1)
1109 return STATUS_INVALID_PARAMETER;
1110 i--;
1111 cp->FormatCode = IOCTL_CDROM_CURRENT_POSITION;
1112 cp->Control = toc.TrackData[i-toc.FirstTrack].Control;
1113 cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr;
1114 cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber;
1115 cp->IndexNumber = 0; /* FIXME: where do they keep these? */
1116 cp->AbsoluteAddress[0] = 0;
1117 cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1];
1118 cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2];
1119 cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3];
1120 frame -= FRAME_OF_TOC(toc,i);
1121 cp->TrackRelativeAddress[0] = 0;
1122 MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame);
1124 /* If playing, then issue a seek command, otherwise do nothing */
1125 #ifdef linux
1126 sc.cdsc_format = CDROM_MSF;
1128 io = ioctl(cdrom_cache[dev].fd, CDROMSUBCHNL, &sc);
1129 if (io == -1)
1131 TRACE("opened or no_media (%s)!\n", strerror(errno));
1132 CDROM_ClearCacheEntry(dev);
1133 return CDROM_GetStatusCode(io);
1135 if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY)
1137 msf.minute = audio_msf->M;
1138 msf.second = audio_msf->S;
1139 msf.frame = audio_msf->F;
1140 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMSEEK, &msf));
1142 return STATUS_SUCCESS;
1143 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1144 read_sc.address_format = CD_MSF_FORMAT;
1145 read_sc.track = 0;
1146 read_sc.data_len = sizeof(sc);
1147 read_sc.data = &sc;
1148 read_sc.data_format = CD_CURRENT_POSITION;
1150 io = ioctl(cdrom_cache[dev].fd, CDIOCREADSUBCHANNEL, &read_sc);
1151 if (io == -1)
1153 TRACE("opened or no_media (%s)!\n", strerror(errno));
1154 CDROM_ClearCacheEntry(dev);
1155 return CDROM_GetStatusCode(io);
1157 if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS)
1160 msf.start_m = audio_msf->M;
1161 msf.start_s = audio_msf->S;
1162 msf.start_f = audio_msf->F;
1163 final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1;
1164 MSF_OF_FRAME(msf.end_m, final_frame);
1166 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCPLAYMSF, &msf));
1168 return STATUS_SUCCESS;
1169 #else
1170 return STATUS_NOT_SUPPORTED;
1171 #endif
1174 /******************************************************************
1175 * CDROM_PauseAudio
1179 static NTSTATUS CDROM_PauseAudio(int dev)
1181 #if defined(linux)
1182 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMPAUSE));
1183 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1184 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCPAUSE, NULL));
1185 #else
1186 return STATUS_NOT_SUPPORTED;
1187 #endif
1190 /******************************************************************
1191 * CDROM_ResumeAudio
1195 static NTSTATUS CDROM_ResumeAudio(int dev)
1197 #if defined(linux)
1198 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMRESUME));
1199 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1200 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCRESUME, NULL));
1201 #else
1202 return STATUS_NOT_SUPPORTED;
1203 #endif
1206 /******************************************************************
1207 * CDROM_StopAudio
1211 static NTSTATUS CDROM_StopAudio(int dev)
1213 #if defined(linux)
1214 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMSTOP));
1215 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1216 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCSTOP, NULL));
1217 #else
1218 return STATUS_NOT_SUPPORTED;
1219 #endif
1222 /******************************************************************
1223 * CDROM_GetVolume
1227 static NTSTATUS CDROM_GetVolume(int dev, VOLUME_CONTROL* vc)
1229 #if defined(linux)
1230 struct cdrom_volctrl volc;
1231 int io;
1233 io = ioctl(cdrom_cache[dev].fd, CDROMVOLREAD, &volc);
1234 if (io != -1)
1236 vc->PortVolume[0] = volc.channel0;
1237 vc->PortVolume[1] = volc.channel1;
1238 vc->PortVolume[2] = volc.channel2;
1239 vc->PortVolume[3] = volc.channel3;
1241 return CDROM_GetStatusCode(io);
1242 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1243 struct ioc_vol volc;
1244 int io;
1246 io = ioctl(cdrom_cache[dev].fd, CDIOCGETVOL, &volc);
1247 if (io != -1)
1249 vc->PortVolume[0] = volc.vol[0];
1250 vc->PortVolume[1] = volc.vol[1];
1251 vc->PortVolume[2] = volc.vol[2];
1252 vc->PortVolume[3] = volc.vol[3];
1254 return CDROM_GetStatusCode(io);
1255 #else
1256 return STATUS_NOT_SUPPORTED;
1257 #endif
1260 /******************************************************************
1261 * CDROM_SetVolume
1265 static NTSTATUS CDROM_SetVolume(int dev, const VOLUME_CONTROL* vc)
1267 #if defined(linux)
1268 struct cdrom_volctrl volc;
1270 volc.channel0 = vc->PortVolume[0];
1271 volc.channel1 = vc->PortVolume[1];
1272 volc.channel2 = vc->PortVolume[2];
1273 volc.channel3 = vc->PortVolume[3];
1275 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDROMVOLCTRL, &volc));
1276 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1277 struct ioc_vol volc;
1279 volc.vol[0] = vc->PortVolume[0];
1280 volc.vol[1] = vc->PortVolume[1];
1281 volc.vol[2] = vc->PortVolume[2];
1282 volc.vol[3] = vc->PortVolume[3];
1284 return CDROM_GetStatusCode(ioctl(cdrom_cache[dev].fd, CDIOCSETVOL, &volc));
1285 #else
1286 return STATUS_NOT_SUPPORTED;
1287 #endif
1290 /******************************************************************
1291 * CDROM_RawRead
1295 static NTSTATUS CDROM_RawRead(int dev, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
1297 int ret = STATUS_NOT_SUPPORTED;
1298 int io = -1;
1299 DWORD sectSize;
1301 switch (raw->TrackMode)
1303 case YellowMode2: sectSize = 2336; break;
1304 case XAForm2: sectSize = 2328; break;
1305 case CDDA: sectSize = 2352; break;
1306 default: return STATUS_INVALID_PARAMETER;
1308 if (len < raw->SectorCount * sectSize) return STATUS_BUFFER_TOO_SMALL;
1309 /* strangely enough, it seems that sector offsets are always indicated with a size of 2048,
1310 * even if a larger size if read...
1312 #if defined(linux)
1314 struct cdrom_read cdr;
1315 struct cdrom_read_audio cdra;
1317 switch (raw->TrackMode)
1319 case YellowMode2:
1320 if (raw->DiskOffset.s.HighPart) FIXME("Unsupported value\n");
1321 cdr.cdread_lba = raw->DiskOffset.s.LowPart; /* FIXME ? */
1322 cdr.cdread_bufaddr = buffer;
1323 cdr.cdread_buflen = raw->SectorCount * sectSize;
1324 io = ioctl(cdrom_cache[dev].fd, CDROMREADMODE2, &cdr);
1325 break;
1326 case XAForm2:
1327 FIXME("XAForm2: NIY\n");
1328 return ret;
1329 case CDDA:
1330 /* FIXME: the output doesn't seem 100% correct... in fact output is shifted
1331 * between by NT2K box and this... should check on the same drive...
1332 * otherwise, I fear a 2352/2368 mismatch somewhere in one of the drivers
1333 * (linux/NT).
1334 * Anyway, that's not critical at all. We're talking of 16/32 bytes, we're
1335 * talking of 0.2 ms of sound
1337 /* 2048 = 2 ** 11 */
1338 if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
1339 cdra.addr.lba = ((raw->DiskOffset.s.LowPart >> 11) |
1340 (raw->DiskOffset.s.HighPart << (32 - 11))) - 1;
1341 FIXME("reading at %u\n", cdra.addr.lba);
1342 cdra.addr_format = CDROM_LBA;
1343 cdra.nframes = raw->SectorCount;
1344 cdra.buf = buffer;
1345 io = ioctl(cdrom_cache[dev].fd, CDROMREADAUDIO, &cdra);
1346 break;
1347 default:
1348 FIXME("NIY: %d\n", raw->TrackMode);
1349 return ret;
1352 #elif defined(__FreeBSD__)
1354 struct ioc_read_audio ira;
1356 switch (raw->TrackMode)
1358 case YellowMode2:
1359 FIXME("YellowMode2: NIY\n");
1360 return ret;
1361 case XAForm2:
1362 FIXME("XAForm2: NIY\n");
1363 return ret;
1364 case CDDA:
1365 /* 2048 = 2 ** 11 */
1366 if (raw->DiskOffset.s.HighPart & ~2047) FIXME("Unsupported value\n");
1367 ira.address.lba = ((raw->DiskOffset.s.LowPart >> 11) |
1368 raw->DiskOffset.s.HighPart << (32 - 11)) - 1;
1369 ira.address_format = CD_LBA_FORMAT;
1370 ira.nframes = raw->SectorCount;
1371 ira.buffer = buffer;
1372 io = ioctl(cdrom_cache[dev].fd, CDIOCREADAUDIO, &ira);
1373 break;
1376 #elif defined(__NetBSD__)
1378 switch (raw->TrackMode)
1380 case YellowMode2:
1381 FIXME("YellowMode2: NIY\n");
1382 return ret;
1383 case XAForm2:
1384 FIXME("XAForm2: NIY\n");
1385 return ret;
1386 case CDDA:
1387 FIXME("CDDA: NIY\n");
1388 return ret;
1391 #endif
1392 *sz = sectSize * raw->SectorCount;
1393 ret = CDROM_GetStatusCode(io);
1394 return ret;
1397 /******************************************************************
1398 * CDROM_ScsiPassThroughDirect
1402 static NTSTATUS CDROM_ScsiPassThroughDirect(int dev, PSCSI_PASS_THROUGH_DIRECT pPacket)
1404 int ret = STATUS_NOT_SUPPORTED;
1405 #if defined(linux) && defined(CDROM_SEND_PACKET)
1406 struct linux_cdrom_generic_command cmd;
1407 struct request_sense sense;
1408 int io;
1410 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1411 return STATUS_BUFFER_TOO_SMALL;
1413 if (pPacket->CdbLength > 12)
1414 return STATUS_INVALID_PARAMETER;
1416 if (pPacket->SenseInfoLength > sizeof(sense))
1417 return STATUS_INVALID_PARAMETER;
1419 memset(&cmd, 0, sizeof(cmd));
1420 memset(&sense, 0, sizeof(sense));
1422 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1424 cmd.buffer = pPacket->DataBuffer;
1425 cmd.buflen = pPacket->DataTransferLength;
1426 cmd.sense = &sense;
1427 cmd.quiet = 0;
1428 cmd.timeout = pPacket->TimeOutValue*HZ;
1430 switch (pPacket->DataIn)
1432 case SCSI_IOCTL_DATA_OUT:
1433 cmd.data_direction = CGC_DATA_WRITE;
1434 break;
1435 case SCSI_IOCTL_DATA_IN:
1436 cmd.data_direction = CGC_DATA_READ;
1437 break;
1438 case SCSI_IOCTL_DATA_UNSPECIFIED:
1439 cmd.data_direction = CGC_DATA_NONE;
1440 break;
1441 default:
1442 return STATUS_INVALID_PARAMETER;
1445 io = ioctl(cdrom_cache[dev].fd, CDROM_SEND_PACKET, &cmd);
1447 if (pPacket->SenseInfoLength != 0)
1449 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1450 &sense, pPacket->SenseInfoLength);
1453 pPacket->ScsiStatus = cmd.stat;
1455 ret = CDROM_GetStatusCode(io);
1457 #elif defined(__NetBSD__)
1458 scsireq_t cmd;
1459 int io;
1461 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1462 return STATUS_BUFFER_TOO_SMALL;
1464 if (pPacket->CdbLength > 12)
1465 return STATUS_INVALID_PARAMETER;
1467 if (pPacket->SenseInfoLength > SENSEBUFLEN)
1468 return STATUS_INVALID_PARAMETER;
1470 memset(&cmd, 0, sizeof(cmd));
1471 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1473 cmd.cmdlen = pPacket->CdbLength;
1474 cmd.databuf = pPacket->DataBuffer;
1475 cmd.datalen = pPacket->DataTransferLength;
1476 cmd.senselen = pPacket->SenseInfoLength;
1477 cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */
1479 switch (pPacket->DataIn)
1481 case SCSI_IOCTL_DATA_OUT:
1482 cmd.flags |= SCCMD_WRITE;
1483 break;
1484 case SCSI_IOCTL_DATA_IN:
1485 cmd.flags |= SCCMD_READ;
1486 break;
1487 case SCSI_IOCTL_DATA_UNSPECIFIED:
1488 cmd.flags = 0;
1489 break;
1490 default:
1491 return STATUS_INVALID_PARAMETER;
1494 io = ioctl(cdrom_cache[dev].fd, SCIOCCOMMAND, &cmd);
1496 switch (cmd.retsts)
1498 case SCCMD_OK: break;
1499 case SCCMD_TIMEOUT: return STATUS_TIMEOUT;
1500 break;
1501 case SCCMD_BUSY: return STATUS_DEVICE_BUSY;
1502 break;
1503 case SCCMD_SENSE: break;
1504 case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL;
1505 break;
1508 if (pPacket->SenseInfoLength != 0)
1510 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1511 cmd.sense, pPacket->SenseInfoLength);
1514 pPacket->ScsiStatus = cmd.status;
1516 ret = CDROM_GetStatusCode(io);
1517 #endif
1518 return ret;
1521 /******************************************************************
1522 * CDROM_ScsiPassThrough
1526 static NTSTATUS CDROM_ScsiPassThrough(int dev, PSCSI_PASS_THROUGH pPacket)
1528 int ret = STATUS_NOT_SUPPORTED;
1529 #if defined(linux) && defined(CDROM_SEND_PACKET)
1530 struct linux_cdrom_generic_command cmd;
1531 struct request_sense sense;
1532 int io;
1534 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1535 return STATUS_BUFFER_TOO_SMALL;
1537 if (pPacket->CdbLength > 12)
1538 return STATUS_INVALID_PARAMETER;
1540 if (pPacket->SenseInfoLength > sizeof(sense))
1541 return STATUS_INVALID_PARAMETER;
1543 memset(&cmd, 0, sizeof(cmd));
1544 memset(&sense, 0, sizeof(sense));
1546 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1548 if ( pPacket->DataBufferOffset > 0x1000 )
1550 cmd.buffer = (void*)pPacket->DataBufferOffset;
1552 else
1554 cmd.buffer = (char*)pPacket + pPacket->DataBufferOffset;
1556 cmd.buflen = pPacket->DataTransferLength;
1557 cmd.sense = &sense;
1558 cmd.quiet = 0;
1559 cmd.timeout = pPacket->TimeOutValue*HZ;
1561 switch (pPacket->DataIn)
1563 case SCSI_IOCTL_DATA_OUT:
1564 cmd.data_direction = CGC_DATA_WRITE;
1565 break;
1566 case SCSI_IOCTL_DATA_IN:
1567 cmd.data_direction = CGC_DATA_READ;
1568 break;
1569 case SCSI_IOCTL_DATA_UNSPECIFIED:
1570 cmd.data_direction = CGC_DATA_NONE;
1571 break;
1572 default:
1573 return STATUS_INVALID_PARAMETER;
1576 io = ioctl(cdrom_cache[dev].fd, CDROM_SEND_PACKET, &cmd);
1578 if (pPacket->SenseInfoLength != 0)
1580 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1581 &sense, pPacket->SenseInfoLength);
1584 pPacket->ScsiStatus = cmd.stat;
1586 ret = CDROM_GetStatusCode(io);
1588 #elif defined(__NetBSD__)
1589 scsireq_t cmd;
1590 int io;
1592 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1593 return STATUS_BUFFER_TOO_SMALL;
1595 if (pPacket->CdbLength > 12)
1596 return STATUS_INVALID_PARAMETER;
1598 if (pPacket->SenseInfoLength > SENSEBUFLEN)
1599 return STATUS_INVALID_PARAMETER;
1601 memset(&cmd, 0, sizeof(cmd));
1602 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1604 if ( pPacket->DataBufferOffset > 0x1000 )
1606 cmd.databuf = (void*)pPacket->DataBufferOffset;
1608 else
1610 cmd.databuf = (char*)pPacket + pPacket->DataBufferOffset;
1613 cmd.cmdlen = pPacket->CdbLength;
1614 cmd.datalen = pPacket->DataTransferLength;
1615 cmd.senselen = pPacket->SenseInfoLength;
1616 cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */
1618 switch (pPacket->DataIn)
1620 case SCSI_IOCTL_DATA_OUT:
1621 cmd.flags |= SCCMD_WRITE;
1622 break;
1623 case SCSI_IOCTL_DATA_IN:
1624 cmd.flags |= SCCMD_READ;
1625 break;
1626 case SCSI_IOCTL_DATA_UNSPECIFIED:
1627 cmd.flags = 0;
1628 break;
1629 default:
1630 return STATUS_INVALID_PARAMETER;
1633 io = ioctl(cdrom_cache[dev].fd, SCIOCCOMMAND, &cmd);
1635 switch (cmd.retsts)
1637 case SCCMD_OK: break;
1638 case SCCMD_TIMEOUT: return STATUS_TIMEOUT;
1639 break;
1640 case SCCMD_BUSY: return STATUS_DEVICE_BUSY;
1641 break;
1642 case SCCMD_SENSE: break;
1643 case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL;
1644 break;
1647 if (pPacket->SenseInfoLength != 0)
1649 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1650 cmd.sense, pPacket->SenseInfoLength);
1653 pPacket->ScsiStatus = cmd.status;
1655 ret = CDROM_GetStatusCode(io);
1656 #endif
1657 return ret;
1660 /******************************************************************
1661 * CDROM_ScsiGetCaps
1665 static NTSTATUS CDROM_ScsiGetCaps(int dev, PIO_SCSI_CAPABILITIES caps)
1667 NTSTATUS ret = STATUS_NOT_IMPLEMENTED;
1669 caps->Length = sizeof(*caps);
1670 #if defined(linux)
1671 caps->MaximumTransferLength = SG_SCATTER_SZ; /* FIXME */
1672 caps->MaximumPhysicalPages = SG_SCATTER_SZ / getpagesize();
1673 caps->SupportedAsynchronousEvents = TRUE;
1674 caps->AlignmentMask = getpagesize();
1675 caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */
1676 caps->AdapterScansDown = FALSE; /* FIXME ? */
1677 caps->AdapterUsesPio = FALSE; /* FIXME ? */
1678 ret = STATUS_SUCCESS;
1679 #else
1680 FIXME("Unimplemented\n");
1681 #endif
1682 return ret;
1685 /******************************************************************
1686 * CDROM_GetAddress
1688 * implements IOCTL_SCSI_GET_ADDRESS
1690 static NTSTATUS CDROM_GetAddress(int dev, SCSI_ADDRESS* address)
1692 int portnum, busid, targetid, lun;
1694 address->Length = sizeof(SCSI_ADDRESS);
1695 if ( ! CDROM_GetInterfaceInfo(cdrom_cache[dev].fd, &portnum,
1696 &busid, &targetid, &lun))
1697 return STATUS_NOT_SUPPORTED;
1699 address->PortNumber = portnum;
1700 address->PathId = busid; /* bus number */
1701 address->TargetId = targetid;
1702 address->Lun = lun;
1703 return STATUS_SUCCESS;
1706 /******************************************************************
1707 * CDROM_DeviceIoControl
1711 NTSTATUS CDROM_DeviceIoControl(DWORD clientID, HANDLE hDevice,
1712 HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
1713 PVOID UserApcContext,
1714 PIO_STATUS_BLOCK piosb,
1715 ULONG dwIoControlCode,
1716 LPVOID lpInBuffer, DWORD nInBufferSize,
1717 LPVOID lpOutBuffer, DWORD nOutBufferSize)
1719 DWORD sz;
1720 NTSTATUS status = STATUS_SUCCESS;
1721 int dev;
1723 TRACE("%lx[%c] %s %lx %ld %lx %ld %p\n",
1724 (DWORD)hDevice, 'A' + LOWORD(clientID), iocodex(dwIoControlCode), (DWORD)lpInBuffer, nInBufferSize,
1725 (DWORD)lpOutBuffer, nOutBufferSize, piosb);
1727 piosb->Information = 0;
1729 if ((status = CDROM_Open(hDevice, clientID, &dev))) goto error;
1731 switch (dwIoControlCode)
1733 case IOCTL_STORAGE_CHECK_VERIFY:
1734 case IOCTL_CDROM_CHECK_VERIFY:
1735 sz = 0;
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);
1740 break;
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:
1749 sz = 0;
1750 CDROM_ClearCacheEntry(dev);
1751 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1752 status = STATUS_INVALID_PARAMETER;
1753 else status = CDROM_SetTray(dev, FALSE);
1754 break;
1755 case IOCTL_STORAGE_EJECT_MEDIA:
1756 sz = 0;
1757 CDROM_ClearCacheEntry(dev);
1758 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1759 status = STATUS_INVALID_PARAMETER;
1760 else status = CDROM_SetTray(dev, TRUE);
1761 break;
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 */
1769 sz = 0;
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(dev, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
1774 break;
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);
1783 break;
1785 case IOCTL_STORAGE_RESET_DEVICE:
1786 sz = 0;
1787 CDROM_ClearCacheEntry(dev);
1788 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1789 status = STATUS_INVALID_PARAMETER;
1790 else status = CDROM_ResetAudio(dev);
1791 break;
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);
1798 break;
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, (DISK_GEOMETRY*)lpOutBuffer);
1805 break;
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, (CDROM_DISK_DATA*)lpOutBuffer);
1813 break;
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, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
1823 (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
1824 break;
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, (CDROM_TOC*)lpOutBuffer);
1831 break;
1833 /* EPP case IOCTL_CDROM_READ_TOC_EX: */
1835 case IOCTL_CDROM_PAUSE_AUDIO:
1836 sz = 0;
1837 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1838 status = STATUS_INVALID_PARAMETER;
1839 else status = CDROM_PauseAudio(dev);
1840 break;
1841 case IOCTL_CDROM_PLAY_AUDIO_MSF:
1842 sz = 0;
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(dev, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
1846 break;
1847 case IOCTL_CDROM_RESUME_AUDIO:
1848 sz = 0;
1849 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
1850 status = STATUS_INVALID_PARAMETER;
1851 else status = CDROM_ResumeAudio(dev);
1852 break;
1853 case IOCTL_CDROM_SEEK_AUDIO_MSF:
1854 sz = 0;
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, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
1858 break;
1859 case IOCTL_CDROM_STOP_AUDIO:
1860 sz = 0;
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(dev);
1865 break;
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(dev, (VOLUME_CONTROL*)lpOutBuffer);
1871 break;
1872 case IOCTL_CDROM_SET_VOLUME:
1873 sz = 0;
1874 CDROM_ClearCacheEntry(dev);
1875 if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
1876 status = STATUS_INVALID_PARAMETER;
1877 else status = CDROM_SetVolume(dev, (const VOLUME_CONTROL*)lpInBuffer);
1878 break;
1879 case IOCTL_CDROM_RAW_READ:
1880 sz = 0;
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(dev, (const RAW_READ_INFO*)lpInBuffer,
1884 lpOutBuffer, nOutBufferSize, &sz);
1885 break;
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(dev, (SCSI_ADDRESS*)lpOutBuffer);
1891 break;
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(dev, (PSCSI_PASS_THROUGH_DIRECT)lpOutBuffer);
1897 break;
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(dev, (PSCSI_PASS_THROUGH)lpOutBuffer);
1903 break;
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(dev, (PIO_SCSI_CAPABILITIES)lpOutBuffer);
1909 break;
1910 default:
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);
1914 sz = 0;
1915 status = STATUS_INVALID_PARAMETER;
1916 break;
1918 CDROM_Close(clientID);
1919 error:
1920 piosb->u.Status = status;
1921 piosb->Information = sz;
1922 if (hEvent) NtSetEvent(hEvent, NULL);
1923 return status;