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"
36 static DRIVER_OBJECT cdrom_DriverObject;
37 static DISK_GEOMETRY cdrom_DiskGeometry;
38 static DISK_GEOMETRY cdrom_DiskGeometry_check; /* for g_assert() checking against foreign modifications */
39 static int Image_fd=-1;
40 static off_t Image_size; /* FIXME: lseek64() */
43 static gboolean validate_DeviceObject(DEVICE_OBJECT *DeviceObject)
45 DEVICE_EXTENSION *DeviceExtension;
46 DISK_GEOMETRY *DiskGeometry;
48 g_return_val_if_fail(DeviceObject!=NULL,FALSE);
49 g_return_val_if_fail(DeviceObject->DriverObject==&cdrom_DriverObject,FALSE);
51 DeviceExtension=DeviceObject->DeviceExtension;
52 DiskGeometry=DeviceExtension->DiskGeometry;
53 g_return_val_if_fail(DiskGeometry==&cdrom_DiskGeometry,FALSE);
54 g_return_val_if_fail(DiskGeometry->MediaType==cdrom_DiskGeometry_check.MediaType,FALSE);
55 g_return_val_if_fail(DiskGeometry->TracksPerCylinder==cdrom_DiskGeometry_check.TracksPerCylinder,FALSE);
56 g_return_val_if_fail(DiskGeometry->SectorsPerTrack==cdrom_DiskGeometry_check.SectorsPerTrack,FALSE);
57 g_return_val_if_fail(DiskGeometry->BytesPerSector==cdrom_DiskGeometry_check.BytesPerSector,FALSE);
58 g_return_val_if_fail(DiskGeometry->Cylinders.QuadPart==cdrom_DiskGeometry_check.Cylinders.QuadPart,FALSE);
59 g_return_val_if_fail(DeviceExtension->PartitionLength.QuadPart==Image_size,FALSE);
65 static NTSTATUS MajorFunction_Irp_finish(DEVICE_OBJECT *DeviceObject,IRP *Irp)
69 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
70 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
72 /* required for removable media only */
73 if (!NT_SUCCESS(Irp->IoStatus.Status) && IoIsErrorUserInduced(Irp->IoStatus.Status)) {
74 g_assert(Irp->Tail.Overlay.Thread!=NULL); /* FIXME: Error should be postponed to first !=NULL Irp later */
75 IoSetHardErrorOrVerifyDevice(Irp,DeviceObject);
76 Irp->IoStatus.Information=0; /* may got set during some processing before error occured */
79 /* IoCompleteRequest() will do 'IoFreeIrp(Irp);'!
80 * 'IoStatus.Status' must be saved before its invocation!
82 r=Irp->IoStatus.Status;
83 IoCompleteRequest(Irp,IO_NO_INCREMENT);
88 /* FIXME: We should comply with PDRIVER_DISPATCH prototype but unfortunately
89 * CAPTIVE_STDCALL prevents us to do so at least in RedHat gcc-3.2-4 (gcc bug?).
91 #define MajorFunction_DEVICE_CONTROL ((PDRIVER_DISPATCH)MajorFunction_DEVICE_CONTROL_func)
92 static NTSTATUS CAPTIVE_STDCALL MajorFunction_DEVICE_CONTROL_func(IN DEVICE_OBJECT *DeviceObject,IN IRP *Irp)
94 IO_STACK_LOCATION *IrpStack;
96 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
97 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
99 Irp->IoStatus.Information=0; /* request-specific, may get overriden later */
100 IrpStack=IoGetCurrentIrpStackLocation(Irp);
101 g_assert(IrpStack->MajorFunction==IRP_MJ_DEVICE_CONTROL);
102 g_assert(IrpStack->MinorFunction==0);
104 switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
106 case IOCTL_CDROM_GET_LAST_SESSION:
107 /* Nothing interesting to see, move along.
108 * FIXME: This call is somehow multisession related - must we care?
112 case IOCTL_CDROM_READ_TOC: {
115 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(CDROM_TOC)) {
116 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
117 Irp->IoStatus.Information=sizeof(CDROM_TOC);
118 g_assert_not_reached();
121 CdromToc=(CDROM_TOC *)Irp->AssociatedIrp.SystemBuffer;
122 CAPTIVE_MEMZERO(CdromToc);
123 CdromToc->Length[0]=((sizeof(*CdromToc)-2)>>0U)&0xFFU; /* little-endian */
124 CdromToc->Length[1]=((sizeof(*CdromToc)-2)>>8U)&0xFFU;
125 CdromToc->FirstTrack=0; /* one track; TOC_LAST_TRACK does not count */
126 CdromToc->LastTrack =0; /* one track; TOC_LAST_TRACK does not count */
127 CdromToc->TrackData[0].Control=TOC_DATA_TRACK;
128 CdromToc->TrackData[0].Adr=0; /* Q-subchannel subinfo */
129 CdromToc->TrackData[0].TrackNumber=0;
130 CdromToc->TrackData[0].Address[0]=0>>24U; /* LBA offset; big-endian */
131 CdromToc->TrackData[0].Address[1]=0>>16U;
132 CdromToc->TrackData[0].Address[2]=0>> 8U;
133 CdromToc->TrackData[0].Address[3]=0>> 0U;
134 CdromToc->TrackData[1].Control=0;
135 CdromToc->TrackData[1].Adr=0; /* Q-subchannel subinfo */
136 CdromToc->TrackData[1].TrackNumber=TOC_LAST_TRACK;
137 /* FIXME: should we put the Image_size to TOC_LAST_TRACK? */
138 CdromToc->TrackData[1].Address[0]=(Image_size/512)>>24U; /* LBA offset; big-endian */
139 CdromToc->TrackData[1].Address[1]=(Image_size/512)>>16U;
140 CdromToc->TrackData[1].Address[2]=(Image_size/512)>> 8U;
141 CdromToc->TrackData[1].Address[3]=(Image_size/512)>> 0U;
143 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
144 Irp->IoStatus.Status=STATUS_SUCCESS;
147 case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
148 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(DISK_GEOMETRY)) {
149 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
150 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
151 g_assert_not_reached();
154 *(DISK_GEOMETRY *)Irp->AssociatedIrp.SystemBuffer=cdrom_DiskGeometry;
155 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
156 Irp->IoStatus.Status=STATUS_SUCCESS;
159 case IOCTL_CDROM_CHECK_VERIFY: {
160 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength) {
161 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(ULONG)) {
162 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
163 Irp->IoStatus.Information=sizeof(ULONG);
164 g_assert_not_reached();
167 *(ULONG *)Irp->AssociatedIrp.SystemBuffer=0; /* MediaChangeCount */
168 Irp->IoStatus.Information=sizeof(ULONG);
171 Irp->IoStatus.Information=0;
173 Irp->IoStatus.Status=STATUS_SUCCESS;
177 Irp->IoStatus.Status=STATUS_INVALID_DEVICE_REQUEST;
178 g_assert_not_reached();
183 done: /* 'err:' but we flow here even during success */
184 return MajorFunction_Irp_finish(DeviceObject,Irp);
188 /* FIXME: We should comply with PDRIVER_DISPATCH prototype but unfortunately
189 * CAPTIVE_STDCALL prevents us to do so at least in RedHat gcc-3.2-4 (gcc bug?).
191 #define MajorFunction_READ ((PDRIVER_DISPATCH)MajorFunction_READ_func)
192 static NTSTATUS CAPTIVE_STDCALL MajorFunction_READ_func(IN DEVICE_OBJECT *DeviceObject,IN IRP *Irp)
194 IO_STACK_LOCATION *IrpStack;
198 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
199 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
201 Irp->IoStatus.Information=0; /* request-specific, may get overriden later */
202 IrpStack=IoGetCurrentIrpStackLocation(Irp);
203 g_assert(IrpStack->MajorFunction==IRP_MJ_READ);
204 g_assert(IrpStack->MinorFunction==0);
206 /* What is 'IrpStack->Parameters.Read.Key'? */
207 g_assert(0==(IrpStack->Parameters.Read.Length%2048));
208 g_assert(0==(IrpStack->Parameters.Read.ByteOffset.QuadPart%2048));
209 g_assert(IrpStack->Parameters.Read.ByteOffset.QuadPart>=0);
211 offset=lseek(Image_fd,IrpStack->Parameters.Read.ByteOffset.QuadPart,SEEK_SET);
212 g_assert(offset==IrpStack->Parameters.Read.ByteOffset.QuadPart);
214 count=read(Image_fd,Irp->UserBuffer,IrpStack->Parameters.Read.Length);
215 g_assert((ULONG)count==IrpStack->Parameters.Read.Length);
217 Irp->IoStatus.Information=IrpStack->Parameters.Read.Length;
218 Irp->IoStatus.Status=STATUS_SUCCESS;
221 /* done: */ /* 'err:' but we flow here even during success */
222 return MajorFunction_Irp_finish(DeviceObject,Irp);
226 /* similiar to drivers/storage/cdrom/cdrom.c/DriverEntry()->...
227 * ...->CdromClassCreateDeviceObject()->
228 * ->reactos/drivers/storage/class2/class2.c/ScsiClassCreateDeviceObject()
229 * We should be driving a lower layer PortDevice but currently we
230 * do not provide it, I hope W32 filesystems don't touch it.
232 static NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
234 static IO_SCSI_CAPABILITIES PortCapabilities; /* it is const filled in DriverEntry() */
235 PDEVICE_OBJECT DeviceObject;
236 PDEVICE_EXTENSION DeviceExtension;
239 g_return_val_if_fail(DriverObject!=NULL,STATUS_INVALID_PARAMETER);
240 g_return_val_if_fail(RegistryPath!=NULL,STATUS_INVALID_PARAMETER);
243 DriverObject, /* DriverObject */
244 sizeof(DEVICE_EXTENSION), /* DeviceExtensionSize; additional storage not used */
245 captive_utf8_to_UnicodeString_alloca("\\Device\\CdRom0"), /* DeviceName */
246 FILE_DEVICE_CD_ROM, /* DeviceType */
247 FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE, /* DeviceCharacteristics */
248 FALSE, /* Exclusive */
249 &DeviceObject); /* DeviceObject */
250 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
252 /* CdromClassCreateDeviceObject() sets:
253 * DeviceObject->Flags|=DO_DIRECT_IO;
256 /* should be left from IoCreateDevice(DeviceCharacteristics) above: */
257 g_assert(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA);
258 /* ignored: DeviceObject->StackSize */
259 /* ignored: DeviceObject->AlignmentRequirement */
261 /* from reactos/drivers/storage/scsiport/scsiport.c/ScsiPortCreatePortDevice() */
262 PortCapabilities.Length=sizeof(PortCapabilities);
263 PortCapabilities.MaximumTransferLength=0x10000; /* 64KB */
264 g_assert((PortCapabilities.MaximumTransferLength%PAGE_SIZE)==0);
265 PortCapabilities.MaximumPhysicalPages=PortCapabilities.MaximumTransferLength/PAGE_SIZE;
266 PortCapabilities.SupportedAsynchronousEvents=0;
267 PortCapabilities.AlignmentMask=1; /* no alignment required by us; speced as "integer multiple" */
268 PortCapabilities.TaggedQueuing=FALSE;
269 PortCapabilities.AdapterScansDown=FALSE;
270 PortCapabilities.AdapterUsesPio=TRUE;
272 DeviceExtension=DeviceObject->DeviceExtension;
273 DeviceExtension->MediaChangeCount=0;
274 DeviceExtension->PhysicalDevice=DeviceObject; /* no real PhysicalDeviceObject */
275 DeviceExtension->LockCount=0;
276 DeviceExtension->DeviceNumber=0; /* corresponds to the # in "\\Device\\CdRom0" */
277 /* ignored DeviceExtension->PortDeviceObject
278 * as we are the final driver and we don't have any PortDeviceObject
280 DeviceExtension->PortCapabilities=&PortCapabilities;
281 DeviceExtension->StartingOffset.QuadPart=0;
282 DeviceExtension->PartitionLength.QuadPart=Image_size;
283 DeviceExtension->PortNumber=0;
284 DeviceExtension->PathId=0;
285 DeviceExtension->TargetId=0;
286 DeviceExtension->Lun=0;
288 cdrom_DiskGeometry.MediaType=RemovableMedia;
289 cdrom_DiskGeometry.TracksPerCylinder=64;
290 cdrom_DiskGeometry.SectorsPerTrack=32;
291 cdrom_DiskGeometry.BytesPerSector=2048;
292 cdrom_DiskGeometry.Cylinders.QuadPart=Image_size
293 /cdrom_DiskGeometry.BytesPerSector
294 /cdrom_DiskGeometry.SectorsPerTrack
295 /cdrom_DiskGeometry.TracksPerCylinder;
296 /* 'DeviceExtension->DiskGeometry' is NULL! */
297 cdrom_DiskGeometry_check=cdrom_DiskGeometry; /* for g_assert() checking against foreign modifications */
298 DeviceExtension->DiskGeometry=&cdrom_DiskGeometry;
300 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=MajorFunction_DEVICE_CONTROL;
301 DriverObject->MajorFunction[IRP_MJ_READ ]=MajorFunction_READ;
303 return STATUS_SUCCESS;
307 * captive_cdrom_init:
308 * @image_pathname: Host OS file #utf8 pathname of the disk image to provide.
309 * %NULL value is forbidden.
311 * Creates system device "\Device\CdRom%d" providing readonly access
312 * to the given @image_pathname as emulation of CD-ROM driver.
314 * captive currently supports just one drive and thus "\Device\CdRom0"
315 * is always created. It is forbidden to call this function twice.
317 * Returns: %TRUE if the initialization was successful.
319 gboolean captive_cdrom_init(const gchar *image_pathname)
323 g_return_val_if_fail(image_pathname!=NULL,FALSE);
325 Image_fd=open(image_pathname,O_RDONLY
329 ); /* FIXME: lseek64() */
330 g_return_val_if_fail(Image_fd!=-1,FALSE);
332 Image_size=lseek(Image_fd,0,SEEK_END); /* FIXME: lseek64() */
333 if (Image_size==(off_t)-1) {
334 g_assert_not_reached();
339 &cdrom_DriverObject, /* DriverEntry_DriverObject */
340 captive_utf8_to_UnicodeString_alloca("\\captive\\storage\\cdrom")); /* DriverEntry_RegistryPath; ignored */
341 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
349 g_return_val_if_reached(FALSE);