floppy.c

来自「一个类似windows」· C语言 代码 · 共 1,185 行 · 第 1/3 页

C
1,185
字号
	    continue;

          if(gControllerInfo[i].DriveInfo[j].DeviceObject)
            {
	      UNICODE_STRING Link;

	      RtlInitUnicodeString(&Link, gControllerInfo[i].DriveInfo[j].SymLinkBuffer);
	      IoDeleteSymbolicLink(&Link);

	      RtlInitUnicodeString(&Link, gControllerInfo[i].DriveInfo[j].ArcPathBuffer);
	      IoDeassignArcName(&Link);

              IoDeleteDevice(gControllerInfo[i].DriveInfo[j].DeviceObject);
            }
	}

      IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);

      /* Power down the controller */
      if(HwPowerOff(&gControllerInfo[i]) != STATUS_SUCCESS)
      {
	DPRINT("floppy: unload: warning: HwPowerOff failed\n");
      }
    }
}


static NTSTATUS NTAPI ConfigCallback(PVOID Context,
                                     PUNICODE_STRING PathName,
                                     INTERFACE_TYPE BusType,
                                     ULONG BusNumber,
                                     PKEY_VALUE_FULL_INFORMATION *BusInformation,
                                     CONFIGURATION_TYPE ControllerType,
                                     ULONG ControllerNumber,
                                     PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
                                     CONFIGURATION_TYPE PeripheralType,
                                     ULONG PeripheralNumber,
                                     PKEY_VALUE_FULL_INFORMATION *PeripheralInformation)
