usbd_rndis.c
Go to the documentation of this file.
1 /**
2  * @file usbd_rndis.c
3  * @brief USB RNDIS class
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL TRACE_LEVEL_INFO
33 
34 //Dependencies
35 #include "usbd_ctlreq.h"
36 #include "usbd_desc.h"
37 #include "usbd_rndis.h"
38 #include "core/net.h"
39 #include "rndis.h"
40 #include "rndis_driver.h"
41 #include "rndis_debug.h"
42 #include "debug.h"
43 
44 //Debug macros
45 #if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
46  #undef TRACE_DEBUG
47  #define TRACE_DEBUG(...) fprintf(stderr, __VA_ARGS__)
48  #undef TRACE_DEBUG_ARRAY
49  #define TRACE_DEBUG_ARRAY(p, a, n) debugDisplayArray(stderr, p, a, n)
50 #endif
51 
52 
53 /**
54  * @brief RNDIS class callbacks
55  **/
56 
57 USBD_ClassTypeDef usbdRndisClass =
58 {
62  NULL,
66  NULL,
67  NULL,
68  NULL,
73 };
74 
75 
76 /**
77  * @brief RNDIS class initialization
78  * @param[in] pdev Pointer to a USBD_HandleTypeDef structure
79  * @param[in] cfgidx Configuration index
80  * @return Status code
81  **/
82 
83 uint8_t usbdRndisInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
84 {
85  //Check current speed
86  if(pdev->dev_speed == USBD_SPEED_HIGH)
87  {
88  //Open DATA IN endpoint
89  USBD_LL_OpenEP(pdev, RNDIS_DATA_IN_EP,
90  USBD_EP_TYPE_BULK, RNDIS_DATA_IN_EP_MPS_HS);
91 
92  //Open DATA OUT endpoint
93  USBD_LL_OpenEP(pdev, RNDIS_DATA_OUT_EP,
94  USBD_EP_TYPE_BULK, RNDIS_DATA_OUT_EP_MPS_HS);
95  }
96  else
97  {
98  //Open DATA IN endpoint
99  USBD_LL_OpenEP(pdev, RNDIS_DATA_IN_EP,
100  USBD_EP_TYPE_BULK, RNDIS_DATA_IN_EP_MPS_FS);
101 
102  //Open DATA OUT endpoint
103  USBD_LL_OpenEP(pdev, RNDIS_DATA_OUT_EP,
104  USBD_EP_TYPE_BULK, RNDIS_DATA_OUT_EP_MPS_FS);
105  }
106 
107  //Open notification endpoint
108  USBD_LL_OpenEP(pdev, RNDIS_NOTIFICATION_EP,
109  USBD_EP_TYPE_INTR, RNDIS_NOTIFICATION_EP_MPS);
110 
111  //Initialize RNDIS class context
112  rndisInit();
113 
114  //Link the RNDIS class context
115  pdev->pClassData = &rndisContext;
116 
117  //Debug message
118  TRACE_DEBUG("### usbdRndisReceivePacket 000 ###\r\n");
119 
120  //Prepare DATA OUT endpoint for reception
121  USBD_LL_PrepareReceive(pdev, RNDIS_DATA_OUT_EP,
123 
124  //Reception is active
126 
127  //Switch to the RNDIS_BUS_INITIALIZED state
129 
130  //Successful initialization
131  return USBD_OK;
132 }
133 
134 
135 /**
136  * @brief RNDIS class de-initialization
137  * @param[in] pdev Pointer to a USBD_HandleTypeDef structure
138  * @param[in] cfgidx Configuration index
139  * @return Status code
140  **/
141 
142 uint8_t usbdRndisDeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
143 {
144  //Close DATA IN endpoint
145  USBD_LL_CloseEP(pdev, RNDIS_DATA_IN_EP);
146  //Close DATA OUT endpoint
147  USBD_LL_CloseEP(pdev, RNDIS_DATA_OUT_EP);
148  //Close notification endpoint
149  USBD_LL_CloseEP(pdev, RNDIS_NOTIFICATION_EP);
150 
151  //Unlink the RNDIS class context
152  pdev->pClassData = NULL;
153 
154  //Switch to the RNDIS_UNINITIALIZED state
156 
157  //Successful processing
158  return USBD_OK;
159 }
160 
161 
162 /**
163  * @brief Process incoming setup request
164  * @param[in] pdev Pointer to a USBD_HandleTypeDef structure
165  * @param[in] req Pointer to the setup request
166  * @return Status code
167  **/
168 
169 uint8_t usbdRndisSetup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
170 {
171  //Debug message
172  TRACE_DEBUG("USB setup packet received...\r\n");
173  TRACE_DEBUG(" bmRequest = 0x%02" PRIX8 "\r\n", req->bmRequest);
174  TRACE_DEBUG(" bRequest = 0x%02" PRIX8 "\r\n", req->bRequest);
175  TRACE_DEBUG(" wValue = 0x%04" PRIX16 "\r\n", req->wValue);
176  TRACE_DEBUG(" wIndex = 0x%04" PRIX16 "\r\n", req->wIndex);
177  TRACE_DEBUG(" wLength = 0x%04" PRIX16 "\r\n", req->wLength);
178 
179  //Check request type
180  switch(req->bmRequest & USB_REQ_TYPE_MASK)
181  {
182  //Standard request?
183  case USB_REQ_TYPE_STANDARD:
184  //GET INTERFACE request?
185  if(req->bRequest == USB_REQ_GET_INTERFACE)
186  {
187  //A single alternate setting is supported
188  static uint8_t alternateSetting = 0;
189  //Return data back to the host
190  USBD_CtlSendData(pdev, &alternateSetting, 1);
191  }
192  //SET INTERFACE request
193  else if(req->bRequest == USB_REQ_SET_INTERFACE)
194  {
195  //The device only supports a single alternate setting
196  }
197  break;
198  //Class specific request?
199  case USB_REQ_TYPE_CLASS:
200  //Check direction
201  if((req->bmRequest & 0x80) != 0)
202  {
203  //GET ENCAPSULATED RESPONSE request?
204  if(req->bRequest == RNDIS_GET_ENCAPSULATED_RESPONSE)
205  {
206  //If for some reason the device receives a GET ENCAPSULATED RESPONSE
207  //and is unable to respond with a valid data on the Control endpoint,
208  //then it should return a one-byte packet set to 0x00, rather than
209  //stalling the Control endpoint
211  {
214  }
215 
216  //Debug message
217  TRACE_DEBUG("Sending encapsulated response (%" PRIuSIZE " bytes)...\r\n", rndisContext.encapsulatedRespLen);
219 
220  //Debug message
221  TRACE_DEBUG("Sending RNDIS message (%" PRIuSIZE " bytes)...\r\n", rndisContext.encapsulatedRespLen);
222  //Dump RNDIS message contents
224 
225  //Return data back to the host
227 
228  //Flush the response buffer
230  }
231  else
232  {
233  //Return data back to the host
234  USBD_CtlSendData(pdev, NULL, 0);
235  }
236  }
237  else
238  {
239  if(req->wLength != 0)
240  {
241  //Save request type
242  rndisContext.CmdOpCode = req->bRequest;
243  rndisContext.CmdLength = req->wLength;
244 
245  //Prepare receiving data on EP0
246  USBD_CtlPrepareRx(pdev, (uint8_t *) rndisContext.data, req->wLength);
247  }
248  }
249  break;
250  //Unknown request?
251  default:
252  break;
253  }
254 
255  //Successful processing
256  return USBD_OK;
257 }
258 
259 
260 /**
261  * @brief Handle data stage (control endpoint)
262  * @param[in] pdev Pointer to a USBD_HandleTypeDef structure
263  * @return Status code
264  **/
265 
266 uint8_t usbdRndisEp0RxReady(USBD_HandleTypeDef *pdev)
267 {
268  //Debug message
269  TRACE_DEBUG(" Data (opcode=0x%02" PRIX8 ", length=%u)\r\n", rndisContext.CmdOpCode, rndisContext.CmdLength);
271 
272  if(rndisContext.CmdOpCode != 0xFF)
273  {
274  //Process RNDIS message
276 
277  rndisContext.CmdOpCode = 0xFF;
278  }
279 
280  //Successful processing
281  return USBD_OK;
282 }
283 
284 
285 /**
286  * @brief DATA IN callback
287  * @param[in] pdev Pointer to a USBD_HandleTypeDef structure
288  * @param[in] epnum Endpoint number
289  * @return Status code
290  **/
291 
292 uint8_t usbdRndisDataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
293 {
294  //DATA IN endpoint?
295  if((epnum & 0x7F) == (RNDIS_DATA_IN_EP & 0x7F))
296  {
297  //Debug message
298  TRACE_DEBUG("########## USB DATA IN EP sent #############\r\n");
299 
300  //The current buffer has been transmitted and is now available for writing
302 
303  //Increment index and wrap around if necessary
305  rndisTxReadIndex = 0;
306 
307  //Check whether the next buffer is ready
309  {
310  //Start transmitting data
311  USBD_LL_Transmit(pdev, RNDIS_DATA_IN_EP,
314  }
315  else
316  {
317  //Suspend transmission
319  }
320 
321  //The transmitter can accept another packet
323  }
324 
325  //Successful processing
326  return USBD_OK;
327 }
328 
329 
330 /**
331  * @brief DATA OUT callback
332  * @param[in] pdev Pointer to a USBD_HandleTypeDef structure
333  * @param[in] epnum Endpoint number
334  * @return Status code
335  **/
336 
337 uint8_t usbdRndisDataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
338 {
339  size_t length;
340  RndisRxBufferDesc *rxBufferDesc;
341 
342  //DATA OUT endpoint?
343  if((epnum & 0x7F) == (RNDIS_DATA_OUT_EP & 0x7F))
344  {
345  //Retrieve the length of the packet
346  length = USBD_LL_GetRxDataSize(pdev, epnum);
347 
348  //Debug message
349  TRACE_DEBUG("Data received on DATA OUT endpoint (%" PRIuSIZE " bytes)\r\n", length);
350 
351  //Make sure the total length is acceptable
353  {
354  //Point to the current buffer descriptor
355  rxBufferDesc = &rndisRxBuffer[rndisRxWriteIndex];
356 
357  //Copy data
358  osMemcpy(rxBufferDesc->data + rndisContext.rxBufferLen,
360 
361  //Update the length of the RX buffer
363 
364  //Last packet?
366  {
367  //Debug message
368  TRACE_DEBUG("RNDIS Packet message received (%" PRIuSIZE " bytes)...\r\n",
369  rxBufferDesc->length);
370  //Dump RNDIS Packet message contents
371  rndisDumpMsg((RndisMsg *) rxBufferDesc->data, rxBufferDesc->length);
372 
373  //Store the length of the message
374  rxBufferDesc->length = rndisContext.rxBufferLen;
375  //The current buffer is available for reading
376  rxBufferDesc->ready = TRUE;
377 
378  //Increment index and wrap around if necessary
380  rndisRxWriteIndex = 0;
381 
382  //Suspend reception
384 
385  //Set event flag
386  rndisDriverInterface->nicEvent = TRUE;
387  //Notify the TCP/IP stack of the event
389 
390  //Flush RX buffer
392  }
393  else
394  {
395  //Debug message
396  TRACE_DEBUG("### usbdRndisReceivePacket 222 ###\r\n");
397 
398  //Prepare DATA OUT endpoint for reception
399  USBD_LL_PrepareReceive(&USBD_Device, RNDIS_DATA_OUT_EP,
401  }
402  }
403  else
404  {
405  //Flush RX buffer
407 
408  //Debug message
409  TRACE_DEBUG("### usbdRndisReceivePacket 333 ###\r\n");
410 
411  //Prepare DATA OUT endpoint for reception
412  USBD_LL_PrepareReceive(&USBD_Device, RNDIS_DATA_OUT_EP,
414  }
415  }
416 
417  //Successful processing
418  return USBD_OK;
419 }
420 
421 
422 /**
423  * @brief Retrieve configuration descriptor (high speed)
424  * @param[out] length Length of the descriptor, in bytes
425  * @return Pointer to the descriptor
426  **/
427 
429 {
430  //Not implemented
431  *length = 0;
432  return NULL;
433 }
434 
435 
436 /**
437  * @brief Retrieve configuration descriptor (full speed)
438  * @param[out] length Length of the descriptor, in bytes
439  * @return Pointer to the descriptor
440  **/
441 
443 {
444  //Not implemented
445  *length = sizeof(usbdConfigDescriptors);
446  return (uint8_t *) &usbdConfigDescriptors;
447 }
448 
449 
450 /**
451  * @brief Retrieve configuration descriptor (other speed)
452  * @param[out] length Length of the descriptor, in bytes
453  * @return Pointer to the descriptor
454  **/
455 
457 {
458  //Not implemented
459  *length = 0;
460  return NULL;
461 }
462 
463 
464 /**
465  * @brief Retrieve device qualifier descriptor
466  * @param[out] length Length of the descriptor, in bytes
467  * @return Pointer to the descriptor
468  **/
469 
471 {
472  //Not implemented
473  *length = 0;
474  return NULL;
475 }
#define PRIuSIZE
Debugging facilities.
uint8_t data[]
Definition: ethernet.h:222
TCP/IP stack core.
#define netEvent
Definition: net_legacy.h:196
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
bool_t osSetEventFromIsr(OsEvent *event)
Set an event object to the signaled state from an interrupt service routine.
void rndisInit(void)
RNDIS core initialization.
Definition: rndis.c:89
RndisContext rndisContext
Definition: rndis.c:54
error_t rndisProcessMsg(const RndisMsg *message, size_t length)
Process incoming RNDIS message.
Definition: rndis.c:111
void rndisChangeState(RndisState newState)
Update RNDIS state.
Definition: rndis.c:711
RNDIS (Remote Network Driver Interface Specification)
#define RNDIS_MAX_TRANSFER_SIZE
Definition: rndis.h:53
@ RNDIS_STATE_UNINITIALIZED
Definition: rndis.h:197
@ RNDIS_STATE_BUS_INITIALIZED
Definition: rndis.h:198
error_t rndisDumpMsg(const RndisMsg *message, size_t length)
Dump RNDIS message for debugging purpose.
Definition: rndis_debug.c:124
RNDIS (Remote Network Driver Interface Specification)
NetInterface * rndisDriverInterface
Definition: rndis_driver.c:44
uint_t rndisTxReadIndex
Definition: rndis_driver.c:52
RndisRxBufferDesc rndisRxBuffer[RNDIS_RX_BUFFER_COUNT]
Definition: rndis_driver.c:48
uint_t rndisRxWriteIndex
Definition: rndis_driver.c:53
RndisTxBufferDesc rndisTxBuffer[RNDIS_TX_BUFFER_COUNT]
Definition: rndis_driver.c:47
RNDIS driver.
#define RNDIS_RX_BUFFER_COUNT
Definition: rndis_driver.h:53
#define RNDIS_TX_BUFFER_COUNT
Definition: rndis_driver.h:39
uint32_t data[512/4]
Definition: rndis.h:478
uint8_t encapsulatedResp[RNDIS_MAX_TRANSFER_SIZE]
Definition: rndis.h:475
uint8_t CmdOpCode
Definition: rndis.h:479
bool_t txState
Definition: rndis.h:470
uint8_t CmdLength
Definition: rndis.h:480
size_t encapsulatedRespLen
Definition: rndis.h:476
size_t rxBufferLen
Definition: rndis.h:474
bool_t rxState
Definition: rndis.h:471
uint8_t rxBuffer[RNDIS_MAX_TRANSFER_SIZE]
Definition: rndis.h:473
Generic RNDIS message.
Definition: rndis.h:209
RX buffer descriptor.
Definition: rndis_driver.h:83
uint8_t data[RNDIS_RX_BUFFER_SIZE]
Definition: rndis_driver.h:86
uint8_t length
Definition: tcp.h:368
const UsbConfigDescriptors usbdConfigDescriptors
USB configuration descriptors.
Definition: usbd_desc.c:99
USB descriptors.
uint8_t usbdRndisEp0RxReady(USBD_HandleTypeDef *pdev)
Handle data stage (control endpoint)
Definition: usbd_rndis.c:266
uint8_t usbdRndisSetup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
Process incoming setup request.
Definition: usbd_rndis.c:169
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: usbd_rndis.c:49
uint8_t usbdRndisDataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
DATA IN callback.
Definition: usbd_rndis.c:292
uint8_t usbdRndisInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
RNDIS class initialization.
Definition: usbd_rndis.c:83
#define TRACE_DEBUG(...)
Definition: usbd_rndis.c:47
uint8_t usbdRndisDeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
RNDIS class de-initialization.
Definition: usbd_rndis.c:142
uint8_t * usbdRndisGetFullSpeedConfigDesc(uint16_t *length)
Retrieve configuration descriptor (full speed)
Definition: usbd_rndis.c:442
uint8_t usbdRndisDataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
DATA OUT callback.
Definition: usbd_rndis.c:337
uint8_t * usbdRndisGetDeviceQualifierDesc(uint16_t *length)
Retrieve device qualifier descriptor.
Definition: usbd_rndis.c:470
uint8_t * usbdRndisGetHighSpeedConfigDesc(uint16_t *length)
Retrieve configuration descriptor (high speed)
Definition: usbd_rndis.c:428
USBD_ClassTypeDef usbdRndisClass
RNDIS class callbacks.
Definition: usbd_rndis.c:57
uint8_t * usbdRndisGetOtherSpeedConfigDesc(uint16_t *length)
Retrieve configuration descriptor (other speed)
Definition: usbd_rndis.c:456
USB RNDIS class.
#define RNDIS_NOTIFICATION_EP
Definition: usbd_rndis.h:38
#define RNDIS_NOTIFICATION_EP_MPS
Definition: usbd_rndis.h:43
USBD_HandleTypeDef USBD_Device
#define RNDIS_DATA_IN_EP_MPS_FS
Definition: usbd_rndis.h:44
#define RNDIS_GET_ENCAPSULATED_RESPONSE
Definition: usbd_rndis.h:51
#define RNDIS_DATA_IN_EP
Definition: usbd_rndis.h:39
#define RNDIS_DATA_OUT_EP_MPS_FS
Definition: usbd_rndis.h:45
#define RNDIS_DATA_OUT_EP_MPS_HS
Definition: usbd_rndis.h:47
#define RNDIS_DATA_IN_EP_MPS_HS
Definition: usbd_rndis.h:46
#define RNDIS_DATA_OUT_EP
Definition: usbd_rndis.h:40