MODULE UsbMouse;  (** AUTHOR "staubesv"; PURPOSE "USB Mouse Driver"; *)
(**
 * Bluebottle USB Mouse Driver (HID boot protocol)
 *
 * Usage:
 *	UsbMouse.Install ~ loads this driver
 *	SystemTools.Free UsbMouse ~ unloads this driver
 *
 *	The HID boot protocol for USB mice supports 3 buttons and 2 axes.
 *
 * References:
 *	Device Class Definition for Human Interface Devices (HID), Version 1.11, 27.06.2001, www.usb.org
 *
 * History:
 *	01.12.2005	Added MouseSpeed & MouseAcceleration (staubesv)
 *	10.10.2006	Adapted to UsbHid (staubesv)
 *	28.02.2007	Removed mouse wheel hack since new HID driver can correctly handle it (staubesv)
 *)

IMPORT SYSTEM, KernelLog, Modules, Inputs, Usbdi, UsbHid;

CONST

	Name = "UsbMouse";
	Description = "HID boot protocol mouse driver";

	(* Mouse Configuration *)
	MouseSpeed = 50;
	MouseAcceleration = 0;

	Debug = TRUE;

TYPE

	MouseDriver= OBJECT (UsbHid.HidDriver);
	VAR
		buffer : Usbdi.BufferPtr;
		pipe : Usbdi.Pipe;

		lastDx, lastDy : LONGINT;
		accelX, accelY : REAL;

		PROCEDURE HandleEvent(status : Usbdi.Status; actLen : LONGINT);
		VAR mm : Inputs.MouseMsg; dx, dy : LONGINT;
		BEGIN
			IF (status = Usbdi.Ok) OR ((status = Usbdi.ShortPacket) & (actLen >= 3))  THEN
				dx := SYSTEM.VAL(SHORTINT, buffer[1]);
				dy := SYSTEM.VAL(SHORTINT, buffer[2]);

				accelX := 1.0 + ABS(dx - lastDx) / 128 * MouseAcceleration;
				accelY := 1.0 + ABS(dy - lastDy) / 128 * MouseAcceleration;

				lastDx := dx;
				lastDy := dy;

				mm.dx :=ENTIER(MouseSpeed / 50.0 *  dx * accelX);
				mm.dy := ENTIER(MouseSpeed / 50.0 * dy * accelY);

				IF (SYSTEM.VAL(SET, buffer[0]) * {0}) # {} THEN mm.keys := mm.keys + {0}; END;
				IF (SYSTEM.VAL(SET, buffer[0]) * {1}) # {} THEN mm.keys := mm.keys + {2}; END;
				IF (SYSTEM.VAL(SET, buffer[0]) * {2}) # {} THEN mm.keys := mm.keys + {1}; END;

				Inputs.mouse.Handle(mm);
				status := pipe.Transfer(pipe.maxPacketSize, 0, buffer^);
			ELSE
				IF status = Usbdi.Stalled THEN
					IF pipe.ClearHalt() THEN
						IF Debug THEN KernelLog.String("UsbMouse: Stall on Interrupt Pipe cleared."); KernelLog.Ln; END;
						status := pipe.Transfer(pipe.maxPacketSize, 0, buffer^); (* ignore status *)
					ELSE
						IF Debug THEN KernelLog.String("UsbMouse: Couldn't clear stall on interrupt pipe. Abort."); KernelLog.Ln; END;
						device.FreePipe(pipe);
					END;
				END;
			END;
		END HandleEvent;

		PROCEDURE Connect() : BOOLEAN;
		VAR endpoint, i : LONGINT; status : Usbdi.Status;
		BEGIN
			(* Set the HID boot protocol *)
			IF SetProtocol(UsbHid.BootProtocol) = FALSE THEN
				IF Debug THEN KernelLog.String("UsbMouse: Error: Cannot set boot protocol."); KernelLog.Ln; END;
				RETURN FALSE
			END;

			(* Look for the first interrupt IN endpoint of this device *)
			LOOP
				IF i >= LEN(interface.endpoints) THEN EXIT; END;
				IF interface.endpoints[i].type = Usbdi.InterruptIn THEN
					endpoint := interface.endpoints[i].bEndpointAddress;
					EXIT;
				END;
				INC(i);
			END;

			IF endpoint = 0 THEN
				IF Debug THEN KernelLog.String("UsbMouse: No interrupt IN endpoint found."); KernelLog.Ln; END;
				RETURN FALSE;
			END;

			pipe := device.GetPipe(endpoint);
			IF pipe = NIL THEN RETURN FALSE END;

			NEW(buffer, pipe.maxPacketSize);

			pipe.SetTimeout(0);
			pipe.SetCompletionHandler(HandleEvent);

			status := pipe.Transfer(pipe.maxPacketSize, 0, buffer^); (* ignore res *)
			RETURN TRUE;
		END Connect;

		PROCEDURE Disconnect;
		BEGIN
			KernelLog.String("USB mouse disconnected."); KernelLog.Ln;
		END Disconnect;

	END MouseDriver;

PROCEDURE Probe(dev : Usbdi.UsbDevice; id : Usbdi.InterfaceDescriptor) : Usbdi.Driver;
VAR driver : MouseDriver;
BEGIN
	IF id.bInterfaceClass # 3 THEN RETURN NIL END; (* HID class *)
	IF id.bInterfaceSubClass # 1 THEN RETURN NIL END; (* Boot protocol subclass *)
	IF id.bInterfaceProtocol # 2 THEN RETURN NIL END; (* Mouse *)
	NEW(driver); RETURN driver;
END Probe;

PROCEDURE Install*;
END Install;

PROCEDURE Cleanup;
BEGIN
	Usbdi.drivers.Remove(Name);
END Cleanup;

BEGIN
	Modules.InstallTermHandler(Cleanup);
	Usbdi.drivers.Add(Probe, Name, Description, 10)
END UsbMouse.

UsbMouse.Install ~  SystemTools.Free UsbMouse ~