/*
 * FUNCTION: Callback to IoQueryDeviceDescription, which tells us about our controllers
 * ARGUMENTS:
 *     Context: Unused
 *     PathName: Unused
 *     BusType: Type of the bus that our controller is on
 *     BusNumber: Number of the bus that our controller is on
 *     BusInformation: Unused
 *     ControllerType: Unused
 *     ControllerNumber: Number of the controller that we're adding
 *     ControllerInformation: Full configuration information for our controller
 *     PeripheralType: Unused
 *     PeripheralNumber: Unused
 *     PeripheralInformation: Full configuration information for each drive on our controller
 * RETURNS:
 *     STATUS_SUCCESS in all cases
 * NOTES:
 *     - The only documentation I've found about the contents of these structures is
 *       from the various Microsoft floppy samples and from the DDK headers.  They're
 *       very vague, though, so I'm only mostly sure that this stuff is correct, as
 *       the MS samples do things completely differently than I have done them.  Seems
 *       to work in my VMWare, though.
 *     - Basically, the function gets all of the information (port, dma, irq) about the
 *       controller, and then loops through all of the drives presented in PeripheralInformation.
 *     - Each controller has a CONTROLLER_INFO created for it, and each drive has a DRIVE_INFO.
 *     - Device objects are created for each drive (not controller), as that's the targeted
 *       device in the eyes of the rest of the OS.  Each DRIVE_INFO points to a single CONTROLLER_INFO.
 *     - We only support up to four controllers in the whole system, each of which supports up to four
 *       drives.
 */
{
  PKEY_VALUE_FULL_INFORMATION ControllerFullDescriptor = ControllerInformation[IoQueryDeviceConfigurationData];
  PCM_FULL_RESOURCE_DESCRIPTOR ControllerResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PCHAR)ControllerFullDescriptor +
                                                               ControllerFullDescriptor->DataOffset);

  PKEY_VALUE_FULL_INFORMATION PeripheralFullDescriptor = PeripheralInformation[IoQueryDeviceConfigurationData];
  PCM_FULL_RESOURCE_DESCRIPTOR PeripheralResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PCHAR)PeripheralFullDescriptor +
                                                               PeripheralFullDescriptor->DataOffset);

  PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
  PCM_FLOPPY_DEVICE_DATA FloppyDeviceData;
  UCHAR i;

  PAGED_CODE();
  UNREFERENCED_PARAMETER(PeripheralType);
  UNREFERENCED_PARAMETER(PeripheralNumber);
  UNREFERENCED_PARAMETER(BusInformation);
  UNREFERENCED_PARAMETER(Context);
  UNREFERENCED_PARAMETER(ControllerType);
  UNREFERENCED_PARAMETER(PathName);


  DPRINT("floppy: ConfigCallback called with ControllerNumber %d\n", ControllerNumber);

  gControllerInfo[gNumberOfControllers].ControllerNumber = ControllerNumber;
  gControllerInfo[gNumberOfControllers].InterfaceType = BusType;
  gControllerInfo[gNumberOfControllers].BusNumber = BusNumber;

  /* Get controller interrupt level/vector, dma channel, and port base */
  for(i = 0; i < ControllerResourceDescriptor->PartialResourceList.Count; i++)
    {
      KeInitializeEvent(&gControllerInfo[gNumberOfControllers].SynchEvent, NotificationEvent, FALSE);

      PartialDescriptor = &ControllerResourceDescriptor->PartialResourceList.PartialDescriptors[i];

      if(PartialDescriptor->Type == CmResourceTypeInterrupt)
        {
          gControllerInfo[gNumberOfControllers].Level = PartialDescriptor->u.Interrupt.Level;
          gControllerInfo[gNumberOfControllers].Vector = PartialDescriptor->u.Interrupt.Vector;

          if(PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
            gControllerInfo[gNumberOfControllers].InterruptMode = Latched;
          else
            gControllerInfo[gNumberOfControllers].InterruptMode = LevelSensitive;
        }

      else if(PartialDescriptor->Type == CmResourceTypePort)
        {
          PHYSICAL_ADDRESS TranslatedAddress;
          ULONG AddressSpace = 0x1; /* I/O Port Range */

          if(!HalTranslateBusAddress(BusType, BusNumber, PartialDescriptor->u.Port.Start, &AddressSpace, &TranslatedAddress))
	    {
	      DPRINT("floppy: HalTranslateBusAddress failed; returning\n");
	      return STATUS_IO_DEVICE_ERROR;
	    }

          if(AddressSpace == 0)
              gControllerInfo[gNumberOfControllers].BaseAddress = MmMapIoSpace(TranslatedAddress, FDC_PORT_BYTES, MmNonCached);
          else
              gControllerInfo[gNumberOfControllers].BaseAddress = (PUCHAR)TranslatedAddress.u.LowPart;
        }

      else if(PartialDescriptor->Type == CmResourceTypeDma)
        gControllerInfo[gNumberOfControllers].Dma = PartialDescriptor->u.Dma.Channel;
    }

  /* Start with 0 drives, then go looking */
  gControllerInfo[gNumberOfControllers].NumberOfDrives = 0;

  /* learn about drives attached to controller */
  for(i = 0; i < PeripheralResourceDescriptor->PartialResourceList.Count; i++)
    {
      PDRIVE_INFO DriveInfo = &gControllerInfo[gNumberOfControllers].DriveInfo[i];

      PartialDescriptor = &PeripheralResourceDescriptor->PartialResourceList.PartialDescriptors[i];

      if(PartialDescriptor->Type != CmResourceTypeDeviceSpecific)
        continue;

      FloppyDeviceData = (PCM_FLOPPY_DEVICE_DATA)(PartialDescriptor + 1);

      DriveInfo->ControllerInfo = &gControllerInfo[gNumberOfControllers];
      DriveInfo->UnitNumber = i;

      DriveInfo->FloppyDeviceData.MaxDensity = FloppyDeviceData->MaxDensity;
      DriveInfo->FloppyDeviceData.MountDensity = FloppyDeviceData->MountDensity;
      DriveInfo->FloppyDeviceData.StepRateHeadUnloadTime = FloppyDeviceData->StepRateHeadUnloadTime;
      DriveInfo->FloppyDeviceData.HeadLoadTime = FloppyDeviceData->HeadLoadTime;
      DriveInfo->FloppyDeviceData.MotorOffTime = FloppyDeviceData->MotorOffTime;
      DriveInfo->FloppyDeviceData.SectorLengthCode = FloppyDeviceData->SectorLengthCode;
      DriveInfo->FloppyDeviceData.SectorPerTrack = FloppyDeviceData->SectorPerTrack;
      DriveInfo->FloppyDeviceData.ReadWriteGapLength = FloppyDeviceData->ReadWriteGapLength;
      DriveInfo->FloppyDeviceData.FormatGapLength = FloppyDeviceData->FormatGapLength;
      DriveInfo->FloppyDeviceData.FormatFillCharacter = FloppyDeviceData->FormatFillCharacter;
      DriveInfo->FloppyDeviceData.HeadSettleTime = FloppyDeviceData->HeadSettleTime;
      DriveInfo->FloppyDeviceData.MotorSettleTime = FloppyDeviceData->MotorSettleTime;
      DriveInfo->FloppyDeviceData.MaximumTrackValue = FloppyDeviceData->MaximumTrackValue;
      DriveInfo->FloppyDeviceData.DataTransferLength = FloppyDeviceData->DataTransferLength;

      /* Once it's all set up, acknowledge its existance in the controller info object */
      gControllerInfo[gNumberOfControllers].NumberOfDrives++;
    }

  gControllerInfo[gNumberOfControllers].Populated = TRUE;
  gNumberOfControllers++;

  return STATUS_SUCCESS;
}


