diff -r 000000000000 -r f63038272f30 bluetoothengine/bthid/bthidserver/src/bthidconnection.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetoothengine/bthid/bthidserver/src/bthidconnection.cpp Mon Jan 18 20:28:57 2010 +0200 @@ -0,0 +1,1173 @@ +/* +* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: This is the implementation of application class + * +*/ + + +#include +#include "hiddebug.h" +#include "bthidconnection.h" +#include "btconnectionobserver.h" +#include "sockets.pan" +#include "socketinitiator.h" +#include "socketreader.h" +#include "socketwriter.h" +#include "datasegmenter.h" +#include "timeouttimer.h" +#include "bthidtypes.h" +#include "bthiddevice.h" +#include "hidinterfaces.h" +#include "debug.h" + +CBTHidConnection* CBTHidConnection::NewL(RSocketServ& aSocketServ, + MBTConnectionObserver& aObserver, TBTConnectionState aConnectionState) + { + CBTHidConnection* self = CBTHidConnection::NewLC(aSocketServ, aObserver, + aConnectionState); + CleanupStack::Pop(self); + return self; + } + +CBTHidConnection* CBTHidConnection::NewLC(RSocketServ& aSocketServ, + MBTConnectionObserver& aObserver, TBTConnectionState aConnectionState) + { + CBTHidConnection* self = new (ELeave) CBTHidConnection(aSocketServ, + aObserver, aConnectionState); + // This is a CObject derived class so must be closed to destroy it. + CleanupClosePushL(*self); + self->ConstructL(); + return self; + } + +CBTHidConnection::CBTHidConnection(RSocketServ& aSocketServ, + MBTConnectionObserver& aObserver, TBTConnectionState aConnectionState) : + iSocketServ(aSocketServ), iObserver(aObserver) + { + ChangeState(aConnectionState); + } + +CBTHidConnection::~CBTHidConnection() + { + delete iControlSocketReader; + + delete iControlSocketWriter; + + delete iInterruptSocketReader; + + delete iInterruptSocketWriter; + + delete iSocketInitiator; + + delete iCommandSegmenter; + + delete iControlDataBuffer; + + delete iInterruptDataBuffer; + + delete iInactivityTimer; + + delete iDevice; + + // Close connections in the reverse order to when they were opened + if (iInterruptSocket) + { + iInterruptSocket->Close(); + delete iInterruptSocket; + } + + if (iControlSocket) + { + iControlSocket->Close(); + delete iControlSocket; + } + } + +TInt CBTHidConnection::ConnID() + { + return iConnID; + } + +void CBTHidConnection::SetConnID(TInt aConnID) + { + // We shouldn't be trying to set the id for this connection + // after it is connected. + __ASSERT_DEBUG(iConnectionState == ENotConnected || iConnectionState == EConnecting, + User::Panic(KPanicBTConnection, ESocketsBadState)); + + iConnID = aConnID; + } + +void CBTHidConnection::ConstructL() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::ConstructL")); + //ChangeState(ENotConnected); + + // A new hid device information object. + iDevice = CBTHidDevice::NewL(); + + // Socket readers and writer objects to perform asynchronous requests + iControlSocketReader = CSocketReader::NewL(EControlSocketID, *this, + KL2CAPDefaultMTU); + + iControlSocketWriter = CSocketWriter::NewL(EControlSocketID, *this); + + iInterruptSocketReader = CSocketReader::NewL(EInterruptSocketID, *this, + KL2CAPDefaultMTU); + + iInterruptSocketWriter = CSocketWriter::NewL(EInterruptSocketID, *this); + + // Socket initiator to perform socket connections + iSocketInitiator = CSocketInitiator::NewL(iSocketServ, *this); + + // Create a timeout timer + iInactivityTimer = CTimeOutTimer::NewL(EPriorityHigh, *this); + } + +void CBTHidConnection::ConnectL() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::ConnectL")); + // If we aren't in the correct stiDeviceate for an initial connection Panic. + __ASSERT_DEBUG(iConnectionState == ENotConnected || iConnectionState == EConnecting, + User::Panic(KPanicBTConnection, ESocketsBadState)); + + // Create the two sockets. + + delete iControlSocket; + iControlSocket = 0; + iControlSocket = new (ELeave) RSocket; + + // If we leave after this iControlSocket will not be NULL. + // This is ok since any place where it can be set will delete it first + // or it will be finally deleted in the destructor + delete iInterruptSocket; + iInterruptSocket = 0; + iInterruptSocket = new (ELeave) RSocket; + + // If we leave after this iControlSocket & iInterruptSocket will not + // be NULL. + // This is ok since any place where they can be set will delete first + // or they will be finally deleted in the destructor + + // Ask the socket initiator to connect these sockets, + // giving it the bluetooth address of the device and a security flag. + iSocketInitiator->ConnectSocketsL(iDevice->iAddress, + iDevice->iUseSecurity, iControlSocket, iInterruptSocket); + ChangeState(EFirstConnection); + } + +void CBTHidConnection::ReconnectL() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::ReconnectL")); + // If we aren't in the correct state for an reconnection Panic. + __ASSERT_DEBUG(iConnectionState == ENotConnected, + User::Panic(KPanicBTConnection, ESocketsBadState)); + + // Expect the device to reconnect for now + ChangeState(ELinkLost); + } + +void CBTHidConnection::Disconnect() //Virtual Cable Unplug. (this is not Bluetooth disconnect) + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::Disconnect (Virtual Cable Unplug)")); + if (iConnectionState == EConnected) + { + //First Send VCUnplug + iCommandBuffer.Zero(); + iCommandBuffer.Append(EHIDControlVCUnplug); + + TRequestStatus status; + iControlSocket->Write(iCommandBuffer, status); + User::WaitForRequest(status); + + //Then wait for a reply from Su-8W on L2CAP channel(s). If not waited, Su-8W will go mad. + User::After(500000); //0.5 sec + + //but we never handle that acknowledgement (because it is not required by BT HID + //specification, but Su-8W needs to send something.) + iControlSocketWriter->Cancel(); + iControlSocketReader->Cancel(); + iInterruptSocketReader->Cancel(); + iInterruptSocketWriter->Cancel(); + } + } + +void CBTHidConnection::OfferControlSocket(const TBTDevAddr& aAddress, + RSocket*& aSocket) + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::OfferControlSocket")); + if (aAddress == iDevice->iAddress) + { + __ASSERT_DEBUG((iConnectionState == ELinkLost) || + (iConnectionState == EHIDReconnecting), + User::Panic(KPanicBTConnection, ESocketsBadState)); + + // Take ownership of this socket + delete iControlSocket; + iControlSocket = aSocket; + aSocket = 0; + + // Mark that the HID Device is reconnecting to us. + ChangeState(EHIDReconnecting); + } + } + +void CBTHidConnection::OfferInterruptSocket(const TBTDevAddr& aAddress, + RSocket*& aSocket) + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::OfferInterruptSocket")); + + if (aAddress == iDevice->iAddress) + { + __ASSERT_DEBUG((iConnectionState == EHIDReconnecting), //||(iConnectionState == ELinkLost) , + User::Panic(KPanicBTConnection, ESocketsBadState)); + + // Take ownership of this socket + delete iInterruptSocket; + iInterruptSocket = aSocket; + aSocket = 0; + TRAPD(error, PrepareSocketsL()); + + if (KErrNone == error) + { + // Mark that we are now reconnected. + + ChangeState(EConnected); + + // Inform the observer that the connection has been restored. + iObserver.LinkRestored(iConnID); + + } + else + { + // Close the sockets as they can't be used + CloseChannels(); + ChangeState(ELinkLost); + } + } + } + +CBTHidDevice& CBTHidConnection::DeviceDetails() + { + return (*iDevice); + } + +TBool CBTHidConnection::IsConnected() const + { + TRACE_INFO( (_L("[BTHID]\tCBTHidConnection::IsConnected: connection state = %d"), iConnectionState) ); + return (EConnected == iConnectionState); + } + +TBTConnectionState CBTHidConnection::ConnectStatus() const + { + TRACE_INFO( (_L("[BTHID]\tCBTHidConnection::ConnectStatus: connection state = %d"), iConnectionState) ); + return (iConnectionState); + } + +void CBTHidConnection::StartMonitoringChannelsL() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::StartMonitoringChannelsL")); + __ASSERT_DEBUG(iConnectionState == EConnected, + User::Panic(KPanicBTConnection, ESocketsBadState)); + + iControlSocketReader->StartReadingL(iControlSocket, iControlInMTU); + iInterruptSocketReader->StartReadingL(iInterruptSocket, iInterruptInMTU); + + // If the device will reconnect to us we are ok to drop the link + // after a period of inactivity. + if (iDevice->iReconnectInit) + { + iInactivityTimer->Cancel(); + iInactivityTimer->After(KInactivityTimeout); + } + } + +void CBTHidConnection::DropConnection() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::DropConnection")); + // Close the Bluetooth Channels. + CloseChannels(); + + // Update the connection state. + ChangeState(ELinkLost); + + // If a command is outstanding + if (iCommandIssued) + { + // Generate an error to the parent. + iObserver.HandleCommandAck(iConnID, KErrNotReady); + // Reset the command flag. + iCommandIssued = EFalse; + + // Reset this, so we don't leave it in a bad state. + if (iCommandSegmenter) + { + iCommandSegmenter->Reset(); + } + } + } + +// from MSocketObserver +void CBTHidConnection::HandleSocketError(TUint /*aSocketID*/, + TBool aConnectionLost, TInt aErrorCode) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidConnection::HandleSocketError: connlost %d, error code %d"), aConnectionLost, aErrorCode) ); + if (aConnectionLost) + { + ConnectionLost(); + } + else + { + // If we failed to write to the device then inform Generic HID + // of the failure + if (iCommandIssued) + { + iCommandIssued = EFalse; + + // Reset this, so we don't leave it in a bad state. + if (iCommandSegmenter) + { + iCommandSegmenter->Reset(); + } + + iObserver.HandleCommandAck(iConnID, aErrorCode); + } + } + } + +// from MSocketObserver +TBool CBTHidConnection::HandleDataReceived(TUint aSocketID, + const TDesC8& aBuffer) + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::HandledataReceived")); + TBool result = ETrue; + + // Cancel the inactivity timer. + iInactivityTimer->Cancel(); + + switch (aSocketID) + { + case EControlSocketID: + result = ProcessControlData(aBuffer); + break; + case EInterruptSocketID: + result = ProcessInterruptData(aBuffer); + break; + default: + // Shouldn't have any other socket id + User::Panic(KPanicBTConnection, ESocketsUnknownID); + break; + } + + // If the device will reconnect to us we are ok to drop the link + // after a period of inactivity. + if ((result) && (iDevice->iReconnectInit)) + { + iInactivityTimer->After(KInactivityTimeout); + } + + return result; + } + +// from MSocketObserver +void CBTHidConnection::HandleWriteComplete(TUint aSocketID) + { + //to handle DATA Output in interrupt channel (Host to Device DATA) + // Check the ID of the socket + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::HandleWriteComplete")); + if (aSocketID == EControlSocketID || aSocketID == EInterruptSocketID) + { + // Send any additional packets on the control channel + if (iCommandSegmenter) + { + const HBufC8* data = iCommandSegmenter->NextPacket(); + if (data) + { + if (aSocketID == EControlSocketID) + { + TRAPD(err, iControlSocketWriter->IssueWriteL(*data)); + if (KErrNone != err) + { + // Reset this, so we don't leave it in a bad state. + iCommandSegmenter->Reset(); + iCommandIssued = EFalse; + + iObserver.HandleCommandAck(iConnID, err); + } + } + else //aSocketID == EInterruptSocketID + { + TRAPD(err, iInterruptSocketWriter->IssueWriteL(*data)); + if (KErrNone != err) + { + // Reset this, so we don't leave it in a bad state. + iCommandSegmenter->Reset(); + iCommandIssued = EFalse; + + iObserver.HandleCommandAck(iConnID, err); + } + } + } + if (!data && aSocketID == EInterruptSocketID && iCommandIssued) + { //We don't expect response from HID Device, However we'll notify GenericHID + //that async write operation has been finished. + iObserver.HandleCommandAck(iConnID, KErrNone); //Socket write complete! + iCommandIssued = EFalse; + } + } + } + + } + +void CBTHidConnection::SocketsConnected() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::SocketsConnected")); + TRAPD(error, PrepareSocketsL()); + + if (KErrNone == error) + { + switch (iConnectionState) + { + case EFirstConnection: + // We are now connected + + ChangeState(EConnected); + + // If this was an initial connection inform the observer + iObserver.FirstTimeConnectionComplete(iConnID, KErrNone); + break; + + case EHostReconnecting: + // We are now connected + ChangeState(EConnected); + + iObserver.LinkRestored(iConnID); + break; + + default: + User::Panic(KPanicBTConnection, ESocketsBadState); + break; + } + } + else + { + SocketsConnFailed(error); + } + } + +void CBTHidConnection::SocketsConnFailed(TInt aStatus) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidConnection::SocketsConnFailed(%d)"),aStatus) ); + switch (iConnectionState) + { + case EFirstConnection: + // We are not connected + ChangeState(ENotConnected); + // If this was an initial connection inform the observer + iObserver.FirstTimeConnectionComplete(iConnID, aStatus); + break; + + case EHostReconnecting: + ChangeState(ELinkLost); + break; + + default: + // we don't expect to be connecting in any other state. + User::Panic(KPanicBTConnection, ESocketsBadState); + break; + } + } + +void CBTHidConnection::TimerExpired() + { + // Inactivity timer has expired. + // Drop the connection. + DropConnection(); + // Inform the observer of the state change. + iObserver.Disconnected(iConnID); + } + +void CBTHidConnection::GetProtocolL() + { + LeaveIfCommandNotReadyL(); + + iCommandBuffer.Zero(); + iCommandBuffer.Append(EGetProtocol); + iControlSocketWriter->IssueWriteL(iCommandBuffer); + iCommandIssued = ETrue; + } + +void CBTHidConnection::SetProtocolL(TUint16 aValue) + { + LeaveIfCommandNotReadyL(); + + iCommandBuffer.Zero(); + // Value should be + // 0 for boot + // 1 for report + // ESetProtocol is set for Boot mode. Add aValue to get the correct + // command + iCommandBuffer.Append(ESetProtocol + aValue); + iControlSocketWriter->IssueWriteL(iCommandBuffer); + iCommandIssued = ETrue; + } + +void CBTHidConnection::GetReportL(TUint8 aReportType, TUint8 aReportID, + TUint16 aLength) + { + LeaveIfCommandNotReadyL(); + + iCommandBuffer.Zero(); + + // Add the Transaction header byte + // Report Type 0 = Reserved + // Report Type 1 = Input + // Report Type 2 = Output + // Report Type 3 = Feature + iCommandBuffer.Append(EGetReportFullBufReserved + aReportType); + + // If Report ID's are declared in the report descriptor then we + // add this field + if (aReportID != 0) + { + iCommandBuffer.Append(aReportID); + } + + // We need to check we have a buffer large enough to hold the control + // data we get back from the device + if (!iControlDataBuffer) + { + // Allocate the buffer if it didn't exist + iControlDataBuffer = HBufC8::NewL(aLength); + } + else + { + // Get a modifiable pointer to the buffer. + TPtr8 dataPtr = iControlDataBuffer->Des(); + + if (dataPtr.MaxLength() < aLength) + { + // Reallocate the buffer if its too small. + delete iControlDataBuffer; + iControlDataBuffer = 0; + iControlDataBuffer = HBufC8::NewL(aLength); + } + else + { + // Existing buffer is large enough, just zero it. + dataPtr.Zero(); + } + } + + iControlSocketWriter->IssueWriteL(iCommandBuffer); + iCommandIssued = ETrue; + } + +void CBTHidConnection::SetReportL(TUint8 aReportType, TUint8 aReportID, + const TDesC8& aReport) + { + LeaveIfCommandNotReadyL(); + + if (!iCommandSegmenter) + { + iCommandSegmenter = CDataSegmenter::NewL(); + } + + iCommandBuffer.Zero(); + + // Add the Transaction header byte + // Report Type 0 = Reserved + // Report Type 1 = Input + // Report Type 2 = Output + // Report Type 3 = Feature + iCommandBuffer.Append(ESetReportReserved + aReportType); + + // If Report ID's are declared in the report descriptor then we + // add this field + if (aReportID != 0) + { + iCommandBuffer.Append(aReportID); + } + + iCommandSegmenter->SegmentDataL(iCommandBuffer, aReport, EDATCOutput, + iControlOutMTU); + + iControlSocketWriter->IssueWriteL(*(iCommandSegmenter->FirstPacketL())); + + iCommandIssued = ETrue; + } + +void CBTHidConnection::DataOutL(TUint8 aReportID, const TDesC8& aReport) + { + //This implementation is similar to SetReportL, but instead of Control channel, + //sends the data in Interrupt channel. + + LeaveIfCommandNotReadyL(); + //We'll use Command buffer instead of Data buffer to send. + //could they happen at the same time? + if (!iCommandSegmenter) + { + iCommandSegmenter = CDataSegmenter::NewL(); + } + + iCommandBuffer.Zero(); + + // Add the Transaction header byte + // Report Type 0 = Reserved + // Report Type 1 = Input + // Report Type 2 = Output + // Report Type 3 = Feature + iCommandBuffer.Append(EDATAOutput); + + // If Report ID's are declared in the report descriptor then we + // add this field + if (aReportID != 0) + { + iCommandBuffer.Append(aReportID); + } + + iCommandSegmenter->SegmentDataL(iCommandBuffer, aReport, EDATCOutput, + iInterruptOutMTU); + + iInterruptSocketWriter->IssueWriteL(*(iCommandSegmenter->FirstPacketL())); + + iCommandIssued = ETrue; //We have sent the data but we don't expect + //HANDSHAKE SUCCESSFULL for DATA OUT from HID device. + //We will notify Generic HID when data has been sent. + } + +void CBTHidConnection::GetIdleL() + { + LeaveIfCommandNotReadyL(); + + iCommandBuffer.Zero(); + iCommandBuffer.Append(EGetIdle); + iControlSocketWriter->IssueWriteL(iCommandBuffer); + iCommandIssued = ETrue; + } + +void CBTHidConnection::SetIdleL(TUint8 aDuration) + { + LeaveIfCommandNotReadyL(); + + iCommandBuffer.Zero(); + iCommandBuffer.Append(ESetIdle); + iCommandBuffer.Append(aDuration); + iControlSocketWriter->IssueWriteL(iCommandBuffer); + iCommandIssued = ETrue; + } + +void CBTHidConnection::PrepareSocketsL() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::PrepareSockets")); + // Retrieve the MTU values from the sockets + User::LeaveIfError(iControlSocket->GetOpt(KL2CAPGetOutboundMTU, + KSolBtL2CAP, iControlOutMTU)); + User::LeaveIfError(iControlSocket->GetOpt(KL2CAPInboundMTU, KSolBtL2CAP, + iControlInMTU)); + User::LeaveIfError(iInterruptSocket->GetOpt(KL2CAPInboundMTU, + KSolBtL2CAP, iInterruptInMTU)); + User::LeaveIfError(iInterruptSocket->GetOpt(KL2CAPGetOutboundMTU, + KSolBtL2CAP, iInterruptOutMTU)); + + // Initialise the control socket writer with the new socket. + iControlSocketWriter->Initialise(iControlSocket); + + // Initialise the interrupt socket writer with the new socket. + iInterruptSocketWriter->Initialise(iInterruptSocket); + + } + +void CBTHidConnection::ChangeState(TBTConnectionState aNewStatus) + { + iConnectionState = aNewStatus; + } + +void CBTHidConnection::ConnectionLost() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::ConnectionLost")); + CloseChannels(); + + // First go into link loss state + ChangeState(ELinkLost); + + // If a command is outstanding + if (iCommandIssued) + { + TRACE_INFO(_L("[BTHID]\tCBTHidConnection::ConnectionLost, command outstanding")); + // Generate an error to the parent. + iObserver.HandleCommandAck(iConnID, KErrNotReady); + // Reset the command flag. + iCommandIssued = EFalse; + + // Reset this, so we don't leave it in a bad state. + if (iCommandSegmenter) + { + iCommandSegmenter->Reset(); + } + } + + // Check if the device will reconnect to us. + if (iDevice->iReconnectInit) + { + TRACE_INFO(_L("[BTHID]\tCBTHidConnection::ConnectionLost, device inits reconnect")); + // Inform the parent of the link loss and the fact we are not + // reconnecting + iObserver.LinkLost(iConnID); + } + else + { + TRACE_INFO(_L("[BTHID]\tCBTHidConnection::ConnectionLost, host inits reconnect")); + // Device won't reconnect, check if we are able to. + if (iDevice->iNormallyConnectable) + { + TRACE_INFO(_L("[BTHID]\tCBTHidConnection::ConnectionLost, device is normally connectable")); + // Attempt to initiate reconnection to the device. + TRAPD(res, iSocketInitiator->ConnectSocketsL(iDevice->iAddress, + iDevice->iUseSecurity, + iControlSocket, + iInterruptSocket);) + if (res == KErrNone) + { + // Reconnection is in progress, so record this and inform + // the parent. + ChangeState(EHostReconnecting); + iObserver.LinkLost(iConnID); + } + else + { + // Inform the parent of the link loss and the fact we are not + // reconnecting + iObserver.LinkLost(iConnID); + } + } + else + { + TRACE_INFO(_L("[BTHID]\tCBTHidConnection::ConnectionLost, device does not allow reconnection")); + // Device won't allow reconnection, so this connection is + // effectively dead. + // Change to not-connected and inform the parent of the + // disconnection. + ChangeState(ENotConnected); + iObserver.Unplugged(iConnID); + } + } + } + +void CBTHidConnection::ReceivedVirtualCableUnplug() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::ReceivedVirtualCableUnplug")); + CloseChannels(); + ChangeState(ENotConnected); + + iObserver.Unplugged(iConnID); + } + +void CBTHidConnection::LeaveIfCommandNotReadyL() const + { + if (iConnectionState != EConnected) + { + User::Leave(KErrNotReady); + } + + if (iCommandIssued) + { + User::Leave(KErrInUse); + } + } + +void CBTHidConnection::CloseChannels() + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::CloseChannels")); + // Cancel the inactivity timer. + iInactivityTimer->Cancel(); + + iControlSocketReader->Cancel(); + iControlSocketWriter->Cancel(); + iInterruptSocketReader->Cancel(); + iInterruptSocketWriter->Cancel(); + + // BT HID Spec. says the channels should be closed in the reverse + // order to when they were opened + if (iInterruptSocket) + { + if (iConnectionState == EConnected) + { + // Immediate shutdown + TRequestStatus status; + iInterruptSocket->Shutdown(RSocket::EImmediate, status); + User::WaitForRequest(status); + } + + iInterruptSocket->Close(); + } + + if (iControlSocket) + { + if (iConnectionState == EConnected) + { + // Immediate shutdown + TRequestStatus status; + iControlSocket->Shutdown(RSocket::EImmediate, status); + User::WaitForRequest(status); + } + + iControlSocket->Close(); + } + + } + +TBool CBTHidConnection::ProcessControlData(const TDesC8& aBuffer) + { + TBool result = ETrue; + + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::ProcessControlData")); + // Reset this, so we don't leave it in a bad state; + if (iCommandSegmenter) + { + iCommandSegmenter->Reset(); + } + + TInt dataLength = aBuffer.Length(); + + if (dataLength > 0) + { + TBTTransaction transaction = static_cast (aBuffer[0]); + + // The only unsolicited response should be a Virtual Cable Unplugged + // notification. + // All other responses should only be handled, if we have an + // outstanding command + if (transaction == EHIDControlVCUnplug) + { + ReceivedVirtualCableUnplug(); + // Don't want to read any more + result = EFalse; + } + else if (iCommandIssued) + { + // Reset the command issued flag. + iCommandIssued = EFalse; + + TRACE_INFO( (_L("[BTHID]\tCBTHidConnection::ProcessControlData: transaction %d"), transaction) ); + switch (transaction) + { + case EHandshakeSuccess: + iObserver.HandleCommandAck(iConnID, KErrNone); + break; + case EHandshakeNotReady: + iObserver.HandleCommandAck(iConnID, KErrNotReady); + break; + case EHandshakeInvalidRepID: + iObserver.HandleCommandAck(iConnID, + KErrAckInvalidReportID); + break; + case EHandshakeUnsupported: + iObserver.HandleCommandAck(iConnID, KErrNotSupported); + break; + case EHandshakeInvalidParam: + iObserver.HandleCommandAck(iConnID, + KErrAckInvalidParameter); + break; + case EHandshakeUnknown: + iObserver.HandleCommandAck(iConnID, KErrAckUnknown); + break; + case EHandshakeFatal: + iObserver.HandleCommandAck(iConnID, KErrAckFatal); + break; + + case EDATAInput: + // Fall through. + case EDATAFeature: + // If this packet was smaller than the maximum transmission + // unit this is the only data we will get. + if (dataLength < iControlInMTU) + { + // Pass to the observer + iObserver.HandleControlData(iConnID, aBuffer.Mid(1)); + } + else + { + // Data was the size of the MTU, so we will expect + // further DATC packets. + + // Mark that the command hasn't finished + iCommandIssued = ETrue; + + // Store as much data as we can in the buffer allocated + if (iControlDataBuffer) + { + TPtr8 dataPtr = iControlDataBuffer->Des(); + dataPtr.Zero(); + AppendData(dataPtr, aBuffer.Mid(1)); + } + } + break; + + case EDATCInput: + // Fall through. + case EDATCFeature: + // If this packet was smaller than the maximum transmission + // this is the final packet. + HandleEDATCFeature(aBuffer); + break; + + case EDATAOther: + iObserver.HandleControlData(iConnID, aBuffer.Mid(1)); + break; + + default: + // This transaction was unknown or unexpected, but + // we must report the error. + iObserver.HandleCommandAck(iConnID, KErrAckUnknown); + break; + } + } + } + + return result; + } + +void CBTHidConnection::HandleEDATCFeature(const TDesC8& aBuffer) + { + TInt dataLength = aBuffer.Length(); + if (dataLength < iControlInMTU) + { + // If there is a data buffer. + if (iControlDataBuffer) + { + // Get a modifiable pointer to the data + TPtr8 dataPtr = iControlDataBuffer->Des(); + + // If there is already some data then we can + // append. + if (dataPtr.Length() > 0) + { + // Append as much as we can. + AppendData(dataPtr, aBuffer.Mid(1)); + + // Pass up what data we have. + iObserver.HandleControlData(iConnID, *iControlDataBuffer); + } + else + { + // No initial data was stored, so report an + // error. + iObserver.HandleCommandAck(iConnID, KErrAckUnknown); + } + } + else + { + // There is no buffer to handle the data so just + // report an error. + iObserver.HandleCommandAck(iConnID, KErrNoMemory); + } + } + else + { + // This is an intermediate packet. + + // Mark that the command hasn't finished + iCommandIssued = ETrue; + + if (iControlDataBuffer) + { + // Get a modifiable pointer to the data. + TPtr8 dataPtr = iControlDataBuffer->Des(); + + // If there is already some data then we can + // append. + if (dataPtr.Length() > 0) + { + // Append as much as we can. + AppendData(dataPtr, aBuffer.Mid(1)); + } + } + } + + } + +void CBTHidConnection::AppendData(TDes8& aDest, const TDesC8& aSource) + { + TInt remainingSpace = aDest.MaxLength() - aDest.Length(); + + // If the control data buffer can handle this new packet + if (aSource.Length() <= remainingSpace) + { + // Append the data + aDest.Append(aSource); + } + } + +TBool CBTHidConnection::ProcessInterruptData(const TDesC8& aBuffer) + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::ProcessInterruptData")); + TInt dataLength = aBuffer.Length(); + + if (dataLength > 0) + { + TBTTransaction transaction = static_cast (aBuffer[0]); + + switch (transaction) + { + case EDATAInput: + // If this packet was smaller than the maximum transmission + // unit this is the only data we will get + if (dataLength < iInterruptInMTU) + { + // Pass to the observer + iObserver.HandleInterruptData(iConnID, aBuffer.Mid(1)); + } + else + { + // Data was the size of the MTU, so we will expect further + // DATC packets. + // Try to handle the start of this sequence + TRAPD(res, StartSegmentedInterruptDataL(aBuffer.Mid(1));) + if (res != KErrNone) + { + // First segment wasn't handled, so just pass up + // what we have + iObserver.HandleInterruptData(iConnID, aBuffer.Mid(1)); + } + } + break; + + case EDATCInput: + // Handle the next segmented packet + // If this packet was smaller than the maximum transmission + // unit this is the last packet in the sequence + if (dataLength < iInterruptInMTU) + { + ContinueSegmentedInterruptData(aBuffer.Mid(1), ETrue); + } + else + { + ContinueSegmentedInterruptData(aBuffer.Mid(1), EFalse); + } + break; + + default: + // Don't expect anything more here + break; + } + } + + return ETrue; + } + +void CBTHidConnection::StartSegmentedInterruptDataL(const TDesC8& aBuffer) + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::StartSegmentedInterruptDataL")); + // First check to see if we already have a data buffer + if (!iInterruptDataBuffer) + { + // Allocate a buffer based on the size of the data and copy it. + iInterruptDataBuffer = aBuffer.AllocL(); + } + else + { + // Get a modifiable pointer to the buffer + TPtr8 dataPtr = iInterruptDataBuffer->Des(); + + if (dataPtr.MaxLength() < aBuffer.Length()) + { + // If the buffer isn't large enough recreate it + delete iInterruptDataBuffer; + iInterruptDataBuffer = 0; + iInterruptDataBuffer = aBuffer.AllocL(); + } + else + { + // We have a large enough buffer so copy the data + dataPtr.Copy(aBuffer); + } + } + } + +void CBTHidConnection::ContinueSegmentedInterruptData(const TDesC8& aBuffer, + TBool aFinalSegment) + { + TRACE_FUNC + (_L("[BTHID]\tCBTHidConnection::ContinueSegmentedInterruptDataL")); + // If there is no buffer, then we haven't received a start packet, so + // just ignore this data + if (iInterruptDataBuffer) + { + // Get a modifiable pointer to the buffer + TPtr8 dataPtr = iInterruptDataBuffer->Des(); + + // If there is no data already, then we can't handle this continuation + // packet + if (dataPtr.Length() > 0) + { + // Append the new data to the end of the buffer + TBool dataWasAppended = ETrue; + TInt newSize = (dataPtr.Length() + aBuffer.Length()); + + if (dataPtr.MaxLength() < newSize) + { + // Reallocate the buffer if it isn't large enough + HBufC8* newBuffer = iInterruptDataBuffer->ReAlloc(newSize); + + if (newBuffer) + { + iInterruptDataBuffer = newBuffer; + dataPtr.Set(iInterruptDataBuffer->Des()); + dataPtr.Append(aBuffer); + } + else + { + // We couldn't append the data, so reset this + dataWasAppended = EFalse; + } + } + else + { + dataPtr.Append(aBuffer); + } + + // If this is the final segment, or we couldn't append this + // segment, pass up what we have. + if ((aFinalSegment) || (!dataWasAppended)) + { + iObserver.HandleInterruptData(iConnID, *iInterruptDataBuffer); + + // Zero the buffer so any more continuation segments will + // be ignored + dataPtr.Zero(); + } + } + } + } + +//End of file