2 * "\Device\CdRom%d" storage emulation driver for reactos of libcaptive
3 * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "captive/storage.h" /* self */
23 #include "reactos/ddk/iotypes.h" /* for DRIVER_OBJECT */
24 #include <glib/gmessages.h>
25 #include <sys/types.h>
28 #include "reactos/ddk/ntddscsi.h" /* for IO_SCSI_CAPABILITIES */
29 #include "reactos/ddk/class2.h" /* for PDEVICE_EXTENSION */
30 #include "reactos/ddk/status.h" /* for STATUS_INVALID_PARAMETER */
31 #include "reactos/ddk/iofuncs.h" /* for IoCreateDevice() */
32 #include "captive/unicode.h"
33 #include "captive/macros.h"
34 #include "reactos/ddk/mmfuncs.h" /* for MmGetSystemAddressForMdl() */
37 static DRIVER_OBJECT cdrom_DriverObject;
38 static DISK_GEOMETRY cdrom_DiskGeometry;
39 static DISK_GEOMETRY cdrom_DiskGeometry_check; /* for g_assert() checking against foreign modifications */
40 static IO_SCSI_CAPABILITIES cdrom_PortCapabilities;
41 static IO_SCSI_CAPABILITIES cdrom_PortCapabilities_check; /* for g_assert() checking against foreign modifications */
42 static GIOChannel *Image_iochannel=NULL;
43 static guint64 Image_size; /* FIXME: lseek64() */
46 static gboolean validate_DeviceObject(DEVICE_OBJECT *DeviceObject)
48 DEVICE_EXTENSION *DeviceExtension;
49 DISK_GEOMETRY *DiskGeometry;
50 IO_SCSI_CAPABILITIES *PortCapabilities;
52 g_return_val_if_fail(DeviceObject!=NULL,FALSE);
53 g_return_val_if_fail(DeviceObject->DriverObject==&cdrom_DriverObject,FALSE);
55 DeviceExtension=DeviceObject->DeviceExtension;
57 DiskGeometry=DeviceExtension->DiskGeometry;
58 g_return_val_if_fail(DiskGeometry==&cdrom_DiskGeometry,FALSE);
59 g_return_val_if_fail(DiskGeometry->MediaType==cdrom_DiskGeometry_check.MediaType,FALSE);
60 g_return_val_if_fail(DiskGeometry->TracksPerCylinder==cdrom_DiskGeometry_check.TracksPerCylinder,FALSE);
61 g_return_val_if_fail(DiskGeometry->SectorsPerTrack==cdrom_DiskGeometry_check.SectorsPerTrack,FALSE);
62 g_return_val_if_fail(DiskGeometry->BytesPerSector==cdrom_DiskGeometry_check.BytesPerSector,FALSE);
63 g_return_val_if_fail(DiskGeometry->Cylinders.QuadPart==cdrom_DiskGeometry_check.Cylinders.QuadPart,FALSE);
64 g_return_val_if_fail(DeviceExtension->PartitionLength.QuadPart==(gint64)Image_size,FALSE);
66 PortCapabilities=DeviceExtension->PortCapabilities;
67 g_return_val_if_fail(PortCapabilities==&cdrom_PortCapabilities,FALSE);
68 g_return_val_if_fail(PortCapabilities->Length==cdrom_PortCapabilities_check.Length,FALSE);
69 g_return_val_if_fail(PortCapabilities->MaximumTransferLength==cdrom_PortCapabilities_check.MaximumTransferLength,FALSE);
70 g_return_val_if_fail(PortCapabilities->MaximumPhysicalPages==cdrom_PortCapabilities_check.MaximumPhysicalPages,FALSE);
71 g_return_val_if_fail(PortCapabilities->SupportedAsynchronousEvents==cdrom_PortCapabilities_check.SupportedAsynchronousEvents,FALSE);
72 g_return_val_if_fail(PortCapabilities->AlignmentMask==cdrom_PortCapabilities_check.AlignmentMask,FALSE);
73 g_return_val_if_fail(PortCapabilities->TaggedQueuing==cdrom_PortCapabilities_check.TaggedQueuing,FALSE);
74 g_return_val_if_fail(PortCapabilities->AdapterScansDown==cdrom_PortCapabilities_check.AdapterScansDown,FALSE);
75 g_return_val_if_fail(PortCapabilities->AdapterUsesPio==cdrom_PortCapabilities_check.AdapterUsesPio,FALSE);
81 static NTSTATUS MajorFunction_Irp_finish(DEVICE_OBJECT *DeviceObject,IRP *Irp)
85 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
86 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
88 /* required for removable media only */
89 if (!NT_SUCCESS(Irp->IoStatus.Status) && IoIsErrorUserInduced(Irp->IoStatus.Status)) {
90 g_assert(Irp->Tail.Overlay.Thread!=NULL); /* FIXME: Error should be postponed to first !=NULL Irp later */
91 IoSetHardErrorOrVerifyDevice(Irp,DeviceObject);
92 Irp->IoStatus.Information=0; /* may got set during some processing before error occured */
95 /* IoCompleteRequest() will do 'IoFreeIrp(Irp);'!
96 * 'IoStatus.Status' must be saved before its invocation!
98 r=Irp->IoStatus.Status;
99 IoCompleteRequest(Irp,IO_NO_INCREMENT);
104 /* FIXME: We should comply with PDRIVER_DISPATCH prototype but unfortunately
105 * CAPTIVE_STDCALL prevents us to do so at least in RedHat gcc-3.2-4 (gcc bug?).
107 #define MajorFunction_DEVICE_CONTROL ((PDRIVER_DISPATCH)MajorFunction_DEVICE_CONTROL_func)
108 static NTSTATUS CAPTIVE_STDCALL MajorFunction_DEVICE_CONTROL_func(IN DEVICE_OBJECT *DeviceObject,IN IRP *Irp)
110 IO_STACK_LOCATION *IrpStack;
112 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
113 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
115 Irp->IoStatus.Information=0; /* request-specific, may get overriden later */
116 IrpStack=IoGetCurrentIrpStackLocation(Irp);
117 g_assert(IrpStack->MajorFunction==IRP_MJ_DEVICE_CONTROL);
118 g_assert(IrpStack->MinorFunction==0);
120 switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
122 case IOCTL_CDROM_GET_LAST_SESSION:
123 /* Nothing interesting to see, move along.
124 * FIXME: This call is somehow multisession related - must we care?
128 case IOCTL_CDROM_READ_TOC: {
131 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(CDROM_TOC)) {
132 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
133 Irp->IoStatus.Information=sizeof(CDROM_TOC);
134 g_assert_not_reached();
137 CdromToc=(CDROM_TOC *)Irp->AssociatedIrp.SystemBuffer;
138 CAPTIVE_MEMZERO(CdromToc);
139 CdromToc->Length[0]=((sizeof(*CdromToc)-2)>>0U)&0xFFU; /* little-endian */
140 CdromToc->Length[1]=((sizeof(*CdromToc)-2)>>8U)&0xFFU;
141 CdromToc->FirstTrack=0; /* one track; TOC_LAST_TRACK does not count */
142 CdromToc->LastTrack =0; /* one track; TOC_LAST_TRACK does not count */
143 CdromToc->TrackData[0].Control=TOC_DATA_TRACK;
144 CdromToc->TrackData[0].Adr=0; /* Q-subchannel subinfo */
145 CdromToc->TrackData[0].TrackNumber=0;
146 CdromToc->TrackData[0].Address[0]=0>>24U; /* LBA offset; big-endian */
147 CdromToc->TrackData[0].Address[1]=0>>16U;
148 CdromToc->TrackData[0].Address[2]=0>> 8U;
149 CdromToc->TrackData[0].Address[3]=0>> 0U;
150 CdromToc->TrackData[1].Control=0;
151 CdromToc->TrackData[1].Adr=0; /* Q-subchannel subinfo */
152 CdromToc->TrackData[1].TrackNumber=TOC_LAST_TRACK;
153 /* FIXME: should we put the Image_size to TOC_LAST_TRACK? */
154 CdromToc->TrackData[1].Address[0]=(Image_size/512)>>24U; /* LBA offset; big-endian */
155 CdromToc->TrackData[1].Address[1]=(Image_size/512)>>16U;
156 CdromToc->TrackData[1].Address[2]=(Image_size/512)>> 8U;
157 CdromToc->TrackData[1].Address[3]=(Image_size/512)>> 0U;
159 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
160 Irp->IoStatus.Status=STATUS_SUCCESS;
163 case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
164 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(DISK_GEOMETRY)) {
165 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
166 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
167 g_assert_not_reached();
170 *(DISK_GEOMETRY *)Irp->AssociatedIrp.SystemBuffer=cdrom_DiskGeometry;
171 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
172 Irp->IoStatus.Status=STATUS_SUCCESS;
175 case IOCTL_CDROM_CHECK_VERIFY:
176 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength) {
177 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(ULONG)) {
178 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
179 Irp->IoStatus.Information=sizeof(ULONG);
180 g_assert_not_reached();
183 *(ULONG *)Irp->AssociatedIrp.SystemBuffer=0; /* MediaChangeCount */
184 Irp->IoStatus.Information=sizeof(ULONG);
187 Irp->IoStatus.Information=0;
189 Irp->IoStatus.Status=STATUS_SUCCESS;
192 case IOCTL_SCSI_GET_CAPABILITIES:
193 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(IO_SCSI_CAPABILITIES)) {
194 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
195 Irp->IoStatus.Information=sizeof(IO_SCSI_CAPABILITIES);
196 g_assert_not_reached();
199 *(IO_SCSI_CAPABILITIES *)Irp->AssociatedIrp.SystemBuffer=cdrom_PortCapabilities;
200 Irp->IoStatus.Information=sizeof(IO_SCSI_CAPABILITIES);
201 Irp->IoStatus.Status=STATUS_SUCCESS;
205 Irp->IoStatus.Status=STATUS_INVALID_DEVICE_REQUEST;
206 g_assert_not_reached();
211 done: /* 'err:' but we flow here even during success */
212 return MajorFunction_Irp_finish(DeviceObject,Irp);
216 /* FIXME: We should comply with PDRIVER_DISPATCH prototype but unfortunately
217 * CAPTIVE_STDCALL prevents us to do so at least in RedHat gcc-3.2-4 (gcc bug?).
219 #define MajorFunction_READ ((PDRIVER_DISPATCH)MajorFunction_READ_func)
220 static NTSTATUS CAPTIVE_STDCALL MajorFunction_READ_func(IN DEVICE_OBJECT *DeviceObject,IN IRP *Irp)
222 IO_STACK_LOCATION *IrpStack;
224 gpointer buffer=NULL;
225 GIOStatus erriostatus;
227 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
228 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
230 Irp->IoStatus.Information=0; /* request-specific, may get overriden later */
231 IrpStack=IoGetCurrentIrpStackLocation(Irp);
232 g_assert(IrpStack->MajorFunction==IRP_MJ_READ);
233 g_assert(IrpStack->MinorFunction==0);
235 /* What is 'IrpStack->Parameters.Read.Key'? */
236 g_assert(0==(IrpStack->Parameters.Read.Length%2048));
237 g_assert(0==(IrpStack->Parameters.Read.ByteOffset.QuadPart%2048));
238 g_assert(IrpStack->Parameters.Read.ByteOffset.QuadPart>=0);
240 /* Autodetect 'buffer' as we are !DO_BUFFERED_IO && !DO_DIRECT_IO hybrid */
241 if (Irp->UserBuffer) {
242 g_assert(buffer==NULL);
243 buffer=Irp->UserBuffer;
245 if (Irp->AssociatedIrp.SystemBuffer) {
246 g_assert(buffer==NULL);
247 buffer=Irp->AssociatedIrp.SystemBuffer;
249 if (Irp->MdlAddress) {
250 g_assert(buffer==NULL);
251 g_assert(IrpStack->Parameters.Read.Length<=MmGetMdlByteCount(Irp->MdlAddress));
252 buffer=MmGetSystemAddressForMdl(Irp->MdlAddress);
254 g_assert(buffer!=NULL);
256 erriostatus=g_io_channel_seek_position(Image_iochannel,
257 IrpStack->Parameters.Read.ByteOffset.QuadPart, /* offset */
258 G_SEEK_SET, /* type */
260 g_assert(erriostatus==G_IO_STATUS_NORMAL);
262 erriostatus=g_io_channel_read_chars(Image_iochannel,
264 IrpStack->Parameters.Read.Length, /* count */
265 &bytesread, /* bytesread */
267 g_assert(erriostatus==G_IO_STATUS_NORMAL);
268 g_assert(bytesread==IrpStack->Parameters.Read.Length);
270 Irp->IoStatus.Information=IrpStack->Parameters.Read.Length;
271 Irp->IoStatus.Status=STATUS_SUCCESS;
274 /* done: */ /* 'err:' but we flow here even during success */
275 return MajorFunction_Irp_finish(DeviceObject,Irp);
279 /* similiar to drivers/storage/cdrom/cdrom.c/DriverEntry()->...
280 * ...->CdromClassCreateDeviceObject()->
281 * ->reactos/drivers/storage/class2/class2.c/ScsiClassCreateDeviceObject()
282 * We should be driving a lower layer PortDevice but currently we
283 * do not provide it, I hope W32 filesystems don't touch it.
285 static NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
287 PDEVICE_OBJECT DeviceObject;
288 PDEVICE_EXTENSION DeviceExtension;
291 g_return_val_if_fail(DriverObject!=NULL,STATUS_INVALID_PARAMETER);
292 g_return_val_if_fail(RegistryPath!=NULL,STATUS_INVALID_PARAMETER);
295 DriverObject, /* DriverObject */
296 sizeof(DEVICE_EXTENSION), /* DeviceExtensionSize; additional storage not used */
297 captive_utf8_to_UnicodeString_alloca("\\Device\\CdRom0"), /* DeviceName */
298 FILE_DEVICE_CD_ROM, /* DeviceType */
299 FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE, /* DeviceCharacteristics */
300 FALSE, /* Exclusive */
301 &DeviceObject); /* DeviceObject */
302 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
304 /* Currently we are !DO_BUFFERED_IO && !DO_DIRECT_IO and we must solve
305 * being called with 'Irp->UserBuffer', 'Irp->AssociatedIrp.SystemBuffer' or 'Irp->MdlAddress'.
307 /* should be left from IoCreateDevice(DeviceCharacteristics) above: */
308 g_assert(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA);
309 /* ignored: DeviceObject->StackSize */
310 /* ignored: DeviceObject->AlignmentRequirement */
312 /* from reactos/drivers/storage/scsiport/scsiport.c/ScsiPortCreatePortDevice() */
313 cdrom_PortCapabilities.Length=sizeof(cdrom_PortCapabilities);
314 cdrom_PortCapabilities.MaximumTransferLength=0x10000; /* 64KB */
315 g_assert((cdrom_PortCapabilities.MaximumTransferLength%PAGE_SIZE)==0);
316 cdrom_PortCapabilities.MaximumPhysicalPages=cdrom_PortCapabilities.MaximumTransferLength/PAGE_SIZE;
317 cdrom_PortCapabilities.SupportedAsynchronousEvents=0;
318 cdrom_PortCapabilities.AlignmentMask=1; /* no alignment required by us; speced as "integer multiple" */
319 cdrom_PortCapabilities.TaggedQueuing=FALSE;
320 cdrom_PortCapabilities.AdapterScansDown=FALSE;
321 cdrom_PortCapabilities.AdapterUsesPio=TRUE;
322 cdrom_PortCapabilities_check=cdrom_PortCapabilities; /* for g_assert() checking against foreign modifications */
324 DeviceExtension=DeviceObject->DeviceExtension;
325 DeviceExtension->MediaChangeCount=0;
326 DeviceExtension->PhysicalDevice=DeviceObject; /* no real PhysicalDeviceObject */
327 DeviceExtension->LockCount=0;
328 DeviceExtension->DeviceNumber=0; /* corresponds to the # in "\\Device\\CdRom0" */
329 /* ignored DeviceExtension->PortDeviceObject
330 * as we are the final driver and we don't have any PortDeviceObject
332 DeviceExtension->PortCapabilities=&cdrom_PortCapabilities;
333 DeviceExtension->StartingOffset.QuadPart=0;
334 DeviceExtension->PartitionLength.QuadPart=Image_size;
335 DeviceExtension->PortNumber=0;
336 DeviceExtension->PathId=0;
337 DeviceExtension->TargetId=0;
338 DeviceExtension->Lun=0;
340 cdrom_DiskGeometry.MediaType=RemovableMedia;
341 cdrom_DiskGeometry.TracksPerCylinder=64;
342 cdrom_DiskGeometry.SectorsPerTrack=32;
343 cdrom_DiskGeometry.BytesPerSector=2048;
344 cdrom_DiskGeometry.Cylinders.QuadPart=Image_size
345 /cdrom_DiskGeometry.BytesPerSector
346 /cdrom_DiskGeometry.SectorsPerTrack
347 /cdrom_DiskGeometry.TracksPerCylinder;
348 /* 'DeviceExtension->DiskGeometry' is NULL! */
349 cdrom_DiskGeometry_check=cdrom_DiskGeometry; /* for g_assert() checking against foreign modifications */
350 DeviceExtension->DiskGeometry=&cdrom_DiskGeometry;
352 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=MajorFunction_DEVICE_CONTROL;
353 DriverObject->MajorFunction[IRP_MJ_READ ]=MajorFunction_READ;
355 return STATUS_SUCCESS;
359 * captive_cdrom_init:
360 * @image_channel: Host OS file of the disk image to provide.
361 * %NULL value is forbidden.
363 * Creates system device "\Device\CdRom%d" providing readonly access
364 * to the given @image_pathname as emulation of CD-ROM driver.
366 * captive currently supports just one drive and thus "\Device\CdRom0"
367 * is always created. It is forbidden to call this function twice.
369 * Returns: %TRUE if the initialization was successful.
371 gboolean captive_cdrom_init(GIOChannel *image_iochannel)
375 g_return_val_if_fail(image_iochannel!=NULL,FALSE);
377 Image_iochannel=image_iochannel;
378 Image_size=captive_giochannel_size(Image_iochannel);
381 &cdrom_DriverObject, /* DriverEntry_DriverObject */
382 captive_utf8_to_UnicodeString_alloca("\\captive\\storage\\cdrom")); /* DriverEntry_RegistryPath; ignored */
383 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
388 g_io_channel_close(Image_iochannel);
389 Image_iochannel=NULL;
391 g_return_val_if_reached(FALSE);