Open FFBoard
Open source force feedback firmware
cdc_rndis_host.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Ha Thach (tinyusb.org)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 * This file is part of the TinyUSB stack.
25 */
26
27#include "tusb_option.h"
28
29#if (CFG_TUH_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS)
30
31//--------------------------------------------------------------------+
32// INCLUDE
33//--------------------------------------------------------------------+
34#include "common/tusb_common.h"
35#include "cdc_host.h"
36#include "cdc_rndis_host.h"
37
38#if 0 // TODO remove subtask related macros later
39// Sub Task
40#define OSAL_SUBTASK_BEGIN
41#define OSAL_SUBTASK_END return TUSB_ERROR_NONE;
42
43#define STASK_RETURN(_error) return _error;
44#define STASK_INVOKE(_subtask, _status) (_status) = _subtask
45#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED)
46#endif
47
48//--------------------------------------------------------------------+
49// MACRO CONSTANT TYPEDEF
50//--------------------------------------------------------------------+
51#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
52
53CFG_TUH_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8];
54CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
55
56static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX];
57
58// TODO Microsoft requires message length for any get command must be at least 4096 bytes
59
60//--------------------------------------------------------------------+
61// INTERNAL OBJECT & FUNCTION DECLARATION
62//--------------------------------------------------------------------+
63static tusb_error_t rndis_body_subtask(void);
64static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
65 uint8_t * p_mess, uint32_t mess_length,
66 uint8_t *p_response );
67
68//--------------------------------------------------------------------+
69// APPLICATION API
70//--------------------------------------------------------------------+
71tusb_error_t tusbh_cdc_rndis_get_mac_addr(uint8_t dev_addr, uint8_t mac_address[6])
72{
73 TU_ASSERT( tusbh_cdc_rndis_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED);
74 TU_VERIFY( mac_address, TUSB_ERROR_INVALID_PARA);
75
76 memcpy(mac_address, rndish_data[dev_addr-1].mac_address, 6);
77
78 return TUSB_ERROR_NONE;
79}
80
81//--------------------------------------------------------------------+
82// IMPLEMENTATION
83//--------------------------------------------------------------------+
84
85// To enable the TASK_ASSERT style (quick return on false condition) in a real RTOS, a task must act as a wrapper
86// and is used mainly to call subtasks. Within a subtask return statement can be called freely, the task with
87// forever loop cannot have any return at all.
88OSAL_TASK_FUNCTION(cdch_rndis_task) (void* param;)
89{
90 OSAL_TASK_BEGIN
92 OSAL_TASK_END
93}
94
95static tusb_error_t rndis_body_subtask(void)
96{
97 static uint8_t relative_addr;
98
99 OSAL_SUBTASK_BEGIN
100
101 for (relative_addr = 0; relative_addr < CFG_TUH_DEVICE_MAX; relative_addr++)
102 {
103
104 }
105
106 osal_task_delay(100);
107
108 OSAL_SUBTASK_END
109}
110
111//--------------------------------------------------------------------+
112// RNDIS-CDC Driver API
113//--------------------------------------------------------------------+
114void rndish_init(void)
115{
116 tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUH_DEVICE_MAX);
117
118 //------------- Task creation -------------//
119
120 //------------- semaphore creation for notification pipe -------------//
121 for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
122 {
123 rndish_data[i].sem_notification_hdl = osal_semaphore_create( OSAL_SEM_REF(rndish_data[i].semaphore_notification) );
124 }
125}
126
128{
129 osal_semaphore_reset( rndish_data[dev_addr-1].sem_notification_hdl );
130// tu_memclr(&rndish_data[dev_addr-1], sizeof(rndish_data_t)); TODO need to move semaphore & its handle out before memclr
131}
132
133
135{
136 .type = RNDIS_MSG_INITIALIZE,
137 .length = sizeof(rndis_msg_initialize_t),
138 .request_id = 1, // TODO should use some magic number
139 .major_version = 1,
140 .minor_version = 0,
141 .max_xfer_size = 0x4000 // TODO mimic windows
142};
143
145{
146 .type = RNDIS_MSG_QUERY,
147 .length = sizeof(rndis_msg_query_t)+6,
148 .request_id = 1,
150 .buffer_length = 6,
151 .buffer_offset = 20,
152};
153
155{
156 .type = RNDIS_MSG_SET,
157 .length = sizeof(rndis_msg_set_t)+4,
158 .request_id = 1,
160 .buffer_length = 4,
161 .buffer_offset = 20,
162};
163
164tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
165{
166 tusb_error_t error;
167
168 OSAL_SUBTASK_BEGIN
169
170 //------------- Message Initialize -------------//
172 STASK_INVOKE(
176 error
177 );
178 if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
179
180 // TODO currently not support multiple data packets per xfer
182 STASK_ASSERT(p_init_cmpt->type == RNDIS_MSG_INITIALIZE_CMPLT && p_init_cmpt->status == RNDIS_STATUS_SUCCESS &&
183 p_init_cmpt->max_packet_per_xfer == 1 && p_init_cmpt->max_xfer_size <= RNDIS_MSG_PAYLOAD_MAX);
184 rndish_data[dev_addr-1].max_xfer_size = p_init_cmpt->max_xfer_size;
185
186 //------------- Message Query 802.3 Permanent Address -------------//
188 tu_memclr(msg_payload + sizeof(rndis_msg_query_t), 6); // 6 bytes for MAC address
189
190 STASK_INVOKE(
192 msg_payload, sizeof(rndis_msg_query_t) + 6,
194 error
195 );
196 if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
197
199 STASK_ASSERT(p_query_cmpt->type == RNDIS_MSG_QUERY_CMPLT && p_query_cmpt->status == RNDIS_STATUS_SUCCESS);
200 memcpy(rndish_data[dev_addr-1].mac_address, msg_payload + 8 + p_query_cmpt->buffer_offset, 6);
201
202 //------------- Set OID_GEN_CURRENT_PACKET_FILTER to (DIRECTED | MULTICAST | BROADCAST) -------------//
204 tu_memclr(msg_payload + sizeof(rndis_msg_set_t), 4); // 4 bytes for filter flags
206
207 STASK_INVOKE(
209 msg_payload, sizeof(rndis_msg_set_t) + 4,
211 error
212 );
213 if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
214
216 STASK_ASSERT(p_set_cmpt->type == RNDIS_MSG_SET_CMPLT && p_set_cmpt->status == RNDIS_STATUS_SUCCESS);
217
218 tusbh_cdc_rndis_mounted_cb(dev_addr);
219
220 OSAL_SUBTASK_END
221}
222
223void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
224{
225 if ( pipehandle_is_equal(pipe_hdl, p_cdc->pipe_notification) )
226 {
227 osal_semaphore_post( rndish_data[pipe_hdl.dev_addr-1].sem_notification_hdl );
228 }
229}
230
231//--------------------------------------------------------------------+
232// INTERNAL & HELPER
233//--------------------------------------------------------------------+
234static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
235 uint8_t * p_mess, uint32_t mess_length,
236 uint8_t *p_response)
237{
238 tusb_error_t error;
239
240 OSAL_SUBTASK_BEGIN
241
242 //------------- Send RNDIS Control Message -------------//
243 STASK_INVOKE(
244 usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
245 CDC_REQUEST_SEND_ENCAPSULATED_COMMAND, 0, p_cdc->interface_number,
246 mess_length, p_mess),
247 error
248 );
249 if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
250
251 //------------- waiting for Response Available notification -------------//
252 (void) usbh_edpt_xfer(p_cdc->pipe_notification, msg_notification[dev_addr-1], 8);
253 osal_semaphore_wait(rndish_data[dev_addr-1].sem_notification_hdl, OSAL_TIMEOUT_NORMAL, &error);
254 if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
255 STASK_ASSERT(msg_notification[dev_addr-1][0] == 1);
256
257 //------------- Get RNDIS Message Initialize Complete -------------//
258 STASK_INVOKE(
259 usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
260 CDC_REQUEST_GET_ENCAPSULATED_RESPONSE, 0, p_cdc->interface_number,
261 RNDIS_MSG_PAYLOAD_MAX, p_response),
262 error
263 );
264 if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
265
266 OSAL_SUBTASK_END
267}
268
269//static tusb_error_t send_process_msg_initialize_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
270//{
271// tusb_error_t error;
272//
273// OSAL_SUBTASK_BEGIN
274//
275// *((rndis_msg_initialize_t*) msg_payload) = (rndis_msg_initialize_t)
276// {
277// .type = RNDIS_MSG_INITIALIZE,
278// .length = sizeof(rndis_msg_initialize_t),
279// .request_id = 1, // TODO should use some magic number
280// .major_version = 1,
281// .minor_version = 0,
282// .max_xfer_size = 0x4000 // TODO mimic windows
283// };
284//
285//
286//
287// OSAL_SUBTASK_END
288//}
289#endif
OSAL_TASK_FUNCTION() cdch_rndis_task(void *param;)
static rndis_msg_set_t const msg_set_packet_filter
static rndis_msg_initialize_t const msg_init
static tusb_error_t send_message_get_response_subtask(uint8_t dev_addr, cdch_data_t *p_cdc, uint8_t *p_mess, uint32_t mess_length, uint8_t *p_response)
CFG_TUH_MEM_SECTION static CFG_TUH_MEM_ALIGN uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX]
static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX]
static CFG_TUH_MEM_SECTION uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8]
static tusb_error_t rndis_body_subtask(void)
tusb_error_t tusbh_cdc_rndis_get_mac_addr(uint8_t dev_addr, uint8_t mac_address[6])
static rndis_msg_query_t const msg_query_permanent_addr
uint8_t dev_addr
Definition: dcd_pic32mz.c:81
struct rndis_msg_query_t rndis_msg_set_t
@ RNDIS_OID_GEN_CURRENT_PACKET_FILTER
Current packet filter (encoded)
Definition: cdc_rndis.h:252
@ RNDIS_OID_802_3_PERMANENT_ADDRESS
Permanent station address.
Definition: cdc_rndis.h:270
@ RNDIS_PACKET_TYPE_DIRECTED
Directed packets. Directed packets contain a destination address equal to the station address of the ...
Definition: cdc_rndis.h:279
@ RNDIS_PACKET_TYPE_BROADCAST
Broadcast packets.
Definition: cdc_rndis.h:282
@ RNDIS_PACKET_TYPE_MULTICAST
Multicast address packets sent to addresses in the multicast address list.
Definition: cdc_rndis.h:280
@ RNDIS_STATUS_SUCCESS
Success.
Definition: cdc_rndis.h:74
void rndish_init(void)
void rndish_close(uint8_t dev_addr)
tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
@ CDC_REQUEST_SEND_ENCAPSULATED_COMMAND
is used to issue a command in the format of the supported control protocol of the Communications Clas...
Definition: cdc.h:140
@ CDC_REQUEST_GET_ENCAPSULATED_RESPONSE
is used to request a response in the format of the supported control protocol of the Communications C...
Definition: cdc.h:141
static TU_ATTR_ALWAYS_INLINE bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec)
static TU_ATTR_ALWAYS_INLINE bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
static TU_ATTR_ALWAYS_INLINE void osal_semaphore_reset(osal_semaphore_t const sem_hdl)
static TU_ATTR_ALWAYS_INLINE osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t *semdef)
TU_ATTR_WEAK void osal_task_delay(uint32_t msec)
static void * memcpy(void *dst, const void *src, size_t n)
Definition: ringbuffer.c:8
Initialize Complete Message.
Definition: cdc_rndis.h:104
uint32_t type
Message Type, must be RNDIS_MSG_INITIALIZE_CMPLT.
Definition: cdc_rndis.h:105
uint32_t status
The initialization status of the device, has value from rndis_msg_status_t.
Definition: cdc_rndis.h:108
uint32_t max_xfer_size
The maximum size, in bytes, of any single bus data transfer that the device expects to receive from t...
Definition: cdc_rndis.h:114
uint32_t max_packet_per_xfer
The maximum number of concatenated RNDIS_MSG_PACKET messages that the device can handle in a single b...
Definition: cdc_rndis.h:113
Initialize Message.
Definition: cdc_rndis.h:93
uint32_t type
Message type, must be RNDIS_MSG_INITIALIZE.
Definition: cdc_rndis.h:94
Query Complete Message.
Definition: cdc_rndis.h:137
uint32_t buffer_offset
The offset, in bytes, from the beginning of request_id field where the response data for the query is...
Definition: cdc_rndis.h:143
uint32_t status
The status of processing for the query request, has value from rndis_msg_status_t.
Definition: cdc_rndis.h:141
uint32_t type
Message Type, must be RNDIS_MSG_QUERY_CMPLT.
Definition: cdc_rndis.h:138
Query Message.
Definition: cdc_rndis.h:122
uint32_t type
Message Type, must be RNDIS_MSG_QUERY.
Definition: cdc_rndis.h:123
Set Complete Message.
Definition: cdc_rndis.h:188
uint32_t status
The status of processing for the request message request by the device to which this message is the r...
Definition: cdc_rndis.h:192
uint32_t type
Message Type.
Definition: cdc_rndis.h:189
@ TUSB_DIR_IN
Definition: tusb_types.h:67
@ TUSB_DIR_OUT
Definition: tusb_types.h:66
xfer_result_t
Definition: tusb_types.h:236
@ TUSB_REQ_RCPT_INTERFACE
Definition: tusb_types.h:152
@ TUSB_REQ_TYPE_CLASS
Definition: tusb_types.h:145
static TU_ATTR_ALWAYS_INLINE bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
Definition: usbh_pvt.h:87