summaryrefslogtreecommitdiff
path: root/saprotocol.h
blob: 68434c1fee44a8273bcb345ff0b8e8c4ba86e75f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#ifndef SAPROTOCOL_H
#define SAPROTOCOL_H

#include <QtCore/QObject>
#include <QtBluetooth/QBluetoothUuid>

class SAProtocol
{
	Q_GADGET
	Q_ENUMS(Role FrameType ControlFrameType)

public:
	static const QBluetoothUuid dataServiceUuid;
	static const QBluetoothUuid nudgeServiceUuid;

	enum Role {
		ClientRole,
		ServerRole
	};

	static const quint8 currentProtocolVersion = 0;

	static const quint16 defaultSessionId = 0x3FF;
	static const quint16 maxSessionId = 0x3FF;

	static quint16 computeCrc16(const QByteArray &buf);

	static const char peerDescriptionSeparator = ';';

	/** One of the early connection setup messages. Seems to describe each
	 *  other's capabilities and network settings to the remote peer. */
	struct PeerDescription {
		/** No idea: either 5 or 6. Wild guess: 5 is request, 6 is response. */
		quint8 messageType;
		quint16 accessoryProtocolVersion;
		quint16 accessorySoftwareVersion;
		/** Status code. 0 generally means "no error". */
		quint8 status;
		quint8 errorCode; /**< Used when status != 0 and messageType == 6 */

		/* Seemingly network parameters, but I am not sure how the other end
		 * actually handles them. Seem to do nothing. */
		quint32 APDUSize;
		quint16 SSDUSize;
		quint16 sessions; /**< Seemingly maximum number of sessions allowed. */
		quint16 timeout; /**< Looks like a timeout, but no idea what for. */

		/* No idea. */
		quint8 unk_1;
		quint16 unk_2;
		quint8 unk_3;

		/* No one cares about the ones below. Seemingly free-form text. */
		QString product;
		QString manufacturer;
		QString name;
		QString profile;
	};

	static PeerDescription unpackPeerDescription(const QByteArray &data);
	static QByteArray packPeerDescription(const PeerDescription &desc);

	enum SecurityFrameType {
		SecurityAuthenticateRequest = 0x10,
		SecurityAuthenticateResponse = 0x11,
		SecurityAuthenticateConfirm = 0x12
	};

	struct SecurityFrame {
		SecurityFrameType type;
		quint8 authType; // ???
		quint8 version; // Always 0 so far.
		quint8 dataType; // ???
		QByteArray data;
	};

	static SecurityFrame unpackSecurityFrame(const QByteArray &data);
	static QByteArray packSecurityFrame(const SecurityFrame &sframe);

	enum FrameType {
		FrameData = 0,
		FrameControl = 1
	};

	struct Frame {
		quint8 protocolVersion; // Always 0 so far.
		FrameType type;
		quint16 sessionId;
		QByteArray data;
	};

	static Frame unpackFrame(const QByteArray &data);
	static QByteArray packFrame(const Frame& frame);
	static QByteArray packFrame(quint16 sessionId, const QByteArray &data, FrameType type = FrameData);

	enum FragmentStatus {
		FragmentNone = 0,
		FragmentMiddle = 1,
		FragmentLast = 3
	};

	struct DataFrame {
		bool withSeqNum; // (not actually present in frame)
		// The following field is only present if "withSeqNum":
		quint16 seqNum; // Monotonically increasing
		bool withFragStatus; // (not actually present in frame)
		FragmentStatus fragStatus;
		QByteArray data;
	};

	static DataFrame unpackDataFrame(const QByteArray &data, bool withSeqNum, bool withFragStatus);
	static QByteArray packDataFrame(const DataFrame& frame);

	enum ControlFrameType {
		ControlFrameImmediateAck = 0,
		ControlFrameBlockAck = 1,
		ControlFrameNak = 2
	};

	typedef QPair<quint16, quint16> SeqNumRange;

	struct ControlFrame {
		ControlFrameType type;
		QList<SeqNumRange> seqNums; // Used for Naks only
		quint16 seqNum;
	};

