Source to bsd/dev/ppc/drvUSBCMD/kbd/KeyboardModule.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "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."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
	File:		KeyboardModule.c

	Contains:	HID Module for USB Keyboard

	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:			xxx put technology here xxx

	Writers:

		(BT)	Barry Twycross
		(DF)	David Ferguson
		(CJK)	Craig Keithley

	Change History (most recent first):

	 <USB35>	10/28/98	CJK		[2281764]  use the supplied interface descriptor (for the ,
									rather than searching the configuration descriptor and using the
									one found.
	 <USB34>	 8/25/98	BT		Isoc name changes
	 <USB33>	 6/22/98	CJK		rename FindNextEndpointDescriptor to
									FindNextEndpointDescriptorImmediate (no functional change, just
									a name change)
	 <USB32>	 6/17/98	CJK		leave retries in place for non-pipe stall situations. That's the
									most compatible with the earlier versions of the driver... It
									feels pretty risky disabling that functionality this close to
									intro.
	 <USB31>	 6/17/98	CJK		change to check pipe status, if stalled, then try to clear it.
	 <USB30>	 6/17/98	CJK		change error handling to not retry on some types of errors
	 <USB29>	 6/16/98	CJK		change FindHIDInterfaceByProtocol to FindHIDInterfaceByNumber
	 <USB28>	 6/15/98	CJK		Change "USL Reported an error" message so that it shows the
									refcon (and not the deviceRef)
	 <USB27>	 6/11/98	CJK		Remove driverentry routine (not needed or used).
	 <USB26>	  6/8/98	CJK		eliminate PBVERSION1 define
	 <USB25>	  6/5/98	CJK		remove ExpertStatus calls; cluttering up expert log
	 <USB24>	 5/20/98	BT		Add V2 param block stuff
	 <USB23>	 5/20/98	CJK		change driver name from USBKeyboardModule to
									USBHIDKeyboardModule
	 <USB22>	 5/20/98	CJK		add prototype for SetParamBlock
	 <USB21>	 4/30/98	CJK		add a "send raw report" mode.  initialize rawdatamode and
									initialized flags appropriately.
	 <USB20>	 4/29/98	CJK		Back out, just for the Columbus demo, the changes since the
									1.0a9 release.
	 <USB19>	 4/29/98	CJK		add break where needed
	 <USB18>	 4/27/98	CJK		don't fatalerror if setidle never succeeds.  just give up after
									5 tries and move on to opening the pipe.
	 <USB17>	 4/26/98	CJK		Add clear param block routine.  Dramatically reorganized to
									match the "new" style used in the mouse module.  The keyboard
									module now searchs for an interface, and when it finds a
									keyboard interface, it locates the interrupt pipe.  No more hard
									coded assumptions!  Okay, so we're still assuming that keyboard
									reports (in boot protocol mode) are 8 bytes.
	 <USB16>	  4/9/98	CJK		change driver services to be a externally supplied header file
									(using < & >) rather than quotes.
	 <USB15>	  4/9/98	DF		Massaged to work with MasterInterfaces
		<14>	  4/8/98	CJK		add initialization of saved interrupt routine ptr.
		<13>	 3/26/98	CJK		remove init of delay field inside of my keyboard structure.
									Clean up USBExpertFatalError messages (to show USB error code,
									etc.)
		<12>	 3/25/98	CJK		Remove unneeded poll & delay code
		<11>	 3/24/98	CJK		turn on interrupt pipe usage.  This required some changes in the
									USL, as well as doing a "set_idle" in order to get the reports
									when keys were released.
		<10>	 3/17/98	CJK		Begin adding support for interrupt transactions. Change the
									occasion "};" to just "}", as MetroWerks has a problem with some
									"};".
		 <9>	 3/12/98	CJK		add initialization of the shim keyboard param block.
		 <8>	  3/2/98	CJK		remove include of KBDHIDEmulation.h
		 <7>	 2/26/98	CJK		change poll rate to 10ms.  Add support for LEDs
		 <6>	 2/17/98	CJK		remove "" from each notify expert call.
		 <5>	 2/16/98	CJK		change poll time to 8ms
		 <4>	 2/11/98	CJK		change debugstrs to USBExpertFatalError calls.
		 <3>	 2/11/98	CJK		remove unneeded states
		 <2>	 2/10/98	CJK		Correct change history (to reflect the keyboard module changes)
		 <1>	 2/10/98	CJK		First time checkin.  Cloned from Mouse HID Module.
*/
/*
#include <Types.h>
#include <Devices.h>
#include <processes.h>
*/
#include "../driverservices.h"
#include "../USB.h"

#include "KeyboardModule.h"

usbKeyboardPBStruct myKeyboardPB;
usbKeyboardPBStruct shimKeyboardPB;


static void KeyboardModuleDelay1CompletionProc(USBPB *pb);
void SetParamBlock(USBDeviceRef theDeviceRef, USBPB * paramblock);

void SetParamBlock(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 KeyboardModuleInitiateTransaction(USBPB *pb)
{
register usbKeyboardPBStruct *pKeyboardPB;
OSStatus myErr;

//A.W. 12/9/98 It seems this code is never called 
	pKeyboardPB = (usbKeyboardPBStruct *)(pb);
	pKeyboardPB->transDepth++;
	if (pKeyboardPB->transDepth < 0)
	{
printf("transdepth<0\n");
		USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: transDepth < 0 (initiation)", pKeyboardPB->pb.usbRefcon );
	}
	
//A.W. 12/9/98 It seems this code is never called 
	if (pKeyboardPB->transDepth > 1)
	{
printf("transdepth>1\n");
		USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: transDepth > 1 (initiation)", pKeyboardPB->pb.usbRefcon );
	}
	
	switch(pKeyboardPB->pb.usbRefcon & ~kRetryTransaction)
	{
		case kSetKeyboardLEDs:
			SetParamBlock(pKeyboardPB->deviceRef, &pKeyboardPB->pb);
			
			pKeyboardPB->pb.usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);			
			
			pKeyboardPB->pb.usb.cntl.BRequest = kHIDRqSetReport;
			pKeyboardPB->pb.usb.cntl.WValue = (kHIDRtOutputReport << 8); 
			pKeyboardPB->pb.usb.cntl.WIndex = pKeyboardPB->interfaceDescriptor.interfaceNumber;
			
			pKeyboardPB->pb.usbBuffer = (Ptr)&pKeyboardPB->hidReport[0];
			pKeyboardPB->pb.usbReqCount = 1;
				
			pKeyboardPB->pb.usbCompletion = (USBCompletion)KeyboardCompletionProc;
			pKeyboardPB->pb.usbRefcon |= kCompletionPending;
			
			myErr = USBDeviceRequest(&pKeyboardPB->pb);
			if(immediateError(myErr))
			{
				USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: kSetKeyboardLEDs - immediate error", myErr);
			}
//A.W. 12/9/98 This code seems to be going through OK

			break;

		case kGetFullConfiguration:
			SetParamBlock(pKeyboardPB->deviceRef, &pKeyboardPB->pb);
			
			pKeyboardPB->pb.usbRefcon |= kCompletionPending;
			pKeyboardPB->pb.usbCompletion = (USBCompletion)KeyboardCompletionProc;
			myErr = USBGetFullConfigurationDescriptor(pb);
			if(immediateError(myErr))
			{
				USBExpertFatalError(pKeyboardPB->pb.usbReference, kUSBInternalErr, "USBHIDKeyboardModule: kGetFullConfiguration (ImmediateError)", myErr);
			}
			break;
			
		case kFindInterfaceAndSetProtocol:
			myErr = kbd_FindHIDInterfaceByNumber(pKeyboardPB->pFullConfigDescriptor, pKeyboardPB->interfaceDescriptor.interfaceNumber, &pKeyboardPB->pInterfaceDescriptor );	// find the HID interface
			if ((pKeyboardPB->pInterfaceDescriptor == NULL) || 
			    (myErr != noErr))
			{
				USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: Interface not found", myErr);
				pKeyboardPB->pb.usbRefcon = kReturnFromDriver;
			}
			
			SetParamBlock(pKeyboardPB->deviceRef, &pKeyboardPB->pb);
			
			pKeyboardPB->pb.usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);			
			pKeyboardPB->pb.usb.cntl.BRequest = kHIDRqSetProtocol;
			pKeyboardPB->pb.usb.cntl.WValue = kHIDBootProtocolValue; 
			pKeyboardPB->pb.usb.cntl.WIndex = pKeyboardPB->interfaceDescriptor.interfaceNumber;
			pKeyboardPB->pb.usbCompletion = (USBCompletion)KeyboardCompletionProc;
			
			pKeyboardPB->pb.usbRefcon |= kCompletionPending;
			myErr = USBDeviceRequest(&pKeyboardPB->pb);
			if (immediateError(myErr))
			{
				USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: kSetProtocol (ImmediateError)", myErr);
			}
			break;
			
		case kSetIdleRequest:
			SetParamBlock(pKeyboardPB->deviceRef, &pKeyboardPB->pb);
			
			pKeyboardPB->pb.usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);			
			
			pKeyboardPB->pb.usb.cntl.BRequest = kHIDRqSetIdle;
			pKeyboardPB->pb.usb.cntl.WValue = ((24/4)<<8); 				// force a read completion if idle for more than 24ms
			pKeyboardPB->pb.usb.cntl.WIndex = pKeyboardPB->interfaceDescriptor.interfaceNumber;
			pKeyboardPB->pb.usbCompletion = (USBCompletion)KeyboardCompletionProc;
			
			pKeyboardPB->pb.usbRefcon |= kCompletionPending;

			myErr = USBDeviceRequest(&pKeyboardPB->pb);
			if(immediateError(myErr))
			{
				USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: kSetIdleRequest - immediate error", myErr);
			}
			break;

		case kFindAndOpenInterruptPipe:
			pKeyboardPB->pb.usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBIn, kUSBClass, kUSBInterface);
			pKeyboardPB->pb.usbFlags = kUSBIn;
			pKeyboardPB->pb.usbClassType = kUSBInterrupt;
			pKeyboardPB->pb.usbOther = 0;			
			pKeyboardPB->pb.usbBuffer = pKeyboardPB->pInterfaceDescriptor;
			pKeyboardPB->pb.usbReqCount = (UInt8*)pKeyboardPB->pInterfaceDescriptor - (UInt8*)pKeyboardPB->pFullConfigDescriptor;
			myErr = USBFindNextEndpointDescriptorImmediate( &pKeyboardPB->pb );
			if((immediateError(myErr)) || (pKeyboardPB->pb.usbBuffer == nil))
			{
				USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: Endpoint not found", myErr);
				pKeyboardPB->pb.usbRefcon = kReturnFromDriver;
			}
			else
			{
				pKeyboardPB->pEndpointDescriptor = (USBEndPointDescriptorPtr) pKeyboardPB->pb.usbBuffer;

				SetParamBlock(pKeyboardPB->deviceRef, &pKeyboardPB->pb);
				
				pKeyboardPB->pb.usbFlags = kUSBIn;
				pKeyboardPB->pb.usb.cntl.WValue = 0x08;
				pKeyboardPB->pb.usbClassType = kUSBInterrupt;
				pKeyboardPB->pb.usbOther = (pKeyboardPB->pEndpointDescriptor->endpointAddress & 0x0f);
				pKeyboardPB->pb.usbRefcon |= kCompletionPending;
				pKeyboardPB->pb.usbCompletion = (USBCompletion)KeyboardCompletionProc;
		
				myErr = USBOpenPipe( &pKeyboardPB->pb );
				if(immediateError(myErr))
				{
					USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: USBOpenPipe Failed (ImmediateError)", myErr);
				}
			}
			break;
		
		case kReadInterruptPipe:
			SetParamBlock(pKeyboardPB->pipeRef, &pKeyboardPB->pb);

			pKeyboardPB->pb.usbBuffer = (Ptr)pKeyboardPB->hidReport;
			pKeyboardPB->pb.usbReqCount = 0x08;
			pKeyboardPB->pb.usb.cntl.WIndex = pKeyboardPB->interfaceDescriptor.interfaceNumber;	
			pKeyboardPB->pb.usbCompletion = (USBCompletion)KeyboardCompletionProc;
			
			pKeyboardPB->pb.usbRefcon |= kCompletionPending;
	kprintf("***Issuing keyboard USBIntRead\n");
			myErr = USBIntRead(&pKeyboardPB->pb);
			if(immediateError(myErr))
			{
				USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: Read Interrupt Pipe (ImmediateError)", myErr);
			}
			break;
			
		default:
			USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule - Transaction completed with bad refcon value", pKeyboardPB->pb.usbRefcon );
			pKeyboardPB->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 KeyboardCompletionProc(USBPB *pb)
{
unsigned char	* errstring;
register usbKeyboardPBStruct *pKeyboardPB;
USBPipeState pipeState;

	pKeyboardPB = (usbKeyboardPBStruct *)(pb);
	pKeyboardPB->transDepth--;
	if (pKeyboardPB->transDepth < 0)
	{
printf("transDepth<0\n");
		USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: transDepth < 0 (completion)", pKeyboardPB->pb.usbRefcon );
	}
	
	if (pKeyboardPB->transDepth > 1)
	{
printf("transDepth>1\n");
		USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule: transDepth > 1 (completion)", pKeyboardPB->pb.usbRefcon );
	}
	
	if(pKeyboardPB->pb.usbStatus != noErr)													// was there an error?
	{
		switch(pKeyboardPB->pb.usbRefcon & 0x0fff)											// yes, so show where the error occurred
		{
			case kSetKeyboardLEDs:  			errstring = "USBHIDKeyboardModule: Error during SetKeyboardLEDs"; break;
			case kGetFullConfiguration:  		errstring = "USBHIDKeyboardModule: Error during GetFullConfiguration"; break;
			case kFindInterfaceAndSetProtocol:	errstring = "USBHIDKeyboardModule: Error during FindInterfaceAndSetProtocol"; break;
			case kFindAndOpenInterruptPipe:  	errstring = "USBHIDKeyboardModule: Error during FindAndOpenInterruptPipe"; break;
			case kReadInterruptPipe: 	 		errstring = "USBHIDKeyboardModule: Error during ReadInterruptPipe"; break;
			default: 	 						errstring = "USBHIDKeyboardModule: Error occurred, but state is unknown"; break;
		};
		USBExpertFatalError(pKeyboardPB->deviceRef, pKeyboardPB->pb.usbStatus, errstring, (pKeyboardPB->pb.usbRefcon & 0x0fff));
		
		pKeyboardPB->pb.usbRefcon &= ~(kCompletionPending + kReturnFromDriver);				// set up to retry the transaction
		pKeyboardPB->pb.usbRefcon |= kRetryTransaction;
		pKeyboardPB->retryCount--;
		
		if ((!pKeyboardPB->retryCount)	|| (pKeyboardPB->pb.usbStatus == kUSBAbortedError))	// have we exhausted the retries?
		{																					// or received an abort?
			USBExpertStatus(pKeyboardPB->deviceRef, "USBHIDKeyboardModule: Pipe abort or unable to recover from error", pKeyboardPB->deviceRef);
			pKeyboardPB->pb.usbRefcon = kReturnFromDriver;									// if so, just exit.
		}
		else																				// if it didn't abort and there's retries left, then...
		{
			if (pKeyboardPB->pipeRef)														// check if the pipe is open.
			{
				USBGetPipeStatusByReference(pKeyboardPB->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(pKeyboardPB->deviceRef, "USBHIDKeyboardModule: Pipe is open and stalled, clearing stall...", pKeyboardPB->deviceRef);
					USBClearPipeStallByReference(pKeyboardPB->pipeRef);
				}
			}
		}
	}
	else
	{
		pKeyboardPB->pb.usbRefcon &= ~kRetryTransaction;
		pKeyboardPB->retryCount = kKeyboardRetryCount;
	}

	if (pKeyboardPB->pb.usbRefcon & kCompletionPending)			 
	{												
		pKeyboardPB->pb.usbRefcon &= ~(kCompletionPending + kReturnFromDriver);
		switch(pKeyboardPB->pb.usbRefcon)
		{
			case kSetKeyboardLEDs:
				pKeyboardPB->pb.usbRefcon = kReturnFromDriver;
				break;
				
			case kGetFullConfiguration:
				pKeyboardPB->pFullConfigDescriptor = pKeyboardPB->pb.usbBuffer;
				if (pKeyboardPB->pFullConfigDescriptor == nil)
				{
					USBExpertFatalError(pKeyboardPB->pb.usbReference, kUSBInternalErr, "USBHIDKeyboardModule: USBGetFullConfiguration - pointer is nil", pKeyboardPB->pb.usbRefcon);
					pKeyboardPB->pb.usbRefcon = kReturnFromDriver;
				}
				else
				{
					pKeyboardPB->pb.usbRefcon = kFindInterfaceAndSetProtocol;
				}
				break;
				
			case kFindInterfaceAndSetProtocol:
				pKeyboardPB->pb.usbRefcon = kSetIdleRequest;
				break;
				
			case kSetIdleRequest:
				pKeyboardPB->pb.usbRefcon = kFindAndOpenInterruptPipe;
				break;
				
			case kFindAndOpenInterruptPipe:
				pKeyboardPB->pipeRef = pKeyboardPB->pb.usbReference;
				pKeyboardPB->pb.usbRefcon = kReadInterruptPipe;
				break;
				
			case kReadInterruptPipe:
				kbd_NotifyRegisteredHIDUser(pKeyboardPB->hidDeviceType, pKeyboardPB->hidReport);
				pKeyboardPB->pb.usbRefcon = kReadInterruptPipe;
				break;
				
			default:
				USBExpertFatalError(pKeyboardPB->deviceRef, kUSBInternalErr, "USBHIDKeyboardModule - Transaction completed with bad refcon value", pKeyboardPB->pb.usbRefcon );
				pKeyboardPB->pb.usbRefcon = kUndefined + kReturnFromDriver;
				break;
		}
	}
	if (!(pKeyboardPB->pb.usbRefcon & kReturnFromDriver))
		KeyboardModuleInitiateTransaction(pb);
}


