summaryrefslogtreecommitdiff
path: root/sap/sapbtpeer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sap/sapbtpeer.cc')
-rw-r--r--sap/sapbtpeer.cc191
1 files changed, 191 insertions, 0 deletions
diff --git a/sap/sapbtpeer.cc b/sap/sapbtpeer.cc
new file mode 100644
index 0000000..188c37b
--- /dev/null
+++ b/sap/sapbtpeer.cc
@@ -0,0 +1,191 @@
+#include <QtCore/QtEndian>
+#include "saprotocol.h"
+#include "wmspeer.h"
+#include "crc16.h"
+#include "sapsocket.h"
+#include "sapbtpeer.h"
+
+#define PROTO_DEBUG 0
+
+SAPBTPeer::SAPBTPeer(SAProtocol::Role role, QBluetoothSocket *socket, QObject *parent) :
+ SAPPeer(role, socket->localAddress().toString(), socket->peerAddress().toString(), parent),
+ _socket(socket),
+ _curFrameLength(0),
+ _peerDescriptionExchangeDone(false), _authenticationDone(false)
+{
+ connect(_socket, SIGNAL(readyRead()), SLOT(handleSocketData()));
+ connect(_socket, SIGNAL(disconnected()), SLOT(handleSocketDisconnected()));
+}
+
+void SAPBTPeer::handleSocketData()
+{
+ uint bytes = _socket->bytesAvailable();
+ const bool need_crc = _peerDescriptionExchangeDone && _authenticationDone;
+ const uint header_size = need_crc ? 2 * sizeof(quint16) : 1 * sizeof(quint16);
+ const uint footer_size = need_crc ? sizeof(quint16) : 0;
+
+ while ((_curFrameLength == 0 && bytes >= header_size) ||
+ (_curFrameLength > 0 && bytes >= _curFrameLength + footer_size)) {
+ if (_curFrameLength > 0) {
+ // We are waiting for a full frame of known length
+ QByteArray frame = _socket->read(_curFrameLength);
+ Q_ASSERT(frame.size() == (int)_curFrameLength);
+ _curFrameLength = 0;
+
+ if (need_crc) {
+ quint16 computed_crc = crc16(0, reinterpret_cast<const quint8*>(frame.constData()), frame.size());
+ quint16 crc;
+ _socket->read(reinterpret_cast<char*>(&crc), sizeof(quint16));
+ crc = qFromBigEndian(crc);
+
+ if (crc != computed_crc) {
+ qWarning() << "CRC data failure";
+ _socket->close(); // Drop the connection, no provision for resync.
+ return;
+ }
+ }
+
+ handleFrame(frame);
+ } else {
+ quint16 frame_length;
+ bytes = _socket->read(reinterpret_cast<char*>(&frame_length), sizeof(quint16));
+ Q_ASSERT(bytes == sizeof(quint16));
+ _curFrameLength = qFromBigEndian(frame_length);
+ Q_ASSERT(_curFrameLength > 0);
+
+ if (need_crc) {
+ // Compute the checksum of the BIG ENDIAN frame length.
+ quint16 computed_crc = crc16(0, reinterpret_cast<quint8*>(&frame_length), sizeof(quint16));
+ quint16 crc;
+ _socket->read(reinterpret_cast<char*>(&crc), sizeof(quint16));
+ crc = qFromBigEndian(crc);
+
+ if (crc != computed_crc) {
+ qWarning() << "CRC length failure";
+ _curFrameLength = 0;
+ _socket->close(); // Drop the connection, no provision for resync.
+ return;
+ }
+ }
+ }
+
+ bytes = _socket->bytesAvailable();
+ }
+}
+
+void SAPBTPeer::handleSocketDisconnected()
+{
+ qDebug() << "Socket disconnected";
+ handleDisconnected();
+}
+
+void SAPBTPeer::sendFrame(const QByteArray &data)
+{
+ const bool need_crc = _peerDescriptionExchangeDone && _authenticationDone;
+ quint16 frame_length = qToBigEndian<quint16>(data.length());
+ _socket->write(reinterpret_cast<const char*>(&frame_length), sizeof(quint16));
+
+ // Compute the checksum of the BIG ENDIAN frame length.
+ if (need_crc) {
+ quint16 crc = qToBigEndian(crc16(0, reinterpret_cast<quint8*>(&frame_length), sizeof(quint16)));
+ _socket->write(reinterpret_cast<const char*>(&crc), sizeof(quint16));
+ }
+
+ _socket->write(data.constData(), data.size());
+
+ if (need_crc) {
+ quint16 crc = qToBigEndian(crc16(0, reinterpret_cast<const quint8*>(data.constData()), data.size()));
+ _socket->write(reinterpret_cast<const char*>(&crc), sizeof(quint16));
+ }
+
+#if PROTO_DEBUG
+ qDebug() << "Sent:" << data.toHex();
+#endif
+}
+
+void SAPBTPeer::handleFrame(const QByteArray &data)
+{
+#if PROTO_DEBUG
+ qDebug() << "Recv:" << data.toHex();
+#endif
+
+ if (!_peerDescriptionExchangeDone) {
+ // This must be a peer description frame!
+ SAProtocol::PeerDescription peerDesc = SAProtocol::unpackPeerDescription(data);
+ qDebug() << peerDesc.product << peerDesc.manufacturer << peerDesc.name;
+ qDebug() << "apdu=" << peerDesc.APDUSize << "ssdu=" << peerDesc.SSDUSize
+ << "sessions=" << peerDesc.sessions << "timeout=" << peerDesc.timeout;
+
+ SAProtocol::PeerDescription myDesc = peerDesc;
+ myDesc.messageType = 6; // Why?
+ myDesc.status = 0; // This seems to be "accepted"
+ myDesc.product = "RandomPhone";
+ myDesc.manufacturer = "me";
+ myDesc.name = "gearbttest";
+ myDesc.profile = "SWatch"; // This is what Gear manager sends
+
+ sendFrame(SAProtocol::packPeerDescription(myDesc));
+
+ _peerDescriptionExchangeDone = true;
+ } else if (!_authenticationDone) {
+ // This must be a authentication frame...
+ handleAuthenticationFrame(data);
+ } else {
+ SAProtocol::Frame frame = SAProtocol::unpackFrame(data);
+ switch (frame.type) {
+ case SAProtocol::FrameData:
+ handleDataFrame(frame);
+ break;
+ case SAProtocol::FrameControl:
+ handleControlFrame(frame);
+ break;
+ default:
+ qWarning() << "Unknown frame type" << frame.type;
+ break;
+ }
+ }
+}
+
+void SAPBTPeer::handleDataFrame(const SAProtocol::Frame &frame)
+{
+ Q_ASSERT(frame.type == SAProtocol::FrameData);
+
+ handleSessionData(frame.sessionId, frame.data);
+}
+
+void SAPBTPeer::handleControlFrame(const SAProtocol::Frame &frame)
+{
+ Q_ASSERT(frame.type == SAProtocol::FrameControl);
+
+ handleSessionControl(frame.sessionId, frame.data);
+}
+
+void SAPBTPeer::handleAuthenticationFrame(const QByteArray &data)
+{
+ SAProtocol::SecurityFrame sframe = SAProtocol::unpackSecurityFrame(data);
+
+ switch (sframe.type) {
+ case SAProtocol::SecurityAuthenticateRequest: {
+ qDebug() << "Starting authorization...";
+ SAProtocol::SecurityFrame response = _wms->respondToServerChallenge(sframe);
+ if (!response.data.isEmpty()) {
+ sendFrame(SAProtocol::packSecurityFrame(response));
+ }
+ break;
+ }
+ case SAProtocol::SecurityAuthenticateConfirm: {
+ _authenticationDone = _wms->verifyServerResponse(sframe);
+ if (_authenticationDone) {
+ qDebug() << "Authentication confirmed";
+ handleConnected();
+ } else {
+ qWarning() << "Authentication failure, closing connection";
+ _socket->close(); // Will call "handleDisconnected" and emit disconnected signals.
+ }
+ break;
+ }
+ default:
+ qWarning() << "Unknown security frame type" << sframe.type;
+ break;
+ }
+}