static BOOLEAN NTAPI Isr(PKINTERRUPT Interrupt,
                         PVOID ServiceContext)
/*
 * FUNCTION: Interrupt service routine for the controllers
 * ARGUMENTS:
 *     Interrupt: Interrupt object representing the interrupt that occured
 *     ServiceContext: Pointer to the ControllerInfo object that caused the interrupt
 * RETURNS:
 *     TRUE in all cases (see notes)
 * NOTES:
 *     - We should always be the target of the interrupt, being an edge-triggered ISA interrupt, but
 *       this won't be the case with a level-sensitive system like PCI
 *     - Note that it probably doesn't matter if the interrupt isn't dismissed, as it's edge-triggered.
 *       It probably won't keep re-interrupting.
 *     - There are two different ways to dismiss a floppy interrupt.  If the command has a result phase
 *       (see intel datasheet), you dismiss the interrupt by reading the first data byte.  If it does
 *       not, you dismiss the interrupt by doing a Sense Interrupt command.  Again, because it's edge-
 *       triggered, this is safe to not do here, as we can just wait for the DPC.
 *     - Either way, we don't want to do this here.  The controller shouldn't interrupt again, so we'll
 *       schedule a DPC to take care of it.
 *     - This driver really cannot shrare interrupts, as I don't know how to conclusively say
 *       whether it was our controller that interrupted or not.  I just have to assume that any time
 *       my ISR gets called, it was my board that called it.  Dumb design, yes, but it goes back to
 *       the semantics of ISA buses.  That, and I don't know much about ISA drivers. :-)
 *       UPDATE: The high bit of Status Register A seems to work on non-AT controllers.
 *     - Called at DIRQL
 */
{
  PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)ServiceContext;

  UNREFERENCED_PARAMETER(Interrupt);

  ASSERT(ControllerInfo);

  DPRINT("floppy: ISR called\n");

  /*
   * Due to the stupidity of the drive/controller relationship on the floppy drive, only one device object
   * can have an active interrupt pending.  Due to the nature of these IRPs, though, there will only ever
   * be one thread expecting an interrupt at a time, and furthermore, Interrupts (outside of spurious ones)
   * won't ever happen unless a thread is expecting them.  Therefore, all we have to do is signal an event
   * and we're done.  Queue a DPC and leave.
   */
  KeInsertQueueDpc(&ControllerInfo->Dpc, NULL, NULL);

  return TRUE;
}


VOID NTAPI DpcForIsr(PKDPC UnusedDpc,
                     PVOID Context,
                     PVOID SystemArgument1,
                     PVOID SystemArgument2)
/*
 * FUNCTION: This DPC gets queued by every ISR.  Does the real per-interrupt work.
 * ARGUMENTS:
 *     UnusedDpc: Pointer to the DPC object that represents our function
 *     DeviceObject: Device that this DPC is running for
 *     Irp: Unused
 *     Context: Pointer to our ControllerInfo struct
 * NOTES:
 *     - This function just kicks off whatever the SynchEvent is and returns.  We depend on
 *       the thing that caused the drive to interrupt to handle the work of clearing the interrupt.
 *       This enables us to get back to PASSIVE_LEVEL and not hog system time on a really stupid,
 *       slow, screwed-up piece of hardare.
 *     - If nothing is waiting for us to set the event, the interrupt is effectively lost and will
 *       never be dismissed.  I wonder if this will become a problem.
 *     - Called at DISPATCH_LEVEL
 */
{
  PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)Context;

  UNREFERENCED_PARAMETER(UnusedDpc);
  UNREFERENCED_PARAMETER(SystemArgument1);
  UNREFERENCED_PARAMETER(SystemArgument2);

  ASSERT(ControllerInfo);

  DPRINT("floppy: DpcForIsr called\n");

  KeSetEvent(&ControllerInfo->SynchEvent, EVENT_INCREMENT, FALSE);
}


