update for HEAD-2003091401
[reactos.git] / drivers / storage / cdrom / cdrom.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2001, 2002 ReactOS Team
4  *
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.
9  *
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.
14  *
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.
18  */
19 /* $Id$
20  *
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)
26  */
27
28 /*
29  * TODO:
30  *  - Add io timer routine for autorun support.
31  *  - Add cdaudio support (cd player).
32  */
33
34 /* INCLUDES *****************************************************************/
35
36 #include <ddk/ntddk.h>
37 #include <ddk/scsi.h>
38 #include <ddk/class2.h>
39 #include <ddk/ntddscsi.h>
40
41 #define NDEBUG
42 #include <debug.h>
43
44 #define VERSION "0.0.1"
45
46
47 #define SCSI_CDROM_TIMEOUT 10           /* Default timeout: 10 seconds */
48
49
50 typedef struct _ERROR_RECOVERY_DATA6
51 {
52   MODE_PARAMETER_HEADER Header;
53   MODE_PARAMETER_BLOCK BlockDescriptor;
54   MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
55 } ERROR_RECOVERY_DATA6, *PERROR_RECOVERY_DATA6;
56
57
58 typedef struct _ERROR_RECOVERY_DATA10
59 {
60   MODE_PARAMETER_HEADER10 Header;
61   MODE_PARAMETER_BLOCK BlockDescriptor;
62   MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
63 } ERROR_RECOVERY_DATA10, *PERROR_RECOVERY_DATA10;
64
65
66 typedef struct _CDROM_DATA
67 {
68   BOOLEAN PlayActive;
69   BOOLEAN RawAccess;
70   USHORT XaFlags;
71
72   union
73     {
74       ERROR_RECOVERY_DATA6 Data6;
75       ERROR_RECOVERY_DATA10 Data10;
76     } RecoveryData;
77
78 } CDROM_DATA, *PCDROM_DATA;
79
80 /* CDROM_DATA.XaFlags */
81 #define XA_USE_6_BYTE           0x0001
82 #define XA_USE_10_BYTE          0x0002
83 #define XA_USE_READ_CD          0x0004
84 #define XA_NOT_SUPPORTED        0x0008
85
86
87 BOOLEAN STDCALL
88 CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
89                       IN PUNICODE_STRING RegistryPath,
90                       IN PCLASS_INIT_DATA InitializationData,
91                       IN PDEVICE_OBJECT PortDeviceObject,
92                       IN ULONG PortNumber);
93
94 BOOLEAN STDCALL
95 CdromClassCheckDevice(IN PINQUIRYDATA InquiryData);
96
97 NTSTATUS STDCALL
98 CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
99                          IN PIRP Irp);
100
101 static VOID
102 CdromClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension,
103                                  IN ULONG DeviceNumber);
104
105 static NTSTATUS
106 CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
107                              IN PUNICODE_STRING RegistryPath,
108                              IN PDEVICE_OBJECT PortDeviceObject,
109                              IN ULONG PortNumber,
110                              IN ULONG DeviceNumber,
111                              IN PIO_SCSI_CAPABILITIES Capabilities,
112                              IN PSCSI_INQUIRY_DATA InquiryData,
113                              IN PCLASS_INIT_DATA InitializationData);
114
115
116 NTSTATUS STDCALL
117 CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
118                         IN PIRP Irp);
119
120 VOID STDCALL
121 CdromClassStartIo (IN PDEVICE_OBJECT DeviceObject,
122                    IN PIRP Irp);
123
124 VOID STDCALL
125 CdromTimerRoutine(IN PDEVICE_OBJECT DeviceObject,
126                   IN PVOID Context);
127
128
129 /* FUNCTIONS ****************************************************************/
130
131 /**********************************************************************
132  * NAME                                                 EXPORTED
133  *      DriverEntry
134  *
135  * DESCRIPTION:
136  *      This function initializes the driver, locates and claims 
137  *      hardware resources, and creates various NT objects needed
138  *      to process I/O requests.
139  *
140  * RUN LEVEL:
141  *      PASSIVE_LEVEL
142  *
143  * ARGUMENTS:
144  *      DriverObject
145  *              System allocated Driver Object for this driver
146  *      RegistryPath
147  *              Name of registry driver service key
148  *
149  * RETURNS:
150  *      Status.
151  */
152
153 NTSTATUS STDCALL
154 DriverEntry(IN PDRIVER_OBJECT DriverObject,
155             IN PUNICODE_STRING RegistryPath)
156 {
157   CLASS_INIT_DATA InitData;
158
159   DPRINT("CD-ROM Class Driver %s\n",
160          VERSION);
161   DPRINT("RegistryPath '%wZ'\n",
162          RegistryPath);
163
164   InitData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
165   InitData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION) + sizeof(CDROM_DATA);
166   InitData.DeviceType = FILE_DEVICE_CD_ROM;
167   InitData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE;
168
169   InitData.ClassError = NULL;
170   InitData.ClassReadWriteVerification = CdromClassCheckReadWrite;
171   InitData.ClassFindDeviceCallBack = CdromClassCheckDevice;
172   InitData.ClassFindDevices = CdromClassFindDevices;
173   InitData.ClassDeviceControl = CdromClassDeviceControl;
174   InitData.ClassShutdownFlush = NULL;
175   InitData.ClassCreateClose = NULL;
176   InitData.ClassStartIo = CdromClassStartIo;
177
178   return(ScsiClassInitialize(DriverObject,
179                              RegistryPath,
180                              &InitData));
181 }
182
183
184 /**********************************************************************
185  * NAME                                                 EXPORTED
186  *      CdromClassFindDevices
187  *
188  * DESCRIPTION:
189  *      This function searches for device that are attached to the
190  *      given scsi port.
191  *
192  * RUN LEVEL:
193  *      PASSIVE_LEVEL
194  *
195  * ARGUMENTS:
196  *      DriverObject
197  *              System allocated Driver Object for this driver
198  *      RegistryPath
199  *              Name of registry driver service key.
200  *      InitializationData
201  *              Pointer to the main initialization data
202  *      PortDeviceObject
203  *              Scsi port device object
204  *      PortNumber
205  *              Port number
206  *
207  * RETURNS:
208  *      TRUE: At least one disk drive was found
209  *      FALSE: No disk drive found
210  */
211
212 BOOLEAN STDCALL
213 CdromClassFindDevices(IN PDRIVER_OBJECT DriverObject,
214                       IN PUNICODE_STRING RegistryPath,
215                       IN PCLASS_INIT_DATA InitializationData,
216                       IN PDEVICE_OBJECT PortDeviceObject,
217                       IN ULONG PortNumber)
218 {
219   PCONFIGURATION_INFORMATION ConfigInfo;
220   PIO_SCSI_CAPABILITIES PortCapabilities;
221   PSCSI_ADAPTER_BUS_INFO AdapterBusInfo;
222   PSCSI_INQUIRY_DATA UnitInfo;
223   PINQUIRYDATA InquiryData;
224   PCHAR Buffer;
225   ULONG Bus;
226   ULONG DeviceCount;
227   BOOLEAN FoundDevice;
228   NTSTATUS Status;
229
230   DPRINT("CdromClassFindDevices() called.\n");
231
232   /* Get port capabilities */
233   Status = ScsiClassGetCapabilities(PortDeviceObject,
234                                     &PortCapabilities);
235   if (!NT_SUCCESS(Status))
236     {
237       DPRINT1("ScsiClassGetCapabilities() failed! (Status 0x%lX)\n", Status);
238       return(FALSE);
239     }
240
241   DPRINT("PortCapabilities: %p\n", PortCapabilities);
242   DPRINT("MaximumTransferLength: %lu\n", PortCapabilities->MaximumTransferLength);
243   DPRINT("MaximumPhysicalPages: %lu\n", PortCapabilities->MaximumPhysicalPages);
244
245   /* Get inquiry data */
246   Status = ScsiClassGetInquiryData(PortDeviceObject,
247                                    (PSCSI_ADAPTER_BUS_INFO *)&Buffer);
248   if (!NT_SUCCESS(Status))
249     {
250       DPRINT1("ScsiClassGetInquiryData() failed! (Status 0x%lX)\n", Status);
251       return(FALSE);
252     }
253
254   /* Check whether there are unclaimed devices */
255   AdapterBusInfo = (PSCSI_ADAPTER_BUS_INFO)Buffer;
256   DeviceCount = ScsiClassFindUnclaimedDevices(InitializationData,
257                                               AdapterBusInfo);
258   if (DeviceCount == 0)
259     {
260       DPRINT("No unclaimed devices!\n");
261       return(FALSE);
262     }
263
264   DPRINT("Found %lu unclaimed devices!\n", DeviceCount);
265
266   ConfigInfo = IoGetConfigurationInformation();
267   DPRINT("Number of SCSI ports: %lu\n", ConfigInfo->ScsiPortCount);
268
269   /* Search each bus of this adapter */
270   for (Bus = 0; Bus < (ULONG)AdapterBusInfo->NumberOfBuses; Bus++)
271     {
272       DPRINT("Searching bus %lu\n", Bus);
273
274       UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + AdapterBusInfo->BusData[Bus].InquiryDataOffset);
275
276       while (AdapterBusInfo->BusData[Bus].InquiryDataOffset)
277         {
278           InquiryData = (PINQUIRYDATA)UnitInfo->InquiryData;
279
280           if ((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
281               (InquiryData->DeviceTypeQualifier == 0) &&
282               (UnitInfo->DeviceClaimed == FALSE))
283             {
284               DPRINT("Vendor: '%.24s'\n",
285                      InquiryData->VendorId);
286
287               /* Create device objects for disk */
288               Status = CdromClassCreateDeviceObject(DriverObject,
289                                                     RegistryPath,
290                                                     PortDeviceObject,
291                                                     PortNumber,
292                                                     ConfigInfo->CdRomCount,
293                                                     PortCapabilities,
294                                                     UnitInfo,
295                                                     InitializationData);
296               if (NT_SUCCESS(Status))
297                 {
298                   ConfigInfo->CdRomCount++;
299                   FoundDevice = TRUE;
300                 }
301             }
302
303           if (UnitInfo->NextInquiryDataOffset == 0)
304             break;
305
306           UnitInfo = (PSCSI_INQUIRY_DATA)(Buffer + UnitInfo->NextInquiryDataOffset);
307         }
308     }
309
310   ExFreePool(Buffer);
311
312   DPRINT("CdromClassFindDevices() done\n");
313
314   return(FoundDevice);
315 }
316
317
318 /**********************************************************************
319  * NAME                                                 EXPORTED
320  *      CdromClassCheckDevice
321  *
322  * DESCRIPTION
323  *      This function checks the InquiryData for the correct device
324  *      type and qualifier.
325  *
326  * RUN LEVEL
327  *      PASSIVE_LEVEL
328  *
329  * ARGUMENTS
330  *      InquiryData
331  *              Pointer to the inquiry data for the device in question.
332  *
333  * RETURN VALUE
334  *      TRUE: A disk device was found.
335  *      FALSE: Otherwise.
336  */
337
338 BOOLEAN STDCALL
339 CdromClassCheckDevice(IN PINQUIRYDATA InquiryData)
340 {
341   return((InquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
342          (InquiryData->DeviceTypeQualifier == 0));
343 }
344
345
346 /**********************************************************************
347  * NAME                                                 EXPORTED
348  *      CdromClassCheckReadWrite
349  *
350  * DESCRIPTION
351  *      This function checks the given IRP for correct data.
352  *
353  * RUN LEVEL
354  *      PASSIVE_LEVEL
355  *
356  * ARGUMENTS
357  *      DeviceObject
358  *              Pointer to the device.
359  *      Irp
360  *              Irp to check.
361  *
362  * RETURN VALUE
363  *      STATUS_SUCCESS: The IRP matches the requirements of the given device.
364  *      Others: Failure.
365  */
366
367 NTSTATUS STDCALL
368 CdromClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
369                          IN PIRP Irp)
370 {
371   DPRINT("CdromClassCheckReadWrite() called\n");
372
373   return(STATUS_SUCCESS);
374 }
375
376
377 static VOID
378 CdromClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension,
379                                  IN ULONG DeviceNumber)
380 {
381   WCHAR NameBuffer[MAX_PATH];
382   UNICODE_STRING Name;
383
384   swprintf (NameBuffer,
385             L"\\Device\\MediaChangeEvent%lu",
386             DeviceNumber);
387   RtlInitUnicodeString (&Name,
388                         NameBuffer);
389
390   DeviceExtension->MediaChangeEvent =
391     IoCreateSynchronizationEvent (&Name,
392                                   &DeviceExtension->MediaChangeEventHandle);
393
394   KeClearEvent (DeviceExtension->MediaChangeEvent);
395 }
396
397
398 /**********************************************************************
399  * NAME                                                 EXPORTED
400  *      CdromClassCreateDeviceObject
401  *
402  * DESCRIPTION:
403  *      Create the raw device and any partition devices on this drive
404  *
405  * RUN LEVEL:
406  *      PASSIVE_LEVEL
407  *
408  * ARGUMENTS:
409  *      DriverObject
410  *              System allocated Driver Object for this driver.
411  *      RegistryPath
412  *              Name of registry driver service key.
413  *      PortDeviceObject
414  *      PortNumber
415  *      DeviceNumber
416  *      Capabilities
417  *      InquiryData
418  *      InitializationData
419  *
420  * RETURNS:
421  *      Status.
422  */
423
424 static NTSTATUS
425 CdromClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
426                              IN PUNICODE_STRING RegistryPath,
427                              IN PDEVICE_OBJECT PortDeviceObject,
428                              IN ULONG PortNumber,
429                              IN ULONG DeviceNumber,
430                              IN PIO_SCSI_CAPABILITIES Capabilities,
431                              IN PSCSI_INQUIRY_DATA InquiryData,
432                              IN PCLASS_INIT_DATA InitializationData)
433 {
434   PDEVICE_EXTENSION DiskDeviceExtension; /* defined in class2.h */
435   OBJECT_ATTRIBUTES ObjectAttributes;
436   UNICODE_STRING UnicodeDeviceDirName;
437   PDEVICE_OBJECT DiskDeviceObject;
438   SCSI_REQUEST_BLOCK Srb;
439   PCDROM_DATA CdromData;
440   CHAR NameBuffer[80];
441   HANDLE Handle;
442   PUCHAR Buffer;
443   ULONG Length;
444   PCDB Cdb;
445   NTSTATUS Status;
446
447   DPRINT("CdromClassCreateDeviceObject() called\n");
448
449   /* Claim the cdrom device */
450   Status = ScsiClassClaimDevice(PortDeviceObject,
451                                 InquiryData,
452                                 FALSE,
453                                 &PortDeviceObject);
454   if (!NT_SUCCESS(Status))
455     {
456       DbgPrint("Could not claim cdrom device\n");
457       return(Status);
458     }
459
460   /* Create cdrom device */
461   sprintf(NameBuffer,
462           "\\Device\\CdRom%lu",
463           DeviceNumber);
464
465   Status = ScsiClassCreateDeviceObject(DriverObject,
466                                        NameBuffer,
467                                        NULL,
468                                        &DiskDeviceObject,
469                                        InitializationData);
470   if (!NT_SUCCESS(Status))
471     {
472       DPRINT1("ScsiClassCreateDeviceObject() failed (Status %x)\n", Status);
473
474       /* Release (unclaim) the disk */
475       ScsiClassClaimDevice(PortDeviceObject,
476                            InquiryData,
477                            TRUE,
478                            NULL);
479
480       return(Status);
481     }
482
483   DiskDeviceObject->Flags |= DO_DIRECT_IO;
484   DiskDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
485   DiskDeviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1;
486
487   if (PortDeviceObject->AlignmentRequirement > DiskDeviceObject->AlignmentRequirement)
488     {
489       DiskDeviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
490     }
491
492   DiskDeviceExtension = DiskDeviceObject->DeviceExtension;
493   DiskDeviceExtension->LockCount = 0;
494   DiskDeviceExtension->DeviceNumber = DeviceNumber;
495   DiskDeviceExtension->PortDeviceObject = PortDeviceObject;
496   DiskDeviceExtension->PhysicalDevice = DiskDeviceObject;
497   DiskDeviceExtension->PortCapabilities = Capabilities;
498   DiskDeviceExtension->StartingOffset.QuadPart = 0;
499   DiskDeviceExtension->PortNumber = (UCHAR)PortNumber;
500   DiskDeviceExtension->PathId = InquiryData->PathId;
501   DiskDeviceExtension->TargetId = InquiryData->TargetId;
502   DiskDeviceExtension->Lun = InquiryData->Lun;
503
504   /* zero-out disk data */
505   CdromData = (PCDROM_DATA)(DiskDeviceExtension + 1);
506   RtlZeroMemory(CdromData,
507                 sizeof(CDROM_DATA));
508
509   DiskDeviceExtension->SenseData = ExAllocatePool(NonPagedPool,
510                                                   sizeof(SENSE_DATA));
511   if (DiskDeviceExtension->SenseData == NULL)
512     {
513       DPRINT1("Failed to allocate sense data buffer!\n");
514
515       IoDeleteDevice(DiskDeviceObject);
516
517       /* Release (unclaim) the disk */
518       ScsiClassClaimDevice(PortDeviceObject,
519                            InquiryData,
520                            TRUE,
521                            NULL);
522
523       return(STATUS_INSUFFICIENT_RESOURCES);
524     }
525
526   /* Get timeout value */
527   DiskDeviceExtension->TimeOutValue =
528     ScsiClassQueryTimeOutRegistryValue(RegistryPath);
529   if (DiskDeviceExtension->TimeOutValue == 0)
530     DiskDeviceExtension->TimeOutValue = SCSI_CDROM_TIMEOUT;
531
532   /* Initialize lookaside list for SRBs */
533   ScsiClassInitializeSrbLookasideList(DiskDeviceExtension,
534                                       4);
535
536   /* Get disk geometry */
537   DiskDeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
538                                                      sizeof(DISK_GEOMETRY));
539   if (DiskDeviceExtension->DiskGeometry == NULL)
540     {
541       DPRINT1("Failed to allocate geometry buffer!\n");
542
543       ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead);
544
545       IoDeleteDevice(DiskDeviceObject);
546
547       /* Release (unclaim) the disk */
548       ScsiClassClaimDevice(PortDeviceObject,
549                            InquiryData,
550                            TRUE,
551                            NULL);
552
553       return(STATUS_INSUFFICIENT_RESOURCES);
554     }
555
556   /* Read the drive's capacity */
557   Status = ScsiClassReadDriveCapacity(DiskDeviceObject);
558   if (!NT_SUCCESS(Status) ||
559       DiskDeviceExtension->DiskGeometry->BytesPerSector == 0)
560     {
561       /* Set ISO9660 defaults */
562       DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048;
563       DiskDeviceExtension->DiskGeometry->MediaType = RemovableMedia;
564       DiskDeviceExtension->SectorShift = 11;
565       DiskDeviceExtension->PartitionLength.QuadPart = (ULONGLONG)0x7fffffff;
566     }
567   else
568     {
569       /* Make sure the BytesPerSector value is a power of 2 */
570 //      DiskDeviceExtension->DiskGeometry->BytesPerSector = 2048;
571     }
572
573   DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector);
574
575   /* Initialize media change support */
576   CdromClassCreateMediaChangeEvent (DiskDeviceExtension,
577                                     DeviceNumber);
578   if (DiskDeviceExtension->MediaChangeEvent != NULL)
579     {
580       DPRINT("Allocated media change event!\n");
581
582       /* FIXME: Allocate media change IRP and SRB */
583     }
584
585   /* Use 6 byte xa commands by default */
586   CdromData->XaFlags |= XA_USE_6_BYTE;
587
588   /* Read 'error recovery page' to get additional drive capabilities */
589   Length = sizeof(MODE_READ_RECOVERY_PAGE) +
590            MODE_BLOCK_DESC_LENGTH +
591            MODE_HEADER_LENGTH;
592
593   RtlZeroMemory (&Srb,
594                  sizeof(SCSI_REQUEST_BLOCK));
595   Srb.CdbLength = 6;
596   Srb.TimeOutValue = DiskDeviceExtension->TimeOutValue;
597
598   Cdb = (PCDB)Srb.Cdb;
599   Cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
600   Cdb->MODE_SENSE.PageCode = 0x01;
601   Cdb->MODE_SENSE.AllocationLength = (UCHAR)Length;
602
603   Buffer = ExAllocatePool (NonPagedPool,
604                            sizeof(MODE_READ_RECOVERY_PAGE) +
605                              MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH10);
606   if (Buffer == NULL)
607     {
608       DPRINT1("Allocating recovery page buffer failed!\n");
609       return STATUS_INSUFFICIENT_RESOURCES;
610     }
611
612   Status = ScsiClassSendSrbSynchronous (DiskDeviceObject,
613                                         &Srb,
614                                         Buffer,
615                                         Length,
616                                         FALSE);
617   if (!NT_SUCCESS (Status))
618     {
619       DPRINT("MODE_SENSE(6) failed\n");
620
621       /* Try the 10 byte version */
622       Length = sizeof(MODE_READ_RECOVERY_PAGE) +
623                MODE_BLOCK_DESC_LENGTH +
624                MODE_HEADER_LENGTH10;
625
626       RtlZeroMemory (&Srb,
627                      sizeof(SCSI_REQUEST_BLOCK));
628       Srb.CdbLength = 10;
629       Srb.TimeOutValue = DiskDeviceExtension->TimeOutValue;
630
631       Cdb = (PCDB)Srb.Cdb;
632       Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
633       Cdb->MODE_SENSE10.PageCode = 0x01;
634       Cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(Length >> 8);
635       Cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(Length && 0xFF);
636
637       Status = ScsiClassSendSrbSynchronous (DiskDeviceObject,
638                                             &Srb,
639                                             Buffer,
640                                             Length,
641                                             FALSE);
642       if (Status == STATUS_DATA_OVERRUN)
643         {
644           DPRINT1("Data overrun\n");
645
646           /* FIXME */
647         }
648       else if (NT_SUCCESS (Status))
649         {
650           DPRINT("Use 10 byte commands\n");
651           RtlCopyMemory (&CdromData->RecoveryData.Data10.Header,
652                          Buffer,
653                          sizeof (ERROR_RECOVERY_DATA10));
654           CdromData->RecoveryData.Data10.Header.ModeDataLength[0] = 0;
655           CdromData->RecoveryData.Data10.Header.ModeDataLength[1] = 0;
656
657           CdromData->XaFlags &= XA_USE_6_BYTE;
658           CdromData->XaFlags |= XA_USE_10_BYTE;
659         }
660       else
661         {
662           DPRINT("XA not supported\n");
663           CdromData->XaFlags |= XA_NOT_SUPPORTED;
664         }
665     }
666   else
667     {
668       DPRINT("Use 6 byte commands\n");
669       RtlCopyMemory (&CdromData->RecoveryData.Data6.Header,
670                      Buffer,
671                      sizeof (ERROR_RECOVERY_DATA6));
672       CdromData->RecoveryData.Data6.Header.ModeDataLength = 0;
673     }
674   ExFreePool (Buffer);
675
676   /* Initialize device timer */
677   IoInitializeTimer(DiskDeviceObject,
678                     CdromTimerRoutine,
679                     NULL);
680   IoStartTimer(DiskDeviceObject);
681
682   DPRINT("CdromClassCreateDeviceObjects() done\n");
683
684   return(STATUS_SUCCESS);
685 }
686
687
688 /**********************************************************************
689  * NAME
690  *      CdromClassReadTocEntry
691  *
692  * ARGUMENTS:
693  *      DeviceObject
694  *      TrackNo
695  *      Buffer
696  *      Length
697  *
698  * RETURNS:
699  *      Status.
700  */
701
702 static NTSTATUS
703 CdromClassReadTocEntry (PDEVICE_OBJECT DeviceObject,
704                         UINT TrackNo,
705                         PVOID Buffer,
706                         UINT Length)
707 {
708   PDEVICE_EXTENSION DeviceExtension;
709   SCSI_REQUEST_BLOCK Srb;
710   PCDB Cdb;
711
712   DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
713
714   RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
715   Srb.CdbLength = 10;
716   Srb.TimeOutValue = DeviceExtension->TimeOutValue;
717
718   Cdb = (PCDB)Srb.Cdb;
719   Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
720   Cdb->READ_TOC.StartingTrack = TrackNo;
721   Cdb->READ_TOC.Format = 0;
722   Cdb->READ_TOC.AllocationLength[0] = Length >> 8;
723   Cdb->READ_TOC.AllocationLength[1] = Length & 0xff;
724   Cdb->READ_TOC.Msf = 1;
725
726   return(ScsiClassSendSrbSynchronous(DeviceObject,
727                                      &Srb,
728                                      Buffer,
729                                      Length,
730                                      FALSE));
731 }
732
733
734 static NTSTATUS
735 CdromClassReadLastSession (PDEVICE_OBJECT DeviceObject,
736                            UINT TrackNo,
737                            PVOID Buffer,
738                            UINT Length)
739 {
740   PDEVICE_EXTENSION DeviceExtension;
741   SCSI_REQUEST_BLOCK Srb;
742   PCDB Cdb;
743
744   DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
745
746   RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
747   Srb.CdbLength = 10;
748   Srb.TimeOutValue = DeviceExtension->TimeOutValue;
749
750   Cdb = (PCDB)Srb.Cdb;
751   Cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
752   Cdb->READ_TOC.StartingTrack = TrackNo;
753   Cdb->READ_TOC.Format = 1;
754   Cdb->READ_TOC.AllocationLength[0] = Length >> 8;
755   Cdb->READ_TOC.AllocationLength[1] = Length & 0xff;
756   Cdb->READ_TOC.Msf = 0;
757
758   return(ScsiClassSendSrbSynchronous(DeviceObject,
759                                      &Srb,
760                                      Buffer,
761                                      Length,
762                                      FALSE));
763 }
764
765
766 /**********************************************************************
767  * NAME                                                 EXPORTED
768  *      CdromClassDeviceControl
769  *
770  * DESCRIPTION:
771  *      Answer requests for device control calls
772  *
773  * RUN LEVEL:
774  *      PASSIVE_LEVEL
775  *
776  * ARGUMENTS:
777  *      DeviceObject
778  *      Irp
779  *              Standard dispatch arguments
780  *
781  * RETURNS:
782  *      Status.
783  */
784
785 NTSTATUS STDCALL
786 CdromClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
787                         IN PIRP Irp)
788 {
789   PDEVICE_EXTENSION DeviceExtension;
790   PIO_STACK_LOCATION IrpStack;
791   ULONG ControlCode, InputLength, OutputLength;
792   PCDROM_DATA CdromData;
793   ULONG Information;
794   NTSTATUS Status;
795
796   DPRINT("CdromClassDeviceControl() called!\n");
797
798   Status = STATUS_INVALID_DEVICE_REQUEST;
799   Information = 0;
800   IrpStack = IoGetCurrentIrpStackLocation(Irp);
801   ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
802   InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
803   OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
804   DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
805   CdromData = (PCDROM_DATA)(DeviceExtension + 1);
806
807   switch (ControlCode)
808     {
809       case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
810         DPRINT("IOCTL_CDROM_GET_DRIVE_GEOMETRY\n");
811         if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
812           {
813             Status = STATUS_INVALID_PARAMETER;
814           }
815         else
816           {
817             PDISK_GEOMETRY Geometry;
818
819             if (DeviceExtension->DiskGeometry == NULL)
820               {
821                 DPRINT("No cdrom geometry available!\n");
822                 DeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
823                                                                sizeof(DISK_GEOMETRY));
824               }
825             Status = ScsiClassReadDriveCapacity(DeviceObject);
826             if (NT_SUCCESS(Status))
827               {
828                 Geometry = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer;
829                 RtlMoveMemory(Geometry,
830                               DeviceExtension->DiskGeometry,
831                               sizeof(DISK_GEOMETRY));
832
833                 Status = STATUS_SUCCESS;
834                 Information = sizeof(DISK_GEOMETRY);
835               }
836           }
837         break;
838
839       case IOCTL_CDROM_READ_TOC:
840         DPRINT("IOCTL_CDROM_READ_TOC\n");
841         if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC))
842           {
843             Status = STATUS_INFO_LENGTH_MISMATCH;
844           }
845         else
846           {
847             PCDROM_TOC TocBuffer;
848             USHORT Length;
849
850             TocBuffer = Irp->AssociatedIrp.SystemBuffer;
851
852             /* First read the lead out */
853             Length = 4 + sizeof(TRACK_DATA);
854             Status = CdromClassReadTocEntry(DeviceObject,
855                                             0xAA,
856                                             TocBuffer,
857                                             Length);
858             if (NT_SUCCESS(Status))
859               {
860                 if (TocBuffer->FirstTrack == 0xaa)
861                   {
862                     /* there is an empty cd */
863                     Information = Length;
864                   }
865                 else
866                   {
867                     /* read the toc */
868                     Length = 4 + sizeof(TRACK_DATA) * (TocBuffer->LastTrack - TocBuffer->FirstTrack + 2);
869                     Status = CdromClassReadTocEntry(DeviceObject,
870                                                     TocBuffer->FirstTrack,
871                                                     TocBuffer, Length);
872                     if (NT_SUCCESS(Status))
873                       {
874                         Information = Length;
875                       }
876                   }
877               }
878           }
879         break;
880
881       case IOCTL_CDROM_GET_LAST_SESSION:
882         DPRINT("IOCTL_CDROM_GET_LAST_SESSION\n");
883         if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < 4 + sizeof(TRACK_DATA))
884           {
885             Status = STATUS_INFO_LENGTH_MISMATCH;
886           }
887         else
888           {
889             PCDROM_TOC TocBuffer;
890             USHORT Length;
891
892             TocBuffer = Irp->AssociatedIrp.SystemBuffer;
893             Length = 4 + sizeof(TRACK_DATA);
894             Status = CdromClassReadLastSession(DeviceObject,
895                                                0,
896                                                TocBuffer,
897                                                Length);
898             if (NT_SUCCESS(Status))
899               {
900                 Information = Length;
901               }
902           }
903         break;
904
905       default:
906         /* Call the common device control function */
907         return(ScsiClassDeviceControl(DeviceObject, Irp));
908     }
909
910   /* Verify the device if the user caused the error */
911   if (!NT_SUCCESS(Status) && IoIsErrorUserInduced(Status))
912     {
913       IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
914     }
915
916   Irp->IoStatus.Status = Status;
917   Irp->IoStatus.Information = Information;
918   IoCompleteRequest(Irp,
919                     IO_NO_INCREMENT);
920
921   return(STATUS_SUCCESS);
922 }
923
924
925 /**********************************************************************
926  * NAME
927  *      CdromClassStartIo
928  *
929  * DESCRIPTION:
930  *      Starts IRP processing.
931  *
932  * RUN LEVEL:
933  *      PASSIVE_LEVEL
934  *
935  * ARGUMENTS:
936  *      DeviceObject
937  *      Irp
938  *              Standard dispatch arguments
939  *
940  * RETURNS:
941  *      None.
942  */
943 VOID STDCALL
944 CdromClassStartIo (IN PDEVICE_OBJECT DeviceObject,
945                    IN PIRP Irp)
946 {
947   PDEVICE_EXTENSION DeviceExtension;
948   PIO_STACK_LOCATION IrpStack;
949   ULONG MaximumTransferLength;
950   ULONG TransferPages;
951
952   DPRINT("CdromClassStartIo() called!\n");
953
954   DeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
955   IrpStack = IoGetCurrentIrpStackLocation (Irp);
956
957   MaximumTransferLength = DeviceExtension->PortCapabilities->MaximumTransferLength;
958
959   if (IrpStack->MajorFunction == IRP_MJ_READ)
960     {
961       DPRINT("  IRP_MJ_READ\n");
962
963       TransferPages =
964         ADDRESS_AND_SIZE_TO_SPAN_PAGES (MmGetMdlVirtualAddress(Irp->MdlAddress),
965                                         IrpStack->Parameters.Read.Length);
966
967       /* Check transfer size */
968       if ((IrpStack->Parameters.Read.Length > MaximumTransferLength) ||
969           (TransferPages > DeviceExtension->PortCapabilities->MaximumPhysicalPages))
970         {
971           /* Transfer size is too large - split it */
972           TransferPages =
973             DeviceExtension->PortCapabilities->MaximumPhysicalPages - 1;
974
975           /* Adjust transfer size */
976           if (MaximumTransferLength > TransferPages * PAGE_SIZE)
977             MaximumTransferLength = TransferPages * PAGE_SIZE;
978
979           if (MaximumTransferLength == 0)
980             MaximumTransferLength = PAGE_SIZE;
981
982           /* Split the transfer */
983           ScsiClassSplitRequest (DeviceObject,
984                                  Irp,
985                                  MaximumTransferLength);
986           return;
987         }
988       else
989         {
990           /* Build SRB */
991           ScsiClassBuildRequest (DeviceObject,
992                                  Irp);
993         }
994     }
995   else if (IrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
996     {
997       DPRINT1("  IRP_MJ_IRP_MJ_DEVICE_CONTROL\n");
998
999       UNIMPLEMENTED;
1000 #if 0
1001       switch (IrpStack->Parameters.DeviceIoControl.IoControlCode)
1002         {
1003
1004           default:
1005             IoCompleteRequest (Irp,
1006                                IO_NO_INCREMENT);
1007             return;
1008         }
1009 #endif
1010     }
1011
1012   /* Call the SCSI port driver */
1013   IoCallDriver (DeviceExtension->PortDeviceObject,
1014                 Irp);
1015 }
1016
1017
1018 VOID STDCALL
1019 CdromTimerRoutine(PDEVICE_OBJECT DeviceObject,
1020                   PVOID Context)
1021 {
1022   DPRINT("CdromTimerRoutine() called\n");
1023
1024 }
1025
1026 /* EOF */