Exemplo n.º 1
0
int  MotorServo::check(){

	int aCurrent_pot = Current_pot();
 
	switch (_DIRECTION)
	{
	case DIRECTION_OFF:
		if (_lastdirection != DIRECTION_OFF);
		StopMotor();
		return 0;
		break;

	case DIRECTION_UP:
		if (aCurrent_pot > POT_MAX) {
			StopMotor();
			return 0;
		}
		else{

			if (_lastdirection != DIRECTION_UP){
				donot_burn_out_hbridge(_PIN_MOTOR_UP, UP_SPEED);
			 

			}
			 
		}

		break;
	case DIRECTION_DOWN:

		if (aCurrent_pot < POT_MIN) {
			StopMotor();
			return 0;
		}
		else{

			if (_lastdirection != DIRECTION_DOWN){

				donot_burn_out_hbridge(_PIN_MOTOR_DOWN, DOWN_SPEED);
			 

			}
		 
		}

		break;
		//	case DIRECTION_UP_BRAKE:
		//		break;
		//	case DIRECTION_DOWN_BRAKE:
		//		break;
	default:
		break;
	}
	_lastdirection = _DIRECTION;


}
Exemplo n.º 2
0
NTSTATUS NTAPI
ResetChangeFlag(PDRIVE_INFO DriveInfo)
/*
 * FUNCTION: Reset the drive's change flag (as reflected in the DIR)
 * ARGUMENTS:
 *     DriveInfo: the drive to reset
 * RETURNS:
 *     STATUS_SUCCESS if the changeline is cleared
 *     STATUS_NO_MEDIA_IN_DEVICE if the changeline cannot be cleared
 *     STATUS_IO_DEVICE_ERROR if the controller cannot be communicated with
 * NOTES:
 *     - Change reset procedure: recalibrate, seek 1, seek 0
 *     - If the line is still set after that, there's clearly no disk in the
 *       drive, so we return STATUS_NO_MEDIA_IN_DEVICE
 *     - PAGED_CODE because we wait
 */
{
    BOOLEAN DiskChanged;

    PAGED_CODE();
    ASSERT(DriveInfo);

    TRACE_(FLOPPY, "ResetChangeFlag called\n");

    /* Try to recalibrate.  We don't care if it works. */
    Recalibrate(DriveInfo);

    /* clear spurious interrupts in prep for seeks */
    KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);

    /* must re-start the drive because Recalibrate() stops it */
    StartMotor(DriveInfo);

    /* Seek to 1 */
    if(HwSeek(DriveInfo, 1) != STATUS_SUCCESS)
    {
        WARN_(FLOPPY, "ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n");
        StopMotor(DriveInfo->ControllerInfo);
        return STATUS_IO_DEVICE_ERROR;
    }

    WaitForControllerInterrupt(DriveInfo->ControllerInfo);

    if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
    {
        WARN_(FLOPPY, "ResetChangeFlag(): HwSenseInterruptStatus failed; bailing out\n");
        StopMotor(DriveInfo->ControllerInfo);
        return STATUS_IO_DEVICE_ERROR;
    }

    /* Seek back to 0 */
    if(HwSeek(DriveInfo, 0) != STATUS_SUCCESS)
    {
        WARN_(FLOPPY, "ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n");
        StopMotor(DriveInfo->ControllerInfo);
        return STATUS_IO_DEVICE_ERROR;
    }

    WaitForControllerInterrupt(DriveInfo->ControllerInfo);

    if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
    {
        WARN_(FLOPPY, "ResetChangeFlag(): HwSenseInterruptStatus #2 failed; bailing\n");
        StopMotor(DriveInfo->ControllerInfo);
        return STATUS_IO_DEVICE_ERROR;
    }

    /* Check the change bit */
    if(HwDiskChanged(DriveInfo, &DiskChanged) != STATUS_SUCCESS)
    {
        WARN_(FLOPPY, "ResetChangeFlag(): HwDiskChanged failed; returning STATUS_IO_DEVICE_ERROR\n");
        StopMotor(DriveInfo->ControllerInfo);
        return STATUS_IO_DEVICE_ERROR;
    }

    StopMotor(DriveInfo->ControllerInfo);

    /* if the change flag is still set, there's probably no media in the drive. */
    if(DiskChanged)
        return STATUS_NO_MEDIA_IN_DEVICE;

    /* else we're done! */
    return STATUS_SUCCESS;
}
Exemplo n.º 3
0
static NTSTATUS NTAPI
Recalibrate(PDRIVE_INFO DriveInfo)
/*
 * FUNCTION: Start the recalibration process
 * ARGUMENTS:
 *     DriveInfo: Pointer to the driveinfo struct associated with the targeted drive
 * RETURNS:
 *     STATUS_SUCCESS on successful starting of the process
 *     STATUS_IO_DEVICE_ERROR if it fails
 * NOTES:
 *     - Sometimes you have to do two recalibrations, particularly if the disk has <80 tracks.
 *     - PAGED_CODE because we wait
 */
{
    NTSTATUS Status;
    ULONG i;

    PAGED_CODE();
    ASSERT(DriveInfo);

    /* first turn on the motor */
    /* Must stop after every start, prior to return */
    StartMotor(DriveInfo);

    /* set the data rate */
    WARN_(FLOPPY, "FIXME: UN-HARDCODE DATA RATE\n");
    if(HwSetDataRate(DriveInfo->ControllerInfo, 0) != STATUS_SUCCESS)
    {
        WARN_(FLOPPY, "Recalibrate: HwSetDataRate failed\n");
        StopMotor(DriveInfo->ControllerInfo);
        return STATUS_IO_DEVICE_ERROR;
    }

    /* clear the event just in case the last call forgot */
    KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);

    /* sometimes you have to do this twice; we'll just do it twice all the time since
     * we don't know if the people calling this Recalibrate routine expect a disk to
     * even be in the drive, and if so, if that disk is formatted.
     */
    for(i = 0; i < 2; i++)
    {
        /* Send the command */
        Status = HwRecalibrate(DriveInfo);
        if(Status != STATUS_SUCCESS)
        {
            WARN_(FLOPPY, "Recalibrate: HwRecalibrate returned error\n");
            continue;
        }

        WaitForControllerInterrupt(DriveInfo->ControllerInfo);

        /* Get the results */
        Status = HwRecalibrateResult(DriveInfo->ControllerInfo);
        if(Status != STATUS_SUCCESS)
        {
            WARN_(FLOPPY, "Recalibrate: HwRecalibrateResult returned error\n");
            break;
        }
    }

    KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);

    /* Must stop after every start, prior to return */
    StopMotor(DriveInfo->ControllerInfo);

    return Status;
}
void DaisyChain(void)
{
	uint8_t i=0;
	status_t status=0;
	uint32_t data=0;
	
	static enum MotorState LastmotorState = Stopped;

	if ( motorState == StartUp )
	{
		CharCNT = 0;
		USIC_FlushRxFIFO(UART001_Handle0.UartRegs);
	}

	if ( (LastmotorState == StartUp) && (motorState != Running) )//(motorState == Running)StartUp
	{
		CCU40_CC42->TCCLR |= 0x02;	//定时器清零
		//Start slicesCCU4定时器运行
		CCU40_CC42->TCSET |= 0x01UL;
		
		CharCNT++;
		if ( CharCNT >= 13 )	//能收到连续的13字节完整包
		{
			CharCNT = 0;
			LastmotorState = motorState;
			CCU40_CC42->TCCLR |= 3UL;	//定时器停止运行
			USIC_FlushRxFIFO(UART001_Handle0.UartRegs);///added
		}
		else
		{
			return;
		}
	}
	else
		LastmotorState = motorState;


	//	if (DaisyTimeOut)
	//		StopMotor();

	if(USIC_GetRxFIFOFillingLevel(UART001_Handle0.UartRegs) >= DAISY_BUFFER_SIZE)
	{
		CCU40_CC42->TCCLR |= 3UL;	//定时器停止运行

		//Read data from UART buffer
		UART001_ReadDataBytes(&UART001_Handle0,FifoRecBuffer,DAISY_BUFFER_SIZE);
		//Assumption that communication is lost --> emtpy Receive Buffer
		if (FifoRecBuffer[DAISY_BUFFER_SIZE-1] != DAISY_STOP_BYTE)
		{
			//IO004_TogglePin(IO004_Handle1);
			USIC_FlushRxFIFO(UART001_Handle0.UartRegs);
			return;
		}

		uint8_t cmd = FifoRecBuffer[0];
		uint16_t params =  (FifoRecBuffer[1] << 8 | FifoRecBuffer[2]);

		switch (cmd)
		{
			case START_MOTOR:
				StartMotor();
				break;
			case STOP_MOTOR:
				StopMotor();
				break;
			case SET_REF_CURRENT:
				SetReferenceCurrent(params);
				break;
		}

		for(i=DAISY_MESSAGE_LENGTH; i<DAISY_BUFFER_SIZE-1; i++)
			FifoTransBuffer[i-DAISY_MESSAGE_LENGTH]=FifoRecBuffer[i];

		//Status-Code
		FifoTransBuffer[i-DAISY_MESSAGE_LENGTH]=status;
		i++;
		//Data
		FifoTransBuffer[i-DAISY_MESSAGE_LENGTH]=(uint8_t)(data >> 8);
		i++;
		FifoTransBuffer[i-DAISY_MESSAGE_LENGTH]=(uint8_t)data;
		i++;
		FifoTransBuffer[i-DAISY_MESSAGE_LENGTH]=DAISY_STOP_BYTE;
		DaisyTimeOut = 0;
		DaisyCount++;

		UART001_WriteDataBytes(&UART001_Handle0, FifoTransBuffer, DAISY_BUFFER_SIZE);
	}
Exemplo n.º 5
0
/****************************************************************************
 Function
    RunZoneFourSM

 Parameters
   ES_Event: the event to process

 Returns
   ES_Event: an event to return

 Description
   add your description here
 Notes
   uses nested switch/case to implement the machine.
 Author
   J. Edward Carryer, 2/11/05, 10:45AM
****************************************************************************/
ES_Event RunZoneFourSM( ES_Event CurrentEvent )
{
   bool MakeTransition = false;/* are we making a state transition? */
   ZoneFourState_t NextState = CurrentState;
   ES_Event EntryEventKind = { ES_ENTRY, 0 };// default to normal entry to new state
   ES_Event ReturnEvent = CurrentEvent; // assume we are not consuming event

   switch ( CurrentState )
   {
       case FOLLOW_TAPE_ZONE_FOUR :  
				  printf("FOLLOW_TAPE_ZONE_FOUR \r\n");
         CurrentEvent = DuringFollowTape(CurrentEvent);
         if ( CurrentEvent.EventType != ES_NO_EVENT ) 
         {
            switch (CurrentEvent.EventType)
            {
               case CenterOfObstacle: 
								  printf("Center Of Obstacle \r\n");
									StopMotor();
                  NextState = ALIGN_WITH_OBSTACLE;
                  MakeTransition = true;
                  EntryEventKind.EventType = ES_ENTRY_HISTORY;
                  ReturnEvent.EventType = ES_NO_EVENT;
                  break;
            }
         }
         break;
				 //really driving forward
				 case BACK_AWAY_FROM_OBSTACLE :
					printf("BACK_AWAY_FROM_OBSTACLE \r\n");
					CurrentEvent = DuringBackAwayFromObstacle(CurrentEvent);
					if ( CurrentEvent.EventType != ES_NO_EVENT ) 
					{
            switch (CurrentEvent.EventType)
            {
               case PositionReached : 
								  printf("Position Reached \r\n");
									StopMotor();
									ES_Timer_InitTimer(BACK_UP_TIMER,POST_BACK_UP_TIME);
							 break;
							 
							 case ES_TIMEOUT :
									if(CurrentEvent.EventParam == BACK_UP_TIMER){
										NextState = ALIGN_WITH_OBSTACLE_ROUND_TWO;
										MakeTransition = true;
										EntryEventKind.EventType = ES_ENTRY_HISTORY;
										ReturnEvent.EventType = ES_NO_EVENT;
									}
                  break;
            }
         }
         break;
				 
				  
				 case ALIGN_WITH_OBSTACLE_ROUND_TWO :  
			    printf("ALIGN_WITH_OBSTACLE_ROUND_TWO \r\n");
          CurrentEvent = DuringAlignWithObstacleRoundTwo(CurrentEvent);
         if ( CurrentEvent.EventType != ES_NO_EVENT ) 
         {
            switch (CurrentEvent.EventType)
            {
               case TurnComplete : 
								  printf("Turn Complete \r\n");
									StopMotor();
									setZonesForAlign(0, 0,true);
									//enableSpeedControl(true, INTO_RAMP_SPEED, false); //was 100
									//enableSpeedControl(true, INTO_RAMP_SPEED, true); //was 100
									SetPWMDutyDrive(-99, -99);
                  //enableSpeedControl(true, INTO_RAMP_SPEED,false);
                  break;
            }
         }
         break;
				 
				 case ALIGN_WITH_OBSTACLE :  
			    printf("ALIGN_WITH_OBSTACLE \r\n");
         CurrentEvent = DuringAlignWithObstacle(CurrentEvent);
         if ( CurrentEvent.EventType != ES_NO_EVENT ) 
         {
            switch (CurrentEvent.EventType)
            {
               case TurnComplete: 
							//case PositionReached:
								  printf("Turn Complete \r\n");
									StopMotor();
                  NextState = BACK_AWAY_FROM_OBSTACLE;
									//enableSpeedControl(true, 100, false);
                  MakeTransition = true;
                  EntryEventKind.EventType = ES_ENTRY_HISTORY;
                  ReturnEvent.EventType = ES_NO_EVENT;
                  break;
            }
         }
         break;
    }
    if (MakeTransition == true)
    {
       CurrentEvent.EventType = ES_EXIT;
       RunZoneFourSM(CurrentEvent);
       CurrentState = NextState; 
       RunZoneFourSM(EntryEventKind);
     }
     return(ReturnEvent);
}
Exemplo n.º 6
0
/****************************************************************************
 Function
    RunZoneTwoSM

 Parameters
   ES_Event: the event to process

 Returns
   ES_Event: an event to return

 Description
   add your description here
 Notes
   uses nested switch/case to implement the machine.
 Author
   J. Edward Carryer, 2/11/05, 10:45AM
****************************************************************************/
ES_Event RunZoneTwoSM( ES_Event CurrentEvent )
{
   bool MakeTransition = false;/* are we making a state transition? */
   ZoneTwoState_t NextState = CurrentState;
   ES_Event EntryEventKind = { ES_ENTRY, 0 };// default to normal entry to new state
   ES_Event ReturnEvent = CurrentEvent; // assume we are not consuming event

   switch ( CurrentState )
   {
       case FOLLOW_TAPE_ZONE_TWO :
					printf("FOLLOW_TAPE_ZONE_TWO \r\n");
         CurrentEvent = DuringFollowTape(CurrentEvent);
         
         if ( CurrentEvent.EventType != ES_NO_EVENT ) //If an event is active
         {
            switch (CurrentEvent.EventType)
            {
               case PositionReached: 
								  printf("Position Reached \r\n");
                  StopMotor();
									NextState = ENTER_SHOOT;
                  MakeTransition = true; 
                  EntryEventKind.EventType = ES_ENTRY_HISTORY;
                  break;
            }
         }
         break;
				 
				 case ENTER_SHOOT:  
					printf("ENTER_SHOOT \r\n");
					CurrentEvent = DuringEnterShoot(CurrentEvent);
					 switch (CurrentEvent.EventType)
            {
               case TurnComplete: 
								  printf("Turn Complete \r\n");
                  StopMotor();
									setZonesForAlign(0, 0,true);
									enableSpeedControl(true,INTO_ZONE_THREE_SPEED,false);
                  break;
            }
         break;
				 case FINDING_TAPE_ZONE_TWO:  
					printf("FINDING_TAPE_ZONE_TWO \r\n");
					CurrentEvent = DuringFindingTape(CurrentEvent);
					 switch (CurrentEvent.EventType)
            {
						 case TapeFound:
									printf("Tape Found\r\n");
									StopMotor();
									NextState = FORWARD_TO_ALIGN_ZONE_TWO;
                  MakeTransition = true;
                  EntryEventKind.EventType = ES_ENTRY_HISTORY;
                  break;
            }
         break;
				case FORWARD_TO_ALIGN_ZONE_TWO:   
					printf("FORWARD_TO_ALIGN_ZONE_TWO \r\n");
					CurrentEvent = DuringForwardToAlign(CurrentEvent);
					 switch (CurrentEvent.EventType)
            {
						 case PositionReached:
									printf("Position Reached \r\n");
									StopMotor();
									NextState = REALIGN_ZONE_TWO;
                  MakeTransition = true;
                  EntryEventKind.EventType = ES_ENTRY_HISTORY;
                  break;
            }
         break;
						 
				 case REALIGN_ZONE_TWO:  
					printf("REALIGN_ZONE_TWO \r\n");
					CurrentEvent = DuringRealign(CurrentEvent);
					 switch (CurrentEvent.EventType)
            {
                case TurnComplete: 
									printf("Turn Complete \r\n");
									StopMotor();
                  NextState = FOLLOW_TAPE_ZONE_TWO;
                  MakeTransition = true;
                  EntryEventKind.EventType = ES_ENTRY_HISTORY;
                  break;
            }
         break;
					
    }
    if (MakeTransition == true)
    {

       CurrentEvent.EventType = ES_EXIT;
       RunZoneTwoSM(CurrentEvent);
       CurrentState = NextState;
       RunZoneTwoSM(EntryEventKind);
     }
     return(ReturnEvent);
}
Exemplo n.º 7
0
int main(void) {
	/********/
	/* INIT */
	/********/
	Pause(10000);
	SetUart0(115200, 2);
	InitialGpioState(PD8, 0, 2);
	InitialGpioState(PD9, 0, 2);
	
	myLCD.BacklightOn(0);
	myLCD.SetBacklight(255);
	
	SetTm2Decoder(1, 0, 65535); // IP
	SetTm3Decoder(3, 0, 65535); // WHEEL
	SetTm4Counter(0, 0, 7199, 10000);
	
	unsigned short T = 0,    PrevT = 0, Multiplier = 0;
	float          Time = 0, PrevTime = 0;
	short IPEncoder, MotorEncoder, ControllerState = 1;
	
	while(true) {
		Transmit("0");
		
		/********/
		/* TIME */
		/********/
		PrevT = T;
		GetTm4CounterValue(T);
		if(T < PrevT)
			Multiplier += 1;	
		PrevTime = Time;
		Time = Multiplier + float(T) / 10000;
		
		/*********/
		/* BLOCK */
		/*********/
		GetTm2DecoderValue(IPEncoder);
		GetTm3DecoderValue(MotorEncoder);	
		if(ControllerState == 0)
			SwingControl(IPEncoder, Time, ControllerState);
		else
			PIDControl(IPEncoder, MotorEncoder, Time - PrevTime);
		
		/********/
		/* SEND */
		/********/
		Transmit(int(Time*10000), false);
		Transmit(int(IPEncoder), false);
		Transmit(int(MotorEncoder), true);
		
		/*********/
		/* CATCH */
		/*********/
		short IPEncoderError = IPEncoder - ENCODER_RANGE / 2;
		if( (ControllerState == 1) && ((IPEncoder > 200) || (IPEncoder < -200)) )
			break;
		Pause(50);
	}
	StopMotor();	
	return 0;
}
Exemplo n.º 8
0
VOID NTAPI
ReadWritePassive(PDRIVE_INFO DriveInfo, PIRP Irp)
/*
 * FUNCTION: Handle the first phase of a read or write IRP
 * ARGUMENTS:
 *     DeviceObject: DeviceObject that is the target of the IRP
 *     Irp: IRP to process
 * RETURNS:
 *     STATUS_VERIFY_REQUIRED if the media has changed and we need the filesystems to re-synch
 *     STATUS_SUCCESS otherwise
 * NOTES:
 *     - Must be called at PASSIVE_LEVEL
 *     - This function is about 250 lines longer than I wanted it to be.  Sorry.
 *
 * DETAILS:
 *  This routine manages the whole process of servicing a read or write request.  It goes like this:
 *    1) Check the DO_VERIFY_VOLUME flag and return if it's set
 *    2) Check the disk change line and notify the OS if it's set and return
 *    3) Detect the media if we haven't already
 *    4) Set up DiskByteOffset, Length, and WriteToDevice parameters
 *    5) Get DMA map registers
 *    6) Then, in a loop for each track, until all bytes are transferred:
 *      a) Compute the current CHS to set the read/write head to
 *      b) Seek to that spot
 *      c) Compute the last sector to transfer on that track
 *      d) Map the transfer through DMA
 *      e) Send the read or write command to the controller
 *      f) Read the results of the command
 */
{
    PDEVICE_OBJECT DeviceObject = DriveInfo->DeviceObject;
    PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
    BOOLEAN WriteToDevice;
    ULONG Length;
    ULONG DiskByteOffset;
    KIRQL OldIrql;
    NTSTATUS Status;
    BOOLEAN DiskChanged;
    ULONG_PTR TransferByteOffset;
    UCHAR Gap;

    PAGED_CODE();

    TRACE_(FLOPPY, "ReadWritePassive called to %s 0x%x bytes from offset 0x%x\n",
           (Stack->MajorFunction == IRP_MJ_READ ? "read" : "write"),
           (Stack->MajorFunction == IRP_MJ_READ ? Stack->Parameters.Read.Length : Stack->Parameters.Write.Length),
           (Stack->MajorFunction == IRP_MJ_READ ? Stack->Parameters.Read.ByteOffset.u.LowPart :
            Stack->Parameters.Write.ByteOffset.u.LowPart));

    /* Default return codes */
    Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
    Irp->IoStatus.Information = 0;

    /*
     * Check to see if the volume needs to be verified.  If so,
     * we can get out of here quickly.
     */
    if(DeviceObject->Flags & DO_VERIFY_VOLUME && !(Stack->Flags & SL_OVERRIDE_VERIFY_VOLUME))
    {
        INFO_(FLOPPY, "ReadWritePassive(): DO_VERIFY_VOLUME set; Completing with  STATUS_VERIFY_REQUIRED\n");
        Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return;
    }

    /*
     * Check the change line, and if it's set, return
     */
    StartMotor(DriveInfo);
    if(HwDiskChanged(DeviceObject->DeviceExtension, &DiskChanged) != STATUS_SUCCESS)
    {
        WARN_(FLOPPY, "ReadWritePassive(): unable to detect disk change; Completing with STATUS_UNSUCCESSFUL\n");
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        StopMotor(DriveInfo->ControllerInfo);
        return;
    }

    if(DiskChanged)
    {
        INFO_(FLOPPY, "ReadWritePhase1(): signalling media changed; Completing with STATUS_MEDIA_CHANGED\n");

        /* The following call sets IoStatus.Status and IoStatus.Information */
        SignalMediaChanged(DeviceObject, Irp);

        /*
         * Guessing at something... see ioctl.c for more info
         */
        if(ResetChangeFlag(DriveInfo) == STATUS_NO_MEDIA_IN_DEVICE)
            Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        StopMotor(DriveInfo->ControllerInfo);
        return;
    }

    /*
     * Figure out the media type, if we don't know it already
     */
    if(DriveInfo->DiskGeometry.MediaType == Unknown)
    {
        if(RWDetermineMediaType(DriveInfo) != STATUS_SUCCESS)
        {
            WARN_(FLOPPY, "ReadWritePassive(): unable to determine media type; completing with STATUS_UNSUCCESSFUL\n");
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            StopMotor(DriveInfo->ControllerInfo);
            return;
        }

        if(DriveInfo->DiskGeometry.MediaType == Unknown)
        {
            WARN_(FLOPPY, "ReadWritePassive(): Unknown media in drive; completing with STATUS_UNRECOGNIZED_MEDIA\n");
            Irp->IoStatus.Status = STATUS_UNRECOGNIZED_MEDIA;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            StopMotor(DriveInfo->ControllerInfo);
            return;
        }
    }

    /* Set up parameters for read or write */
    if(Stack->MajorFunction == IRP_MJ_READ)
    {
        Length = Stack->Parameters.Read.Length;
        DiskByteOffset = Stack->Parameters.Read.ByteOffset.u.LowPart;
        WriteToDevice = FALSE;
    }
    else
    {
        Length = Stack->Parameters.Write.Length;
        DiskByteOffset = Stack->Parameters.Write.ByteOffset.u.LowPart;
        WriteToDevice = TRUE;
    }

    /*
     * FIXME:
     *   FloppyDeviceData.ReadWriteGapLength specify the value for the physical drive.
     *   We should set this value depend on the format of the inserted disk and possible
     *   depend on the request (read or write). A value of 0 results in one rotation
     *   between the sectors (7.2sec for reading a track).
     */
    Gap = DriveInfo->FloppyDeviceData.ReadWriteGapLength;

    /*
     * Set up DMA transfer
     *
     * This is as good of a place as any to document something that used to confuse me
     * greatly (and I even wrote some of the kernel's DMA code, so if it confuses me, it
     * probably confuses at least a couple of other people too).
     *
     * MmGetMdlVirtualAddress() returns the virtal address, as mapped in the buffer's original
     * process context, of the MDL.  In other words:  say you start with a buffer at address X, then
     * you build an MDL out of that buffer called Mdl. If you call MmGetMdlVirtualAddress(Mdl), it
     * will return X.
     *
     * There are two parameters that the function looks at to produce X again, given the MDL:  the
     * first is the StartVa, which is the base virtual address of the page that the buffer starts
     * in.  If your buffer's virtual address is 0x12345678, StartVa will be 0x12345000, assuming 4K pages
     * (which is (almost) always the case on x86).  Note well: this address is only valid in the
     * process context that you initially built the MDL from.  The physical pages that make up
     * the MDL might perhaps be mapped in other process contexts too (or even in the system space,
     * above 0x80000000 (default; 0xc0000000 on current Odyssey or /3GB Windows)), but it will
     * (possibly) be mapped at a different address.
     *
     * The second parameter is the ByteOffset.  Given an original buffer address of 0x12345678,
     * the ByteOffset would be 0x678.  Because MDLs can only describe full pages (and therefore
     * StartVa always points to the start address of a page), the ByteOffset must be used to
     * find the real start of the buffer.
     *
     * In general, if you add the StartVa and ByteOffset together, you get back your original
     * buffer pointer, which you are free to use if you're sure you're in the right process
     * context.  You could tell by accessing the (hidden and not-to-be-used) Process member of
     * the MDL, but in general, if you have to ask whether or not you are in the right context,
     * then you shouldn't be using this address for anything anyway.  There are also security implications
     * (big ones, really, I wouldn't kid about this) to directly accessing a user's buffer by VA, so
     * Don't Do That.
     *
     * There is a somewhat weird but very common use of the virtual address associated with a MDL
     * that pops up often in the context of DMA.  DMA APIs (particularly MapTransfer()) need to
     * know where the memory is that they should DMA into and out of.  This memory is described
     * by a MDL.  The controller eventually needs to know a physical address on the host side,
     * which is generally a 32-bit linear address (on x86), and not just a page address.  Therefore,
     * the DMA APIs look at the ByteOffset field of the MDL to reconstruct the real address that
     * should be programmed into the DMA controller.
     *
     * It is often the case that a transfer needs to be broken down over more than one DMA operation,
     * particularly when it is a big transfer and the HAL doesn't give you enough map registers
     * to map the whole thing at once.  Therefore, the APIs need a way to tell how far into the MDL
     * they should look to transfer the next chunk of bytes.  Now, Microsoft could have designed
     * MapTransfer to take a  "MDL offset" argument, starting with 0, for how far into the buffer to
     * start, but it didn't.  Instead, MapTransfer asks for the virtual address of the MDL as an "index" into
     * the MDL.  The way it computes how far into the page to start the transfer is by masking off all but
     * the bottom 12 bits (on x86) of the number you supply as the CurrentVa and using *that* as the
     * ByteOffset instead of the one in the MDL.  (OK, this varies a bit by OS and version, but this
     * is the effect).
     *
     * In other words, you get a number back from MmGetMdlVirtualAddress that represents the start of your
     * buffer, and you pass it to the first MapTransfer call.  Then, for each successive operation
     * on the same buffer, you increment that address to point to the next spot in the MDL that
     * you want to DMA to/from.  The fact that the virtual address you're manipulating is probably not
     * mapped into the process context that you're running in is irrelevant, since it's only being
     * used to index into the MDL.
     */

    /* Get map registers for DMA */
    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
    Status = IoAllocateAdapterChannel(DriveInfo->ControllerInfo->AdapterObject, DeviceObject,
                                      DriveInfo->ControllerInfo->MapRegisters, MapRegisterCallback, DriveInfo->ControllerInfo);
    KeLowerIrql(OldIrql);

    if(Status != STATUS_SUCCESS)
    {
        WARN_(FLOPPY, "ReadWritePassive(): unable allocate an adapter channel; completing with STATUS_UNSUCCESSFUL\n");
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        StopMotor(DriveInfo->ControllerInfo);
        return ;
    }


    /*
     * Read from (or write to) the device
     *
     * This has to be called in a loop, as you can only transfer data to/from a single track at
     * a time.
     */
    TransferByteOffset = 0;
    while(TransferByteOffset < Length)
    {
        UCHAR Cylinder;
        UCHAR Head;
        UCHAR StartSector;
        ULONG CurrentTransferBytes;
        UCHAR CurrentTransferSectors;

        INFO_(FLOPPY, "ReadWritePassive(): iterating in while (TransferByteOffset = 0x%x of 0x%x total) - allocating %d registers\n",
              TransferByteOffset, Length, DriveInfo->ControllerInfo->MapRegisters);

        KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);

        /*
         * Compute starting CHS
         */
        if(RWComputeCHS(DriveInfo, DiskByteOffset+TransferByteOffset, &Cylinder, &Head, &StartSector) != STATUS_SUCCESS)
        {
            WARN_(FLOPPY, "ReadWritePassive(): unable to compute CHS; completing with STATUS_UNSUCCESSFUL\n");
            RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            StopMotor(DriveInfo->ControllerInfo);
            return;
        }

        /*
         * Seek to the right track
         */
        if(!DriveInfo->ControllerInfo->ImpliedSeeks)
        {
            if(RWSeekToCylinder(DriveInfo, Cylinder) != STATUS_SUCCESS)
            {
                WARN_(FLOPPY, "ReadWritePassive(): unable to seek; completing with STATUS_UNSUCCESSFUL\n");
                RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
                IoCompleteRequest(Irp, IO_NO_INCREMENT);
                StopMotor(DriveInfo->ControllerInfo);
                return ;
            }
        }

        /*
         * Compute last sector
         *
         * We can only ask for a transfer up to the end of the track.  Then we have to re-seek and do more.
         * TODO: Support the MT bit
         */
        INFO_(FLOPPY, "ReadWritePassive(): computing number of sectors to transfer (StartSector 0x%x): ", StartSector);

        /* 1-based sector number */
        if( (((DriveInfo->DiskGeometry.TracksPerCylinder - Head) * DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) + 1 ) <
                (Length - TransferByteOffset) / DriveInfo->DiskGeometry.BytesPerSector)
        {
            CurrentTransferSectors = (UCHAR)((DriveInfo->DiskGeometry.TracksPerCylinder - Head) * DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) + 1;
        }
        else
        {
            CurrentTransferSectors = (UCHAR)((Length - TransferByteOffset) / DriveInfo->DiskGeometry.BytesPerSector);
        }

        INFO_(FLOPPY, "0x%x\n", CurrentTransferSectors);

        CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;

        /*
         * Adjust to map registers
         * BUG: Does this take into account page crossings?
         */
        INFO_(FLOPPY, "ReadWritePassive(): Trying to transfer 0x%x bytes\n", CurrentTransferBytes);

        ASSERT(CurrentTransferBytes);

        if(BYTES_TO_PAGES(CurrentTransferBytes) > DriveInfo->ControllerInfo->MapRegisters)
        {
            CurrentTransferSectors = (UCHAR)((DriveInfo->ControllerInfo->MapRegisters * PAGE_SIZE) /
                                             DriveInfo->DiskGeometry.BytesPerSector);

            CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;

            INFO_(FLOPPY, "ReadWritePassive: limiting transfer to 0x%x bytes (0x%x sectors) due to map registers\n",
                  CurrentTransferBytes, CurrentTransferSectors);
        }

        /* set up this round's dma operation */
        /* param 2 is ReadOperation --> opposite of WriteToDevice that IoMapTransfer takes.  BAD MS. */
        KeFlushIoBuffers(Irp->MdlAddress, !WriteToDevice, TRUE);

        IoMapTransfer(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress,
                      DriveInfo->ControllerInfo->MapRegisterBase,
                      (PVOID)((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress) + TransferByteOffset),
                      &CurrentTransferBytes, WriteToDevice);

        /*
         * Read or Write
         */
        KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);

        /* Issue the read/write command to the controller.  Note that it expects the opposite of WriteToDevice. */
        if(HwReadWriteData(DriveInfo->ControllerInfo, !WriteToDevice, DriveInfo->UnitNumber, Cylinder, Head, StartSector,
                           DriveInfo->BytesPerSectorCode, DriveInfo->DiskGeometry.SectorsPerTrack, Gap, 0xff) != STATUS_SUCCESS)
        {
            WARN_(FLOPPY, "ReadWritePassive(): HwReadWriteData returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
            RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            StopMotor(DriveInfo->ControllerInfo);
            return ;
        }

        INFO_(FLOPPY, "ReadWritePassive(): HwReadWriteData returned -- waiting on event\n");

        /*
         * At this point, we block and wait for an interrupt
         * FIXME: this seems to take too long
         */
        WaitForControllerInterrupt(DriveInfo->ControllerInfo);

        /* Read is complete; flush & free adapter channel */
        IoFlushAdapterBuffers(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress,
                              DriveInfo->ControllerInfo->MapRegisterBase,
                              (PVOID)((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress) + TransferByteOffset),
                              CurrentTransferBytes, WriteToDevice);

        /* Read the results from the drive */
        if(HwReadWriteResult(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
        {
            WARN_(FLOPPY, "ReadWritePassive(): HwReadWriteResult returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
            HwDumpRegisters(DriveInfo->ControllerInfo);
            RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            StopMotor(DriveInfo->ControllerInfo);
            return ;
        }

        TransferByteOffset += CurrentTransferBytes;
    }

    RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);

    /* That's all folks! */
    INFO_(FLOPPY, "ReadWritePassive(): success; Completing with STATUS_SUCCESS\n");
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = Length;
    IoCompleteRequest(Irp, IO_DISK_INCREMENT);
    StopMotor(DriveInfo->ControllerInfo);
}