msi: Increase verbosity in some failing tests.
[wine/multimedia.git] / dlls / ntdll / cdrom.c
blob60074b07fe4c541589d5de58c8b8111f882f532b
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
7 * Copyright 2005 Ivan Leo Puoti
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <errno.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #ifdef HAVE_IO_H
32 # include <io.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 #include <fcntl.h>
38 #ifdef HAVE_SYS_STAT_H
39 # include <sys/stat.h>
40 #endif
41 #include <sys/types.h>
43 #ifdef HAVE_SYS_IOCTL_H
44 #include <sys/ioctl.h>
45 #endif
46 #ifdef HAVE_SCSI_SG_H
47 # include <scsi/sg.h>
48 #endif
49 #ifdef HAVE_SCSI_SCSI_H
50 # include <scsi/scsi.h>
51 # undef REASSIGN_BLOCKS /* avoid conflict with winioctl.h */
52 #endif
53 #ifdef HAVE_SCSI_SCSI_IOCTL_H
54 # include <scsi/scsi_ioctl.h>
55 #endif
56 #ifdef HAVE_LINUX_MAJOR_H
57 # include <linux/major.h>
58 #endif
59 #ifdef HAVE_LINUX_HDREG_H
60 # include <linux/hdreg.h>
61 #endif
62 #ifdef HAVE_LINUX_PARAM_H
63 # include <linux/param.h>
64 #endif
65 #ifdef HAVE_LINUX_CDROM_H
66 # include <linux/cdrom.h>
67 #endif
68 #ifdef HAVE_LINUX_UCDROM_H
69 # include <linux/ucdrom.h>
70 #endif
71 #ifdef HAVE_SYS_CDIO_H
72 # include <sys/cdio.h>
73 #endif
74 #ifdef HAVE_SYS_SCSIIO_H
75 # include <sys/scsiio.h>
76 #endif
78 #ifdef HAVE_IOKIT_IOKITLIB_H
79 # ifndef SENSEBUFLEN
80 # include <IOKit/IOKitLib.h>
81 # include <IOKit/scsi/SCSICmds_REQUEST_SENSE_Defs.h>
82 # define SENSEBUFLEN kSenseDefaultSize
83 # endif
84 #endif
86 #define NONAMELESSUNION
87 #define NONAMELESSSTRUCT
88 #include "ntstatus.h"
89 #define WIN32_NO_STATUS
90 #include "windef.h"
91 #include "winternl.h"
92 #include "winioctl.h"
93 #include "ntddstor.h"
94 #include "ntddcdrm.h"
95 #include "ddk/ntddcdvd.h"
96 #include "ntddscsi.h"
97 #include "ntdll_misc.h"
98 #include "wine/server.h"
99 #include "wine/library.h"
100 #include "wine/debug.h"
102 WINE_DEFAULT_DEBUG_CHANNEL(cdrom);
104 /* Non-Linux systems do not have linux/cdrom.h and the like, and thus
105 lack the following constants. */
107 #ifndef CD_SECS
108 # define CD_SECS 60 /* seconds per minute */
109 #endif
110 #ifndef CD_FRAMES
111 # define CD_FRAMES 75 /* frames per second */
112 #endif
114 static const struct iocodexs
116 DWORD code;
117 const char *codex;
118 } iocodextable[] = {
119 #define X(x) { x, #x },
120 X(IOCTL_CDROM_CHECK_VERIFY)
121 X(IOCTL_CDROM_CURRENT_POSITION)
122 X(IOCTL_CDROM_DISK_TYPE)
123 X(IOCTL_CDROM_GET_CONTROL)
124 X(IOCTL_CDROM_GET_DRIVE_GEOMETRY)
125 X(IOCTL_CDROM_GET_VOLUME)
126 X(IOCTL_CDROM_LOAD_MEDIA)
127 X(IOCTL_CDROM_MEDIA_CATALOG)
128 X(IOCTL_CDROM_MEDIA_REMOVAL)
129 X(IOCTL_CDROM_PAUSE_AUDIO)
130 X(IOCTL_CDROM_PLAY_AUDIO_MSF)
131 X(IOCTL_CDROM_RAW_READ)
132 X(IOCTL_CDROM_READ_Q_CHANNEL)
133 X(IOCTL_CDROM_READ_TOC)
134 X(IOCTL_CDROM_RESUME_AUDIO)
135 X(IOCTL_CDROM_SEEK_AUDIO_MSF)
136 X(IOCTL_CDROM_SET_VOLUME)
137 X(IOCTL_CDROM_STOP_AUDIO)
138 X(IOCTL_CDROM_TRACK_ISRC)
139 X(IOCTL_DISK_MEDIA_REMOVAL)
140 X(IOCTL_DVD_END_SESSION)
141 X(IOCTL_DVD_GET_REGION)
142 X(IOCTL_DVD_READ_KEY)
143 X(IOCTL_DVD_READ_STRUCTURE)
144 X(IOCTL_DVD_SEND_KEY)
145 X(IOCTL_DVD_START_SESSION)
146 X(IOCTL_SCSI_GET_ADDRESS)
147 X(IOCTL_SCSI_GET_CAPABILITIES)
148 X(IOCTL_SCSI_GET_INQUIRY_DATA)
149 X(IOCTL_SCSI_PASS_THROUGH)
150 X(IOCTL_SCSI_PASS_THROUGH_DIRECT)
151 X(IOCTL_STORAGE_CHECK_VERIFY)
152 X(IOCTL_STORAGE_EJECTION_CONTROL)
153 X(IOCTL_STORAGE_EJECT_MEDIA)
154 X(IOCTL_STORAGE_GET_DEVICE_NUMBER)
155 X(IOCTL_STORAGE_LOAD_MEDIA)
156 X(IOCTL_STORAGE_MEDIA_REMOVAL)
157 X(IOCTL_STORAGE_RESET_DEVICE)
158 #undef X
160 static const char *iocodex(DWORD code)
162 unsigned int i;
163 static char buffer[25];
164 for(i=0; i<sizeof(iocodextable)/sizeof(struct iocodexs); i++)
165 if (code==iocodextable[i].code)
166 return iocodextable[i].codex;
167 sprintf(buffer, "IOCTL_CODE_%x", (int)code);
168 return buffer;
171 #define INQ_REPLY_LEN 36
172 #define INQ_CMD_LEN 6
174 #define FRAME_OF_ADDR(a) (((int)(a)[1] * CD_SECS + (a)[2]) * CD_FRAMES + (a)[3])
175 #define FRAME_OF_MSF(a) (((int)(a).M * CD_SECS + (a).S) * CD_FRAMES + (a).F)
176 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
177 #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;}
179 /* The documented format of DVD_LAYER_DESCRIPTOR is wrong. Even the format in the
180 * DDK's header is wrong. There are four bytes at the start which always seem to
181 * follow the sequence "02 08 00 00".
183 typedef struct
185 UCHAR MagicHeader[4];
186 UCHAR BookVersion : 4;
187 UCHAR BookType : 4;
188 UCHAR MinimumRate : 4;
189 UCHAR DiskSize : 4;
190 UCHAR LayerType : 4;
191 UCHAR TrackPath : 1;
192 UCHAR NumberOfLayers : 2;
193 UCHAR Reserved1 : 1;
194 UCHAR TrackDensity : 4;
195 UCHAR LinearDensity : 4;
196 ULONG StartingDataSector;
197 ULONG EndDataSector;
198 ULONG EndLayerZeroSector;
199 UCHAR Reserved5 : 7;
200 UCHAR BCAFlag : 1;
201 UCHAR Reserved6;
202 } internal_dvd_layer_descriptor;
205 static NTSTATUS CDROM_ReadTOC(int, int, CDROM_TOC*);
206 static NTSTATUS CDROM_GetStatusCode(int);
209 #ifdef linux
211 # ifndef IDE6_MAJOR
212 # define IDE6_MAJOR 88
213 # endif
214 # ifndef IDE7_MAJOR
215 # define IDE7_MAJOR 89
216 # endif
218 # ifdef CDROM_SEND_PACKET
219 /* structure for CDROM_PACKET_COMMAND ioctl */
220 /* not all Linux versions have all the fields, so we define the
221 * structure ourselves to make sure */
222 struct linux_cdrom_generic_command
224 unsigned char cmd[CDROM_PACKET_SIZE];
225 unsigned char *buffer;
226 unsigned int buflen;
227 int stat;
228 struct request_sense *sense;
229 unsigned char data_direction;
230 int quiet;
231 int timeout;
232 void *reserved[1];
234 # endif /* CDROM_SEND_PACKET */
236 #endif /* linux */
238 /* FIXME: this is needed because we can't open simultaneously several times /dev/cdrom
239 * this should be removed when a proper device interface is implemented
241 * (WS) We need this to keep track of current position and to safely
242 * detect media changes. Besides this should provide a great speed up
243 * for toc inquiries.
245 struct cdrom_cache {
246 dev_t device;
247 ino_t inode;
248 char toc_good; /* if false, will reread TOC from disk */
249 CDROM_TOC toc;
250 SUB_Q_CURRENT_POSITION CurrentPosition;
252 /* who has more than 5 cdroms on his/her machine ?? */
253 /* FIXME: this should grow depending on the number of cdroms we install/configure
254 * at startup
256 #define MAX_CACHE_ENTRIES 5
257 static struct cdrom_cache cdrom_cache[MAX_CACHE_ENTRIES];
259 static RTL_CRITICAL_SECTION cache_section;
260 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
262 0, 0, &cache_section,
263 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
264 0, 0, { (DWORD_PTR)(__FILE__ ": cache_section") }
266 static RTL_CRITICAL_SECTION cache_section = { &critsect_debug, -1, 0, 0, 0, 0 };
268 /* Proposed media change function: not really needed at this time */
269 /* This is a 1 or 0 type of function */
270 #if 0
271 static int CDROM_MediaChanged(int dev)
273 int i;
275 struct cdrom_tochdr hdr;
276 struct cdrom_tocentry entry;
278 if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
279 return 0;
280 if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCHDR, &hdr) == -1 )
281 return 0;
283 if ( memcmp(&hdr, &cdrom_cache[dev].hdr, sizeof(struct cdrom_tochdr)) )
284 return 1;
286 for (i=hdr.cdth_trk0; i<=hdr.cdth_trk1+1; i++)
288 if (i == hdr.cdth_trk1 + 1)
290 entry.cdte_track = CDROM_LEADOUT;
291 } else {
292 entry.cdte_track = i;
294 entry.cdte_format = CDROM_MSF;
295 if ( ioctl(cdrom_cache[dev].fd, CDROMREADTOCENTRY, &entry) == -1)
296 return 0;
297 if ( memcmp(&entry, cdrom_cache[dev].entry+i-hdr.cdth_trk0,
298 sizeof(struct cdrom_tocentry)) )
299 return 1;
301 return 0;
303 #endif
305 /******************************************************************
306 * CDROM_SyncCache [internal]
308 * Read the TOC in and store it in the cdrom_cache structure.
309 * Further requests for the TOC will be copied from the cache
310 * unless certain events like disk ejection is detected, in which
311 * case the cache will be cleared, causing it to be resynced.
312 * The cache section must be held by caller.
314 static NTSTATUS CDROM_SyncCache(int dev, int fd)
316 #ifdef linux
317 int i, tsz;
318 struct cdrom_tochdr hdr;
319 struct cdrom_tocentry entry;
321 CDROM_TOC *toc = &cdrom_cache[dev].toc;
322 cdrom_cache[dev].toc_good = 0;
324 if (ioctl(fd, CDROMREADTOCHDR, &hdr) == -1)
326 WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
327 return FILE_GetNtStatus();
330 toc->FirstTrack = hdr.cdth_trk0;
331 toc->LastTrack = hdr.cdth_trk1;
332 tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
333 + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
334 toc->Length[0] = tsz >> 8;
335 toc->Length[1] = tsz;
337 TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
339 for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
341 if (i == toc->LastTrack + 1)
342 entry.cdte_track = CDROM_LEADOUT;
343 else
344 entry.cdte_track = i;
345 entry.cdte_format = CDROM_MSF;
346 if (ioctl(fd, CDROMREADTOCENTRY, &entry) == -1)
348 WARN("error read entry (%s)\n", strerror(errno));
349 return FILE_GetNtStatus();
351 toc->TrackData[i - toc->FirstTrack].Control = entry.cdte_ctrl;
352 toc->TrackData[i - toc->FirstTrack].Adr = entry.cdte_adr;
353 /* marking last track with leadout value as index */
354 toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.cdte_track;
355 toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
356 toc->TrackData[i - toc->FirstTrack].Address[1] = entry.cdte_addr.msf.minute;
357 toc->TrackData[i - toc->FirstTrack].Address[2] = entry.cdte_addr.msf.second;
358 toc->TrackData[i - toc->FirstTrack].Address[3] = entry.cdte_addr.msf.frame;
360 cdrom_cache[dev].toc_good = 1;
361 return STATUS_SUCCESS;
363 #elif defined(__FreeBSD__) || defined(__NetBSD__)
365 int i, tsz;
366 struct ioc_toc_header hdr;
367 struct ioc_read_toc_entry entry;
368 struct cd_toc_entry toc_buffer;
370 CDROM_TOC *toc = &cdrom_cache[dev].toc;
371 cdrom_cache[dev].toc_good = 0;
373 if (ioctl(fd, CDIOREADTOCHEADER, &hdr) == -1)
375 WARN("(%d) -- Error occurred (%s)!\n", dev, strerror(errno));
376 return FILE_GetNtStatus();
378 toc->FirstTrack = hdr.starting_track;
379 toc->LastTrack = hdr.ending_track;
380 tsz = sizeof(toc->FirstTrack) + sizeof(toc->LastTrack)
381 + sizeof(TRACK_DATA) * (toc->LastTrack-toc->FirstTrack+2);
382 toc->Length[0] = tsz >> 8;
383 toc->Length[1] = tsz;
385 TRACE("caching toc from=%d to=%d\n", toc->FirstTrack, toc->LastTrack );
387 for (i = toc->FirstTrack; i <= toc->LastTrack + 1; i++)
389 if (i == toc->LastTrack + 1)
391 #define LEADOUT 0xaa
392 entry.starting_track = LEADOUT;
393 } else {
394 entry.starting_track = i;
396 memset((char *)&toc_buffer, 0, sizeof(toc_buffer));
397 entry.address_format = CD_MSF_FORMAT;
398 entry.data_len = sizeof(toc_buffer);
399 entry.data = &toc_buffer;
400 if (ioctl(fd, CDIOREADTOCENTRYS, &entry) == -1)
402 WARN("error read entry (%s)\n", strerror(errno));
403 return FILE_GetNtStatus();
405 toc->TrackData[i - toc->FirstTrack].Control = toc_buffer.control;
406 toc->TrackData[i - toc->FirstTrack].Adr = toc_buffer.addr_type;
407 /* marking last track with leadout value as index */
408 toc->TrackData[i - toc->FirstTrack].TrackNumber = entry.starting_track;
409 toc->TrackData[i - toc->FirstTrack].Address[0] = 0;
410 toc->TrackData[i - toc->FirstTrack].Address[1] = toc_buffer.addr.msf.minute;
411 toc->TrackData[i - toc->FirstTrack].Address[2] = toc_buffer.addr.msf.second;
412 toc->TrackData[i - toc->FirstTrack].Address[3] = toc_buffer.addr.msf.frame;
414 cdrom_cache[dev].toc_good = 1;
415 return STATUS_SUCCESS;
416 #else
417 return STATUS_NOT_SUPPORTED;
418 #endif
421 static void CDROM_ClearCacheEntry(int dev)
423 RtlEnterCriticalSection( &cache_section );
424 cdrom_cache[dev].toc_good = 0;
425 RtlLeaveCriticalSection( &cache_section );
430 /******************************************************************
431 * CDROM_GetInterfaceInfo
433 * Determines the ide interface (the number after the ide), and the
434 * number of the device on that interface for ide cdroms (*iface <= 1).
435 * Determines the scsi information for scsi cdroms (*iface >= 2).
436 * Returns false if the info cannot not be obtained.
438 static int CDROM_GetInterfaceInfo(int fd, UCHAR* iface, UCHAR* port, UCHAR* device, UCHAR* lun)
440 #if defined(linux)
441 struct stat st;
442 if ( fstat(fd, &st) == -1 || ! S_ISBLK(st.st_mode)) return 0;
443 *port = 0;
444 *iface = 0;
445 *device = 0;
446 *lun = 0;
447 switch (major(st.st_rdev)) {
448 case IDE0_MAJOR: *iface = 0; break;
449 case IDE1_MAJOR: *iface = 1; break;
450 case IDE2_MAJOR: *iface = 2; break;
451 case IDE3_MAJOR: *iface = 3; break;
452 case IDE4_MAJOR: *iface = 4; break;
453 case IDE5_MAJOR: *iface = 5; break;
454 case IDE6_MAJOR: *iface = 6; break;
455 case IDE7_MAJOR: *iface = 7; break;
456 default: *port = 1; break;
459 if (*port == 0)
460 *device = (minor(st.st_rdev) >> 6);
461 else
463 #ifdef SCSI_IOCTL_GET_IDLUN
464 UINT32 idlun[2];
465 if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun) != -1)
467 *port = (idlun[0] >> 24) & 0xff;
468 *iface = ((idlun[0] >> 16) & 0xff) + 2;
469 *device = idlun[0] & 0xff;
470 *lun = (idlun[0] >> 8) & 0xff;
472 else
473 #endif
475 WARN("CD-ROM device (%d, %d) not supported\n", major(st.st_rdev), minor(st.st_rdev));
476 return 0;
479 return 1;
480 #elif defined(__NetBSD__)
481 struct scsi_addr addr;
482 if (ioctl(fd, SCIOCIDENTIFY, &addr) != -1)
484 switch (addr.type)
486 case TYPE_SCSI: *port = 1;
487 *iface = addr.addr.scsi.scbus;
488 *device = addr.addr.scsi.target;
489 *lun = addr.addr.scsi.lun;
490 break;
491 case TYPE_ATAPI: *port = 0;
492 *iface = addr.addr.atapi.atbus;
493 *device = addr.addr.atapi.drive;
494 *lun = 0;
495 break;
497 return 1;
499 return 0;
500 #elif defined(__FreeBSD__)
501 FIXME("not implemented for BSD\n");
502 return 0;
503 #else
504 FIXME("not implemented for nonlinux\n");
505 return 0;
506 #endif
510 /******************************************************************
511 * CDROM_Open
514 static NTSTATUS CDROM_Open(int fd, int* dev)
516 struct stat st;
517 NTSTATUS ret = STATUS_SUCCESS;
518 int empty = -1;
520 fstat(fd, &st);
522 RtlEnterCriticalSection( &cache_section );
523 for (*dev = 0; *dev < MAX_CACHE_ENTRIES; (*dev)++)
525 if (empty == -1 &&
526 cdrom_cache[*dev].device == 0 &&
527 cdrom_cache[*dev].inode == 0)
528 empty = *dev;
529 else if (cdrom_cache[*dev].device == st.st_dev &&
530 cdrom_cache[*dev].inode == st.st_ino)
531 break;
533 if (*dev == MAX_CACHE_ENTRIES)
535 if (empty == -1) ret = STATUS_NOT_IMPLEMENTED;
536 else
538 *dev = empty;
539 cdrom_cache[*dev].device = st.st_dev;
540 cdrom_cache[*dev].inode = st.st_ino;
543 RtlLeaveCriticalSection( &cache_section );
545 TRACE("%d, %d\n", *dev, fd);
546 return ret;
549 /******************************************************************
550 * CDROM_GetStatusCode
554 static NTSTATUS CDROM_GetStatusCode(int io)
556 if (io == 0) return STATUS_SUCCESS;
557 return FILE_GetNtStatus();
560 /******************************************************************
561 * CDROM_GetControl
564 static NTSTATUS CDROM_GetControl(int dev, CDROM_AUDIO_CONTROL* cac)
566 cac->LbaFormat = 0; /* FIXME */
567 cac->LogicalBlocksPerSecond = 1; /* FIXME */
568 return STATUS_NOT_SUPPORTED;
571 /******************************************************************
572 * CDROM_GetDeviceNumber
575 static NTSTATUS CDROM_GetDeviceNumber(int dev, STORAGE_DEVICE_NUMBER* devnum)
577 return STATUS_NOT_SUPPORTED;
580 /******************************************************************
581 * CDROM_GetDriveGeometry
584 static NTSTATUS CDROM_GetDriveGeometry(int dev, int fd, DISK_GEOMETRY* dg)
586 CDROM_TOC toc;
587 NTSTATUS ret = 0;
588 int fsize = 0;
590 if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
592 fsize = FRAME_OF_TOC(toc, toc.LastTrack+1)
593 - FRAME_OF_TOC(toc, 1); /* Total size in frames */
595 dg->Cylinders.u.LowPart = fsize / (64 * 32);
596 dg->Cylinders.u.HighPart = 0;
597 dg->MediaType = RemovableMedia;
598 dg->TracksPerCylinder = 64;
599 dg->SectorsPerTrack = 32;
600 dg->BytesPerSector= 2048;
601 return ret;
604 /**************************************************************************
605 * CDROM_Reset [internal]
607 static NTSTATUS CDROM_ResetAudio(int fd)
609 #if defined(linux)
610 return CDROM_GetStatusCode(ioctl(fd, CDROMRESET));
611 #elif defined(__FreeBSD__) || defined(__NetBSD__)
612 return CDROM_GetStatusCode(ioctl(fd, CDIOCRESET, NULL));
613 #else
614 return STATUS_NOT_SUPPORTED;
615 #endif
618 /******************************************************************
619 * CDROM_SetTray
623 static NTSTATUS CDROM_SetTray(int fd, BOOL doEject)
625 #if defined(linux)
626 return CDROM_GetStatusCode(ioctl(fd, doEject ? CDROMEJECT : CDROMCLOSETRAY));
627 #elif defined(__FreeBSD__) || defined(__NetBSD__)
628 return CDROM_GetStatusCode((ioctl(fd, CDIOCALLOW, NULL)) ||
629 (ioctl(fd, doEject ? CDIOCEJECT : CDIOCCLOSE, NULL)) ||
630 (ioctl(fd, CDIOCPREVENT, NULL)));
631 #else
632 return STATUS_NOT_SUPPORTED;
633 #endif
636 /******************************************************************
637 * CDROM_ControlEjection
641 static NTSTATUS CDROM_ControlEjection(int fd, const PREVENT_MEDIA_REMOVAL* rmv)
643 #if defined(linux)
644 return CDROM_GetStatusCode(ioctl(fd, CDROM_LOCKDOOR, rmv->PreventMediaRemoval));
645 #elif defined(__FreeBSD__) || defined(__NetBSD__)
646 return CDROM_GetStatusCode(ioctl(fd, (rmv->PreventMediaRemoval) ? CDIOCPREVENT : CDIOCALLOW, NULL));
647 #else
648 return STATUS_NOT_SUPPORTED;
649 #endif
652 /******************************************************************
653 * CDROM_ReadTOC
657 static NTSTATUS CDROM_ReadTOC(int dev, int fd, CDROM_TOC* toc)
659 NTSTATUS ret = STATUS_NOT_SUPPORTED;
661 if (dev < 0 || dev >= MAX_CACHE_ENTRIES)
662 return STATUS_INVALID_PARAMETER;
664 RtlEnterCriticalSection( &cache_section );
665 if (cdrom_cache[dev].toc_good || !(ret = CDROM_SyncCache(dev, fd)))
667 *toc = cdrom_cache[dev].toc;
668 ret = STATUS_SUCCESS;
670 RtlLeaveCriticalSection( &cache_section );
671 return ret;
674 /******************************************************************
675 * CDROM_GetDiskData
679 static NTSTATUS CDROM_GetDiskData(int dev, int fd, CDROM_DISK_DATA* data)
681 CDROM_TOC toc;
682 NTSTATUS ret;
683 int i;
685 if ((ret = CDROM_ReadTOC(dev, fd, &toc)) != 0) return ret;
686 data->DiskData = 0;
687 for (i = toc.FirstTrack; i <= toc.LastTrack; i++) {
688 if (toc.TrackData[i-toc.FirstTrack].Control & 0x04)
689 data->DiskData |= CDROM_DISK_DATA_TRACK;
690 else
691 data->DiskData |= CDROM_DISK_AUDIO_TRACK;
693 return STATUS_SUCCESS;
696 /******************************************************************
697 * CDROM_ReadQChannel
701 static NTSTATUS CDROM_ReadQChannel(int dev, int fd, const CDROM_SUB_Q_DATA_FORMAT* fmt,
702 SUB_Q_CHANNEL_DATA* data)
704 NTSTATUS ret = STATUS_NOT_SUPPORTED;
705 #ifdef linux
706 unsigned size;
707 SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data;
708 int io;
709 struct cdrom_subchnl sc;
710 sc.cdsc_format = CDROM_MSF;
712 io = ioctl(fd, CDROMSUBCHNL, &sc);
713 if (io == -1)
715 TRACE("opened or no_media (%s)!\n", strerror(errno));
716 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
717 CDROM_ClearCacheEntry(dev);
718 goto end;
721 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
723 switch (sc.cdsc_audiostatus) {
724 case CDROM_AUDIO_INVALID:
725 CDROM_ClearCacheEntry(dev);
726 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
727 break;
728 case CDROM_AUDIO_NO_STATUS:
729 CDROM_ClearCacheEntry(dev);
730 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
731 break;
732 case CDROM_AUDIO_PLAY:
733 hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
734 break;
735 case CDROM_AUDIO_PAUSED:
736 hdr->AudioStatus = AUDIO_STATUS_PAUSED;
737 break;
738 case CDROM_AUDIO_COMPLETED:
739 hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
740 break;
741 case CDROM_AUDIO_ERROR:
742 hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
743 break;
744 default:
745 TRACE("status=%02X !\n", sc.cdsc_audiostatus);
746 break;
748 switch (fmt->Format)
750 case IOCTL_CDROM_CURRENT_POSITION:
751 size = sizeof(SUB_Q_CURRENT_POSITION);
752 RtlEnterCriticalSection( &cache_section );
753 if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
754 data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
755 data->CurrentPosition.Control = sc.cdsc_ctrl;
756 data->CurrentPosition.ADR = sc.cdsc_adr;
757 data->CurrentPosition.TrackNumber = sc.cdsc_trk;
758 data->CurrentPosition.IndexNumber = sc.cdsc_ind;
760 data->CurrentPosition.AbsoluteAddress[0] = 0;
761 data->CurrentPosition.AbsoluteAddress[1] = sc.cdsc_absaddr.msf.minute;
762 data->CurrentPosition.AbsoluteAddress[2] = sc.cdsc_absaddr.msf.second;
763 data->CurrentPosition.AbsoluteAddress[3] = sc.cdsc_absaddr.msf.frame;
765 data->CurrentPosition.TrackRelativeAddress[0] = 0;
766 data->CurrentPosition.TrackRelativeAddress[1] = sc.cdsc_reladdr.msf.minute;
767 data->CurrentPosition.TrackRelativeAddress[2] = sc.cdsc_reladdr.msf.second;
768 data->CurrentPosition.TrackRelativeAddress[3] = sc.cdsc_reladdr.msf.frame;
770 cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
772 else /* not playing */
774 cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
775 data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
777 RtlLeaveCriticalSection( &cache_section );
778 break;
779 case IOCTL_CDROM_MEDIA_CATALOG:
780 size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
781 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
783 struct cdrom_mcn mcn;
784 if ((io = ioctl(fd, CDROM_GET_MCN, &mcn)) == -1) goto end;
786 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
787 data->MediaCatalog.Mcval = 0; /* FIXME */
788 memcpy(data->MediaCatalog.MediaCatalog, mcn.medium_catalog_number, 14);
789 data->MediaCatalog.MediaCatalog[14] = 0;
791 break;
792 case IOCTL_CDROM_TRACK_ISRC:
793 size = sizeof(SUB_Q_CURRENT_POSITION);
794 FIXME("TrackIsrc: NIY on linux\n");
795 data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
796 data->TrackIsrc.Tcval = 0;
797 io = 0;
798 break;
801 end:
802 ret = CDROM_GetStatusCode(io);
803 #elif defined(__FreeBSD__) || defined(__NetBSD__)
804 unsigned size;
805 SUB_Q_HEADER* hdr = (SUB_Q_HEADER*)data;
806 int io;
807 struct ioc_read_subchannel read_sc;
808 struct cd_sub_channel_info sc;
810 read_sc.address_format = CD_MSF_FORMAT;
811 read_sc.track = 0;
812 read_sc.data_len = sizeof(sc);
813 read_sc.data = &sc;
814 switch (fmt->Format)
816 case IOCTL_CDROM_CURRENT_POSITION:
817 read_sc.data_format = CD_CURRENT_POSITION;
818 break;
819 case IOCTL_CDROM_MEDIA_CATALOG:
820 read_sc.data_format = CD_MEDIA_CATALOG;
821 break;
822 case IOCTL_CDROM_TRACK_ISRC:
823 read_sc.data_format = CD_TRACK_INFO;
824 sc.what.track_info.track_number = data->TrackIsrc.Track;
825 break;
827 io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
828 if (io == -1)
830 TRACE("opened or no_media (%s)!\n", strerror(errno));
831 CDROM_ClearCacheEntry(dev);
832 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
833 goto end;
836 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
838 switch (sc.header.audio_status) {
839 case CD_AS_AUDIO_INVALID:
840 CDROM_ClearCacheEntry(dev);
841 hdr->AudioStatus = AUDIO_STATUS_NOT_SUPPORTED;
842 break;
843 case CD_AS_NO_STATUS:
844 CDROM_ClearCacheEntry(dev);
845 hdr->AudioStatus = AUDIO_STATUS_NO_STATUS;
846 break;
847 case CD_AS_PLAY_IN_PROGRESS:
848 hdr->AudioStatus = AUDIO_STATUS_IN_PROGRESS;
849 break;
850 case CD_AS_PLAY_PAUSED:
851 hdr->AudioStatus = AUDIO_STATUS_PAUSED;
852 break;
853 case CD_AS_PLAY_COMPLETED:
854 hdr->AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
855 break;
856 case CD_AS_PLAY_ERROR:
857 hdr->AudioStatus = AUDIO_STATUS_PLAY_ERROR;
858 break;
859 default:
860 TRACE("status=%02X !\n", sc.header.audio_status);
862 switch (fmt->Format)
864 case IOCTL_CDROM_CURRENT_POSITION:
865 size = sizeof(SUB_Q_CURRENT_POSITION);
866 RtlEnterCriticalSection( &cache_section );
867 if (hdr->AudioStatus==AUDIO_STATUS_IN_PROGRESS) {
868 data->CurrentPosition.FormatCode = IOCTL_CDROM_CURRENT_POSITION;
869 data->CurrentPosition.Control = sc.what.position.control;
870 data->CurrentPosition.ADR = sc.what.position.addr_type;
871 data->CurrentPosition.TrackNumber = sc.what.position.track_number;
872 data->CurrentPosition.IndexNumber = sc.what.position.index_number;
874 data->CurrentPosition.AbsoluteAddress[0] = 0;
875 data->CurrentPosition.AbsoluteAddress[1] = sc.what.position.absaddr.msf.minute;
876 data->CurrentPosition.AbsoluteAddress[2] = sc.what.position.absaddr.msf.second;
877 data->CurrentPosition.AbsoluteAddress[3] = sc.what.position.absaddr.msf.frame;
878 data->CurrentPosition.TrackRelativeAddress[0] = 0;
879 data->CurrentPosition.TrackRelativeAddress[1] = sc.what.position.reladdr.msf.minute;
880 data->CurrentPosition.TrackRelativeAddress[2] = sc.what.position.reladdr.msf.second;
881 data->CurrentPosition.TrackRelativeAddress[3] = sc.what.position.reladdr.msf.frame;
882 cdrom_cache[dev].CurrentPosition = data->CurrentPosition;
884 else { /* not playing */
885 cdrom_cache[dev].CurrentPosition.Header = *hdr; /* Preserve header info */
886 data->CurrentPosition = cdrom_cache[dev].CurrentPosition;
888 RtlLeaveCriticalSection( &cache_section );
889 break;
890 case IOCTL_CDROM_MEDIA_CATALOG:
891 size = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
892 data->MediaCatalog.FormatCode = IOCTL_CDROM_MEDIA_CATALOG;
893 data->MediaCatalog.Mcval = sc.what.media_catalog.mc_valid;
894 memcpy(data->MediaCatalog.MediaCatalog, sc.what.media_catalog.mc_number, 15);
895 break;
896 case IOCTL_CDROM_TRACK_ISRC:
897 size = sizeof(SUB_Q_CURRENT_POSITION);
898 data->TrackIsrc.FormatCode = IOCTL_CDROM_TRACK_ISRC;
899 data->TrackIsrc.Tcval = sc.what.track_info.ti_valid;
900 memcpy(data->TrackIsrc.TrackIsrc, sc.what.track_info.ti_number, 15);
901 break;
904 end:
905 ret = CDROM_GetStatusCode(io);
906 #endif
907 return ret;
910 /******************************************************************
911 * CDROM_Verify
912 * Implements: IOCTL_STORAGE_CHECK_VERIFY
913 * IOCTL_CDROM_CHECK_VERIFY
914 * IOCTL_DISK_CHECK_VERIFY
917 static NTSTATUS CDROM_Verify(int dev, int fd)
919 #if defined(linux)
920 int ret;
922 ret = ioctl(fd, CDROM_DRIVE_STATUS, NULL);
923 if(ret == -1) {
924 TRACE("ioctl CDROM_DRIVE_STATUS failed(%s)!\n", strerror(errno));
925 return CDROM_GetStatusCode(ret);
928 if(ret == CDS_DISC_OK)
929 return STATUS_SUCCESS;
930 else
931 return STATUS_NO_MEDIA_IN_DEVICE;
932 #endif
933 FIXME("not implemented for non-linux\n");
934 return STATUS_NOT_SUPPORTED;
937 /******************************************************************
938 * CDROM_PlayAudioMSF
942 static NTSTATUS CDROM_PlayAudioMSF(int fd, const CDROM_PLAY_AUDIO_MSF* audio_msf)
944 NTSTATUS ret = STATUS_NOT_SUPPORTED;
945 #ifdef linux
946 struct cdrom_msf msf;
947 int io;
949 msf.cdmsf_min0 = audio_msf->StartingM;
950 msf.cdmsf_sec0 = audio_msf->StartingS;
951 msf.cdmsf_frame0 = audio_msf->StartingF;
952 msf.cdmsf_min1 = audio_msf->EndingM;
953 msf.cdmsf_sec1 = audio_msf->EndingS;
954 msf.cdmsf_frame1 = audio_msf->EndingF;
956 io = ioctl(fd, CDROMSTART);
957 if (io == -1)
959 WARN("motor doesn't start !\n");
960 goto end;
962 io = ioctl(fd, CDROMPLAYMSF, &msf);
963 if (io == -1)
965 WARN("device doesn't play !\n");
966 goto end;
968 TRACE("msf = %d:%d:%d %d:%d:%d\n",
969 msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0,
970 msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
971 end:
972 ret = CDROM_GetStatusCode(io);
973 #elif defined(__FreeBSD__) || defined(__NetBSD__)
974 struct ioc_play_msf msf;
975 int io;
977 msf.start_m = audio_msf->StartingM;
978 msf.start_s = audio_msf->StartingS;
979 msf.start_f = audio_msf->StartingF;
980 msf.end_m = audio_msf->EndingM;
981 msf.end_s = audio_msf->EndingS;
982 msf.end_f = audio_msf->EndingF;
984 io = ioctl(fd, CDIOCSTART, NULL);
985 if (io == -1)
987 WARN("motor doesn't start !\n");
988 goto end;
990 io = ioctl(fd, CDIOCPLAYMSF, &msf);
991 if (io == -1)
993 WARN("device doesn't play !\n");
994 goto end;
996 TRACE("msf = %d:%d:%d %d:%d:%d\n",
997 msf.start_m, msf.start_s, msf.start_f,
998 msf.end_m, msf.end_s, msf.end_f);
999 end:
1000 ret = CDROM_GetStatusCode(io);
1001 #endif
1002 return ret;
1005 /******************************************************************
1006 * CDROM_SeekAudioMSF
1010 static NTSTATUS CDROM_SeekAudioMSF(int dev, int fd, const CDROM_SEEK_AUDIO_MSF* audio_msf)
1012 CDROM_TOC toc;
1013 int i, io, frame;
1014 SUB_Q_CURRENT_POSITION *cp;
1015 #if defined(linux)
1016 struct cdrom_msf0 msf;
1017 struct cdrom_subchnl sc;
1018 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1019 struct ioc_play_msf msf;
1020 struct ioc_read_subchannel read_sc;
1021 struct cd_sub_channel_info sc;
1022 int final_frame;
1023 #endif
1025 /* Use the information on the TOC to compute the new current
1026 * position, which is shadowed on the cache. [Portable]. */
1027 frame = FRAME_OF_MSF(*audio_msf);
1029 if ((io = CDROM_ReadTOC(dev, fd, &toc)) != 0) return io;
1031 for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
1032 if (FRAME_OF_TOC(toc,i)>frame) break;
1033 if (i <= toc.FirstTrack || i > toc.LastTrack+1)
1034 return STATUS_INVALID_PARAMETER;
1035 i--;
1036 RtlEnterCriticalSection( &cache_section );
1037 cp = &cdrom_cache[dev].CurrentPosition;
1038 cp->FormatCode = IOCTL_CDROM_CURRENT_POSITION;
1039 cp->Control = toc.TrackData[i-toc.FirstTrack].Control;
1040 cp->ADR = toc.TrackData[i-toc.FirstTrack].Adr;
1041 cp->TrackNumber = toc.TrackData[i-toc.FirstTrack].TrackNumber;
1042 cp->IndexNumber = 0; /* FIXME: where do they keep these? */
1043 cp->AbsoluteAddress[0] = 0;
1044 cp->AbsoluteAddress[1] = toc.TrackData[i-toc.FirstTrack].Address[1];
1045 cp->AbsoluteAddress[2] = toc.TrackData[i-toc.FirstTrack].Address[2];
1046 cp->AbsoluteAddress[3] = toc.TrackData[i-toc.FirstTrack].Address[3];
1047 frame -= FRAME_OF_TOC(toc,i);
1048 cp->TrackRelativeAddress[0] = 0;
1049 MSF_OF_FRAME(cp->TrackRelativeAddress[1], frame);
1050 RtlLeaveCriticalSection( &cache_section );
1052 /* If playing, then issue a seek command, otherwise do nothing */
1053 #ifdef linux
1054 sc.cdsc_format = CDROM_MSF;
1056 io = ioctl(fd, CDROMSUBCHNL, &sc);
1057 if (io == -1)
1059 TRACE("opened or no_media (%s)!\n", strerror(errno));
1060 CDROM_ClearCacheEntry(dev);
1061 return CDROM_GetStatusCode(io);
1063 if (sc.cdsc_audiostatus==CDROM_AUDIO_PLAY)
1065 msf.minute = audio_msf->M;
1066 msf.second = audio_msf->S;
1067 msf.frame = audio_msf->F;
1068 return CDROM_GetStatusCode(ioctl(fd, CDROMSEEK, &msf));
1070 return STATUS_SUCCESS;
1071 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1072 read_sc.address_format = CD_MSF_FORMAT;
1073 read_sc.track = 0;
1074 read_sc.data_len = sizeof(sc);
1075 read_sc.data = &sc;
1076 read_sc.data_format = CD_CURRENT_POSITION;
1078 io = ioctl(fd, CDIOCREADSUBCHANNEL, &read_sc);
1079 if (io == -1)
1081 TRACE("opened or no_media (%s)!\n", strerror(errno));
1082 CDROM_ClearCacheEntry(dev);
1083 return CDROM_GetStatusCode(io);
1085 if (sc.header.audio_status==CD_AS_PLAY_IN_PROGRESS)
1088 msf.start_m = audio_msf->M;
1089 msf.start_s = audio_msf->S;
1090 msf.start_f = audio_msf->F;
1091 final_frame = FRAME_OF_TOC(toc,toc.LastTrack+1)-1;
1092 MSF_OF_FRAME(msf.end_m, final_frame);
1094 return CDROM_GetStatusCode(ioctl(fd, CDIOCPLAYMSF, &msf));
1096 return STATUS_SUCCESS;
1097 #else
1098 return STATUS_NOT_SUPPORTED;
1099 #endif
1102 /******************************************************************
1103 * CDROM_PauseAudio
1107 static NTSTATUS CDROM_PauseAudio(int fd)
1109 #if defined(linux)
1110 return CDROM_GetStatusCode(ioctl(fd, CDROMPAUSE));
1111 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1112 return CDROM_GetStatusCode(ioctl(fd, CDIOCPAUSE, NULL));
1113 #else
1114 return STATUS_NOT_SUPPORTED;
1115 #endif
1118 /******************************************************************
1119 * CDROM_ResumeAudio
1123 static NTSTATUS CDROM_ResumeAudio(int fd)
1125 #if defined(linux)
1126 return CDROM_GetStatusCode(ioctl(fd, CDROMRESUME));
1127 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1128 return CDROM_GetStatusCode(ioctl(fd, CDIOCRESUME, NULL));
1129 #else
1130 return STATUS_NOT_SUPPORTED;
1131 #endif
1134 /******************************************************************
1135 * CDROM_StopAudio
1139 static NTSTATUS CDROM_StopAudio(int fd)
1141 #if defined(linux)
1142 return CDROM_GetStatusCode(ioctl(fd, CDROMSTOP));
1143 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1144 return CDROM_GetStatusCode(ioctl(fd, CDIOCSTOP, NULL));
1145 #else
1146 return STATUS_NOT_SUPPORTED;
1147 #endif
1150 /******************************************************************
1151 * CDROM_GetVolume
1155 static NTSTATUS CDROM_GetVolume(int fd, VOLUME_CONTROL* vc)
1157 #if defined(linux)
1158 struct cdrom_volctrl volc;
1159 int io;
1161 io = ioctl(fd, CDROMVOLREAD, &volc);
1162 if (io != -1)
1164 vc->PortVolume[0] = volc.channel0;
1165 vc->PortVolume[1] = volc.channel1;
1166 vc->PortVolume[2] = volc.channel2;
1167 vc->PortVolume[3] = volc.channel3;
1169 return CDROM_GetStatusCode(io);
1170 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1171 struct ioc_vol volc;
1172 int io;
1174 io = ioctl(fd, CDIOCGETVOL, &volc);
1175 if (io != -1)
1177 vc->PortVolume[0] = volc.vol[0];
1178 vc->PortVolume[1] = volc.vol[1];
1179 vc->PortVolume[2] = volc.vol[2];
1180 vc->PortVolume[3] = volc.vol[3];
1182 return CDROM_GetStatusCode(io);
1183 #else
1184 return STATUS_NOT_SUPPORTED;
1185 #endif
1188 /******************************************************************
1189 * CDROM_SetVolume
1193 static NTSTATUS CDROM_SetVolume(int fd, const VOLUME_CONTROL* vc)
1195 #if defined(linux)
1196 struct cdrom_volctrl volc;
1198 volc.channel0 = vc->PortVolume[0];
1199 volc.channel1 = vc->PortVolume[1];
1200 volc.channel2 = vc->PortVolume[2];
1201 volc.channel3 = vc->PortVolume[3];
1203 return CDROM_GetStatusCode(ioctl(fd, CDROMVOLCTRL, &volc));
1204 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1205 struct ioc_vol volc;
1207 volc.vol[0] = vc->PortVolume[0];
1208 volc.vol[1] = vc->PortVolume[1];
1209 volc.vol[2] = vc->PortVolume[2];
1210 volc.vol[3] = vc->PortVolume[3];
1212 return CDROM_GetStatusCode(ioctl(fd, CDIOCSETVOL, &volc));
1213 #else
1214 return STATUS_NOT_SUPPORTED;
1215 #endif
1218 /******************************************************************
1219 * CDROM_RawRead
1221 * Some features of this IOCTL are rather poorly documented and
1222 * not really intuitive either:
1224 * 1. Although the DiskOffset parameter is meant to be a
1225 * byte offset into the disk, it is in fact the sector
1226 * number multiplied by 2048 regardless of the actual
1227 * sector size.
1229 * 2. The least significant 11 bits of DiskOffset are ignored.
1231 * 3. The TrackMode parameter has no effect on the sector
1232 * size. The entire raw sector (i.e. 2352 bytes of data)
1233 * is always returned. IMO the TrackMode is only used
1234 * to check the correct sector type.
1237 static NTSTATUS CDROM_RawRead(int fd, const RAW_READ_INFO* raw, void* buffer, DWORD len, DWORD* sz)
1239 int ret = STATUS_NOT_SUPPORTED;
1240 int io = -1;
1242 TRACE("RAW_READ_INFO: DiskOffset=%li,%li SectorCount=%li TrackMode=%i\n buffer=%p len=%li sz=%p\n",
1243 raw->DiskOffset.u.HighPart, raw->DiskOffset.u.LowPart, raw->SectorCount, raw->TrackMode, buffer, len, sz);
1245 if (len < raw->SectorCount * 2352) return STATUS_BUFFER_TOO_SMALL;
1247 #if defined(linux)
1248 if (raw->DiskOffset.u.HighPart & ~2047) {
1249 WARN("DiskOffset points to a sector >= 2**32\n");
1250 return ret;
1253 switch (raw->TrackMode)
1255 case YellowMode2:
1256 case XAForm2:
1258 DWORD lba = raw->DiskOffset.QuadPart >> 11;
1259 struct cdrom_msf* msf;
1260 PBYTE *bp; /* current buffer pointer */
1261 int i;
1263 if ((lba + raw->SectorCount) >
1264 ((1 << 8*sizeof(msf->cdmsf_min0)) * CD_SECS * CD_FRAMES
1265 - CD_MSF_OFFSET)) {
1266 WARN("DiskOffset not accessible with MSF\n");
1267 return ret;
1270 /* Linux reads only one sector at a time.
1271 * ioctl CDROMREADRAW takes struct cdrom_msf as an argument
1272 * on the contrary to what header comments state.
1274 lba += CD_MSF_OFFSET;
1275 for (i = 0, bp = buffer; i < raw->SectorCount;
1276 i++, lba++, bp += 2352)
1278 msf = (struct cdrom_msf*)bp;
1279 msf->cdmsf_min0 = lba / CD_FRAMES / CD_SECS;
1280 msf->cdmsf_sec0 = lba / CD_FRAMES % CD_SECS;
1281 msf->cdmsf_frame0 = lba % CD_FRAMES;
1282 io = ioctl(fd, CDROMREADRAW, msf);
1283 if (io != 0)
1285 *sz = 2352 * i;
1286 return CDROM_GetStatusCode(io);
1289 break;
1292 case CDDA:
1294 struct cdrom_read_audio cdra;
1296 cdra.addr.lba = raw->DiskOffset.QuadPart >> 11;
1297 TRACE("reading at %u\n", cdra.addr.lba);
1298 cdra.addr_format = CDROM_LBA;
1299 cdra.nframes = raw->SectorCount;
1300 cdra.buf = buffer;
1301 io = ioctl(fd, CDROMREADAUDIO, &cdra);
1302 break;
1305 default:
1306 FIXME("NIY: %d\n", raw->TrackMode);
1307 return STATUS_INVALID_PARAMETER;
1309 #else
1310 switch (raw->TrackMode)
1312 case YellowMode2:
1313 FIXME("YellowMode2: NIY\n");
1314 return ret;
1315 case XAForm2:
1316 FIXME("XAForm2: NIY\n");
1317 return ret;
1318 case CDDA:
1319 FIXME("CDDA: NIY\n");
1320 return ret;
1321 default:
1322 FIXME("NIY: %d\n", raw->TrackMode);
1323 return STATUS_INVALID_PARAMETER;
1325 #endif
1327 *sz = 2352 * raw->SectorCount;
1328 ret = CDROM_GetStatusCode(io);
1329 return ret;
1332 /******************************************************************
1333 * CDROM_ScsiPassThroughDirect
1334 * Implements IOCTL_SCSI_PASS_THROUGH_DIRECT
1337 static NTSTATUS CDROM_ScsiPassThroughDirect(int fd, PSCSI_PASS_THROUGH_DIRECT pPacket)
1339 int ret = STATUS_NOT_SUPPORTED;
1340 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1341 sg_io_hdr_t cmd;
1342 int io;
1343 #elif defined HAVE_SCSIREQ_T_CMD
1344 scsireq_t cmd;
1345 int io;
1346 #endif
1348 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT))
1349 return STATUS_BUFFER_TOO_SMALL;
1351 if (pPacket->CdbLength > 16)
1352 return STATUS_INVALID_PARAMETER;
1354 #ifdef SENSEBUFLEN
1355 if (pPacket->SenseInfoLength > SENSEBUFLEN)
1356 return STATUS_INVALID_PARAMETER;
1357 #elif defined HAVE_REQUEST_SENSE
1358 if (pPacket->SenseInfoLength > sizeof(struct request_sense))
1359 return STATUS_INVALID_PARAMETER;
1360 #endif
1362 if (pPacket->DataTransferLength > 0 && !pPacket->DataBuffer)
1363 return STATUS_INVALID_PARAMETER;
1365 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1366 RtlZeroMemory(&cmd, sizeof(cmd));
1368 cmd.interface_id = 'S';
1369 cmd.cmd_len = pPacket->CdbLength;
1370 cmd.mx_sb_len = pPacket->SenseInfoLength;
1371 cmd.dxfer_len = pPacket->DataTransferLength;
1372 cmd.dxferp = pPacket->DataBuffer;
1373 cmd.cmdp = pPacket->Cdb;
1374 cmd.sbp = (unsigned char*)pPacket + pPacket->SenseInfoOffset;
1375 cmd.timeout = pPacket->TimeOutValue*1000;
1377 switch (pPacket->DataIn)
1379 case SCSI_IOCTL_DATA_IN:
1380 cmd.dxfer_direction = SG_DXFER_FROM_DEV;
1381 break;
1382 case SCSI_IOCTL_DATA_OUT:
1383 cmd.dxfer_direction = SG_DXFER_TO_DEV;
1384 break;
1385 case SCSI_IOCTL_DATA_UNSPECIFIED:
1386 cmd.dxfer_direction = SG_DXFER_NONE;
1387 break;
1388 default:
1389 return STATUS_INVALID_PARAMETER;
1392 io = ioctl(fd, SG_IO, &cmd);
1394 pPacket->ScsiStatus = cmd.status;
1395 pPacket->DataTransferLength = cmd.resid;
1396 pPacket->SenseInfoLength = cmd.sb_len_wr;
1398 ret = CDROM_GetStatusCode(io);
1400 #elif defined HAVE_SCSIREQ_T_CMD
1402 memset(&cmd, 0, sizeof(cmd));
1403 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1405 cmd.cmdlen = pPacket->CdbLength;
1406 cmd.databuf = pPacket->DataBuffer;
1407 cmd.datalen = pPacket->DataTransferLength;
1408 cmd.senselen = pPacket->SenseInfoLength;
1409 cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */
1411 switch (pPacket->DataIn)
1413 case SCSI_IOCTL_DATA_OUT:
1414 cmd.flags |= SCCMD_WRITE;
1415 break;
1416 case SCSI_IOCTL_DATA_IN:
1417 cmd.flags |= SCCMD_READ;
1418 break;
1419 case SCSI_IOCTL_DATA_UNSPECIFIED:
1420 cmd.flags = 0;
1421 break;
1422 default:
1423 return STATUS_INVALID_PARAMETER;
1426 io = ioctl(fd, SCIOCCOMMAND, &cmd);
1428 switch (cmd.retsts)
1430 case SCCMD_OK: break;
1431 case SCCMD_TIMEOUT: return STATUS_TIMEOUT;
1432 break;
1433 case SCCMD_BUSY: return STATUS_DEVICE_BUSY;
1434 break;
1435 case SCCMD_SENSE: break;
1436 case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL;
1437 break;
1440 if (pPacket->SenseInfoLength != 0)
1442 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1443 cmd.sense, pPacket->SenseInfoLength);
1446 pPacket->ScsiStatus = cmd.status;
1448 ret = CDROM_GetStatusCode(io);
1449 #endif
1450 return ret;
1453 /******************************************************************
1454 * CDROM_ScsiPassThrough
1455 * Implements IOCTL_SCSI_PASS_THROUGH
1458 static NTSTATUS CDROM_ScsiPassThrough(int fd, PSCSI_PASS_THROUGH pPacket)
1460 int ret = STATUS_NOT_SUPPORTED;
1461 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1462 sg_io_hdr_t cmd;
1463 int io;
1464 #elif defined HAVE_SCSIREQ_T_CMD
1465 scsireq_t cmd;
1466 int io;
1467 #endif
1469 if (pPacket->Length < sizeof(SCSI_PASS_THROUGH))
1470 return STATUS_BUFFER_TOO_SMALL;
1472 if (pPacket->CdbLength > 16)
1473 return STATUS_INVALID_PARAMETER;
1475 #ifdef SENSEBUFLEN
1476 if (pPacket->SenseInfoLength > SENSEBUFLEN)
1477 return STATUS_INVALID_PARAMETER;
1478 #elif defined HAVE_REQUEST_SENSE
1479 if (pPacket->SenseInfoLength > sizeof(struct request_sense))
1480 return STATUS_INVALID_PARAMETER;
1481 #endif
1483 if (pPacket->DataTransferLength > 0 && pPacket->DataBufferOffset < sizeof(SCSI_PASS_THROUGH))
1484 return STATUS_INVALID_PARAMETER;
1486 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1487 RtlZeroMemory(&cmd, sizeof(cmd));
1489 cmd.interface_id = 'S';
1490 cmd.dxfer_len = pPacket->DataTransferLength;
1491 cmd.dxferp = (char*)pPacket + pPacket->DataBufferOffset;
1492 cmd.cmd_len = pPacket->CdbLength;
1493 cmd.cmdp = pPacket->Cdb;
1494 cmd.mx_sb_len = pPacket->SenseInfoLength;
1495 cmd.timeout = pPacket->TimeOutValue*1000;
1497 if(cmd.mx_sb_len > 0)
1498 cmd.sbp = (unsigned char*)pPacket + pPacket->SenseInfoOffset;
1500 switch (pPacket->DataIn)
1502 case SCSI_IOCTL_DATA_IN:
1503 cmd.dxfer_direction = SG_DXFER_FROM_DEV;
1504 break;
1505 case SCSI_IOCTL_DATA_OUT:
1506 cmd.dxfer_direction = SG_DXFER_TO_DEV;
1507 break;
1508 case SCSI_IOCTL_DATA_UNSPECIFIED:
1509 cmd.dxfer_direction = SG_DXFER_NONE;
1510 break;
1511 default:
1512 return STATUS_INVALID_PARAMETER;
1515 io = ioctl(fd, SG_IO, &cmd);
1517 pPacket->ScsiStatus = cmd.status;
1518 pPacket->DataTransferLength = cmd.resid;
1519 pPacket->SenseInfoLength = cmd.sb_len_wr;
1521 ret = CDROM_GetStatusCode(io);
1523 #elif defined HAVE_SCSIREQ_T_CMD
1525 memset(&cmd, 0, sizeof(cmd));
1526 memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength);
1528 if ( pPacket->DataBufferOffset > 0x1000 )
1530 cmd.databuf = (void*)pPacket->DataBufferOffset;
1532 else
1534 cmd.databuf = (char*)pPacket + pPacket->DataBufferOffset;
1537 cmd.cmdlen = pPacket->CdbLength;
1538 cmd.datalen = pPacket->DataTransferLength;
1539 cmd.senselen = pPacket->SenseInfoLength;
1540 cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */
1542 switch (pPacket->DataIn)
1544 case SCSI_IOCTL_DATA_OUT:
1545 cmd.flags |= SCCMD_WRITE;
1546 break;
1547 case SCSI_IOCTL_DATA_IN:
1548 cmd.flags |= SCCMD_READ;
1549 break;
1550 case SCSI_IOCTL_DATA_UNSPECIFIED:
1551 cmd.flags = 0;
1552 break;
1553 default:
1554 return STATUS_INVALID_PARAMETER;
1557 io = ioctl(fd, SCIOCCOMMAND, &cmd);
1559 switch (cmd.retsts)
1561 case SCCMD_OK: break;
1562 case SCCMD_TIMEOUT: return STATUS_TIMEOUT;
1563 break;
1564 case SCCMD_BUSY: return STATUS_DEVICE_BUSY;
1565 break;
1566 case SCCMD_SENSE: break;
1567 case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL;
1568 break;
1571 if (pPacket->SenseInfoLength != 0)
1573 memcpy((char*)pPacket + pPacket->SenseInfoOffset,
1574 cmd.sense, pPacket->SenseInfoLength);
1577 pPacket->ScsiStatus = cmd.status;
1579 ret = CDROM_GetStatusCode(io);
1580 #endif
1581 return ret;
1584 /******************************************************************
1585 * CDROM_ScsiGetCaps
1589 static NTSTATUS CDROM_ScsiGetCaps(PIO_SCSI_CAPABILITIES caps)
1591 NTSTATUS ret = STATUS_NOT_IMPLEMENTED;
1593 caps->Length = sizeof(*caps);
1594 #ifdef SG_SCATTER_SZ
1595 caps->MaximumTransferLength = SG_SCATTER_SZ; /* FIXME */
1596 caps->MaximumPhysicalPages = SG_SCATTER_SZ / getpagesize();
1597 caps->SupportedAsynchronousEvents = TRUE;
1598 caps->AlignmentMask = getpagesize();
1599 caps->TaggedQueuing = FALSE; /* we could check that it works and answer TRUE */
1600 caps->AdapterScansDown = FALSE; /* FIXME ? */
1601 caps->AdapterUsesPio = FALSE; /* FIXME ? */
1602 ret = STATUS_SUCCESS;
1603 #else
1604 FIXME("Unimplemented\n");
1605 #endif
1606 return ret;
1609 /******************************************************************
1610 * CDROM_GetAddress
1612 * implements IOCTL_SCSI_GET_ADDRESS
1614 static NTSTATUS CDROM_GetAddress(int fd, SCSI_ADDRESS* address)
1616 UCHAR portnum, busid, targetid, lun;
1618 address->Length = sizeof(SCSI_ADDRESS);
1619 if ( ! CDROM_GetInterfaceInfo(fd, &portnum, &busid, &targetid, &lun))
1620 return STATUS_NOT_SUPPORTED;
1622 address->PortNumber = portnum; /* primary=0 secondary=1 for ide */
1623 address->PathId = busid; /* always 0 for ide */
1624 address->TargetId = targetid; /* master=0 slave=1 for ide */
1625 address->Lun = lun;
1626 return STATUS_SUCCESS;
1629 /******************************************************************
1630 * DVD_StartSession
1634 static NTSTATUS DVD_StartSession(int fd, PDVD_SESSION_ID sid_in, PDVD_SESSION_ID sid_out)
1636 #if defined(linux)
1637 NTSTATUS ret = STATUS_NOT_SUPPORTED;
1638 dvd_authinfo auth_info;
1640 memset( &auth_info, 0, sizeof( auth_info ) );
1641 auth_info.type = DVD_LU_SEND_AGID;
1642 if (sid_in) auth_info.lsa.agid = *(int*)sid_in; /* ?*/
1644 TRACE("fd 0x%08x\n",fd);
1645 ret =CDROM_GetStatusCode(ioctl(fd, DVD_AUTH, &auth_info));
1646 *sid_out = auth_info.lsa.agid;
1647 return ret;
1648 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1649 return STATUS_NOT_SUPPORTED;
1650 #else
1651 return STATUS_NOT_SUPPORTED;
1652 #endif
1655 /******************************************************************
1656 * DVD_EndSession
1660 static NTSTATUS DVD_EndSession(int fd, PDVD_SESSION_ID sid)
1662 #if defined(linux)
1663 dvd_authinfo auth_info;
1665 memset( &auth_info, 0, sizeof( auth_info ) );
1666 auth_info.type = DVD_INVALIDATE_AGID;
1667 auth_info.lsa.agid = *(int*)sid;
1669 TRACE("\n");
1670 return CDROM_GetStatusCode(ioctl(fd, DVD_AUTH, &auth_info));
1671 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1672 return STATUS_NOT_SUPPORTED;
1673 #else
1674 return STATUS_NOT_SUPPORTED;
1675 #endif
1678 /******************************************************************
1679 * DVD_SendKey
1683 static NTSTATUS DVD_SendKey(int fd, PDVD_COPY_PROTECT_KEY key)
1685 #if defined(linux)
1686 NTSTATUS ret = STATUS_NOT_SUPPORTED;
1687 dvd_authinfo auth_info;
1689 memset( &auth_info, 0, sizeof( auth_info ) );
1690 switch (key->KeyType)
1692 case DvdChallengeKey:
1693 auth_info.type = DVD_HOST_SEND_CHALLENGE;
1694 auth_info.hsc.agid = (int)key->SessionId;
1695 TRACE("DvdChallengeKey ioc 0x%x\n", DVD_AUTH );
1696 memcpy( auth_info.hsc.chal, key->KeyData, DVD_CHALLENGE_SIZE );
1697 ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1698 break;
1699 case DvdBusKey2:
1700 auth_info.type = DVD_HOST_SEND_KEY2;
1701 auth_info.hsk.agid = (int)key->SessionId;
1703 memcpy( auth_info.hsk.key, key->KeyData, DVD_KEY_SIZE );
1705 TRACE("DvdBusKey2\n");
1706 ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1707 break;
1709 default:
1710 FIXME("Unknown Keytype 0x%x\n",key->KeyType);
1712 return ret;
1713 #else
1714 FIXME("unsupported on this platform\n");
1715 return STATUS_NOT_SUPPORTED;
1716 #endif
1719 /******************************************************************
1720 * DVD_ReadKey
1724 static NTSTATUS DVD_ReadKey(int fd, PDVD_COPY_PROTECT_KEY key)
1726 #if defined(linux)
1727 NTSTATUS ret = STATUS_NOT_SUPPORTED;
1728 dvd_struct dvd;
1729 dvd_authinfo auth_info;
1731 memset( &dvd, 0, sizeof( dvd_struct ) );
1732 memset( &auth_info, 0, sizeof( auth_info ) );
1733 switch (key->KeyType)
1735 case DvdDiskKey:
1737 dvd.type = DVD_STRUCT_DISCKEY;
1738 dvd.disckey.agid = (int)key->SessionId;
1739 memset( dvd.disckey.value, 0, DVD_DISCKEY_SIZE );
1741 TRACE("DvdDiskKey\n");
1742 ret = CDROM_GetStatusCode(ioctl( fd, DVD_READ_STRUCT, &dvd ));
1743 if (ret == STATUS_SUCCESS)
1744 memcpy(key->KeyData,dvd.disckey.value,DVD_DISCKEY_SIZE);
1745 break;
1746 case DvdTitleKey:
1747 auth_info.type = DVD_LU_SEND_TITLE_KEY;
1748 auth_info.lstk.agid = (int)key->SessionId;
1749 auth_info.lstk.lba = (int)(key->Parameters.TitleOffset.QuadPart>>11);
1750 TRACE("DvdTitleKey session %d Quadpart 0x%08lx offset 0x%08x\n",
1751 (int)key->SessionId, (long)key->Parameters.TitleOffset.QuadPart,
1752 auth_info.lstk.lba);
1753 ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1754 if (ret == STATUS_SUCCESS)
1755 memcpy(key->KeyData, auth_info.lstk.title_key, DVD_KEY_SIZE );
1756 break;
1757 case DvdChallengeKey:
1759 auth_info.type = DVD_LU_SEND_CHALLENGE;
1760 auth_info.lsc.agid = (int)key->SessionId;
1762 TRACE("DvdChallengeKey\n");
1763 ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1764 if (ret == STATUS_SUCCESS)
1765 memcpy( key->KeyData, auth_info.lsc.chal, DVD_CHALLENGE_SIZE );
1766 break;
1767 case DvdAsf:
1768 auth_info.type = DVD_LU_SEND_ASF;
1769 TRACE("DvdAsf\n");
1770 auth_info.lsasf.asf=((PDVD_ASF)key->KeyData)->SuccessFlag;
1771 ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1772 ((PDVD_ASF)key->KeyData)->SuccessFlag = auth_info.lsasf.asf;
1773 break;
1774 case DvdBusKey1:
1775 auth_info.type = DVD_LU_SEND_KEY1;
1776 auth_info.lsk.agid = (int)key->SessionId;
1778 TRACE("DvdBusKey1\n");
1779 ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1781 if (ret == STATUS_SUCCESS)
1782 memcpy( key->KeyData, auth_info.lsk.key, DVD_KEY_SIZE );
1783 break;
1784 case DvdGetRpcKey:
1785 auth_info.type = DVD_LU_SEND_RPC_STATE;
1787 TRACE("DvdGetRpcKey\n");
1788 ret = CDROM_GetStatusCode(ioctl( fd, DVD_AUTH, &auth_info ));
1790 if (ret == STATUS_SUCCESS)
1792 ((PDVD_RPC_KEY)key->KeyData)->TypeCode = auth_info.lrpcs.type;
1793 ((PDVD_RPC_KEY)key->KeyData)->RegionMask = auth_info.lrpcs.region_mask;
1794 ((PDVD_RPC_KEY)key->KeyData)->RpcScheme = auth_info.lrpcs.rpc_scheme;
1796 break;
1797 default:
1798 FIXME("Unknown keytype 0x%x\n",key->KeyType);
1800 return ret;
1801 #elif defined(__FreeBSD__) || defined(__NetBSD__)
1802 TRACE("bsd\n");
1803 return STATUS_NOT_SUPPORTED;
1804 #else
1805 TRACE("outside\n");
1806 return STATUS_NOT_SUPPORTED;
1807 #endif
1810 /******************************************************************
1811 * DVD_GetRegion
1815 static NTSTATUS DVD_GetRegion(int dev, PDVD_REGION region)
1817 FIXME("\n");
1818 return STATUS_SUCCESS;
1822 /******************************************************************
1823 * DVD_GetRegion
1827 static NTSTATUS DVD_ReadStructure(int dev, PDVD_READ_STRUCTURE structure, PDVD_LAYER_DESCRIPTOR layer)
1829 #ifdef DVD_READ_STRUCT
1830 dvd_struct s;
1832 if (structure->BlockByteOffset.u.HighPart || structure->BlockByteOffset.u.LowPart)
1833 FIXME(": BlockByteOffset is not handled\n");
1835 switch (structure->Format)
1837 case DvdPhysicalDescriptor:
1838 s.type = DVD_STRUCT_PHYSICAL;
1839 s.physical.layer_num = structure->LayerNumber;
1840 break;
1842 case DvdCopyrightDescriptor:
1843 s.type = DVD_STRUCT_COPYRIGHT;
1844 s.copyright.layer_num = structure->LayerNumber;
1845 break;
1847 case DvdDiskKeyDescriptor:
1848 s.type = DVD_STRUCT_DISCKEY;
1849 s.disckey.agid = structure->SessionId;
1850 break;
1852 case DvdBCADescriptor:
1853 s.type = DVD_STRUCT_BCA;
1854 break;
1856 case DvdManufacturerDescriptor:
1857 s.type = DVD_STRUCT_MANUFACT;
1858 s.manufact.layer_num = structure->LayerNumber;
1859 break;
1861 case DvdMaxDescriptor: /* This is not a real request, no matter what MSDN says! */
1862 default:
1863 return STATUS_INVALID_PARAMETER;
1866 if (ioctl(dev, DVD_READ_STRUCT, &s) < 0)
1867 return STATUS_INVALID_PARAMETER;
1869 switch (structure->Format)
1871 case DvdPhysicalDescriptor:
1873 internal_dvd_layer_descriptor *p = (internal_dvd_layer_descriptor *) layer;
1874 struct dvd_layer *l = &s.physical.layer[s.physical.layer_num];
1876 p->MagicHeader[0] = 2;
1877 p->MagicHeader[1] = 8;
1878 p->MagicHeader[2] = 0;
1879 p->MagicHeader[3] = 0;
1880 p->BookVersion = l->book_version;
1881 p->BookType = l->book_type;
1882 p->MinimumRate = l->min_rate;
1883 p->DiskSize = l->disc_size;
1884 p->LayerType = l->layer_type;
1885 p->TrackPath = l->track_path;
1886 p->NumberOfLayers = l->nlayers;
1887 p->Reserved1 = 0;
1888 p->TrackDensity = l->track_density;
1889 p->LinearDensity = l->linear_density;
1890 p->StartingDataSector = l->start_sector;
1891 p->EndDataSector = l->end_sector;
1892 p->EndLayerZeroSector = l->end_sector_l0;
1893 p->Reserved5 = 0;
1894 p->BCAFlag = l->bca;
1895 p->Reserved6 = 0;
1897 break;
1899 case DvdCopyrightDescriptor:
1901 PDVD_COPYRIGHT_DESCRIPTOR p = (PDVD_COPYRIGHT_DESCRIPTOR) layer;
1903 p->CopyrightProtectionType = s.copyright.cpst;
1904 p->RegionManagementInformation = s.copyright.rmi;
1905 p->Reserved = 0;
1907 break;
1909 case DvdDiskKeyDescriptor:
1911 PDVD_DISK_KEY_DESCRIPTOR p = (PDVD_DISK_KEY_DESCRIPTOR) layer;
1913 memcpy(p->DiskKeyData, s.disckey.value, 2048);
1915 break;
1917 case DvdBCADescriptor:
1919 PDVD_BCA_DESCRIPTOR p = (PDVD_BCA_DESCRIPTOR) layer;
1921 memcpy(p->BCAInformation, s.bca.value, s.bca.len);
1923 break;
1925 case DvdManufacturerDescriptor:
1927 PDVD_MANUFACTURER_DESCRIPTOR p = (PDVD_MANUFACTURER_DESCRIPTOR) layer;
1929 memcpy(p->ManufacturingInformation, s.manufact.value, 2048);
1931 break;
1933 case DvdMaxDescriptor: /* Suppress warning */
1934 break;
1936 #else
1937 FIXME("\n");
1938 #endif
1939 return STATUS_SUCCESS;
1943 /******************************************************************
1944 * GetInquiryData
1945 * Implements the IOCTL_GET_INQUIRY_DATA ioctl.
1946 * Returns Inquiry data for all devices on the specified scsi bus
1947 * Returns STATUS_BUFFER_TOO_SMALL if the output buffer is too small,
1948 * STATUS_INVALID_DEVICE_REQUEST if the given handle isn't to a SCSI device,
1949 * or STATUS_NOT_SUPPORTED if the OS driver is too old
1951 static NTSTATUS GetInquiryData(int fd, PSCSI_ADAPTER_BUS_INFO BufferOut, DWORD OutBufferSize)
1953 #ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID
1954 PSCSI_INQUIRY_DATA pInquiryData = NULL;
1955 UCHAR sense_buffer[32];
1956 int iochk, version;
1957 sg_io_hdr_t iocmd;
1958 UCHAR inquiry[INQ_CMD_LEN] = {INQUIRY, 0, 0, 0, INQ_REPLY_LEN, 0};
1960 /* Check we have a SCSI device and a supported driver */
1961 if(ioctl(fd, SG_GET_VERSION_NUM, &version) != 0)
1963 WARN("IOCTL_SCSI_GET_INQUIRY_DATA sg driver is not loaded\n");
1964 return STATUS_INVALID_DEVICE_REQUEST;
1966 if(version < 30000 )
1967 return STATUS_NOT_SUPPORTED;
1969 /* FIXME: Enumerate devices on the bus */
1970 BufferOut->NumberOfBuses = 1;
1971 BufferOut->BusData[0].NumberOfLogicalUnits = 1;
1972 BufferOut->BusData[0].InquiryDataOffset = sizeof(SCSI_ADAPTER_BUS_INFO);
1974 pInquiryData = (PSCSI_INQUIRY_DATA)(BufferOut + 1);
1976 RtlZeroMemory(&iocmd, sizeof(iocmd));
1977 iocmd.interface_id = 'S';
1978 iocmd.cmd_len = sizeof(inquiry);
1979 iocmd.mx_sb_len = sizeof(sense_buffer);
1980 iocmd.dxfer_direction = SG_DXFER_FROM_DEV;
1981 iocmd.dxfer_len = INQ_REPLY_LEN;
1982 iocmd.dxferp = pInquiryData->InquiryData;
1983 iocmd.cmdp = inquiry;
1984 iocmd.sbp = sense_buffer;
1985 iocmd.timeout = 1000;
1987 iochk = ioctl(fd, SG_IO, &iocmd);
1988 if(iochk != 0)
1989 WARN("ioctl SG_IO returned %d, error (%s)\n", iochk, strerror(errno));
1991 CDROM_GetInterfaceInfo(fd, &BufferOut->BusData[0].InitiatorBusId, &pInquiryData->PathId, &pInquiryData->TargetId, &pInquiryData->Lun);
1992 pInquiryData->DeviceClaimed = TRUE;
1993 pInquiryData->InquiryDataLength = INQ_REPLY_LEN;
1994 pInquiryData->NextInquiryDataOffset = 0;
1995 return STATUS_SUCCESS;
1996 #else
1997 FIXME("not implemented for nonlinux\n");
1998 return STATUS_NOT_SUPPORTED;
1999 #endif
2002 /******************************************************************
2003 * CDROM_DeviceIoControl
2007 NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice,
2008 HANDLE hEvent, PIO_APC_ROUTINE UserApcRoutine,
2009 PVOID UserApcContext,
2010 PIO_STATUS_BLOCK piosb,
2011 ULONG dwIoControlCode,
2012 LPVOID lpInBuffer, DWORD nInBufferSize,
2013 LPVOID lpOutBuffer, DWORD nOutBufferSize)
2015 DWORD sz = 0;
2016 NTSTATUS status = STATUS_SUCCESS;
2017 int fd, dev;
2019 TRACE("%p %s %p %ld %p %ld %p\n",
2020 hDevice, iocodex(dwIoControlCode), lpInBuffer, nInBufferSize,
2021 lpOutBuffer, nOutBufferSize, piosb);
2023 piosb->Information = 0;
2025 if ((status = wine_server_handle_to_fd( hDevice, 0, &fd, NULL ))) goto error;
2026 if ((status = CDROM_Open(fd, &dev)))
2028 wine_server_release_fd( hDevice, fd );
2029 goto error;
2032 switch (dwIoControlCode)
2034 case IOCTL_STORAGE_CHECK_VERIFY:
2035 case IOCTL_CDROM_CHECK_VERIFY:
2036 case IOCTL_DISK_CHECK_VERIFY:
2037 sz = 0;
2038 CDROM_ClearCacheEntry(dev);
2039 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2040 status = STATUS_INVALID_PARAMETER;
2041 else status = CDROM_Verify(dev, fd);
2042 break;
2044 /* EPP case IOCTL_STORAGE_CHECK_VERIFY2: */
2046 /* EPP case IOCTL_STORAGE_FIND_NEW_DEVICES: */
2047 /* EPP case IOCTL_CDROM_FIND_NEW_DEVICES: */
2049 case IOCTL_STORAGE_LOAD_MEDIA:
2050 case IOCTL_CDROM_LOAD_MEDIA:
2051 sz = 0;
2052 CDROM_ClearCacheEntry(dev);
2053 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2054 status = STATUS_INVALID_PARAMETER;
2055 else status = CDROM_SetTray(fd, FALSE);
2056 break;
2057 case IOCTL_STORAGE_EJECT_MEDIA:
2058 sz = 0;
2059 CDROM_ClearCacheEntry(dev);
2060 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2061 status = STATUS_INVALID_PARAMETER;
2062 else status = CDROM_SetTray(fd, TRUE);
2063 break;
2065 case IOCTL_CDROM_MEDIA_REMOVAL:
2066 case IOCTL_DISK_MEDIA_REMOVAL:
2067 case IOCTL_STORAGE_MEDIA_REMOVAL:
2068 case IOCTL_STORAGE_EJECTION_CONTROL:
2069 /* FIXME the last ioctl:s is not the same as the two others...
2070 * lockcount/owner should be handled */
2071 sz = 0;
2072 CDROM_ClearCacheEntry(dev);
2073 if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2074 else if (nInBufferSize < sizeof(PREVENT_MEDIA_REMOVAL)) status = STATUS_BUFFER_TOO_SMALL;
2075 else status = CDROM_ControlEjection(fd, (const PREVENT_MEDIA_REMOVAL*)lpInBuffer);
2076 break;
2078 /* EPP case IOCTL_STORAGE_GET_MEDIA_TYPES: */
2080 case IOCTL_STORAGE_GET_DEVICE_NUMBER:
2081 sz = sizeof(STORAGE_DEVICE_NUMBER);
2082 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2083 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2084 else status = CDROM_GetDeviceNumber(dev, (STORAGE_DEVICE_NUMBER*)lpOutBuffer);
2085 break;
2087 case IOCTL_STORAGE_RESET_DEVICE:
2088 sz = 0;
2089 CDROM_ClearCacheEntry(dev);
2090 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2091 status = STATUS_INVALID_PARAMETER;
2092 else status = CDROM_ResetAudio(fd);
2093 break;
2095 case IOCTL_CDROM_GET_CONTROL:
2096 sz = sizeof(CDROM_AUDIO_CONTROL);
2097 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2098 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2099 else status = CDROM_GetControl(dev, (CDROM_AUDIO_CONTROL*)lpOutBuffer);
2100 break;
2102 case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
2103 sz = sizeof(DISK_GEOMETRY);
2104 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2105 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2106 else status = CDROM_GetDriveGeometry(dev, fd, (DISK_GEOMETRY*)lpOutBuffer);
2107 break;
2109 case IOCTL_CDROM_DISK_TYPE:
2110 sz = sizeof(CDROM_DISK_DATA);
2111 /* CDROM_ClearCacheEntry(dev); */
2112 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2113 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2114 else status = CDROM_GetDiskData(dev, fd, (CDROM_DISK_DATA*)lpOutBuffer);
2115 break;
2117 /* EPP case IOCTL_CDROM_GET_LAST_SESSION: */
2119 case IOCTL_CDROM_READ_Q_CHANNEL:
2120 sz = sizeof(SUB_Q_CHANNEL_DATA);
2121 if (lpInBuffer == NULL || nInBufferSize < sizeof(CDROM_SUB_Q_DATA_FORMAT))
2122 status = STATUS_INVALID_PARAMETER;
2123 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2124 else status = CDROM_ReadQChannel(dev, fd, (const CDROM_SUB_Q_DATA_FORMAT*)lpInBuffer,
2125 (SUB_Q_CHANNEL_DATA*)lpOutBuffer);
2126 break;
2128 case IOCTL_CDROM_READ_TOC:
2129 sz = sizeof(CDROM_TOC);
2130 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2131 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2132 else status = CDROM_ReadTOC(dev, fd, (CDROM_TOC*)lpOutBuffer);
2133 break;
2135 /* EPP case IOCTL_CDROM_READ_TOC_EX: */
2137 case IOCTL_CDROM_PAUSE_AUDIO:
2138 sz = 0;
2139 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2140 status = STATUS_INVALID_PARAMETER;
2141 else status = CDROM_PauseAudio(fd);
2142 break;
2143 case IOCTL_CDROM_PLAY_AUDIO_MSF:
2144 sz = 0;
2145 if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2146 else if (nInBufferSize < sizeof(CDROM_PLAY_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
2147 else status = CDROM_PlayAudioMSF(fd, (const CDROM_PLAY_AUDIO_MSF*)lpInBuffer);
2148 break;
2149 case IOCTL_CDROM_RESUME_AUDIO:
2150 sz = 0;
2151 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2152 status = STATUS_INVALID_PARAMETER;
2153 else status = CDROM_ResumeAudio(fd);
2154 break;
2155 case IOCTL_CDROM_SEEK_AUDIO_MSF:
2156 sz = 0;
2157 if (lpOutBuffer != NULL || nOutBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2158 else if (nInBufferSize < sizeof(CDROM_SEEK_AUDIO_MSF)) status = STATUS_BUFFER_TOO_SMALL;
2159 else status = CDROM_SeekAudioMSF(dev, fd, (const CDROM_SEEK_AUDIO_MSF*)lpInBuffer);
2160 break;
2161 case IOCTL_CDROM_STOP_AUDIO:
2162 sz = 0;
2163 CDROM_ClearCacheEntry(dev); /* Maybe intention is to change media */
2164 if (lpInBuffer != NULL || nInBufferSize != 0 || lpOutBuffer != NULL || nOutBufferSize != 0)
2165 status = STATUS_INVALID_PARAMETER;
2166 else status = CDROM_StopAudio(fd);
2167 break;
2168 case IOCTL_CDROM_GET_VOLUME:
2169 sz = sizeof(VOLUME_CONTROL);
2170 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2171 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2172 else status = CDROM_GetVolume(fd, (VOLUME_CONTROL*)lpOutBuffer);
2173 break;
2174 case IOCTL_CDROM_SET_VOLUME:
2175 sz = 0;
2176 CDROM_ClearCacheEntry(dev);
2177 if (lpInBuffer == NULL || nInBufferSize < sizeof(VOLUME_CONTROL) || lpOutBuffer != NULL)
2178 status = STATUS_INVALID_PARAMETER;
2179 else status = CDROM_SetVolume(fd, (const VOLUME_CONTROL*)lpInBuffer);
2180 break;
2181 case IOCTL_CDROM_RAW_READ:
2182 sz = 0;
2183 if (nInBufferSize < sizeof(RAW_READ_INFO)) status = STATUS_INVALID_PARAMETER;
2184 else if (lpOutBuffer == NULL) status = STATUS_BUFFER_TOO_SMALL;
2185 else status = CDROM_RawRead(fd, (const RAW_READ_INFO*)lpInBuffer,
2186 lpOutBuffer, nOutBufferSize, &sz);
2187 break;
2188 case IOCTL_SCSI_GET_ADDRESS:
2189 sz = sizeof(SCSI_ADDRESS);
2190 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2191 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2192 else status = CDROM_GetAddress(fd, (SCSI_ADDRESS*)lpOutBuffer);
2193 break;
2194 case IOCTL_SCSI_PASS_THROUGH_DIRECT:
2195 sz = sizeof(SCSI_PASS_THROUGH_DIRECT);
2196 if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
2197 else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH_DIRECT)) status = STATUS_BUFFER_TOO_SMALL;
2198 else status = CDROM_ScsiPassThroughDirect(fd, (PSCSI_PASS_THROUGH_DIRECT)lpOutBuffer);
2199 break;
2200 case IOCTL_SCSI_PASS_THROUGH:
2201 sz = sizeof(SCSI_PASS_THROUGH);
2202 if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
2203 else if (nOutBufferSize < sizeof(SCSI_PASS_THROUGH)) status = STATUS_BUFFER_TOO_SMALL;
2204 else status = CDROM_ScsiPassThrough(fd, (PSCSI_PASS_THROUGH)lpOutBuffer);
2205 break;
2206 case IOCTL_SCSI_GET_CAPABILITIES:
2207 sz = sizeof(IO_SCSI_CAPABILITIES);
2208 if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
2209 else if (nOutBufferSize < sizeof(IO_SCSI_CAPABILITIES)) status = STATUS_BUFFER_TOO_SMALL;
2210 else status = CDROM_ScsiGetCaps((PIO_SCSI_CAPABILITIES)lpOutBuffer);
2211 break;
2212 case IOCTL_DVD_START_SESSION:
2213 sz = sizeof(DVD_SESSION_ID);
2214 if (lpOutBuffer == NULL) status = STATUS_INVALID_PARAMETER;
2215 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2216 else
2218 TRACE("before in 0x%08lx out 0x%08lx\n",(lpInBuffer)?*(PDVD_SESSION_ID)lpInBuffer:0,
2219 *(PDVD_SESSION_ID)lpOutBuffer);
2220 status = DVD_StartSession(fd, (PDVD_SESSION_ID)lpInBuffer, (PDVD_SESSION_ID)lpOutBuffer);
2221 TRACE("before in 0x%08lx out 0x%08lx\n",(lpInBuffer)?*(PDVD_SESSION_ID)lpInBuffer:0,
2222 *(PDVD_SESSION_ID)lpOutBuffer);
2224 break;
2225 case IOCTL_DVD_END_SESSION:
2226 sz = sizeof(DVD_SESSION_ID);
2227 if ((lpInBuffer == NULL) || (nInBufferSize < sz))status = STATUS_INVALID_PARAMETER;
2228 else status = DVD_EndSession(fd, (PDVD_SESSION_ID)lpInBuffer);
2229 break;
2230 case IOCTL_DVD_SEND_KEY:
2231 sz = 0;
2232 if (!lpInBuffer ||
2233 (((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength != nInBufferSize))
2234 status = STATUS_INVALID_PARAMETER;
2235 else
2237 TRACE("doing DVD_SendKey\n");
2238 status = DVD_SendKey(fd, (PDVD_COPY_PROTECT_KEY)lpInBuffer);
2240 break;
2241 case IOCTL_DVD_READ_KEY:
2242 if (!lpInBuffer ||
2243 (((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength != nInBufferSize))
2244 status = STATUS_INVALID_PARAMETER;
2245 else if (lpInBuffer !=lpOutBuffer) status = STATUS_BUFFER_TOO_SMALL;
2246 else
2248 TRACE("doing DVD_READ_KEY\n");
2249 sz = ((PDVD_COPY_PROTECT_KEY)lpInBuffer)->KeyLength;
2250 status = DVD_ReadKey(fd, (PDVD_COPY_PROTECT_KEY)lpInBuffer);
2252 break;
2253 case IOCTL_DVD_GET_REGION:
2254 sz = sizeof(DVD_REGION);
2255 if (lpInBuffer != NULL || nInBufferSize != 0) status = STATUS_INVALID_PARAMETER;
2256 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2257 TRACE("doing DVD_Get_REGION\n");
2258 status = DVD_GetRegion(fd, (PDVD_REGION)lpOutBuffer);
2259 break;
2260 case IOCTL_DVD_READ_STRUCTURE:
2261 sz = sizeof(DVD_LAYER_DESCRIPTOR);
2262 if (lpInBuffer == NULL || nInBufferSize != sizeof(DVD_READ_STRUCTURE)) status = STATUS_INVALID_PARAMETER;
2263 else if (nOutBufferSize < sz) status = STATUS_BUFFER_TOO_SMALL;
2264 TRACE("doing DVD_READ_STRUCTURE\n");
2265 status = DVD_ReadStructure(fd, (PDVD_READ_STRUCTURE)lpInBuffer, (PDVD_LAYER_DESCRIPTOR)lpOutBuffer);
2266 break;
2268 case IOCTL_SCSI_GET_INQUIRY_DATA:
2269 sz = INQ_REPLY_LEN;
2270 status = GetInquiryData(fd, (PSCSI_ADAPTER_BUS_INFO)lpOutBuffer, nOutBufferSize);
2271 break;
2273 default:
2274 FIXME("Unsupported IOCTL %lx (type=%lx access=%lx func=%lx meth=%lx)\n",
2275 dwIoControlCode, dwIoControlCode >> 16, (dwIoControlCode >> 14) & 3,
2276 (dwIoControlCode >> 2) & 0xFFF, dwIoControlCode & 3);
2277 sz = 0;
2278 status = STATUS_INVALID_PARAMETER;
2279 break;
2281 wine_server_release_fd( hDevice, fd );
2282 error:
2283 piosb->u.Status = status;
2284 piosb->Information = sz;
2285 if (hEvent) NtSetEvent(hEvent, NULL);
2286 return status;