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)
67 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
68 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
70 /* required for removable media only */
71 if (!NT_SUCCESS(Irp->IoStatus.Status) && IoIsErrorUserInduced(Irp->IoStatus.Status)) {
72 g_assert(Irp->Tail.Overlay.Thread!=NULL); /* FIXME: Error should be postponed to first !=NULL Irp later */
73 IoSetHardErrorOrVerifyDevice(Irp,DeviceObject);
74 Irp->IoStatus.Information=0; /* may got set during some processing before error occured */
77 IoCompleteRequest(Irp,IO_NO_INCREMENT); /* I hope it won't corrupt our Irp->IoStatus.Status */
78 return Irp->IoStatus.Status;
82 /* FIXME: We should comply with PDRIVER_DISPATCH prototype but unfortunately
83 * CAPTIVE_STDCALL prevents us to do so at least in RedHat gcc-3.2-4 (gcc bug?).
85 #define MajorFunction_DEVICE_CONTROL ((PDRIVER_DISPATCH)MajorFunction_DEVICE_CONTROL_func)
86 static NTSTATUS CAPTIVE_STDCALL MajorFunction_DEVICE_CONTROL_func(IN DEVICE_OBJECT *DeviceObject,IN IRP *Irp)
88 IO_STACK_LOCATION *IrpStack;
90 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
91 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
93 Irp->IoStatus.Information=0; /* request-specific, may get overriden later */
94 IrpStack=IoGetCurrentIrpStackLocation(Irp);
95 g_assert(IrpStack->MajorFunction==IRP_MJ_DEVICE_CONTROL);
96 g_assert(IrpStack->MinorFunction==0);
98 switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
100 case IOCTL_CDROM_GET_LAST_SESSION:
101 /* Nothing interesting to see, move along.
102 * FIXME: This call is somehow multisession related - must we care?
106 case IOCTL_CDROM_READ_TOC: {
109 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(CDROM_TOC)) {
110 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
111 Irp->IoStatus.Information=sizeof(CDROM_TOC);
112 g_assert_not_reached();
115 CdromToc=(CDROM_TOC *)Irp->AssociatedIrp.SystemBuffer;
116 CAPTIVE_MEMZERO(CdromToc);
117 CdromToc->Length[0]=((sizeof(*CdromToc)-2)>>0U)&0xFFU; /* little-endian */
118 CdromToc->Length[1]=((sizeof(*CdromToc)-2)>>8U)&0xFFU;
119 CdromToc->FirstTrack=0; /* one track; TOC_LAST_TRACK does not count */
120 CdromToc->LastTrack =0; /* one track; TOC_LAST_TRACK does not count */
121 CdromToc->TrackData[0].Control=TOC_DATA_TRACK;
122 CdromToc->TrackData[0].Adr=0; /* Q-subchannel subinfo */
123 CdromToc->TrackData[0].TrackNumber=0;
124 CdromToc->TrackData[0].Address[0]=0>>24U; /* LBA offset; big-endian */
125 CdromToc->TrackData[0].Address[1]=0>>16U;
126 CdromToc->TrackData[0].Address[2]=0>> 8U;
127 CdromToc->TrackData[0].Address[3]=0>> 0U;
128 CdromToc->TrackData[1].Control=0;
129 CdromToc->TrackData[1].Adr=0; /* Q-subchannel subinfo */
130 CdromToc->TrackData[1].TrackNumber=TOC_LAST_TRACK;
131 /* FIXME: should we put the Image_size to TOC_LAST_TRACK? */
132 CdromToc->TrackData[1].Address[0]=(Image_size/512)>>24U; /* LBA offset; big-endian */
133 CdromToc->TrackData[1].Address[1]=(Image_size/512)>>16U;
134 CdromToc->TrackData[1].Address[2]=(Image_size/512)>> 8U;
135 CdromToc->TrackData[1].Address[3]=(Image_size/512)>> 0U;
137 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
138 Irp->IoStatus.Status=STATUS_SUCCESS;
141 case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
142 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(DISK_GEOMETRY)) {
143 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
144 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
145 g_assert_not_reached();
148 *(DISK_GEOMETRY *)Irp->AssociatedIrp.SystemBuffer=cdrom_DiskGeometry;
149 Irp->IoStatus.Information=sizeof(DISK_GEOMETRY);
150 Irp->IoStatus.Status=STATUS_SUCCESS;
153 case IOCTL_CDROM_CHECK_VERIFY: {
154 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength) {
155 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength<sizeof(ULONG)) {
156 Irp->IoStatus.Status=STATUS_BUFFER_TOO_SMALL;
157 Irp->IoStatus.Information=sizeof(ULONG);
158 g_assert_not_reached();
161 *(ULONG *)Irp->AssociatedIrp.SystemBuffer=0; /* MediaChangeCount */
162 Irp->IoStatus.Information=sizeof(ULONG);
165 Irp->IoStatus.Information=0;
167 Irp->IoStatus.Status=STATUS_SUCCESS;
171 Irp->IoStatus.Status=STATUS_INVALID_DEVICE_REQUEST;
172 g_assert_not_reached();
177 done: /* 'err:' but we flow here even during success */
178 return MajorFunction_Irp_finish(DeviceObject,Irp);
182 /* FIXME: We should comply with PDRIVER_DISPATCH prototype but unfortunately
183 * CAPTIVE_STDCALL prevents us to do so at least in RedHat gcc-3.2-4 (gcc bug?).
185 #define MajorFunction_READ ((PDRIVER_DISPATCH)MajorFunction_READ_func)
186 static NTSTATUS CAPTIVE_STDCALL MajorFunction_READ_func(IN DEVICE_OBJECT *DeviceObject,IN IRP *Irp)
188 IO_STACK_LOCATION *IrpStack;
192 g_return_val_if_fail(TRUE==validate_DeviceObject(DeviceObject),STATUS_INVALID_PARAMETER);
193 g_return_val_if_fail(Irp!=NULL,STATUS_INVALID_PARAMETER);
195 Irp->IoStatus.Information=0; /* request-specific, may get overriden later */
196 IrpStack=IoGetCurrentIrpStackLocation(Irp);
197 g_assert(IrpStack->MajorFunction==IRP_MJ_READ);
198 g_assert(IrpStack->MinorFunction==0);
200 /* What is 'IrpStack->Parameters.Read.Key'? */
201 g_assert(0==(IrpStack->Parameters.Read.Length%2048));
202 g_assert(0==(IrpStack->Parameters.Read.ByteOffset.QuadPart%2048));
203 g_assert(IrpStack->Parameters.Read.ByteOffset.QuadPart>=0);
205 offset=lseek(Image_fd,IrpStack->Parameters.Read.ByteOffset.QuadPart,SEEK_SET);
206 g_assert(offset==IrpStack->Parameters.Read.ByteOffset.QuadPart);
208 count=read(Image_fd,Irp->UserBuffer,IrpStack->Parameters.Read.Length);
209 g_assert((ULONG)count==IrpStack->Parameters.Read.Length);
211 Irp->IoStatus.Information=IrpStack->Parameters.Read.Length;
212 Irp->IoStatus.Status=STATUS_SUCCESS;
215 /* done: */ /* 'err:' but we flow here even during success */
216 return MajorFunction_Irp_finish(DeviceObject,Irp);
220 /* similiar to drivers/storage/cdrom/cdrom.c/DriverEntry()->...
221 * ...->CdromClassCreateDeviceObject()->
222 * ->reactos/drivers/storage/class2/class2.c/ScsiClassCreateDeviceObject()
223 * We should be driving a lower layer PortDevice but currently we
224 * do not provide it, I hope W32 filesystems don't touch it.
226 static NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
228 static IO_SCSI_CAPABILITIES PortCapabilities; /* it is const filled in DriverEntry() */
229 PDEVICE_OBJECT DeviceObject;
230 PDEVICE_EXTENSION DeviceExtension;
233 g_return_val_if_fail(DriverObject!=NULL,STATUS_INVALID_PARAMETER);
234 g_return_val_if_fail(RegistryPath!=NULL,STATUS_INVALID_PARAMETER);
237 DriverObject, /* DriverObject */
238 sizeof(DEVICE_EXTENSION), /* DeviceExtensionSize; additional storage not used */
239 captive_utf8_to_UnicodeString_alloca("\\Device\\CdRom0"), /* DeviceName */
240 FILE_DEVICE_CD_ROM, /* DeviceType */
241 FILE_REMOVABLE_MEDIA|FILE_READ_ONLY_DEVICE, /* DeviceCharacteristics */
242 FALSE, /* Exclusive */
243 &DeviceObject); /* DeviceObject */
244 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
246 /* CdromClassCreateDeviceObject() sets:
247 * DeviceObject->Flags|=DO_DIRECT_IO;
250 /* should be left from IoCreateDevice(DeviceCharacteristics) above: */
251 g_assert(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA);
252 /* ignored: DeviceObject->StackSize */
253 /* ignored: DeviceObject->AlignmentRequirement */
255 /* from reactos/drivers/storage/scsiport/scsiport.c/ScsiPortCreatePortDevice() */
256 PortCapabilities.Length=sizeof(PortCapabilities);
257 PortCapabilities.MaximumTransferLength=0x10000; /* 64KB */
258 g_assert((PortCapabilities.MaximumTransferLength%PAGE_SIZE)==0);
259 PortCapabilities.MaximumPhysicalPages=PortCapabilities.MaximumTransferLength/PAGE_SIZE;
260 PortCapabilities.SupportedAsynchronousEvents=0;
261 PortCapabilities.AlignmentMask=1; /* no alignment required by us; speced as "integer multiple" */
262 PortCapabilities.TaggedQueuing=FALSE;
263 PortCapabilities.AdapterScansDown=FALSE;
264 PortCapabilities.AdapterUsesPio=TRUE;
266 DeviceExtension=DeviceObject->DeviceExtension;
267 DeviceExtension->MediaChangeCount=0;
268 DeviceExtension->PhysicalDevice=DeviceObject; /* no real PhysicalDeviceObject */
269 DeviceExtension->LockCount=0;
270 DeviceExtension->DeviceNumber=0; /* corresponds to the # in "\\Device\\CdRom0" */
271 /* ignored DeviceExtension->PortDeviceObject
272 * as we are the final driver and we don't have any PortDeviceObject
274 DeviceExtension->PortCapabilities=&PortCapabilities;
275 DeviceExtension->StartingOffset.QuadPart=0;
276 DeviceExtension->PartitionLength.QuadPart=Image_size;
277 DeviceExtension->PortNumber=0;
278 DeviceExtension->PathId=0;
279 DeviceExtension->TargetId=0;
280 DeviceExtension->Lun=0;
282 cdrom_DiskGeometry.MediaType=RemovableMedia;
283 cdrom_DiskGeometry.TracksPerCylinder=64;
284 cdrom_DiskGeometry.SectorsPerTrack=32;
285 cdrom_DiskGeometry.BytesPerSector=2048;
286 cdrom_DiskGeometry.Cylinders.QuadPart=Image_size
287 /cdrom_DiskGeometry.BytesPerSector
288 /cdrom_DiskGeometry.SectorsPerTrack
289 /cdrom_DiskGeometry.TracksPerCylinder;
290 /* 'DeviceExtension->DiskGeometry' is NULL! */
291 cdrom_DiskGeometry_check=cdrom_DiskGeometry; /* for g_assert() checking against foreign modifications */
292 DeviceExtension->DiskGeometry=&cdrom_DiskGeometry;
294 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=MajorFunction_DEVICE_CONTROL;
295 DriverObject->MajorFunction[IRP_MJ_READ ]=MajorFunction_READ;
297 return STATUS_SUCCESS;
301 * captive_cdrom_init:
302 * @image_pathname: Host OS file #utf8 pathname of the disk image to provide.
303 * %NULL value is forbidden.
305 * Creates system device "\Device\CdRom%d" providing readonly access
306 * to the given @image_pathname as emulation of CD-ROM driver.
308 * captive currently supports just one drive and thus "\Device\CdRom0"
309 * is always created. It is forbidden to call this function twice.
311 * Returns: %TRUE if the initialization was successful.
313 gboolean captive_cdrom_init(const gchar *image_pathname)
317 g_return_val_if_fail(image_pathname!=NULL,FALSE);
319 Image_fd=open(image_pathname,O_RDONLY
323 ); /* FIXME: lseek64() */
324 g_return_val_if_fail(Image_fd!=-1,FALSE);
326 Image_size=lseek(Image_fd,0,SEEK_END); /* FIXME: lseek64() */
327 if (Image_size==(off_t)-1) {
328 g_assert_not_reached();
333 &cdrom_DriverObject, /* DriverEntry_DriverObject */
334 captive_utf8_to_UnicodeString_alloca("\\captive\\storage\\cdrom")); /* DriverEntry_RegistryPath; ignored */
335 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
343 g_return_val_if_reached(FALSE);