3 * Copyright (C) 2001, 2002 ReactOS Team
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; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * FILE: services/storage/cdrom/cdrom.c
24 * PURPOSE: cdrom class driver
25 * PROGRAMMER: Eric Kohl (ekohl@rz-online.de)
30 * - Add io timer routine for autorun support.
31 * - Add cdaudio support (cd player).
34 /* INCLUDES *****************************************************************/
36 #include <ddk/ntddk.h>
38 #include <ddk/class2.h>
39 #include <ddk/ntddscsi.h>
44 #define VERSION "0.0.1"
47 typedef struct _CDROM_DATA
50 } CDROM_DATA, *PCDROM_DATA;
55 CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
56 IN PUNICODE_STRING RegistryPath,
57 IN PCLASS_INIT_DATA InitializationData,
58 IN PDEVICE_OBJECT PortDeviceObject,
62 CdromClassCheckDevice(IN PINQUIRYDATA InquiryData);
65 CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
69 CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
70 IN PUNICODE_STRING RegistryPath,
71 IN PDEVICE_OBJECT PortDeviceObject,
73 IN ULONG DeviceNumber,
74 IN PIO_SCSI_CAPABILITIES Capabilities,
75 IN PSCSI_INQUIRY_DATA InquiryData,
76 IN PCLASS_INIT_DATA InitializationData);
80 CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
84 CdromClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
88 CdromTimerRoutine(IN PDEVICE_OBJECT DeviceObject,
92 /* FUNCTIONS ****************************************************************/
94 /**********************************************************************
99 * This function initializes the driver, locates and claims
100 * hardware resources, and creates various NT objects needed
101 * to process I/O requests.
108 * System allocated Driver Object for this driver
110 * Name of registry driver service key
117 DriverEntry(IN PDRIVER_OBJECT DriverObject,
118 IN PUNICODE_STRING RegistryPath)
120 CLASS_INIT_DATA InitData;
122 DPRINT("CD-ROM Class Driver %s\n",
124 DPRINT("RegistryPath '%wZ'\n",
127 InitData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
128 InitData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION) + sizeof(CDROM_DATA);
129 InitData.DeviceType = FILE_DEVICE_CD_ROM;
130 InitData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE;
132 InitData.ClassError = NULL; // CdromClassProcessError;
133 InitData.ClassReadWriteVerification = CdromClassCheckReadWrite;
134 InitData.ClassFindDeviceCallBack = CdromClassCheckDevice;
135 InitData.ClassFindDevices = CdromClassFindDevices;
136 InitData.ClassDeviceControl = CdromClassDeviceControl;
137 InitData.ClassShutdownFlush = CdromClassShutdownFlush;
138 InitData.ClassCreateClose = NULL;
139 InitData.ClassStartIo = NULL;
141 return(ScsiClassInitialize(DriverObject,
147 /**********************************************************************
149 * CdromClassFindDevices
152 * This function searches for device that are attached to the
160 * System allocated Driver Object for this driver
162 * Name of registry driver service key.
164 * Pointer to the main initialization data
166 * Scsi port device object
171 * TRUE: At least one disk drive was found
172 * FALSE: No disk drive found
176 CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
177 IN PUNICODE_STRING RegistryPath,
178 IN PCLASS_INIT_DATA InitializationData,
179 IN PDEVICE_OBJECT PortDeviceObject,
182 PCONFIGURATION_INFORMATION ConfigInfo;
183 PIO_SCSI_CAPABILITIES PortCapabilities;
184 PSCSI_ADAPTER_BUS_INFO AdapterBusInfo;
185 PSCSI_INQUIRY_DATA UnitInfo;
186 PINQUIRYDATA InquiryData;
193 DPRINT("CdromClassFindDevices() called.\n");
195 /* Get port capabilities */
196 Status = ScsiClassGetCapabilities(PortDeviceObject,
198 if (!NT_SUCCESS(Status))
200 DPRINT1("ScsiClassGetCapabilities() failed! (Status 0x%lX)\n", Status);
204 DPRINT("PortCapabilities: %p\n", PortCapabilities);
205 DPRINT("MaximumTransferLength: %lu\n", PortCapabilities->MaximumTransferLength);
206 DPRINT("MaximumPhysicalPages: %lu\n", PortCapabilities->MaximumPhysicalPages);
208 /* Get inquiry data */
209 Status = ScsiClassGetInquiryData(PortDeviceObject,
210 (PSCSI_ADAPTER_BUS_INFO *)&Buffer);
211 if (!NT_SUCCESS(Status))
213 DPRINT1("ScsiClassGetInquiryData() failed! (Status 0x%lX)\n", Status);
217 /* Check whether there are unclaimed devices */
218 AdapterBusInfo = (PSCSI_ADAPTER_BUS_INFO)Buffer;
219 DeviceCount = ScsiClassFindUnclaimedDevices(InitializationData,
221 if (DeviceCount == 0)
223 DPRINT("No unclaimed devices!\n");
227 DPRINT("Found %lu unclaimed devices!\n", DeviceCount);
229 ConfigInfo = IoGetConfigurationInformation();
230 DPRINT("Number of SCSI ports: %lu\n", ConfigInfo->ScsiPortCount);
232 /* Search each bus of this adapter */
233 for (Bus = 0; Bus < (ULONG)AdapterBusInfo->NumberOfBuses; Bus++)
235 DPRINT("Searching bus %lu\n", Bus);
237 UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + AdapterBusInfo->BusData[Bus].InquiryDataOffset);
239 while (AdapterBusInfo->BusData[Bus].InquiryDataOffset)
241 InquiryData = (PINQUIRYDATA)UnitInfo->InquiryData;
243 if ((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
244 (InquiryData->DeviceTypeQualifier == 0) &&
245 (UnitInfo->DeviceClaimed == FALSE))
247 DPRINT("Vendor: '%.24s'\n",
248 InquiryData->VendorId);
250 /* Create device objects for disk */
251 Status = CdromClassCreateDeviceObject(DriverObject,
255 ConfigInfo->CDRomCount,
259 if (NT_SUCCESS(Status))
261 ConfigInfo->CDRomCount++;
266 if (UnitInfo->NextInquiryDataOffset == 0)
269 UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + UnitInfo->NextInquiryDataOffset);
275 DPRINT("CdromClassFindDevices() done\n");
282 /**********************************************************************
284 * CdromClassCheckDevice
287 * This function checks the InquiryData for the correct device
288 * type and qualifier.
295 * Pointer to the inquiry data for the device in question.
298 * TRUE: A disk device was found.
303 CdromClassCheckDevice(IN PINQUIRYDATA InquiryData)
305 return((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
306 (InquiryData->DeviceTypeQualifier == 0));
310 /**********************************************************************
312 * CdromClassCheckReadWrite
315 * This function checks the given IRP for correct data.
322 * Pointer to the device.
327 * STATUS_SUCCESS: The IRP matches the requirements of the given device.
332 CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
335 DPRINT("CdromClassCheckReadWrite() called\n");
337 return(STATUS_SUCCESS);
341 /**********************************************************************
343 * CdromClassCreateDeviceObject
346 * Create the raw device and any partition devices on this drive
353 * System allocated Driver Object for this driver.
355 * Name of registry driver service key.
368 CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
369 IN PUNICODE_STRING RegistryPath,
370 IN PDEVICE_OBJECT PortDeviceObject,
372 IN ULONG DeviceNumber,
373 IN PIO_SCSI_CAPABILITIES Capabilities,
374 IN PSCSI_INQUIRY_DATA InquiryData,
375 IN PCLASS_INIT_DATA InitializationData)
377 OBJECT_ATTRIBUTES ObjectAttributes;
378 UNICODE_STRING UnicodeDeviceDirName;
380 PDEVICE_OBJECT DiskDeviceObject;
381 PDEVICE_EXTENSION DiskDeviceExtension; /* defined in class2.h */
383 PCDROM_DATA CdromData;
386 DPRINT("CdromClassCreateDeviceObject() called\n");
388 /* Claim the cdrom device */
389 Status = ScsiClassClaimDevice(PortDeviceObject,
393 if (!NT_SUCCESS(Status))
395 DbgPrint("Could not claim cdrom device\n");
399 /* Create cdrom device */
401 "\\Device\\CdRom%lu",
404 Status = ScsiClassCreateDeviceObject(DriverObject,
409 if (!NT_SUCCESS(Status))
411 DPRINT1("ScsiClassCreateDeviceObject() failed (Status %x)\n", Status);
413 /* Release (unclaim) the disk */
414 ScsiClassClaimDevice(PortDeviceObject,
422 DiskDeviceObject->Flags |= DO_DIRECT_IO;
423 DiskDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
424 DiskDeviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1;
426 if (PortDeviceObject->AlignmentRequirement > DiskDeviceObject->AlignmentRequirement)
428 DiskDeviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
431 DiskDeviceExtension = DiskDeviceObject->DeviceExtension;
432 DiskDeviceExtension->LockCount = 0;
433 DiskDeviceExtension->DeviceNumber = DeviceNumber;
434 DiskDeviceExtension->PortDeviceObject = PortDeviceObject;
435 DiskDeviceExtension->PhysicalDevice = DiskDeviceObject;
436 DiskDeviceExtension->PortCapabilities = Capabilities;
437 DiskDeviceExtension->StartingOffset.QuadPart = 0;
438 DiskDeviceExtension->PortNumber = (UCHAR)PortNumber;
439 DiskDeviceExtension->PathId = InquiryData->PathId;
440 DiskDeviceExtension->TargetId = InquiryData->TargetId;
441 DiskDeviceExtension->Lun = InquiryData->Lun;
443 /* zero-out disk data */
444 CdromData = (PCDROM_DATA)(DiskDeviceExtension + 1);
445 RtlZeroMemory(CdromData,
448 DiskDeviceExtension->SenseData = ExAllocatePool(NonPagedPool,
450 if (DiskDeviceExtension->SenseData == NULL)
452 DPRINT1("Failed to allocate sense data buffer!\n");
454 IoDeleteDevice(DiskDeviceObject);
456 /* Release (unclaim) the disk */
457 ScsiClassClaimDevice(PortDeviceObject,
462 return(STATUS_INSUFFICIENT_RESOURCES);
465 /* Get disk geometry */
466 DiskDeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
467 sizeof(DISK_GEOMETRY));
468 if (DiskDeviceExtension->DiskGeometry == NULL)
470 DPRINT1("Failed to allocate geometry buffer!\n");
472 IoDeleteDevice(DiskDeviceObject);
474 /* Release (unclaim) the disk */
475 ScsiClassClaimDevice(PortDeviceObject,
480 return(STATUS_INSUFFICIENT_RESOURCES);
483 /* Read the drive's capacity */
484 Status = ScsiClassReadDriveCapacity(DiskDeviceObject);
485 if (!NT_SUCCESS(Status) ||
486 DiskDeviceExtension->DiskGeometry->BytesPerSector == 0)
488 /* Set ISO9660 defaults */
489 DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048;
490 DiskDeviceExtension->DiskGeometry->MediaType = RemovableMedia;
491 DiskDeviceExtension->SectorShift = 11;
492 DiskDeviceExtension->PartitionLength.QuadPart = (ULONGLONG)0x7fffffff;
496 /* Make sure the BytesPerSector value is a power of 2 */
497 // DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048;
500 DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector);
502 /* FIXME: initialize media change support */
504 IoInitializeTimer(DiskDeviceObject,
507 IoStartTimer(DiskDeviceObject);
509 DPRINT("CdromClassCreateDeviceObjects() done\n");
511 return(STATUS_SUCCESS);
514 /**********************************************************************
516 * CdromClassReadTocEntry
529 CdromClassReadTocEntry(PDEVICE_OBJECT DeviceObject, UINT TrackNo, PVOID Buffer, UINT Length)
531 PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
532 SCSI_REQUEST_BLOCK Srb;
535 RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
537 Srb.TimeOutValue = DeviceExtension->TimeOutValue;
540 Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
541 Cdb->READ_TOC.StartingTrack = TrackNo;
542 Cdb->READ_TOC.Format = 0;
543 Cdb->READ_TOC.AllocationLength[0] = Length >> 8;
544 Cdb->READ_TOC.AllocationLength[1] = Length & 0xff;
545 Cdb->READ_TOC.Msf = 1;
547 return ScsiClassSendSrbSynchronous(DeviceObject,
555 CdromClassReadLastSession(PDEVICE_OBJECT DeviceObject, UINT TrackNo, PVOID Buffer, UINT Length)
557 PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
558 SCSI_REQUEST_BLOCK Srb;
561 RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
563 Srb.TimeOutValue = DeviceExtension->TimeOutValue;
566 Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
567 Cdb->READ_TOC.StartingTrack = TrackNo;
568 Cdb->READ_TOC.Format = 1;
569 Cdb->READ_TOC.AllocationLength[0] = Length >> 8;
570 Cdb->READ_TOC.AllocationLength[1] = Length & 0xff;
571 Cdb->READ_TOC.Msf = 0;
573 return ScsiClassSendSrbSynchronous(DeviceObject,
580 /**********************************************************************
582 * CdromClassDeviceControl
585 * Answer requests for device control calls
593 * Standard dispatch arguments
600 CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
603 PDEVICE_EXTENSION DeviceExtension;
604 PIO_STACK_LOCATION IrpStack;
605 ULONG ControlCode, InputLength, OutputLength;
606 PCDROM_DATA CdromData;
610 DPRINT("CdromClassDeviceControl() called!\n");
612 Status = STATUS_INVALID_DEVICE_REQUEST;
614 IrpStack = IoGetCurrentIrpStackLocation(Irp);
615 ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
616 InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
617 OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
618 DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
619 CdromData = (PCDROM_DATA)(DeviceExtension + 1);
623 case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
624 DPRINT("IOCTL_CDROM_GET_DRIVE_GEOMETRY\n");
625 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
627 Status = STATUS_INVALID_PARAMETER;
631 PDISK_GEOMETRY Geometry;
633 if (DeviceExtension->DiskGeometry == NULL)
635 DPRINT("No cdrom geometry available!\n");
636 DeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
637 sizeof(DISK_GEOMETRY));
639 Status = ScsiClassReadDriveCapacity(DeviceObject);
640 if (NT_SUCCESS(Status))
642 Geometry = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer;
643 RtlMoveMemory(Geometry,
644 DeviceExtension->DiskGeometry,
645 sizeof(DISK_GEOMETRY));
647 Status = STATUS_SUCCESS;
648 Information = sizeof(DISK_GEOMETRY);
652 case IOCTL_CDROM_READ_TOC:
653 DPRINT("IOCTL_CDROM_READ_TOC\n");
654 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC))
656 Status = STATUS_INFO_LENGTH_MISMATCH;
660 PCDROM_TOC TocBuffer;
663 TocBuffer = Irp->AssociatedIrp.SystemBuffer;
665 /* First read the lead out */
666 Length = 4 + sizeof(TRACK_DATA);
667 Status = CdromClassReadTocEntry(DeviceObject, 0xaa, TocBuffer, Length);
669 if (NT_SUCCESS(Status))
671 if (TocBuffer->FirstTrack == 0xaa)
673 /* there is an empty cd */
674 Information = Length;
679 Length = 4 + sizeof(TRACK_DATA) * (TocBuffer->LastTrack - TocBuffer->FirstTrack + 2);
680 Status = CdromClassReadTocEntry(DeviceObject, TocBuffer->FirstTrack, TocBuffer, Length);
681 if (NT_SUCCESS(Status))
683 Information = Length;
689 case IOCTL_CDROM_GET_LAST_SESSION:
690 DPRINT("IOCTL_CDROM_GET_LAST_SESSION\n");
691 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < 4 + sizeof(TRACK_DATA))
693 Status = STATUS_INFO_LENGTH_MISMATCH;
698 PCDROM_TOC TocBuffer = Irp->AssociatedIrp.SystemBuffer;
700 Length = 4 + sizeof(TRACK_DATA);
701 Status = CdromClassReadLastSession(DeviceObject, 0, TocBuffer, Length);
702 if (NT_SUCCESS(Status))
704 Information = Length;
709 /* Call the common device control function */
710 return(ScsiClassDeviceControl(DeviceObject, Irp));
713 /* Verify the device if the user caused the error */
714 if (!NT_SUCCESS(Status) && IoIsErrorUserInduced(Status))
716 IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
719 Irp->IoStatus.Status = Status;
720 Irp->IoStatus.Information = Information;
721 IoCompleteRequest(Irp,
724 return(STATUS_SUCCESS);
728 /**********************************************************************
730 * CdromClassShutdownFlush
733 * Answer requests for shutdown and flush calls
741 * Standard dispatch arguments
748 CdromClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
751 DPRINT("CdromClassShutdownFlush() called!\n");
753 Irp->IoStatus.Status = STATUS_SUCCESS;
754 Irp->IoStatus.Information = 0;
755 IoCompleteRequest(Irp, IO_NO_INCREMENT);
757 return(STATUS_SUCCESS);
762 CdromTimerRoutine(PDEVICE_OBJECT DeviceObject,
765 DPRINT("CdromTimerRoutine() called\n");