	static ControlFrame unpackControlFrame(const QByteArray &data);
	static QByteArray packControlFrame(const ControlFrame& frame);

	/* Default session messages */

	enum DefaultSessionMessageType {
		ServiceConnectionRequest = 1,
		ServiceConnectionResponse = 2,
		ServiceTerminationRequest = 3,
		ServiceTerminationResponse = 4
	};

	/** The following settings map 1:1 to contents in service.xml files (thank god).
	 *  That does not mean I understand what they do, specially the QoS part. */

	struct ServiceConnectionRequestSession {
		quint16 sessionId;
		quint16 channelId;
		quint8 qosType;
		quint8 qosDataRate;
		quint8 qosPriority;
		quint8 payloadType;
	};

	struct ServiceConnectionRequestFrame {
		quint8 messageType;
		quint16 acceptorId;
		quint16 initiatorId;
		QString profile;
		QList<ServiceConnectionRequestSession> sessions;
	};

	static ServiceConnectionRequestFrame unpackServiceConnectionRequestFrame(const QByteArray &data);
	static QByteArray packServiceConnectionRequestFrame(const ServiceConnectionRequestFrame &frame);

	struct ServiceConnectionResponseFrame {
		quint8 messageType;
		quint16 acceptorId;
		quint16 initiatorId;
		QString profile;
		quint8 statusCode;
		QList<quint16> sessions;
	};

	static ServiceConnectionResponseFrame unpackServiceConnectionResponseFrame(const QByteArray &data);
	static QByteArray packServiceConnectionResponseFrame(const ServiceConnectionResponseFrame &frame);

	struct ServiceTerminationRequestFrame {
		quint8 messageType;
		quint16 acceptorId;
		quint16 initiatorId;
		QString profile;
	};

	static ServiceTerminationRequestFrame unpackServiceTerminationRequestFrame(const QByteArray &data);
	static QByteArray packServiceTerminationRequestFrame(const ServiceTerminationRequestFrame &frame);

	struct ServiceTerminationResponseFrame {
		quint8 messageType;
		quint16 acceptorId;
		quint16 initiatorId;
		QString profile;
		quint8 statusCode;
	};

	static ServiceTerminationResponseFrame unpackServiceTerminationResponseFrame(const QByteArray &data);
	static QByteArray packServiceTerminationResponseFrame(const ServiceTerminationResponseFrame &frame);

	/* Capability discovery messages */

	static const QLatin1String capabilityDiscoveryProfile;
	static const quint16 capabilityDiscoveryChannel = 255;
	static const quint16 capabilityDiscoveryAgentId = 0xFFFF;

	enum CapabilityDiscoveryMessageType {
		CapabilityDiscoveryMessageTypeQuery = 1,
		CapabilityDiscoveryMessageTypeResponse = 2
	};

	struct CapabilityDiscoveryQuery {
		CapabilityDiscoveryMessageType messageType;
		quint8 queryType;
		quint32 checksum;
		QList<QString> records;
	};

	static CapabilityDiscoveryQuery unpackCapabilityDiscoveryQuery(const QByteArray &data);
	static QByteArray packCapabilityDiscoveryQuery(const CapabilityDiscoveryQuery &msg);

	struct CapabilityDiscoveryService {
		quint16 componentId;
		quint16 aspVersion;
		quint8 role;
		quint16 connTimeout;
		QString profile;
	};

	struct CapabilityDiscoveryProvider {
		quint16 uuid;
		QString name;
		QList<CapabilityDiscoveryService> services;
	};

	struct CapabilityDiscoveryResponse {
		CapabilityDiscoveryMessageType messageType;
		quint8 queryType;
		quint32 checksum;
		QList<CapabilityDiscoveryProvider> providers;
	};

	static CapabilityDiscoveryResponse unpackCapabilityDiscoveryResponse(const QByteArray &data);
	static QByteArray packCapabilityDiscoveryResponse(const CapabilityDiscoveryResponse &msg);

private:
	Q_DISABLE_COPY(SAProtocol)
	SAProtocol();
};

#endif // SAPROTOCOL_H