static NTSTATUS NTAPI InitController(PCONTROLLER_INFO ControllerInfo)
/*
 * FUNCTION:  Initialize a newly-found controller
 * ARGUMENTS:
 *     ControllerInfo: pointer to the controller to be initialized
 * RETURNS:
 *     STATUS_SUCCESS if the controller is successfully initialized
 *     STATUS_IO_DEVICE_ERROR otherwise
 */
{
  int i;
  UCHAR HeadLoadTime;
  UCHAR HeadUnloadTime;
  UCHAR StepRateTime;

  PAGED_CODE();
  ASSERT(ControllerInfo);

  DPRINT("floppy: InitController called with Controller 0x%x\n", ControllerInfo);

  KeClearEvent(&ControllerInfo->SynchEvent);

  DPRINT("floppy: InitController: resetting the controller\n");

  /* Reset the controller */
  if(HwReset(ControllerInfo) != STATUS_SUCCESS)
    {
      DPRINT("floppy: InitController: unable to reset controller\n");
      return STATUS_IO_DEVICE_ERROR;
    }

  DPRINT("floppy: InitController: setting data rate\n");

  /* Set data rate */
  if(HwSetDataRate(ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
    {
      DPRINT("floppy: InitController: unable to set data rate\n");
      return STATUS_IO_DEVICE_ERROR;
    }

  DPRINT("floppy: InitController: waiting for initial interrupt\n");

  /* Wait for an interrupt */
  WaitForControllerInterrupt(ControllerInfo);

  /* Reset means you have to clear each of the four interrupts (one per drive) */
  for(i = 0; i < MAX_DRIVES_PER_CONTROLLER; i++)
    {
      DPRINT("floppy: InitController: Sensing interrupt %d\n", i);

      if(HwSenseInterruptStatus(ControllerInfo) != STATUS_SUCCESS)
	{
	  DPRINT("floppy: InitController: Unable to clear interrupt 0x%x\n", i);
	  return STATUS_IO_DEVICE_ERROR;
	}
    }

  DPRINT("floppy: InitController: done sensing interrupts\n");

  /* Next, see if we have the right version to do implied seek */
  if(HwGetVersion(ControllerInfo) == VERSION_ENHANCED)
    {
      /* If so, set that up -- all defaults below except first TRUE for EIS */
      if(HwConfigure(ControllerInfo, TRUE, TRUE, FALSE, 0, 0) != STATUS_SUCCESS)
	{
	  DPRINT("floppy: InitController: unable to set up implied seek\n");
          ControllerInfo->ImpliedSeeks = FALSE;
	}
      else
	{
	  DPRINT("floppy: InitController: implied seeks set!\n");
          ControllerInfo->ImpliedSeeks = TRUE;
	}

      /*
       * FIXME: Figure out the answer to the below
       *
       * I must admit that I'm really confused about the Model 30 issue.  At least one
       * important bit (the disk change bit in the DIR) is flipped if this is a Model 30
       * controller.  However, at least one other floppy driver believes that there are only
       * two computers that are guaranteed to have a Model 30 controller:
       *  - IBM Thinkpad 750
       *  - IBM PS2e
       *
       * ...and another driver only lists a config option for "thinkpad", that flips
       * the change line.  A third driver doesn't mention the Model 30 issue at all.
       *
       * What I can't tell is whether or not the average, run-of-the-mill computer now has
       * a Model 30 controller.  For the time being, I'm going to wire this to FALSE,
       * and just not support the computers mentioned above, while I try to figure out
       * how ubiquitous these newfangled 30 thingies are.
       */
      //ControllerInfo->Model30 = TRUE;
      ControllerInfo->Model30 = FALSE;
    }
  else
    {
      DPRINT("floppy: InitController: enhanced version not supported; disabling implied seeks\n");
      ControllerInfo->ImpliedSeeks = FALSE;
      ControllerInfo->Model30 = FALSE;
    }

  /* Specify */
  DPRINT("FLOPPY: FIXME: Figure out speed\n");
  HeadLoadTime = SPECIFY_HLT_500K;
  HeadUnloadTime = SPECIFY_HUT_500K;
  StepRateTime = SPECIFY_SRT_500K;

  DPRINT("floppy: InitController: issuing specify command to controller\n");

  /* Don't disable DMA --> enable dma (dumb & confusing) */
  if(HwSpecify(ControllerInfo, HeadLoadTime, HeadUnloadTime, StepRateTime, FALSE) != STATUS_SUCCESS)
    {
      DPRINT("floppy: InitController: unable to specify options\n");
      return STATUS_IO_DEVICE_ERROR;
    }

  /* Init the stop stuff */
  KeInitializeDpc(&ControllerInfo->MotorStopDpc, MotorStopDpcFunc, ControllerInfo);
  KeInitializeTimer(&ControllerInfo->MotorTimer);
  KeInitializeEvent(&ControllerInfo->MotorStoppedEvent, NotificationEvent, FALSE);
  ControllerInfo->StopDpcQueued = FALSE;

  /*
   * Recalibrate each drive on the controller (depends on StartMotor, which depends on the timer stuff above)
   * We don't even know if there is a disk in the drive, so this may not work, but that's OK.
   */
  for(i = 0; i < ControllerInfo->NumberOfDrives; i++)
    {
      DPRINT("floppy: InitController: recalibrating drive 0x%x on controller 0x%x\n", i, ControllerInfo);
      Recalibrate(&ControllerInfo->DriveInfo[i]);
    }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?