ced33390f3943a6aa0df28d82fbb03c99a33a7aa
[pub/USBasp.git] / Demos / Device / LowLevel / RNDISEthernet / Lib / RNDIS.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2009.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.fourwalledcubicle.com
7 */
8
9 /*
10 Copyright 2009 Dean Camera (dean [at] fourwalledcubicle [dot] com)
11
12 Permission to use, copy, modify, and distribute this software
13 and its documentation for any purpose and without fee is hereby
14 granted, provided that the above copyright notice appear in all
15 copies and that both that the copyright notice and this
16 permission notice and warranty disclaimer appear in supporting
17 documentation, and that the name of the author not be used in
18 advertising or publicity pertaining to distribution of the
19 software without specific, written prior permission.
20
21 The author disclaim all warranties with regard to this
22 software, including all implied warranties of merchantability
23 and fitness. In no event shall the author be liable for any
24 special, indirect or consequential damages or any damages
25 whatsoever resulting from loss of use, data or profits, whether
26 in an action of contract, negligence or other tortious action,
27 arising out of or in connection with the use or performance of
28 this software.
29 */
30
31 /** \file
32 *
33 * RNDIS command handler functions. This handles RNDIS commands according to
34 * the Microsoft RNDIS specification, creating a USB Ethernet network adapter.
35 */
36
37 #define INCLUDE_FROM_RNDIS_C
38 #include "RNDIS.h"
39
40 /* Global Variables: */
41 /** Physical MAC address of the network adapter, which becomes the MAC address of the host for packets sent to the adapter. */
42 static MAC_Address_t PROGMEM AdapterMACAddress = {ADAPTER_MAC_ADDRESS};
43
44 /** Vendor description of the adapter. This is overridden by the INF file required to install the appropriate RNDIS drivers for
45 * the device, but may still be used by the OS in some circumstances.
46 */
47 static char PROGMEM AdapterVendorDescription[] = "LUFA RNDIS Adapter";
48
49 /** List of RNDIS OID commands supported by this adapter. */
50 static const uint32_t PROGMEM AdapterSupportedOIDList[] =
51 {
52 OID_GEN_SUPPORTED_LIST,
53 OID_GEN_PHYSICAL_MEDIUM,
54 OID_GEN_HARDWARE_STATUS,
55 OID_GEN_MEDIA_SUPPORTED,
56 OID_GEN_MEDIA_IN_USE,
57 OID_GEN_MAXIMUM_FRAME_SIZE,
58 OID_GEN_MAXIMUM_TOTAL_SIZE,
59 OID_GEN_LINK_SPEED,
60 OID_GEN_TRANSMIT_BLOCK_SIZE,
61 OID_GEN_RECEIVE_BLOCK_SIZE,
62 OID_GEN_VENDOR_ID,
63 OID_GEN_VENDOR_DESCRIPTION,
64 OID_GEN_CURRENT_PACKET_FILTER,
65 OID_GEN_MAXIMUM_TOTAL_SIZE,
66 OID_GEN_MEDIA_CONNECT_STATUS,
67 OID_GEN_XMIT_OK,
68 OID_GEN_RCV_OK,
69 OID_GEN_XMIT_ERROR,
70 OID_GEN_RCV_ERROR,
71 OID_GEN_RCV_NO_BUFFER,
72 OID_802_3_PERMANENT_ADDRESS,
73 OID_802_3_CURRENT_ADDRESS,
74 OID_802_3_MULTICAST_LIST,
75 OID_802_3_MAXIMUM_LIST_SIZE,
76 OID_802_3_RCV_ERROR_ALIGNMENT,
77 OID_802_3_XMIT_ONE_COLLISION,
78 OID_802_3_XMIT_MORE_COLLISIONS,
79 };
80
81 /** Buffer for RNDIS messages (as distinct from Ethernet frames sent through the adapter. This must be big enough to hold the entire
82 * 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
83 * reason, care must be taken when constructing RNDIS responses that unread data is not overwritten when writing in responses.
84 */
85 uint8_t RNDISMessageBuffer[sizeof(AdapterSupportedOIDList) + sizeof(RNDIS_Query_Complete_t)];
86
87 /** Pointer to the RNDIS message header at the top of the RNDIS message buffer, for convenience. */
88 RNDIS_Message_Header_t* MessageHeader = (RNDIS_Message_Header_t*)&RNDISMessageBuffer;
89
90 /** Indicates if a RNDIS message response is ready to be sent back to the host. */
91 bool ResponseReady = false;
92
93 /** Current RNDIS adapter state, a value from the RNDIS_States_t enum. */
94 uint8_t CurrRNDISState = RNDIS_Uninitialized;
95
96 /** Current Ethernet packet filter mask. This is non-zero when the adapter is initialized, or zero when disabled. */
97 uint32_t CurrPacketFilter = 0;
98
99
100 /** Processes the RNDIS message received by the host and stored in the RNDISMessageBuffer global buffer. If a response is
101 * created, the ResponseReady global is updated so that the response is written back to the host upon request.
102 */
103 void ProcessRNDISControlMessage(void)
104 {
105 /* Note: Only a single buffer is used for both the received message and its response to save SRAM. Because of
106 this, response bytes should be filled in order so that they do not clobber unread data in the buffer. */
107
108 switch (MessageHeader->MessageType)
109 {
110 case REMOTE_NDIS_INITIALIZE_MSG:
111 /* Initialize the adapter - return information about the supported RNDIS version and buffer sizes */
112
113 ResponseReady = true;
114
115 RNDIS_Initialize_Message_t* INITIALIZE_Message = (RNDIS_Initialize_Message_t*)&RNDISMessageBuffer;
116 RNDIS_Initialize_Complete_t* INITIALIZE_Response = (RNDIS_Initialize_Complete_t*)&RNDISMessageBuffer;
117
118 INITIALIZE_Response->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT;
119 INITIALIZE_Response->MessageLength = sizeof(RNDIS_Initialize_Complete_t);
120 INITIALIZE_Response->RequestId = INITIALIZE_Message->RequestId;
121 INITIALIZE_Response->Status = REMOTE_NDIS_STATUS_SUCCESS;
122
123 INITIALIZE_Response->MajorVersion = REMOTE_NDIS_VERSION_MAJOR;
124 INITIALIZE_Response->MinorVersion = REMOTE_NDIS_VERSION_MINOR;
125 INITIALIZE_Response->DeviceFlags = REMOTE_NDIS_DF_CONNECTIONLESS;
126 INITIALIZE_Response->Medium = REMOTE_NDIS_MEDIUM_802_3;
127 INITIALIZE_Response->MaxPacketsPerTransfer = 1;
128 INITIALIZE_Response->MaxTransferSize = (sizeof(RNDIS_Packet_Message_t) + ETHERNET_FRAME_SIZE_MAX);
129 INITIALIZE_Response->PacketAlignmentFactor = 0;
130 INITIALIZE_Response->AFListOffset = 0;
131 INITIALIZE_Response->AFListSize = 0;
132
133 CurrRNDISState = RNDIS_Initialized;
134
135 break;
136 case REMOTE_NDIS_HALT_MSG:
137 /* Halt the adapter, reset the adapter state - note that no response should be returned when completed */
138
139 ResponseReady = false;
140 MessageHeader->MessageLength = 0;
141
142 CurrRNDISState = RNDIS_Uninitialized;
143
144 break;
145 case REMOTE_NDIS_QUERY_MSG:
146 /* Request for information about a parameter about the adapter, specified as an OID token */
147
148 ResponseReady = true;
149
150 RNDIS_Query_Message_t* QUERY_Message = (RNDIS_Query_Message_t*)&RNDISMessageBuffer;
151 RNDIS_Query_Complete_t* QUERY_Response = (RNDIS_Query_Complete_t*)&RNDISMessageBuffer;
152 uint32_t Query_Oid = QUERY_Message->Oid;
153
154 void* QueryData = &RNDISMessageBuffer[sizeof(RNDIS_Message_Header_t) +
155 QUERY_Message->InformationBufferOffset];
156 void* ResponseData = &RNDISMessageBuffer[sizeof(RNDIS_Query_Complete_t)];
157 uint16_t ResponseSize;
158
159 QUERY_Response->MessageType = REMOTE_NDIS_QUERY_CMPLT;
160 QUERY_Response->MessageLength = sizeof(RNDIS_Query_Complete_t);
161
162 if (ProcessNDISQuery(Query_Oid, QueryData, QUERY_Message->InformationBufferLength,
163 ResponseData, &ResponseSize))
164 {
165 QUERY_Response->Status = REMOTE_NDIS_STATUS_SUCCESS;
166 QUERY_Response->MessageLength += ResponseSize;
167
168 QUERY_Response->InformationBufferLength = ResponseSize;
169 QUERY_Response->InformationBufferOffset = (sizeof(RNDIS_Query_Complete_t) - sizeof(RNDIS_Message_Header_t));
170 }
171 else
172 {
173 QUERY_Response->Status = REMOTE_NDIS_STATUS_NOT_SUPPORTED;
174
175 QUERY_Response->InformationBufferLength = 0;
176 QUERY_Response->InformationBufferOffset = 0;
177 }
178
179 break;
180 case REMOTE_NDIS_SET_MSG:
181 /* Request to set a parameter of the adapter, specified as an OID token */
182
183 ResponseReady = true;
184
185 RNDIS_Set_Message_t* SET_Message = (RNDIS_Set_Message_t*)&RNDISMessageBuffer;
186 RNDIS_Set_Complete_t* SET_Response = (RNDIS_Set_Complete_t*)&RNDISMessageBuffer;
187 uint32_t SET_Oid = SET_Message->Oid;
188
189 SET_Response->MessageType = REMOTE_NDIS_SET_CMPLT;
190 SET_Response->MessageLength = sizeof(RNDIS_Set_Complete_t);
191 SET_Response->RequestId = SET_Message->RequestId;
192
193 void* SetData = &RNDISMessageBuffer[sizeof(RNDIS_Message_Header_t) +
194 SET_Message->InformationBufferOffset];
195
196 if (ProcessNDISSet(SET_Oid, SetData, SET_Message->InformationBufferLength))
197 SET_Response->Status = REMOTE_NDIS_STATUS_SUCCESS;
198 else
199 SET_Response->Status = REMOTE_NDIS_STATUS_NOT_SUPPORTED;
200
201 break;
202 case REMOTE_NDIS_RESET_MSG:
203 /* Soft reset the adapter */
204
205 ResponseReady = true;
206
207 RNDIS_Reset_Complete_t* RESET_Response = (RNDIS_Reset_Complete_t*)&RNDISMessageBuffer;
208
209 RESET_Response->MessageType = REMOTE_NDIS_RESET_CMPLT;
210 RESET_Response->MessageLength = sizeof(RNDIS_Reset_Complete_t);
211 RESET_Response->Status = REMOTE_NDIS_STATUS_SUCCESS;
212 RESET_Response->AddressingReset = 0;
213
214 break;
215 case REMOTE_NDIS_KEEPALIVE_MSG:
216 /* Keep alive message sent to the adapter every 5 seconds when idle to ensure it is still responding */
217
218 ResponseReady = true;
219
220 RNDIS_KeepAlive_Message_t* KEEPALIVE_Message = (RNDIS_KeepAlive_Message_t*)&RNDISMessageBuffer;
221 RNDIS_KeepAlive_Complete_t* KEEPALIVE_Response = (RNDIS_KeepAlive_Complete_t*)&RNDISMessageBuffer;
222
223 KEEPALIVE_Response->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT;
224 KEEPALIVE_Response->MessageLength = sizeof(RNDIS_KeepAlive_Complete_t);
225 KEEPALIVE_Response->RequestId = KEEPALIVE_Message->RequestId;
226 KEEPALIVE_Response->Status = REMOTE_NDIS_STATUS_SUCCESS;
227
228 break;
229 }
230 }
231
232 /** Processes RNDIS query commands, retrieving information from the adapter and reporting it back to the host. The requested
233 * parameter is given as an OID value.
234 *
235 * \param[in] OId OId value of the parameter being queried
236 * \param[in] QueryData Pointer to any extra query data being sent by the host to the device inside the RNDIS message buffer
237 * \param[in] QuerySize Size in bytes of the extra query data being sent by the host
238 * \param[out] ResponseData Pointer to the start of the query response inside the RNDIS message buffer
239 * \param[out] ResponseSize Pointer to the size in bytes of the response data being sent to the host
240 *
241 * \return Boolean true if the query was handled, false otherwise
242 */
243 static bool ProcessNDISQuery(uint32_t OId, void* QueryData, uint16_t QuerySize,
244 void* ResponseData, uint16_t* ResponseSize)
245 {
246 /* Handler for REMOTE_NDIS_QUERY_MSG messages */
247
248 switch (OId)
249 {
250 case OID_GEN_SUPPORTED_LIST:
251 *ResponseSize = sizeof(AdapterSupportedOIDList);
252
253 /* Copy the list of supported NDIS OID tokens to the response buffer */
254 memcpy_P(ResponseData, AdapterSupportedOIDList, sizeof(AdapterSupportedOIDList));
255
256 return true;
257 case OID_GEN_PHYSICAL_MEDIUM:
258 *ResponseSize = sizeof(uint32_t);
259
260 /* Indicate that the device is a true ethernet link */
261 *((uint32_t*)ResponseData) = 0;
262
263 return true;
264 case OID_GEN_HARDWARE_STATUS:
265 *ResponseSize = sizeof(uint32_t);
266
267 /* Always indicate hardware ready */
268 *((uint32_t*)ResponseData) = NdisHardwareStatusReady;
269
270 return true;
271 case OID_GEN_MEDIA_SUPPORTED:
272 case OID_GEN_MEDIA_IN_USE:
273 *ResponseSize = sizeof(uint32_t);
274
275 /* Indicate 802.3 (Ethernet) supported by the adapter */
276 *((uint32_t*)ResponseData) = REMOTE_NDIS_MEDIUM_802_3;
277
278 return true;
279 case OID_GEN_VENDOR_ID:
280 *ResponseSize = sizeof(uint32_t);
281
282 /* Vendor ID 0x0xFFFFFF is reserved for vendors who have not purchased a NDIS VID */
283 *((uint32_t*)ResponseData) = 0x00FFFFFF;
284
285 return true;
286 case OID_GEN_MAXIMUM_FRAME_SIZE:
287 case OID_GEN_TRANSMIT_BLOCK_SIZE:
288 case OID_GEN_RECEIVE_BLOCK_SIZE:
289 *ResponseSize = sizeof(uint32_t);
290
291 /* Indicate that the maximum frame size is the size of the ethernet frame buffer */
292 *((uint32_t*)ResponseData) = ETHERNET_FRAME_SIZE_MAX;
293
294 return true;
295 case OID_GEN_VENDOR_DESCRIPTION:
296 *ResponseSize = sizeof(AdapterVendorDescription);
297
298 /* Copy vendor description string to the response buffer */
299 memcpy_P(ResponseData, AdapterVendorDescription, sizeof(AdapterVendorDescription));
300
301 return true;
302 case OID_GEN_MEDIA_CONNECT_STATUS:
303 *ResponseSize = sizeof(uint32_t);
304
305 /* Always indicate that the adapter is connected to a network */
306 *((uint32_t*)ResponseData) = REMOTE_NDIS_MEDIA_STATE_CONNECTED;
307
308 return true;
309 case OID_GEN_LINK_SPEED:
310 *ResponseSize = sizeof(uint32_t);
311
312 /* Indicate 10Mb/s link speed */
313 *((uint32_t*)ResponseData) = 100000;
314
315 return true;
316 case OID_802_3_PERMANENT_ADDRESS:
317 case OID_802_3_CURRENT_ADDRESS:
318 *ResponseSize = sizeof(MAC_Address_t);
319
320 /* Copy over the fixed adapter MAC to the response buffer */
321 memcpy_P(ResponseData, &AdapterMACAddress, sizeof(MAC_Address_t));
322
323 return true;
324 case OID_802_3_MAXIMUM_LIST_SIZE:
325 *ResponseSize = sizeof(uint32_t);
326
327 /* Indicate only one multicast address supported */
328 *((uint32_t*)ResponseData) = 1;
329
330 return true;
331 case OID_GEN_CURRENT_PACKET_FILTER:
332 *ResponseSize = sizeof(uint32_t);
333
334 /* Indicate the current packet filter mask */
335 *((uint32_t*)ResponseData) = CurrPacketFilter;
336
337 return true;
338 case OID_GEN_XMIT_OK:
339 case OID_GEN_RCV_OK:
340 case OID_GEN_XMIT_ERROR:
341 case OID_GEN_RCV_ERROR:
342 case OID_GEN_RCV_NO_BUFFER:
343 case OID_802_3_RCV_ERROR_ALIGNMENT:
344 case OID_802_3_XMIT_ONE_COLLISION:
345 case OID_802_3_XMIT_MORE_COLLISIONS:
346 *ResponseSize = sizeof(uint32_t);
347
348 /* Unused statistic OIDs - always return 0 for each */
349 *((uint32_t*)ResponseData) = 0;
350
351 return true;
352 case OID_GEN_MAXIMUM_TOTAL_SIZE:
353 *ResponseSize = sizeof(uint32_t);
354
355 /* Indicate maximum overall buffer (Ethernet frame and RNDIS header) the adapter can handle */
356 *((uint32_t*)ResponseData) = (sizeof(RNDISMessageBuffer) + ETHERNET_FRAME_SIZE_MAX);
357
358 return true;
359 default:
360 return false;
361 }
362 }
363
364 /** Processes RNDIS set commands, setting adapter parameters to values given by the host. The requested parameter is given
365 * as an OID value.
366 *
367 * \param[in] OId OId value of the parameter being set
368 * \param[in] SetData Pointer to the parameter value in the RNDIS message buffer
369 * \param[in] SetSize Size in bytes of the parameter value being sent by the host
370 *
371 * \return Boolean true if the set was handled, false otherwise
372 */
373 static bool ProcessNDISSet(uint32_t OId, void* SetData, uint16_t SetSize)
374 {
375 /* Handler for REMOTE_NDIS_SET_MSG messages */
376
377 switch (OId)
378 {
379 case OID_GEN_CURRENT_PACKET_FILTER:
380 /* Save the packet filter mask in case the host queries it again later */
381 CurrPacketFilter = *((uint32_t*)SetData);
382
383 /* Set the RNDIS state to initialized if the packet filter is non-zero */
384 CurrRNDISState = ((CurrPacketFilter) ? RNDIS_Data_Initialized : RNDIS_Data_Initialized);
385
386 return true;
387 case OID_802_3_MULTICAST_LIST:
388 /* Do nothing - throw away the value from the host as it is unused */
389
390 return true;
391 default:
392 return false;
393 }
394 }