aboutsummaryrefslogtreecommitdiffstats
path: root/Demos/Device/LowLevel/RNDISEthernet/Lib/RNDIS.c
blob: e6dd691ece1442e4cf6a8c5c4769bd1d149177a4 (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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
/*
             LUFA Library
     Copyright (C) Dean Camera, 2010.

  dean [at] fourwalledcubicle [dot] com
           www.lufa-lib.org
*/

/*
  Copyright 2010  Dean Camera (dean [at] fourwalledcubicle [dot] com)

  Permission to use, copy, modify, distribute, and sell this
  software and its documentation for any purpose is hereby granted
  without fee, provided that the above copyright notice appear in
  all copies and that both that the copyright notice and this
  permission notice and warranty disclaimer appear in supporting
  documentation, and that the name of the author not be used in
  advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.

  The author disclaim all warranties with regard to this
  software, including all implied warranties of merchantability
  and fitness.  In no event shall the author be liable for any
  special, indirect or consequential damages or any damages
  whatsoever resulting from loss of use, data or profits, whether
  in an action of contract, negligence or other tortious action,
  arising out of or in connection with the use or performance of
  this software.
*/

/** \file
 *
 *  RNDIS command handler functions. This handles RNDIS commands according to
 *  the Microsoft RNDIS specification, creating a USB Ethernet network adapter.
 */

#define  INCLUDE_FROM_RNDIS_C
#include "RNDIS.h"

/** Physical MAC address of the network adapter, which becomes the MAC address of the host for packets sent to the adapter. */
static MAC_Address_t  PROGMEM AdapterMACAddress          = {ADAPTER_MAC_ADDRESS};

/** Vendor description of the adapter. This is overridden by the INF file required to install the appropriate RNDIS drivers for
 *  the device, but may still be used by the OS in some circumstances.
 */
static char           PROGMEM AdapterVendorDescription[] = "LUFA RNDIS Adapter";

/** List of RNDIS OID commands supported by this adapter. */
static const uint32_t PROGMEM AdapterSupportedOIDList[]  =
							{
								OID_GEN_SUPPORTED_LIST,
								OID_GEN_PHYSICAL_MEDIUM,
								OID_GEN_HARDWARE_STATUS,
								OID_GEN_MEDIA_SUPPORTED,
								OID_GEN_MEDIA_IN_USE,
								OID_GEN_MAXIMUM_FRAME_SIZE,
								OID_GEN_MAXIMUM_TOTAL_SIZE,
								OID_GEN_LINK_SPEED,
								OID_GEN_TRANSMIT_BLOCK_SIZE,
								OID_GEN_RECEIVE_BLOCK_SIZE,
								OID_GEN_VENDOR_ID,
								OID_GEN_VENDOR_DESCRIPTION,
								OID_GEN_CURRENT_PACKET_FILTER,
								OID_GEN_MAXIMUM_TOTAL_SIZE,
								OID_GEN_MEDIA_CONNECT_STATUS,
								OID_GEN_XMIT_OK,
								OID_GEN_RCV_OK,
								OID_GEN_XMIT_ERROR,
								OID_GEN_RCV_ERROR,
								OID_GEN_RCV_NO_BUFFER,
								OID_802_3_PERMANENT_ADDRESS,
								OID_802_3_CURRENT_ADDRESS,
								OID_802_3_MULTICAST_LIST,
								OID_802_3_MAXIMUM_LIST_SIZE,
								OID_802_3_RCV_ERROR_ALIGNMENT,
								OID_802_3_XMIT_ONE_COLLISION,
								OID_802_3_XMIT_MORE_COLLISIONS,
							};

/** Buffer for RNDIS messages (as distinct from Ethernet frames sent through the adapter. This must be big enough to hold the entire
 *  Supported OID list, plus the response header. The buffer is half-duplex, and is written to as it is read to save on SRAM - for this
 *  reason, care must be taken when constructing RNDIS responses that unread data is not overwritten when writing in responses.
 */
uint8_t                 RNDISMessageBuffer[sizeof(AdapterSupportedOIDList) + sizeof(RNDIS_Query_Complete_t)];

/** Pointer to the RNDIS message header at the top of the RNDIS message buffer, for convenience. */
RNDIS_Message_Header_t* MessageHeader = (RNDIS_Message_Header_t*)&RNDISMessageBuffer;

/** Indicates if a RNDIS message response is ready to be sent back to the host. */
bool                    ResponseReady               = false;

/** Current RNDIS adapter state, a value from the RNDIS_States_t enum. */
uint8_t                 CurrRNDISState              = RNDIS_Uninitialized;

/** Current Ethernet packet filter mask. This is non-zero when the adapter is initialized, or zero when disabled. */
uint32_t                CurrPacketFilter            = 0;


/** Processes the RNDIS message received by the host and stored in the RNDISMessageBuffer global buffer. If a response is
 *  created, the ResponseReady global is updated so that the response is written back to the host upon request.
 */
void ProcessRNDISControlMessage(void)
{
	/* Note: Only a single buffer is used for both the received message and its response to save SRAM. Because of
	         this, response bytes should be filled in order so that they do not clobber unread data in the buffer. */

	switch (MessageHeader->MessageType)
	{
		case REMOTE_NDIS_INITIALIZE_MSG:
			/* Initialize the adapter - return information about the supported RNDIS version and buffer sizes */

			ResponseReady = true;

			RNDIS_Initialize_Message_t*  INITIALIZE_Message  = (RNDIS_Initialize_Message_t*)&RNDISMessageBuffer;
			RNDIS_Initialize_Complete_t* INITIALIZE_Response = (RNDIS_Initialize_Complete_t*)&RNDISMessageBuffer;

			INITIALIZE_Response->MessageType           = REMOTE_NDIS_INITIALIZE_CMPLT;
			INITIALIZE_Response->MessageLength         = sizeof(RNDIS_Initialize_Complete_t);
			INITIALIZE_Response->RequestId             = INITIALIZE_Message->RequestId;
			INITIALIZE_Response->Status                = REMOTE_NDIS_STATUS_SUCCESS;

			INITIALIZE_Response->MajorVersion          = REMOTE_NDIS_VERSION_MAJOR;
			INITIALIZE_Response->MinorVersion          = REMOTE_NDIS_VERSION_MINOR;
			INITIALIZE_Response->DeviceFlags           = REMOTE_NDIS_DF_CONNECTIONLESS;
			INITIALIZE_Response->Medium                = REMOTE_NDIS_MEDIUM_802_3;
			INITIALIZE_Response->MaxPacketsPerTransfer = 1;
			INITIALIZE_Response->MaxTransferSize       = (sizeof(RNDIS_Packet_Message_t) + ETHERNET_FRAME_SIZE_MAX);
			INITIALIZE_Response->PacketAlignmentFactor = 0;
			INITIALIZE_Response->AFListOffset          = 0;
			INITIALIZE_Response->AFListSize            = 0;

			CurrRNDISState = RNDIS_Initialized;

			break;
		case REMOTE_NDIS_HALT_MSG:
			/* Halt the adapter, reset the adapter state - note that no response should be returned when completed */

			ResponseReady = false;
			MessageHeader->MessageLength = 0;

			CurrRNDISState = RNDIS_Uninitialized;

			break;
		case REMOTE_NDIS_QUERY_MSG:
			/* Request for information about a parameter about the adapter, specified as an OID token */

			ResponseReady = true;

			RNDIS_Query_Message_t*  QUERY_Message  = (RNDIS_Query_Message_t*)&RNDISMessageBuffer;
			RNDIS_Query_Complete_t* QUERY_Response = (RNDIS_Query_Complete_t*)&RNDISMessageBuffer;
			uint32_t                Query_Oid      = QUERY_Message->Oid;

			void*     QueryData                 = &RNDISMessageBuffer[sizeof(RNDIS_Message_Header_t) +
			                                                          QUERY_Message->InformationBufferOffset];
			void*     ResponseData              = &RNDISMessageBuffer[sizeof(RNDIS_Query_Complete_t)];
			uint16_t  ResponseSize;

			QUERY_Response->MessageType         = REMOTE_NDIS_QUERY_CMPLT;
			QUERY_Response->MessageLength       = sizeof(RNDIS_Query_Complete_t);

			if (ProcessNDISQuery(Query_Oid, QueryData, QUERY_Message->InformationBufferLength,
			                     ResponseData, &ResponseSize))
			{
				QUERY_Response->Status                  = REMOTE_NDIS_STATUS_SUCCESS;
				QUERY_Response->MessageLength          += ResponseSize;

				QUERY_Response->InformationBufferLength = ResponseSize;
				QUERY_Response->InformationBufferOffset = (sizeof(RNDIS_Query_Complete_t) - sizeof(RNDIS_Message_Header_t));
			}
			else
			{
				QUERY_Response->Status                  = REMOTE_NDIS_STATUS_NOT_SUPPORTED;

				QUERY_Response->InformationBufferLength = 0;
				QUERY_Response->InformationBufferOffset = 0;
			}

			break;
		case REMOTE_NDIS_SET_MSG:
			/* Request to set a parameter of the adapter, specified as an OID token */

			ResponseReady = true;

			RNDIS_Set_Message_t*  SET_Message  = (RNDIS_Set_Message_t*)&RNDISMessageBuffer;
			RNDIS_Set_Complete_t* SET_Response = (RNDIS_Set_Complete_t*)&RNDISMessageBuffer;
			uint32_t              SET_Oid      = SET_Message->Oid;

			SET_Response->MessageType       = REMOTE_NDIS_SET_CMPLT;
			SET_Response->MessageLength     = sizeof(RNDIS_Set_Complete_t);
			SET_Response->RequestId         = SET_Message->RequestId;

			void* SetData                   = &RNDISMessageBuffer[sizeof(RNDIS_Message_Header_t) +
			                                                      SET_Message->InformationBufferOffset];

			if (ProcessNDISSet(SET_Oid, SetData, SET_Message->InformationBufferLength))
			  SET_Response->Status        = REMOTE_NDIS_STATUS_SUCCESS;
			else
			  SET_Response->Status        = REMOTE_NDIS_STATUS_NOT_SUPPORTED;

			break;
		case REMOTE_NDIS_RESET_MSG:
			/* Soft reset the adapter */

			ResponseReady = true;

			RNDIS_Reset_Complete_t* RESET_Response = (RNDIS_Reset_Complete_t*)&RNDISMessageBuffer;

			RESET_Response->MessageType         = REMOTE_NDIS_RESET_CMPLT;
			RESET_Response->MessageLength       = sizeof(RNDIS_Reset_Complete_t);
			RESET_Response->Status              = REMOTE_NDIS_STATUS_SUCCESS;
			RESET_Response->AddressingReset     = 0;

			break;
		case REMOTE_NDIS_KEEPALIVE_MSG:
			/* Keep alive message sent to the adapter every 5 seconds when idle to ensure it is still responding */

			ResponseReady = true;

			RNDIS_KeepAlive_Message_t*  KEEPALIVE_Message  = (RNDIS_KeepAlive_Message_t*)&RNDISMessageBuffer;
			RNDIS_KeepAlive_Complete_t* KEEPALIVE_Response = (RNDIS_KeepAlive_Complete_t*)&RNDISMessageBuffer;

			KEEPALIVE_Response->MessageType     = REMOTE_NDIS_KEEPALIVE_CMPLT;
			KEEPALIVE_Response->MessageLength   = sizeof(RNDIS_KeepAlive_Complete_t);
			KEEPALIVE_Response->RequestId       = KEEPALIVE_Message->RequestId;
			KEEPALIVE_Response->Status          = REMOTE_NDIS_STATUS_SUCCESS;

			break;
	}
}

/** Processes RNDIS query commands, retrieving information from the adapter and reporting it back to the host. The requested
 *  parameter is given as an OID value.
 *
 *  \param[in] OId            OId value of the parameter being queried
 *  \param[in] QueryData      Pointer to any extra query data being sent by the host to the device inside the RNDIS message buffer
 *  \param[in] QuerySize      Size in bytes of the extra query data being sent by the host
 *  \param[out] ResponseData  Pointer to the start of the query response inside the RNDIS message buffer
 *  \param[out] ResponseSize  Pointer to the size in bytes of the response data being sent to the host
 *
 *  \return Boolean true if the query was handled, false otherwise
 */
static bool ProcessNDISQuery(const uint32_t OId, void* QueryData, uint16_t QuerySize,
                             void* ResponseData, uint16_t* ResponseSize)
{
	/* Handler for REMOTE_NDIS_QUERY_MSG messages */

	switch (OId)
	{
		case OID_GEN_SUPPORTED_LIST:
			*ResponseSize = sizeof(AdapterSupportedOIDList);

			/* Copy the list of supported NDIS OID tokens to the response buffer */
			memcpy_P(ResponseData, AdapterSupportedOIDList, sizeof(AdapterSupportedOIDList));

			return true;
		case OID_GEN_PHYSICAL_MEDIUM:
			*ResponseSize = sizeof(uint32_t);

			/* Indicate that the device is a true ethernet link */
			*((uint32_t*)ResponseData) = 0;

			return true;
		case OID_GEN_HARDWARE_STATUS:
			*ResponseSize = sizeof(uint32_t);

			/* Always indicate hardware ready */
			*((uint32_t*)ResponseData) = NDIS_HardwareStatus_Ready;

			return true;
		case OID_GEN_MEDIA_SUPPORTED:
		case OID_GEN_MEDIA_IN_USE:
			*ResponseSize = sizeof(uint32_t);

			/* Indicate 802.3 (Ethernet) supported by the adapter */
			*((uint32_t*)ResponseData) = REMOTE_NDIS_MEDIUM_802_3;

			return true;
		case OID_GEN_VENDOR_ID:
			*ResponseSize = sizeof(uint32_t);

			/* Vendor ID 0x0xFFFFFF is reserved for vendors who have not purchased a NDIS VID */
			*((uint32_t*)ResponseData) = 0x00FFFFFF;

			return true;
		case OID_GEN_MAXIMUM_FRAME_SIZE:
		case OID_GEN_TRANSMIT_BLOCK_SIZE:
		case OID_GEN_RECEIVE_BLOCK_SIZE:
			*ResponseSize = sizeof(uint32_t);

			/* Indicate that the maximum frame size is the size of the ethernet frame buffer */
			*((uint32_t*)ResponseData) = ETHERNET_FRAME_SIZE_MAX;

			return true;
		case OID_GEN_VENDOR_DESCRIPTION:
			*ResponseSize = sizeof(AdapterVendorDescription);

			/* Copy vendor description string to the response buffer */
			memcpy_P(ResponseData, AdapterVendorDescription, sizeof(AdapterVendorDescription));

			return true;
		case OID_GEN_MEDIA_CONNECT_STATUS:
			*ResponseSize = sizeof(uint32_t);

			/* Always indicate that the adapter is connected to a network */
			*((uint32_t*)ResponseData) = REMOTE_NDIS_MEDIA_STATE_CONNECTED;

			return true;
		case OID_GEN_LINK_SPEED:
			*ResponseSize = sizeof(uint32_t);

			/* Indicate 10Mb/s link speed */
			*((uint32_t*)ResponseData) = 100000;

			return true;
		case OID_802_3_PERMANENT_ADDRESS:
		case OID_802_3_CURRENT_ADDRESS:
			*ResponseSize = sizeof(MAC_Address_t);

			/* Copy over the fixed adapter MAC to the response buffer */
			memcpy_P(ResponseData, &AdapterMACAddress, sizeof(MAC_Address_t));

			return true;
		case OID_802_3_MAXIMUM_LIST_SIZE:
			*ResponseSize = sizeof(uint32_t);

			/* Indicate only one multicast address supported */
			*((uint32_t*)ResponseData) = 1;

			return true;
		case OID_GEN_CURRENT_PACKET_FILTER:
			*ResponseSize = sizeof(uint32_t);

			/* Indicate the current packet filter mask */
			*((uint32_t*)ResponseData) = CurrPacketFilter;

			return true;
		case OID_GEN_XMIT_OK:
		case OID_GEN_RCV_OK:
		case OID_GEN_XMIT_ERROR:
		case OID_GEN_RCV_ERROR:
		case OID_GEN_RCV_NO_BUFFER:
		case OID_802_3_RCV_ERROR_ALIGNMENT:
		case OID_802_3_XMIT_ONE_COLLISION:
		case OID_802_3_XMIT_MORE_COLLISIONS:
			*ResponseSize = sizeof(uint32_t);

			/* Unused statistic OIDs - always return 0 for each */
			*((uint32_t*)ResponseData) = 0;

			return true;
		case OID_GEN_MAXIMUM_TOTAL_SIZE:
			*ResponseSize = sizeof(uint32_t);

			/* Indicate maximum overall buffer (Ethernet frame and RNDIS header) the adapter can handle */
			*((uint32_t*)ResponseData) = (sizeof(RNDISMessageBuffer) + ETHERNET_FRAME_SIZE_MAX);

			return true;
		default:
			return false;
	}
}

/** Processes RNDIS set commands, setting adapter parameters to values given by the host. The requested parameter is given
 *  as an OID value.
 *
 *  \param[in] OId      OId value of the parameter being set
 *  \param[in] SetData  Pointer to the parameter value in the RNDIS message buffer
 *  \param[in] SetSize  Size in bytes of the parameter value being sent by the host
 *
 *  \return Boolean true if the set was handled, false otherwise
 */
static bool ProcessNDISSet(uint32_t OId, void* SetData, uint16_t SetSize)
{
	/* Handler for REMOTE_NDIS_SET_MSG messages */

	switch (OId)
	{
		case OID_GEN_CURRENT_PACKET_FILTER:
			/* Save the packet filter mask in case the host queries it again later */
			CurrPacketFilter = *((uint32_t*)SetData);

			/* Set the RNDIS state to initialized if the packet filter is non-zero */
			CurrRNDISState = ((CurrPacketFilter) ? RNDIS_Data_Initialized : RNDIS_Data_Initialized);

			return true;
		case OID_802_3_MULTICAST_LIST:
			/* Do nothing - throw away the value from the host as it is unused */

			return true;
		default:
			return false;
	}
}