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");
281 /**********************************************************************
283 * CdromClassCheckDevice
286 * This function checks the InquiryData for the correct device
287 * type and qualifier.
294 * Pointer to the inquiry data for the device in question.
297 * TRUE: A disk device was found.
302 CdromClassCheckDevice(IN PINQUIRYDATA InquiryData)
304 return((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
305 (InquiryData->DeviceTypeQualifier == 0));
309 /**********************************************************************
311 * CdromClassCheckReadWrite
314 * This function checks the given IRP for correct data.
321 * Pointer to the device.
326 * STATUS_SUCCESS: The IRP matches the requirements of the given device.
331 CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
334 DPRINT("CdromClassCheckReadWrite() called\n");
336 return(STATUS_SUCCESS);
340 /**********************************************************************
342 * CdromClassCreateDeviceObject
345 * Create the raw device and any partition devices on this drive
352 * System allocated Driver Object for this driver.
354 * Name of registry driver service key.
367 CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
368 IN PUNICODE_STRING RegistryPath,
369 IN PDEVICE_OBJECT PortDeviceObject,
371 IN ULONG DeviceNumber,
372 IN PIO_SCSI_CAPABILITIES Capabilities,
373 IN PSCSI_INQUIRY_DATA InquiryData,
374 IN PCLASS_INIT_DATA InitializationData)
376 OBJECT_ATTRIBUTES ObjectAttributes;
377 UNICODE_STRING UnicodeDeviceDirName;
379 PDEVICE_OBJECT DiskDeviceObject;
380 PDEVICE_EXTENSION DiskDeviceExtension; /* defined in class2.h */
382 PCDROM_DATA CdromData;
385 DPRINT("CdromClassCreateDeviceObject() called\n");
387 /* Claim the cdrom device */
388 Status = ScsiClassClaimDevice(PortDeviceObject,
392 if (!NT_SUCCESS(Status))
394 DbgPrint("Could not claim cdrom device\n");
398 /* Create cdrom device */
400 "\\Device\\CdRom%lu",
403 Status = ScsiClassCreateDeviceObject(DriverObject,
408 if (!NT_SUCCESS(Status))
410 DPRINT1("ScsiClassCreateDeviceObject() failed (Status %x)\n", Status);
412 /* Release (unclaim) the disk */
413 ScsiClassClaimDevice(PortDeviceObject,
421 DiskDeviceObject->Flags |= DO_DIRECT_IO;
422 DiskDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
423 DiskDeviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1;
425 if (PortDeviceObject->AlignmentRequirement > DiskDeviceObject->AlignmentRequirement)
427 DiskDeviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
430 DiskDeviceExtension = DiskDeviceObject->DeviceExtension;
431 DiskDeviceExtension->LockCount = 0;
432 DiskDeviceExtension->DeviceNumber = DeviceNumber;
433 DiskDeviceExtension->PortDeviceObject = PortDeviceObject;
434 DiskDeviceExtension->PhysicalDevice = DiskDeviceObject;
435 DiskDeviceExtension->PortCapabilities = Capabilities;
436 DiskDeviceExtension->StartingOffset.QuadPart = 0;
437 DiskDeviceExtension->PortNumber = (UCHAR)PortNumber;
438 DiskDeviceExtension->PathId = InquiryData->PathId;
439 DiskDeviceExtension->TargetId = InquiryData->TargetId;
440 DiskDeviceExtension->Lun = InquiryData->Lun;
442 /* zero-out disk data */
443 CdromData = (PCDROM_DATA)(DiskDeviceExtension + 1);
444 RtlZeroMemory(CdromData,
447 DiskDeviceExtension->SenseData = ExAllocatePool(NonPagedPool,
449 if (DiskDeviceExtension->SenseData == NULL)
451 DPRINT1("Failed to allocate sense data buffer!\n");
453 IoDeleteDevice(DiskDeviceObject);
455 /* Release (unclaim) the disk */
456 ScsiClassClaimDevice(PortDeviceObject,
461 return(STATUS_INSUFFICIENT_RESOURCES);
464 /* Initialize lookaside list for SRBs */
465 ScsiClassInitializeSrbLookasideList(DiskDeviceExtension,
468 /* Get disk geometry */
469 DiskDeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
470 sizeof(DISK_GEOMETRY));
471 if (DiskDeviceExtension->DiskGeometry == NULL)
473 DPRINT1("Failed to allocate geometry buffer!\n");
475 ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead);
477 IoDeleteDevice(DiskDeviceObject);
479 /* Release (unclaim) the disk */
480 ScsiClassClaimDevice(PortDeviceObject,
485 return(STATUS_INSUFFICIENT_RESOURCES);
488 /* Read the drive's capacity */
489 Status = ScsiClassReadDriveCapacity(DiskDeviceObject);
490 if (!NT_SUCCESS(Status) ||
491 DiskDeviceExtension->DiskGeometry->BytesPerSector == 0)
493 /* Set ISO9660 defaults */
494 DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048;
495 DiskDeviceExtension->DiskGeometry->MediaType = RemovableMedia;
496 DiskDeviceExtension->SectorShift = 11;
497 DiskDeviceExtension->PartitionLength.QuadPart = (ULONGLONG)0x7fffffff;
501 /* Make sure the BytesPerSector value is a power of 2 */
502 // DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048;
505 DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector);
507 /* FIXME: initialize media change support */
509 IoInitializeTimer(DiskDeviceObject,
512 IoStartTimer(DiskDeviceObject);
514 DPRINT("CdromClassCreateDeviceObjects() done\n");
516 return(STATUS_SUCCESS);
520 /**********************************************************************
522 * CdromClassReadTocEntry
535 CdromClassReadTocEntry(PDEVICE_OBJECT DeviceObject, UINT TrackNo, PVOID Buffer, UINT Length)
537 PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
538 SCSI_REQUEST_BLOCK Srb;
541 RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
543 Srb.TimeOutValue = DeviceExtension->TimeOutValue;
546 Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
547 Cdb->READ_TOC.StartingTrack = TrackNo;
548 Cdb->READ_TOC.Format = 0;
549 Cdb->READ_TOC.AllocationLength[0] = Length >> 8;
550 Cdb->READ_TOC.AllocationLength[1] = Length & 0xff;
551 Cdb->READ_TOC.Msf = 1;
553 return(ScsiClassSendSrbSynchronous(DeviceObject,
562 CdromClassReadLastSession(PDEVICE_OBJECT DeviceObject, UINT TrackNo, PVOID Buffer, UINT Length)
564 PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
565 SCSI_REQUEST_BLOCK Srb;
568 RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
570 Srb.TimeOutValue = DeviceExtension->TimeOutValue;
573 Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
574 Cdb->READ_TOC.StartingTrack = TrackNo;
575 Cdb->READ_TOC.Format = 1;
576 Cdb->READ_TOC.AllocationLength[0] = Length >> 8;
577 Cdb->READ_TOC.AllocationLength[1] = Length & 0xff;
578 Cdb->READ_TOC.Msf = 0;
580 return(ScsiClassSendSrbSynchronous(DeviceObject,
588 /**********************************************************************
590 * CdromClassDeviceControl
593 * Answer requests for device control calls
601 * Standard dispatch arguments
608 CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
611 PDEVICE_EXTENSION DeviceExtension;
612 PIO_STACK_LOCATION IrpStack;
613 ULONG ControlCode, InputLength, OutputLength;
614 PCDROM_DATA CdromData;
618 DPRINT("CdromClassDeviceControl() called!\n");
620 Status = STATUS_INVALID_DEVICE_REQUEST;
622 IrpStack = IoGetCurrentIrpStackLocation(Irp);
623 ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
624 InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
625 OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
626 DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
627 CdromData = (PCDROM_DATA)(DeviceExtension + 1);
631 case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
632 DPRINT("IOCTL_CDROM_GET_DRIVE_GEOMETRY\n");
633 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
635 Status = STATUS_INVALID_PARAMETER;
639 PDISK_GEOMETRY Geometry;
641 if (DeviceExtension->DiskGeometry == NULL)
643 DPRINT("No cdrom geometry available!\n");
644 DeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
645 sizeof(DISK_GEOMETRY));
647 Status = ScsiClassReadDriveCapacity(DeviceObject);
648 if (NT_SUCCESS(Status))
650 Geometry = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer;
651 RtlMoveMemory(Geometry,
652 DeviceExtension->DiskGeometry,
653 sizeof(DISK_GEOMETRY));
655 Status = STATUS_SUCCESS;
656 Information = sizeof(DISK_GEOMETRY);
661 case IOCTL_CDROM_READ_TOC:
662 DPRINT("IOCTL_CDROM_READ_TOC\n");
663 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC))
665 Status = STATUS_INFO_LENGTH_MISMATCH;
669 PCDROM_TOC TocBuffer;
672 TocBuffer = Irp->AssociatedIrp.SystemBuffer;
674 /* First read the lead out */
675 Length = 4 + sizeof(TRACK_DATA);
676 Status = CdromClassReadTocEntry(DeviceObject,
680 if (NT_SUCCESS(Status))
682 if (TocBuffer->FirstTrack == 0xaa)
684 /* there is an empty cd */
685 Information = Length;
690 Length = 4 + sizeof(TRACK_DATA) * (TocBuffer->LastTrack - TocBuffer->FirstTrack + 2);
691 Status = CdromClassReadTocEntry(DeviceObject,
692 TocBuffer->FirstTrack,
694 if (NT_SUCCESS(Status))
696 Information = Length;
703 case IOCTL_CDROM_GET_LAST_SESSION:
704 DPRINT("IOCTL_CDROM_GET_LAST_SESSION\n");
705 if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < 4 + sizeof(TRACK_DATA))
707 Status = STATUS_INFO_LENGTH_MISMATCH;
711 PCDROM_TOC TocBuffer;
714 TocBuffer = Irp->AssociatedIrp.SystemBuffer;
715 Length = 4 + sizeof(TRACK_DATA);
716 Status = CdromClassReadLastSession(DeviceObject,
720 if (NT_SUCCESS(Status))
722 Information = Length;
728 /* Call the common device control function */
729 return(ScsiClassDeviceControl(DeviceObject, Irp));
732 /* Verify the device if the user caused the error */
733 if (!NT_SUCCESS(Status) && IoIsErrorUserInduced(Status))
735 IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
738 Irp->IoStatus.Status = Status;
739 Irp->IoStatus.Information = Information;
740 IoCompleteRequest(Irp,
743 return(STATUS_SUCCESS);
747 /**********************************************************************
749 * CdromClassShutdownFlush
752 * Answer requests for shutdown and flush calls
760 * Standard dispatch arguments
767 CdromClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
770 DPRINT("CdromClassShutdownFlush() called!\n");
772 Irp->IoStatus.Status = STATUS_SUCCESS;
773 Irp->IoStatus.Information = 0;
774 IoCompleteRequest(Irp, IO_NO_INCREMENT);
776 return(STATUS_SUCCESS);
781 CdromTimerRoutine(PDEVICE_OBJECT DeviceObject,
784 DPRINT("CdromTimerRoutine() called\n");