void kbd_InterfaceEntry(UInt32 interfacenum, USBInterfaceDescriptorPtr pInterfaceDescriptor, USBDeviceDescriptorPtr pDeviceDescriptor, USBDeviceRef device)
{
#pragma unused (interfacenum)

static Boolean beenThereDoneThat = false;

	if(beenThereDoneThat)
	{
		USBExpertFatalError(device, kUSBInternalErr, "USBHIDKeyboardModule is not reentrant", 12);
		return;
	}
//Hot Plug n Play
//naga	beenThereDoneThat = true;
	
//	DebugStr("In Keyboard Module Interface entry routine");
	shimKeyboardPB.deviceDescriptor = *pDeviceDescriptor;				
	shimKeyboardPB.interfaceDescriptor = *pInterfaceDescriptor;				
	shimKeyboardPB.transDepth = 0;							
	shimKeyboardPB.retryCount = kKeyboardRetryCount;
	shimKeyboardPB.pSHIMInterruptRoutine = nil;
	shimKeyboardPB.pSavedInterruptRoutine = nil;
	shimKeyboardPB.deviceRef = device;
	shimKeyboardPB.interfaceRef = 0;
	shimKeyboardPB.pipeRef = 0;
	
	shimKeyboardPB.deviceRef = device;		
	shimKeyboardPB.interfaceRef = nil;		
	shimKeyboardPB.pipeRef = nil;		
	
	SetParamBlock(device, &shimKeyboardPB.pb);
	shimKeyboardPB.pb.pbLength = sizeof(usbKeyboardPBStruct);
	shimKeyboardPB.pb.usbRefcon = 0;				

	myKeyboardPB.deviceDescriptor = *pDeviceDescriptor;				
	myKeyboardPB.interfaceDescriptor = *pInterfaceDescriptor;				
	myKeyboardPB.transDepth = 0;							
	myKeyboardPB.retryCount = kKeyboardRetryCount;
	myKeyboardPB.pSHIMInterruptRoutine = nil;
	myKeyboardPB.pSavedInterruptRoutine = nil;
	myKeyboardPB.deviceRef = device;
	myKeyboardPB.interfaceRef = 0;
	myKeyboardPB.pipeRef = 0;

	myKeyboardPB.deviceRef = device;		
	myKeyboardPB.interfaceRef = nil;		
	myKeyboardPB.pipeRef = nil;	
	
	myKeyboardPB.sendRawReportFlag = false;
	myKeyboardPB.hidEmulationInit = false;
	
	SetParamBlock(device, &myKeyboardPB.pb);
	myKeyboardPB.pb.pbLength = sizeof(usbKeyboardPBStruct);
	myKeyboardPB.pb.usbRefcon = kGetFullConfiguration;		/* Start with setting the interface protocol */
	
	
	kbd_USBHIDControlDevice(kHIDEnableDemoMode,0);
	KeyboardModuleInitiateTransaction(&myKeyboardPB.pb);
}