bluetoothengine/bthid/bthidserver/src/bthidconnection.cpp
changeset 0 f63038272f30
child 11 a42ed326b458
--- /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 <e32debug.h>
+#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<TBTTransaction> (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<TBTTransaction> (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