Source to bsd/dev/ppc/drvUSBCMD/mouse/MouseModule.c
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @[email protected]
*
* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
* Reserved. This file contains Original Code and/or Modifications of
* Original Code as defined in and that are subject to the Apple Public
* Source License Version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. Please obtain a copy of the
* License at http://www.apple.com/publicsource and read it before using
* this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License."
*
* @[email protected]
*/
/*
File: MouseModule.c
Contains: HID Module for USB Mouse
Version: xxx put version here xxx
Copyright: © 1997-1998 by Apple Computer, Inc., all rights reserved.
File Ownership:
DRI: Craig Keithley
Other Contact: xxx put other contact here xxx
Technology: USB on Mac
Writers:
(bwm) Bruce Merritt
(BT) Barry Twycross
(DF) David Ferguson
(CJK) Craig Keithley
Change History (most recent first):
<USB53> 10/28/98 CJK [2282187] changed to use interface number from the interface
descriptor supplied by the composite driver. See the keyboard
driver (there's a similar change in there as well).
<USB52> 10/21/98 DF Change LMGetMBState to it's new name LMGetMouseButtonState, now
we can include LowMem.h instead of LowMemPriv.h
<USB51> 10/21/98 bwm [2229080] NewWorld: whenever a mouse driver fails in an
interrupt pipe read, force the mouse button up to prevent a
possible hung system: we do this in case someone was holding the
mouse button down while the mouse was unplugged, leaving MacOS
is a somewhat constipated state.
<USB50> 8/25/98 BT Isoc name changes
<USB49> 6/22/98 CJK move cursordevice setup to InterfaceEntry, which is guarenteed
to be at task time
<USB48> 6/22/98 CJK rename FindNextEndpointDescriptor to
FindNextEndpointDescriptorImmediate
<USB47> 6/18/98 CJK update to use new style of error handling implemented in
keyboard module.
<USB46> 6/16/98 CJK change FindHIDInterfaceByProtocol to FindHIDInterfaceByNumber
<USB45> 6/15/98 CJK change transaction depth error messages so that they output the
refcon (current state machine stage)
<USB44> 6/11/98 CJK Remove driverentry routine (not needed or used).
<USB43> 6/8/98 CJK eliminate PBVERSION1 define
<USB42> 6/5/98 CJK remove ExpertStatus; too many clutter up expert log
<USB41> 5/26/98 DF Look for the incorrect vendorID (0x1452), when checking for
version 0.04 Rudi
<USB40> 5/26/98 CJK add prototype for InitParamBlock
<USB39> 5/22/98 CJK add check for the 0.04 version of the Rudi mouse. Do a SetIdle
of 50ms if found.
<USB38> 5/20/98 BT Set version 1 PB compatability
<USB37> 5/20/98 CJK change driver name from USBMouseModule to USBHIDMouseModule
<USB36> 5/15/98 CJK Things seem to have stabilized; no longer need to try various
different int read report sizes until we find the one that
works. Just use the maxpacketsize value.
<USB35> 5/13/98 CJK move DPI setting to interface entry code
<USB34> 5/11/98 CJK setidle to 0
<USB33> 5/4/98 CJK implement adaptive reqCount. This way, when a mouse gives back
an OverRun error, the mouse driver automatically increases the
reqCount value (never exceeding the maxPacketSize).
<USB32> 5/4/98 CJK recover from repeated stalls when doing a setidle. added detect
for MS mouse, at which we ask for a 4 byte report (rather than a
3 byte report)
<USB31> 4/29/98 CJK add break where needed
<USB30> 4/26/98 CJK Add back in SetIdle. This seems to solve the stall problem and
double click problem.
<USB29> 4/26/98 CJK change so that pipe stall clear is only called if happens as a
result of a read or open pipe. Also cleaned up transaction
depth error messages so that the user can tell if the problem
occurred in the completion or initiation routines.
<USB28> 4/24/98 CJK add call to clear pipestall by reference
<USB27> 4/24/98 CJK add call to turn on demo mode for shim-less environments.
<USB26> 4/23/98 CJK add status message to show who's driver this is...
<USB25> 4/23/98 CJK Reworked mouse module dramatically. It now 1) gets the full
configuration and looks for the mouse HID interface, 2) uses
this mouse HID interface to find the interrupt endpoint, 3) uses
the interrupt endpoint descriptor to open the the interrupt
pipe. Of note is that all the stuff to support the non-spec'd
mice has been removed. It will be added back in, either in a
different mouse module, or within this mouse module, as further
testing demands.
<USB24> 4/14/98 CJK Change to use driverservices strcpy
<USB23> 4/9/98 CJK change driver services to be a externally supplied header file
(using < & >) rather than quotes.
<USB22> 4/9/98 DF Massaged to work with MasterInterfaces
<21> 4/8/98 CJK correct mouse status & fatal error messages (should read
"mouse", not Mouse)
<20> 4/8/98 CJK add initialization of saved interrupt routine ptr.
<19> 3/31/98 CJK change error messages
<18> 3/26/98 CJK fix delay callback depth problem. Clean up USBExpertFatal
messages.
<17> 3/25/98 CJK Add support for interrupt transactions
<16> 3/17/98 CJK Replace "};" with just "}". Add parens to pragma unused
variables.
<15> 3/5/98 CJK I feel like a darn ping pong ball, changing the X-Delta &
Y-Delta checks back and forth. But now that the mouse 'skip'
problem has been resolved, let's see if I can't make both the
BTC PS/2 mouse AND the Logitech mouse work properly. See Radar
2216777.
<14> 3/3/98 CJK change poll rate to 20ms
<13> 3/2/98 CJK change back to check for non-zero
<12> 3/2/98 CJK Add include of USBHIDModules.h. Remove include of HIDEmulation.h
<11> 2/19/98 CJK change polling rate back to 10ms.
<10> 2/19/98 CJK change back to notifying the MacOS when the deltas change from
report to report, not just when they're non-zero.
<9> 2/18/98 CJK Implement Ferg's suggestion that we notify the HID Emulation
routine if the X & Y deltas are non-zero.
<8> 2/17/98 CJK remove "" from expert fatal error calls.
<7> 2/11/98 CJK Change debugstr to expertfatal calls
<6> 2/11/98 CJK remove unneeded states
<5> 2/10/98 CJK change hid byte count from 8 to 3. No need to look at all eight
bytes as mouse reports only consist of 3 bytes.
<4> 2/10/98 CJK Remove keyboard related code
<3> 2/9/98 CJK change first state to setprotocol (when entering driver via
InterfaceEntry
<2> 2/9/98 CJK Add InterfaceEntry function
<1> 2/9/98 CJK First time check in. Cloned from CompoundDriver.
*/
//#include <Types.h>
//#include <Devices.h>
//#include <processes.h>
#include "../driverservices.h"
#include "../USB.h"
//#include <LowMem.h>
#include "MouseModule.h"
usbMousePBStruct myMousePB;
static void InitParamBlock(USBDeviceRef theDeviceRef, USBPB * paramblock)
{
paramblock->usbReference = theDeviceRef;
paramblock->pbVersion = kUSBCurrentPBVersion;
paramblock->usb.cntl.WIndex = 0;
paramblock->usbBuffer = nil;
paramblock->usbStatus = noErr;
paramblock->usbReqCount = 0;
paramblock->usb.cntl.WValue = 0;
paramblock->usbFlags = 0;
}
static Boolean immediateError(OSStatus err)
{
return((err != kUSBPending) && (err != noErr) );
}
void MouseModuleInitiateTransaction(USBPB *pb)
{
register usbMousePBStruct *pMousePB;
OSStatus myErr;
pMousePB = (usbMousePBStruct *)(pb);
pMousePB->transDepth++;
if (pMousePB->transDepth < 0)
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: transDepth < 0 (initiation)", pMousePB->pb.usbRefcon );
}
if (pMousePB->transDepth > 1)
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: transDepth > 1 (initiation)", pMousePB->pb.usbRefcon );
}
switch(pMousePB->pb.usbRefcon & ~kRetryTransaction)
{
case kGetFullConfiguration:
InitParamBlock(pMousePB->deviceRef, &pMousePB->pb);
pMousePB->pb.usbRefcon |= kCompletionPending;
pMousePB->pb.usbCompletion = (USBCompletion)TransactionCompletionProc;
myErr = USBGetFullConfigurationDescriptor(pb);
if(immediateError(myErr))
{
USBExpertFatalError(pMousePB->pb.usbReference, kUSBInternalErr, "USBHIDMouseModule: kGetFullConfiguration (ImmediateError)", myErr);
}
break;
case kFindInterfaceAndSetProtocol:
myErr = FindHIDInterfaceByNumber(pMousePB->pFullConfigDescriptor, pMousePB->interfaceDescriptor.interfaceNumber, &pMousePB->pInterfaceDescriptor ); // find the HID interface
if ((pMousePB->pInterfaceDescriptor == NULL) || (myErr != noErr))
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: Interface not found", myErr);
pMousePB->pb.usbRefcon = kReturnFromDriver;
}
InitParamBlock(pMousePB->deviceRef, &pMousePB->pb);
pMousePB->pb.usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);
pMousePB->pb.usb.cntl.BRequest = kHIDRqSetProtocol;
pMousePB->pb.usb.cntl.WValue = kHIDBootProtocolValue;
pMousePB->pb.usb.cntl.WIndex = pMousePB->interfaceDescriptor.interfaceNumber;
pMousePB->pb.usbCompletion = (USBCompletion)TransactionCompletionProc;
pMousePB->pb.usbRefcon |= kCompletionPending;
myErr = USBDeviceRequest(&pMousePB->pb);
if (immediateError(myErr))
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: kSetProtocol (ImmediateError)", myErr);
}
break;
case kSetIdleRequest:
InitParamBlock(pMousePB->deviceRef, &pMousePB->pb);
pMousePB->pb.usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);
pMousePB->pb.usb.cntl.BRequest = kHIDRqSetIdle;
if ((myMousePB.deviceDescriptor.vendor == USB_CONSTANT16(0x1452)) &&
(myMousePB.deviceDescriptor.product == USB_CONSTANT16(0x0301)) &&
(pMousePB->deviceDescriptor.devRel == USB_CONSTANT16(0x0004)))
{
pMousePB->pb.usb.cntl.WValue = ((50/4)<<8); // force a read completion if idle for more than 50ms
}
else
{
pMousePB->pb.usb.cntl.WValue = 0; // Turn off SetIdle time (set it to 0ms)
}
pMousePB->pb.usb.cntl.WIndex = pMousePB->interfaceDescriptor.interfaceNumber;
pMousePB->pb.usbCompletion = (USBCompletion)TransactionCompletionProc;
pMousePB->pb.usbRefcon |= kCompletionPending;
myErr = USBDeviceRequest(&pMousePB->pb);
if(immediateError(myErr))
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: kSetIdleRequest - immediate error", myErr);
}
break;
case kFindAndOpenInterruptPipe:
pMousePB->pb.usbClassType = kUSBInterrupt;
pMousePB->pb.usbOther = 0;
pMousePB->pb.usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBIn, kUSBClass, kUSBInterface);
pMousePB->pb.usbFlags = kUSBIn;
pMousePB->pb.usbBuffer = pMousePB->pInterfaceDescriptor;
pMousePB->pb.usbReqCount = (UInt8*)pMousePB->pInterfaceDescriptor - (UInt8*)pMousePB->pFullConfigDescriptor;
myErr = USBFindNextEndpointDescriptorImmediate( &pMousePB->pb );
if((immediateError(myErr)) || (pMousePB->pb.usbBuffer == nil))
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: Endpoint not found", myErr);
pMousePB->pb.usbRefcon = kReturnFromDriver;
}
else
{
pMousePB->pEndpointDescriptor = (USBEndPointDescriptorPtr) pMousePB->pb.usbBuffer;
InitParamBlock(pMousePB->deviceRef, &pMousePB->pb);
pMousePB->pb.usbFlags = kUSBIn;
pMousePB->pb.usb.cntl.WValue = USBToHostWord(pMousePB->pEndpointDescriptor->maxPacketSize);
pMousePB->pb.usbClassType = kUSBInterrupt;
pMousePB->pb.usbOther = (pMousePB->pEndpointDescriptor->endpointAddress & 0x0f);
pMousePB->pb.usbRefcon |= kCompletionPending;
pMousePB->pb.usbCompletion = (USBCompletion)TransactionCompletionProc;
myErr = USBOpenPipe( &pMousePB->pb );
if(immediateError(myErr))
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: USBOpenPipe Failed (ImmediateError)", myErr);
}
}
break;
case kReadInterruptPipe:
InitParamBlock(pMousePB->pipeRef, &pMousePB->pb);
pMousePB->pb.usbBuffer = (Ptr)pMousePB->hidReport;
pMousePB->pb.usbReqCount = USBToHostWord(pMousePB->pEndpointDescriptor->maxPacketSize);
pMousePB->pb.usb.cntl.WIndex = pMousePB->interfaceDescriptor.interfaceNumber;
pMousePB->pb.usbCompletion = (USBCompletion)TransactionCompletionProc;
pMousePB->pb.usbRefcon |= kCompletionPending;
myErr = USBIntRead(&pMousePB->pb);
if(immediateError(myErr))
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: Read Interrupt Pipe (ImmediateError)", myErr);
}
break;
default:
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: Transaction initiated with bad refcon value", pMousePB->pb.usbRefcon);
pMousePB->pb.usbRefcon = kUndefined + kReturnFromDriver;
break;
}
// At this point the control is returned to the system. If a USB transaction
// has been initiated, then it will call the Complete procs
// (below) to handle the results of the transaction.
}
void TransactionCompletionProc(USBPB *pb)
{
register usbMousePBStruct *pMousePB;
unsigned char * errstring;
USBPipeState pipeState;
kprintf("TransactionCompletionProc is called for Mouse read\n");
pMousePB = (usbMousePBStruct *)(pb);
pMousePB->transDepth--;
if (pMousePB->transDepth < 0)
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: transDepth < 0 (completion)", pMousePB->pb.usbRefcon );
}
if (pMousePB->transDepth > 1)
{
USBExpertFatalError(pMousePB->deviceRef, kUSBInternalErr, "USBHIDMouseModule: transDepth > 1 (completion)", pMousePB->pb.usbRefcon );
}
if(pMousePB->pb.usbStatus != noErr) // was there an error?
{
switch(pMousePB->pb.usbRefcon & 0x0fff) // yes, so show where the error occurred
{
case kGetFullConfiguration: errstring = "USBHIDMouseModule: Error during GetFullConfiguration"; break;
case kFindInterfaceAndSetProtocol: errstring = "USBHIDMouseModule: Error during FindInterfaceAndSetProtocol"; break;
case kSetIdleRequest: errstring = "USBHIDMouseModule: Error during kSetIdleRequest"; break;
case kFindAndOpenInterruptPipe: errstring = "USBHIDMouseModule: Error during FindAndOpenInterruptPipe"; break;
case kReadInterruptPipe:
{
errstring = "USBHIDMouseModule: Error during ReadInterruptPipe";
//naga LMSetMouseButtonState(0x80); // release any possibly held-down mouse button
break;
}
default: errstring = "USBHIDMouseModule: Error occurred, but state is unknown"; break;
};
USBExpertFatalError(pMousePB->deviceRef, pMousePB->pb.usbStatus, errstring, (pMousePB->pb.usbRefcon & 0x0fff));
pMousePB->pb.usbRefcon &= ~(kCompletionPending + kReturnFromDriver); // set up to retry the transaction
pMousePB->pb.usbRefcon |= kRetryTransaction;
pMousePB->retryCount--;
if ((pMousePB->retryCount == 1) && ((pMousePB->pb.usbRefcon&pMousePB->pb.usbRefcon & 0x0fff) == kSetIdleRequest))
{
USBExpertStatus(pMousePB->deviceRef, "USBHIDMouseModule: Device doesn't accept SetIdle", pMousePB->deviceRef);
pMousePB->pb.usbRefcon = kFindAndOpenInterruptPipe;
pMousePB->pb.usbStatus = noErr;
}
else
{
if ((!pMousePB->retryCount) || (pMousePB->pb.usbStatus == kUSBAbortedError)) // have we exhausted the retries?
{ // or received an abort?
USBExpertStatus(pMousePB->deviceRef, "USBHIDMouseModule: Pipe abort or unable to recover from error", pMousePB->deviceRef);
pMousePB->pb.usbRefcon = kReturnFromDriver; // if so, just exit.
}
else // if it didn't abort and there's retries left, then...
{
if (pMousePB->pipeRef) // check if the pipe is open.
{
USBGetPipeStatusByReference(pMousePB->pipeRef, &pipeState); // yes, so what it's state?
if (pipeState != kUSBActive) // if it's not active, try to clear it. It might be stalled...
{
USBExpertStatus(pMousePB->deviceRef, "USBHIDMouseModule: Pipe is open and stalled, clearing stall...", pMousePB->deviceRef);
USBClearPipeStallByReference(pMousePB->pipeRef);
}
}
}
}
}
else
{
kprintf(".");
//Adam added this 11/30
pMousePB->pb.usbRefcon &= ~kRetryTransaction;
pMousePB->retryCount = kMouseRetryCount;
}
if (pMousePB->pb.usbRefcon & kCompletionPending)
{
pMousePB->pb.usbRefcon &= ~(kCompletionPending + kReturnFromDriver);
switch(pMousePB->pb.usbRefcon)
{
case kGetFullConfiguration:
pMousePB->pFullConfigDescriptor = pMousePB->pb.usbBuffer;
if (pMousePB->pFullConfigDescriptor == nil)
{
USBExpertFatalError(pMousePB->pb.usbReference, kUSBInternalErr, "USBHIDMouseModule: USBGetFullConfiguration - pointer is nil", pMousePB->pb.usbRefcon);
pMousePB->pb.usbRefcon = kReturnFromDriver;
}
else
{
pMousePB->pb.usbRefcon = kFindInterfaceAndSetProtocol;
}
break;
case kFindInterfaceAndSetProtocol:
pMousePB->pb.usbRefcon = kSetIdleRequest;
break;
case kSetIdleRequest:
pMousePB->pb.usbRefcon = kFindAndOpenInterruptPipe;
break;
case kFindAndOpenInterruptPipe:
pMousePB->pipeRef = pMousePB->pb.usbReference;
pMousePB->pb.usbRefcon = kReadInterruptPipe;
break;
case kReadInterruptPipe:
NotifyRegisteredHIDUser(pMousePB->hidDeviceType, pMousePB->hidReport);
pMousePB->pb.usbRefcon = kReadInterruptPipe;
break;
}
}
if (!(pMousePB->pb.usbRefcon & kReturnFromDriver))
MouseModuleInitiateTransaction(pb);
}
void InterfaceEntry(UInt32 interfacenum, USBInterfaceDescriptorPtr pInterfaceDescriptor, USBDeviceDescriptorPtr pDeviceDescriptor, USBDeviceRef device)
{
#pragma unused (interfacenum)
static Boolean beenThereDoneThat = false;
if(beenThereDoneThat)
{
USBExpertFatalError(device, kUSBInternalErr, "USBHIDMouseModule is not reentrant", 0);
return;
}
//Hot Plug n Play
//naga beenThereDoneThat = true;
// DebugStr("In Mouse Interface Entry routine");
kprintf("In Mouse Interface Entry routine");
myMousePB.deviceDescriptor = *pDeviceDescriptor;
myMousePB.interfaceDescriptor = *pInterfaceDescriptor;
myMousePB.transDepth = 0;
myMousePB.retryCount = kMouseRetryCount;
myMousePB.pSHIMInterruptRoutine = nil;
myMousePB.pSavedInterruptRoutine = nil;
myMousePB.deviceRef = device;
myMousePB.interfaceRef = nil;
myMousePB.pipeRef = nil;
InitParamBlock(device, &myMousePB.pb);
myMousePB.pb.usbReference = device;
myMousePB.pb.pbLength = sizeof(usbMousePBStruct);
myMousePB.pb.usbRefcon = kGetFullConfiguration;
if ((myMousePB.deviceDescriptor.vendor == USB_CONSTANT16(0x046e)) &&
(myMousePB.deviceDescriptor.product == USB_CONSTANT16(0x6782)))
{
myMousePB.unitsPerInch = (Fixed)(100<<16);
}
else
{
myMousePB.unitsPerInch = (Fixed)(400<<16);
}
myMousePB.pCursorDeviceInfo = 0;
USBHIDControlDevice(kHIDEnableDemoMode,0);
MouseModuleInitiateTransaction(&myMousePB.pb);
}
/* naga */
OSErr CursorDeviceNewDevice(CursorDevicePtr * ourDevice)
{
// TWOWORDINLINE(0x700C, 0xAADB);
return 0;
}
OSErr CursorDeviceDisposeDevice(CursorDevicePtr ourDevice)
{
// TWOWORDINLINE(0x700D, 0xAADB);
return 0;
}
OSErr CursorDeviceMove(CursorDevicePtr ourDevice, long deltaX, long deltaY)
{
//TWOWORDINLINE(0x7000, 0xAADB);
return 0;
}
OSErr CursorDeviceMoveTo(CursorDevicePtr ourDevice, long absX, long absY)
{
//TWOWORDINLINE(0x7001, 0xAADB);
return 0;
}
OSErr CursorDeviceFlush (CursorDevicePtr ourDevice)
{
//TWOWORDINLINE(0x7002, 0xAADB);
return 0;
}
OSErr CursorDeviceButtons(CursorDevicePtr ourDevice, short buttons)
{
//TWOWORDINLINE(0x7003, 0xAADB);
return 0;
}
OSErr CursorDeviceButtonDown(CursorDevicePtr ourDevice)
{
return 0;
}
OSErr CursorDeviceButtonUp(CursorDevicePtr ourDevice)
{
//TWOWORDINLINE(0x7005, 0xAADB);
return 0;
}
OSErr CursorDeviceButtonOp(CursorDevicePtr ourDevice, short buttonNumber, ButtonOpcode opcode, long data)
{
//TWOWORDINLINE(0x7006, 0xAADB);
return 0;
}
OSErr CursorDeviceSetButtons(CursorDevicePtr ourDevice, short numberOfButtons)
{
//TWOWORDINLINE(0x7007, 0xAADB);
return 0;
}
OSErr CursorDeviceSetAcceleration(CursorDevicePtr ourDevice,Fixed acceleration)
{
//TWOWORDINLINE(0x7008, 0xAADB);
return 0;
}
OSErr CursorDeviceDoubleTime(CursorDevicePtr ourDevice, long durationTicks)
{
//TWOWORDINLINE(0x7009, 0xAADB);
return 0;
}
OSErr CursorDeviceUnitsPerInch(CursorDevicePtr ourDevice, Fixed resolution)
{
// TWOWORDINLINE(0x700A, 0xAADB);
return 0;
}
OSErr CursorDeviceNextDevice(CursorDevicePtr * ourDevice)
{
//TWOWORDINLINE(0x700B, 0xAADB);
return 0;
}