Open FFBoard
Open source force feedback firmware
ncm_device.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Ha Thach (tinyusb.org)
5 * Copyright (c) 2024 Hardy Griech
6 * Copyright (c) 2020 Jacob Berg Potter
7 * Copyright (c) 2020 Peter Lawrence
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 *
27 * This file is part of the TinyUSB stack.
28 */
29
49#include "tusb_option.h"
50
51#if (CFG_TUD_ENABLED && CFG_TUD_NCM)
52
53#include <stdbool.h>
54#include <stdint.h>
55#include <stdio.h>
56
57#include "device/usbd.h"
58#include "device/usbd_pvt.h"
59
60#include "ncm.h"
61#include "net_device.h"
62
63// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
64#ifndef CFG_TUD_NCM_LOG_LEVEL
65 #define CFG_TUD_NCM_LOG_LEVEL CFG_TUD_LOG_LEVEL
66#endif
67
68#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_NCM_LOG_LEVEL, __VA_ARGS__)
69
70// Alignment must be 4
71#define TUD_NCM_ALIGNMENT 4
72// calculate alignment of xmit datagrams within an NTB
73#define XMIT_ALIGN_OFFSET(x) ((TUD_NCM_ALIGNMENT - ((x) & (TUD_NCM_ALIGNMENT - 1))) & (TUD_NCM_ALIGNMENT - 1))
74
75//-----------------------------------------------------------------------------
76//
77// Module global things
78//
79#define XMIT_NTB_N CFG_TUD_NCM_IN_NTB_N
80#define RECV_NTB_N CFG_TUD_NCM_OUT_NTB_N
81
82typedef struct {
83 // general
84 uint8_t ep_in; // endpoint for outgoing datagrams (naming is a little bit confusing)
85 uint8_t ep_out; // endpoint for incoming datagrams (naming is a little bit confusing)
86 uint8_t ep_notif; // endpoint for notifications
87 uint8_t itf_num; // interface number
88 uint8_t itf_data_alt; // ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3)
89 uint8_t rhport; // storage of \a rhport because some callbacks are done without it
90
91 // recv handling
92 CFG_TUSB_MEM_ALIGN recv_ntb_t recv_ntb[RECV_NTB_N]; // actual recv NTBs
93 recv_ntb_t *recv_free_ntb[RECV_NTB_N]; // free list of recv NTBs
94 recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; // NTBs waiting for transmission to glue logic
95 recv_ntb_t *recv_tinyusb_ntb; // buffer for the running transfer TinyUSB -> driver
96 recv_ntb_t *recv_glue_ntb; // buffer for the running transfer driver -> glue logic
97 uint16_t recv_glue_ntb_datagram_ndx; // index into \a recv_glue_ntb_datagram
98
99 // xmit handling
100 CFG_TUSB_MEM_ALIGN xmit_ntb_t xmit_ntb[XMIT_NTB_N]; // actual xmit NTBs
101 xmit_ntb_t *xmit_free_ntb[XMIT_NTB_N]; // free list of xmit NTBs
102 xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; // NTBs waiting for transmission to TinyUSB
103 xmit_ntb_t *xmit_tinyusb_ntb; // buffer for the running transfer driver -> TinyUSB
104 xmit_ntb_t *xmit_glue_ntb; // buffer for the running transfer glue logic -> driver
105 uint16_t xmit_sequence; // NTB sequence counter
106 uint16_t xmit_glue_ntb_datagram_ndx; // index into \a xmit_glue_ntb_datagram
107
108 // notification handling
109 enum {
112 NOTIFICATION_DONE
113 } notification_xmit_state; // state of notification transmission
114 bool notification_xmit_is_running; // notification is currently transmitted
115
116 // misc
117 bool tud_network_recv_renew_active; // tud_network_recv_renew() is active (avoid recursive invocations)
118 bool tud_network_recv_renew_process_again; // tud_network_recv_renew() should process again
120
121CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static ncm_interface_t ncm_interface;
122
129CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = {
130 .wLength = sizeof(ntb_parameters_t),
131 .bmNtbFormatsSupported = 0x01,// 16-bit NTB supported
132 .dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
133 .wNdbInDivisor = 1,
134 .wNdbInPayloadRemainder = 0,
135 .wNdbInAlignment = TUD_NCM_ALIGNMENT,
136 .wReserved = 0,
137 .dwNtbOutMaxSize = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
138 .wNdbOutDivisor = 1,
139 .wNdbOutPayloadRemainder = 0,
140 .wNdbOutAlignment = TUD_NCM_ALIGNMENT,
141 .wNtbOutMaxDatagrams = CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB,
142};
143
144// Some confusing remarks about wNtbOutMaxDatagrams...
145// ==1 -> SystemView packets/s goes up to 2000 and events are lost during startup
146// ==0 -> SystemView runs fine, iperf shows in wireshark a lot of error
147// ==6 -> SystemView runs fine, iperf also
148// >6 -> iperf starts to show errors
149// -> 6 seems to be the best value. Why? Don't know, perhaps only on my system?
150//
151// iperf: for MSS in 100 200 400 800 1200 1450 1500; do iperf -c 192.168.14.1 -e -i 1 -M $MSS -l 8192 -P 1; sleep 2; done
152// sysview: SYSTICKS_PER_SEC=35000, IDLE_US=1000, PRINT_MOD=1000
153//
154
155//-----------------------------------------------------------------------------
156//
157// everything about notifications
158//
160 .header = {
162 .recipient = TUSB_REQ_RCPT_INTERFACE,
163 .type = TUSB_REQ_TYPE_CLASS,
164 .direction = TUSB_DIR_IN},
166 .wValue = 1 /* Connected */,
167 .wLength = 0,
168 },
169};
170
172 .header = {
174 .recipient = TUSB_REQ_RCPT_INTERFACE,
175 .type = TUSB_REQ_TYPE_CLASS,
176 .direction = TUSB_DIR_IN},
178 .wLength = 8,
179 },
180 .downlink = TUD_OPT_HIGH_SPEED ? 480000000 : 12000000,
181 .uplink = TUD_OPT_HIGH_SPEED ? 480000000 : 12000000,
182};
183
188static void notification_xmit(uint8_t rhport, bool force_next) {
189 TU_LOG_DRV("notification_xmit(%d, %d) - %d %d\n", force_next, rhport, ncm_interface.notification_xmit_state, ncm_interface.notification_xmit_is_running);
190
191 if (!force_next && ncm_interface.notification_xmit_is_running) {
192 return;
193 }
194
195 if (ncm_interface.notification_xmit_state == NOTIFICATION_SPEED) {
196 TU_LOG_DRV(" NOTIFICATION_SPEED\n");
199 ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
201 } else if (ncm_interface.notification_xmit_state == NOTIFICATION_CONNECTED) {
202 TU_LOG_DRV(" NOTIFICATION_CONNECTED\n");
205 ncm_interface.notification_xmit_state = NOTIFICATION_DONE;
207 } else {
208 TU_LOG_DRV(" NOTIFICATION_FINISHED\n");
209 }
210} // notification_xmit
211
212//-----------------------------------------------------------------------------
213//
214// everything about packet transmission (driver -> TinyUSB)
215//
216
221 TU_LOG_DRV("xmit_put_ntb_into_free_list() - %p\n", ncm_interface.xmit_tinyusb_ntb);
222
223 if (free_ntb == NULL) { // can happen due to ZLPs
224 return;
225 }
226
227 for (int i = 0; i < XMIT_NTB_N; ++i) {
228 if (ncm_interface.xmit_free_ntb[i] == NULL) {
229 ncm_interface.xmit_free_ntb[i] = free_ntb;
230 return;
231 }
232 }
233 TU_LOG_DRV("(EE) xmit_put_ntb_into_free_list - no entry in free list\n");// this should not happen
234} // xmit_put_ntb_into_free_list
235
240 TU_LOG_DRV("xmit_get_free_ntb()\n");
241
242 for (int i = 0; i < XMIT_NTB_N; ++i) {
243 if (ncm_interface.xmit_free_ntb[i] != NULL) {
246 return free;
247 }
248 }
249 return NULL;
250} // xmit_get_free_ntb
251
256 TU_LOG_DRV("xmit_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
257
258 for (int i = 0; i < XMIT_NTB_N; ++i) {
259 if (ncm_interface.xmit_ready_ntb[i] == NULL) {
260 ncm_interface.xmit_ready_ntb[i] = ready_ntb;
261 return;
262 }
263 }
264 TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen
265} // xmit_put_ntb_into_ready_list
266
272 xmit_ntb_t *r = NULL;
273
276 ncm_interface.xmit_ready_ntb[XMIT_NTB_N - 1] = NULL;
277
278 TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
279 return r;
280} // xmit_get_next_ready_ntb
281
293static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) {
294 TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes);
295
296 if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
297 return false;
298 }
299
300 TU_ASSERT(ncm_interface.itf_data_alt == 1, false);
301 TU_ASSERT(!usbd_edpt_busy(rhport, ncm_interface.ep_in), false);
302
303 TU_LOG_DRV("xmit_insert_required_zlp! (%u)\n", (unsigned) xferred_bytes);
304
305 // start transmission of the ZLP
306 usbd_edpt_xfer(rhport, ncm_interface.ep_in, NULL, 0);
307
308 return true;
309} // xmit_insert_required_zlp
310
314static void xmit_start_if_possible(uint8_t rhport) {
315 TU_LOG_DRV("xmit_start_if_possible()\n");
316
317 if (ncm_interface.xmit_tinyusb_ntb != NULL) {
318 TU_LOG_DRV(" !xmit_start_if_possible 1\n");
319 return;
320 }
321 if (ncm_interface.itf_data_alt != 1) {
322 TU_LOG_DRV("(EE) !xmit_start_if_possible 2\n");
323 return;
324 }
325 if (usbd_edpt_busy(rhport, ncm_interface.ep_in)) {
326 TU_LOG_DRV(" !xmit_start_if_possible 3\n");
327 return;
328 }
329
331 if (ncm_interface.xmit_tinyusb_ntb == NULL) {
333 // -> really nothing is waiting
334 return;
335 }
338 }
339
340 #if CFG_TUD_NCM_LOG_LEVEL >= 3
341 {
343 TU_LOG_BUF(3, ncm_interface.xmit_tinyusb_ntb->data[i], len);
344 }
345 #endif
346
349 }
350
351 // Kick off an endpoint transfer
353} // xmit_start_if_possible
354
358static bool xmit_requested_datagram_fits_into_current_ntb(uint16_t datagram_size) {
359 TU_LOG_DRV("xmit_requested_datagram_fits_into_current_ntb(%d) - %p %p\n", datagram_size, ncm_interface.xmit_tinyusb_ntb, ncm_interface.xmit_glue_ntb);
360
361 if (ncm_interface.xmit_glue_ntb == NULL) {
362 return false;
363 }
364 if (ncm_interface.xmit_glue_ntb_datagram_ndx >= CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB) {
365 return false;
366 }
367 if (ncm_interface.xmit_glue_ntb->nth.wBlockLength + datagram_size + XMIT_ALIGN_OFFSET(datagram_size) > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
368 return false;
369 }
370 return true;
371} // xmit_requested_datagram_fits_into_current_ntb
372
376static bool xmit_setup_next_glue_ntb(void) {
377 TU_LOG_DRV("xmit_setup_next_glue_ntb - %p\n", ncm_interface.xmit_glue_ntb);
378
379 if (ncm_interface.xmit_glue_ntb != NULL) {
380 // put NTB into waiting list (the new datagram did not fit in)
382 }
383
384 ncm_interface.xmit_glue_ntb = xmit_get_free_ntb();// get next buffer (if any)
385 if (ncm_interface.xmit_glue_ntb == NULL) {
386 TU_LOG_DRV(" xmit_setup_next_glue_ntb - nothing free\n");// should happen rarely
387 return false;
388 }
389
391
393
394 // Fill in NTB header
395 ntb->nth.dwSignature = NTH16_SIGNATURE;
396 ntb->nth.wHeaderLength = sizeof(ntb->nth);
398 ntb->nth.wBlockLength = sizeof(ntb->nth) + sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
399 ntb->nth.wNdpIndex = sizeof(ntb->nth);
400
401 // Fill in NDP16 header and terminator
402 ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
403 ntb->ndp.wLength = sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
404 ntb->ndp.wNextNdpIndex = 0;
405
406 memset(ntb->ndp_datagram, 0, sizeof(ntb->ndp_datagram));
407 return true;
408} // xmit_setup_next_glue_ntb
409
410//-----------------------------------------------------------------------------
411//
412// all the recv_*() stuff (TinyUSB -> driver -> glue logic)
413//
414
420 TU_LOG_DRV("recv_get_free_ntb()\n");
421
422 for (int i = 0; i < RECV_NTB_N; ++i) {
423 if (ncm_interface.recv_free_ntb[i] != NULL) {
426 return free;
427 }
428 }
429 return NULL;
430} // recv_get_free_ntb
431
437 recv_ntb_t *r = NULL;
438
441 ncm_interface.recv_ready_ntb[RECV_NTB_N - 1] = NULL;
442
443 TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
444 return r;
445} // recv_get_next_ready_ntb
446
451 TU_LOG_DRV("recv_put_ntb_into_free_list(%p)\n", free_ntb);
452
453 for (int i = 0; i < RECV_NTB_N; ++i) {
454 if (ncm_interface.recv_free_ntb[i] == NULL) {
455 ncm_interface.recv_free_ntb[i] = free_ntb;
456 return;
457 }
458 }
459 TU_LOG_DRV("(EE) recv_put_ntb_into_free_list - no entry in free list\n");// this should not happen
460} // recv_put_ntb_into_free_list
461
467 TU_LOG_DRV("recv_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
468
469 for (int i = 0; i < RECV_NTB_N; ++i) {
470 if (ncm_interface.recv_ready_ntb[i] == NULL) {
471 ncm_interface.recv_ready_ntb[i] = ready_ntb;
472 return;
473 }
474 }
475 TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen
476} // recv_put_ntb_into_ready_list
477
481static void recv_try_to_start_new_reception(uint8_t rhport) {
482 TU_LOG_DRV("recv_try_to_start_new_reception(%d)\n", rhport);
483
484 if (ncm_interface.itf_data_alt != 1) {
485 return;
486 }
487 if (ncm_interface.recv_tinyusb_ntb != NULL) {
488 return;
489 }
490 if (usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
491 return;
492 }
493
495 if (ncm_interface.recv_tinyusb_ntb == NULL) {
496 return;
497 }
498
499 // initiate transfer
500 TU_LOG_DRV(" start reception\n");
501 bool r = usbd_edpt_xfer(rhport, ncm_interface.ep_out, ncm_interface.recv_tinyusb_ntb->data, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
502 if (!r) {
505 }
506} // recv_try_to_start_new_reception
507
515static bool recv_validate_datagram(const recv_ntb_t *ntb, uint32_t len) {
516 const nth16_t *nth16 = &(ntb->nth);
517
518 TU_LOG_DRV("recv_validate_datagram(%p, %d)\n", ntb, (int) len);
519
520 // check header
521 if (nth16->wHeaderLength != sizeof(nth16_t)) {
522 TU_LOG_DRV("(EE) ill nth16 length: %d\n", nth16->wHeaderLength);
523 return false;
524 }
525 if (nth16->dwSignature != NTH16_SIGNATURE) {
526 TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) nth16->dwSignature);
527 return false;
528 }
529 if (len < sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
530 TU_LOG_DRV("(EE) ill min len: %lu\n", len);
531 return false;
532 }
533 if (nth16->wBlockLength > len) {
534 TU_LOG_DRV("(EE) ill block length: %d > %lu\n", nth16->wBlockLength, len);
535 return false;
536 }
537 if (nth16->wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
538 TU_LOG_DRV("(EE) ill block length2: %d > %d\n", nth16->wBlockLength, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
539 return false;
540 }
541 if (nth16->wNdpIndex < sizeof(nth16) || nth16->wNdpIndex > len - (sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t))) {
542 TU_LOG_DRV("(EE) ill position of first ndp: %d (%lu)\n", nth16->wNdpIndex, len);
543 return false;
544 }
545
546 // check (first) NDP(16)
547 const ndp16_t *ndp16 = (const ndp16_t *) (ntb->data + nth16->wNdpIndex);
548
549 if (ndp16->wLength < sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
550 TU_LOG_DRV("(EE) ill ndp16 length: %d\n", ndp16->wLength);
551 return false;
552 }
553 if (ndp16->dwSignature != NDP16_SIGNATURE_NCM0 && ndp16->dwSignature != NDP16_SIGNATURE_NCM1) {
554 TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) ndp16->dwSignature);
555 return false;
556 }
557 if (ndp16->wNextNdpIndex != 0) {
558 TU_LOG_DRV("(EE) cannot handle wNextNdpIndex!=0 (%d)\n", ndp16->wNextNdpIndex);
559 return false;
560 }
561
562 const ndp16_datagram_t *ndp16_datagram = (const ndp16_datagram_t *) (ntb->data + nth16->wNdpIndex + sizeof(ndp16_t));
563 int ndx = 0;
564 uint16_t max_ndx = (uint16_t) ((ndp16->wLength - sizeof(ndp16_t)) / sizeof(ndp16_datagram_t));
565
566 if (max_ndx > 2) { // number of datagrams in NTB > 1
567 TU_LOG_DRV("<< %d (%d)\n", max_ndx - 1, ntb->nth.wBlockLength);
568 }
569 if (ndp16_datagram[max_ndx - 1].wDatagramIndex != 0 || ndp16_datagram[max_ndx - 1].wDatagramLength != 0) {
570 TU_LOG_DRV(" max_ndx != 0\n");
571 return false;
572 }
573 while (ndp16_datagram[ndx].wDatagramIndex != 0 && ndp16_datagram[ndx].wDatagramLength != 0) {
574 TU_LOG_DRV(" << %d %d\n", ndp16_datagram[ndx].wDatagramIndex, ndp16_datagram[ndx].wDatagramLength);
575 if (ndp16_datagram[ndx].wDatagramIndex > len) {
576 TU_LOG_DRV("(EE) ill start of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex, len);
577 return false;
578 }
579 if (ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength > len) {
580 TU_LOG_DRV("(EE) ill end of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength, len);
581 return false;
582 }
583 ++ndx;
584 }
585
586 #if CFG_TUD_NCM_LOG_LEVEL >= 3
587 TU_LOG_BUF(3, ntb->data[i], len);
588 #endif
589
590 // -> ntb contains a valid packet structure
591 // ok... I did not check for garbage within the datagram indices...
592 return true;
593} // recv_validate_datagram
594
599 TU_LOG_DRV("recv_transfer_datagram_to_glue_logic()\n");
600
601 if (ncm_interface.recv_glue_ntb == NULL) {
603 TU_LOG_DRV(" new buffer for glue logic: %p\n", ncm_interface.recv_glue_ntb);
605 }
606
607 if (ncm_interface.recv_glue_ntb != NULL) {
609
611 TU_LOG_DRV("(EE) SOMETHING WENT WRONG 1\n");
612 } else if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength == 0) {
613 TU_LOG_DRV("(EE) SOMETHING WENT WRONG 2\n");
614 } else {
615 uint16_t datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex;
616 uint16_t datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength;
617
618 TU_LOG_DRV(" recv[%d] - %d %d\n", ncm_interface.recv_glue_ntb_datagram_ndx, datagramIndex, datagramLength);
619 if (tud_network_recv_cb(ncm_interface.recv_glue_ntb->data + datagramIndex, datagramLength)) {
620 // send datagram successfully to glue logic
621 TU_LOG_DRV(" OK\n");
622 datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramIndex;
623 datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramLength;
624
625 if (datagramIndex != 0 && datagramLength != 0) {
626 // -> next datagram
628 } else {
629 // end of datagrams reached
632 }
633 }
634 }
635 }
636} // recv_transfer_datagram_to_glue_logic
637
638//-----------------------------------------------------------------------------
639//
640// all the tud_network_*() stuff (glue logic -> driver)
641//
642
648bool tud_network_can_xmit(uint16_t size) {
649 TU_LOG_DRV("tud_network_can_xmit(%d)\n", size);
650
651 TU_ASSERT(size <= CFG_TUD_NCM_OUT_NTB_MAX_SIZE - (sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)), false);
652
654 // -> everything is fine
655 return true;
656 }
658 TU_LOG_DRV("(II) tud_network_can_xmit: request blocked\n");// could happen if all xmit buffers are full (but should happen rarely)
659 return false;
660} // tud_network_can_xmit
661
666void tud_network_xmit(void *ref, uint16_t arg) {
667 TU_LOG_DRV("tud_network_xmit(%p, %d)\n", ref, arg);
668
669 if (ncm_interface.xmit_glue_ntb == NULL) {
670 TU_LOG_DRV("(EE) tud_network_xmit: no buffer\n");// must not happen (really)
671 return;
672 }
673
675
676 // copy new datagram to the end of the current NTB
677 uint16_t size = tud_network_xmit_cb(ntb->data + ntb->nth.wBlockLength, ref, arg);
678
679 // correct NTB internals
683
684 ntb->nth.wBlockLength += (uint16_t) (size + XMIT_ALIGN_OFFSET(size));
685
686 if (ntb->nth.wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
687 TU_LOG_DRV("(EE) tud_network_xmit: buffer overflow\n"); // must not happen (really)
688 return;
689 }
690
692} // tud_network_xmit
693
700 TU_LOG_DRV("tud_network_recv_renew()\n");
701
703
705 TU_LOG_DRV("Re-entrant into tud_network_recv_renew, will process later\n");
706 return;
707 }
708
711
712 // If the current function is called within recv_transfer_datagram_to_glue_logic,
713 // tud_network_recv_renew_process_again will become true, and the loop will run again
714 // Otherwise the loop will not run again
718 }
720} // tud_network_recv_renew
721
725void tud_network_recv_renew_r(uint8_t rhport) {
726 TU_LOG_DRV("tud_network_recv_renew_r(%d)\n", rhport);
727
728 ncm_interface.rhport = rhport;
730} // tud_network_recv_renew
731
732//-----------------------------------------------------------------------------
733//
734// all the netd_*() stuff (interface TinyUSB -> driver)
735//
740void netd_init(void) {
741 TU_LOG_DRV("netd_init()\n");
742
743 memset(&ncm_interface, 0, sizeof(ncm_interface));
744
745 for (int i = 0; i < XMIT_NTB_N; ++i) {
747 }
748 for (int i = 0; i < RECV_NTB_N; ++i) {
750 }
751} // netd_init
752
756bool netd_deinit(void) {
757 return true;
758}
759
764void netd_reset(uint8_t rhport) {
765 (void) rhport;
766
767 netd_init();
768} // netd_reset
769
784uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
785 TU_ASSERT(ncm_interface.ep_notif == 0, 0);// assure that the interface is only opened once
786
787 ncm_interface.itf_num = itf_desc->bInterfaceNumber;// management interface
788
789 // skip the two first entries and the following TUSB_DESC_CS_INTERFACE entries
790 uint16_t drv_len = sizeof(tusb_desc_interface_t);
791 uint8_t const *p_desc = tu_desc_next(itf_desc);
792 while (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && drv_len <= max_len) {
793 drv_len += tu_desc_len(p_desc);
794 p_desc = tu_desc_next(p_desc);
795 }
796
797 // get notification endpoint
798 TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
799 TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
800 ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
801 drv_len += tu_desc_len(p_desc);
802 p_desc = tu_desc_next(p_desc);
803
804 // skip the following TUSB_DESC_INTERFACE entries (which must be TUSB_CLASS_CDC_DATA)
805 while (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && drv_len <= max_len) {
806 tusb_desc_interface_t const *data_itf_desc = (tusb_desc_interface_t const *) p_desc;
807 TU_ASSERT(data_itf_desc->bInterfaceClass == TUSB_CLASS_CDC_DATA, 0);
808
809 drv_len += tu_desc_len(p_desc);
810 p_desc = tu_desc_next(p_desc);
811 }
812
813 // a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints
814 TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
816 drv_len += 2 * sizeof(tusb_desc_endpoint_t);
817
818 return drv_len;
819} // netd_open
820
824bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
825 (void) result;
826
827 if (ep_addr == ncm_interface.ep_out) {
828 // new NTB received
829 // - make the NTB valid
830 // - if ready transfer datagrams to the glue logic for further processing
831 // - if there is a free receive buffer, initiate reception
833 // verification failed: ignore NTB and return it to free
834 TU_LOG_DRV("(EE) VALIDATION FAILED. WHAT CAN WE DO IN THIS CASE?\n");
835 } else {
836 // packet ok -> put it into ready list
838 }
841 } else if (ep_addr == ncm_interface.ep_in) {
842 // transmission of an NTB finished
843 // - free the transmitted NTB buffer
844 // - insert ZLPs when necessary
845 // - if there is another transmit NTB waiting, try to start transmission
848 if (!xmit_insert_required_zlp(rhport, xferred_bytes)) {
850 }
851 } else if (ep_addr == ncm_interface.ep_notif) {
852 // next transfer on notification channel
853 notification_xmit(rhport, true);
854 }
855
856 return true;
857} // netd_xfer_cb
858
863bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
864 if (stage != CONTROL_STAGE_SETUP) {
865 return true;
866 }
867
868 switch (request->bmRequestType_bit.type) {
870
871 switch (request->bRequest) {
873 TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex, false);
874
876 } break;
877
879 TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex && request->wValue < 2, false);
880
882
883 if (ncm_interface.itf_data_alt == 1) {
885 notification_xmit(rhport, false);
886 }
888 } break;
889
890 // unsupported request
891 default:
892 return false;
893 }
894 break;
895
897 TU_VERIFY(ncm_interface.itf_num == request->wIndex, false);
898 switch (request->bRequest) {
900 // transfer NTB parameters to host.
901 tud_control_xfer(rhport, request, (void *) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
902 } break;
903
904 // unsupported request
905 default:
906 return false;
907 }
908 break;
909 // unsupported request
910 default:
911 return false;
912 }
913
914 return true;
915} // netd_control_xfer_cb
916
917#endif // ( CFG_TUD_ENABLED && CFG_TUD_NCM )
void free(void *p)
Definition: cppmain.cpp:135
@ CDC_NOTIF_CONNECTION_SPEED_CHANGE
This notification allows the device to inform the host-networking driver that a change in either the ...
Definition: cdc.h:221
@ CDC_NOTIF_NETWORK_CONNECTION
This notification allows the device to notify the host about network connection status.
Definition: cdc.h:214
struct TU_ATTR_PACKED ndp16_datagram_t
struct TU_ATTR_PACKED ntb_parameters_t
@ NCM_GET_NTB_PARAMETERS
Definition: ncm.h:89
static recv_ntb_t * recv_get_next_ready_ntb(void)
Definition: ncm_device.c:436
CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters
Definition: ncm_device.c:129
CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static ncm_interface_t ncm_interface
Definition: ncm_device.c:121
static void recv_try_to_start_new_reception(uint8_t rhport)
Definition: ncm_device.c:481
static bool xmit_requested_datagram_fits_into_current_ntb(uint16_t datagram_size)
Definition: ncm_device.c:358
void netd_reset(uint8_t rhport)
Definition: ncm_device.c:764
static void recv_put_ntb_into_ready_list(recv_ntb_t *ready_ntb)
Definition: ncm_device.c:466
static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes)
Definition: ncm_device.c:293
uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
Definition: ncm_device.c:784
bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
Definition: ncm_device.c:824
static bool recv_validate_datagram(const recv_ntb_t *ntb, uint32_t len)
Definition: ncm_device.c:515
void tud_network_xmit(void *ref, uint16_t arg)
Definition: ncm_device.c:666
static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb)
Definition: ncm_device.c:255
tu_static struct ncm_notify_t ncm_notify_connected
Definition: ncm_device.c:159
static xmit_ntb_t * xmit_get_next_ready_ntb(void)
Definition: ncm_device.c:271
static void recv_transfer_datagram_to_glue_logic(void)
Definition: ncm_device.c:598
void tud_network_recv_renew(void)
Definition: ncm_device.c:699
bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
Definition: ncm_device.c:863
void tud_network_recv_renew_r(uint8_t rhport)
Definition: ncm_device.c:725
bool netd_deinit(void)
Definition: ncm_device.c:756
static xmit_ntb_t * xmit_get_free_ntb(void)
Definition: ncm_device.c:239
static recv_ntb_t * recv_get_free_ntb(void)
Definition: ncm_device.c:419
static bool xmit_setup_next_glue_ntb(void)
Definition: ncm_device.c:376
void netd_init(void)
Definition: ncm_device.c:740
static void xmit_start_if_possible(uint8_t rhport)
Definition: ncm_device.c:314
static void xmit_put_ntb_into_free_list(xmit_ntb_t *free_ntb)
Definition: ncm_device.c:220
static void recv_put_ntb_into_free_list(recv_ntb_t *free_ntb)
Definition: ncm_device.c:450
tu_static struct ncm_notify_t ncm_notify_speed_change
Definition: ncm_device.c:171
static void notification_xmit(uint8_t rhport, bool force_next)
Definition: ncm_device.c:188
bool tud_network_can_xmit(uint16_t size)
Definition: ncm_device.c:648
bool tud_network_recv_cb(const uint8_t *src, uint16_t size)
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg)
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint16_t wDatagramIndex
Definition: ncm.h:130
uint16_t wNextNdpIndex
Definition: ncm.h:137
nth16_t nth
Definition: ncm.h:143
uint16_t wDatagramLength
Definition: ncm.h:131
uint32_t dwSignature
Definition: ncm.h:122
uint16_t wSequence
Definition: ncm.h:124
uint16_t wValue
Definition: audio.h:934
struct TU_ATTR_PACKED::@16::TU_ATTR_PACKED bmRequestType_bit
uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE]
Definition: ncm.h:147
uint16_t wBlockLength
Definition: ncm.h:125
uint16_t wLength
Definition: audio.h:840
uint16_t wIndex
Definition: audio.h:943
uint16_t wNdpIndex
Definition: ncm.h:126
uint8_t bInterfaceClass
Class code (assigned by the USB-IF).
Definition: tusb_types.h:349
uint16_t wHeaderLength
Definition: ncm.h:123
ndp16_datagram_t ndp_datagram[CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB+1]
Definition: ncm.h:145
uint8_t bRequest
Request type audio_cs_req_t.
Definition: audio.h:831
ndp16_t ndp
Definition: ncm.h:144
uint8_t bInterfaceNumber
Number of this interface. Zero-based value identifying the index in the array of concurrent interface...
Definition: tusb_types.h:346
xmit_ntb_t * xmit_ready_ntb[XMIT_NTB_N]
Definition: ncm_device.c:102
recv_ntb_t * recv_free_ntb[RECV_NTB_N]
Definition: ncm_device.c:93
uint16_t xmit_sequence
Definition: ncm_device.c:105
bool tud_network_recv_renew_active
Definition: ncm_device.c:117
CFG_TUSB_MEM_ALIGN recv_ntb_t recv_ntb[RECV_NTB_N]
Definition: ncm_device.c:92
uint16_t xmit_glue_ntb_datagram_ndx
Definition: ncm_device.c:106
bool notification_xmit_is_running
Definition: ncm_device.c:114
uint16_t recv_glue_ntb_datagram_ndx
Definition: ncm_device.c:97
recv_ntb_t * recv_tinyusb_ntb
Definition: ncm_device.c:95
uint8_t ep_out
Definition: ncm_device.c:85
xmit_ntb_t * xmit_free_ntb[XMIT_NTB_N]
Definition: ncm_device.c:101
recv_ntb_t * recv_ready_ntb[RECV_NTB_N]
Definition: ncm_device.c:94
uint8_t ep_notif
Definition: ncm_device.c:86
uint8_t itf_data_alt
Definition: ncm_device.c:88
xmit_ntb_t * xmit_tinyusb_ntb
Definition: ncm_device.c:103
bool tud_network_recv_renew_process_again
Definition: ncm_device.c:118
uint8_t ep_in
Definition: ncm_device.c:84
CFG_TUSB_MEM_ALIGN xmit_ntb_t xmit_ntb[XMIT_NTB_N]
Definition: ncm_device.c:100
recv_ntb_t * recv_glue_ntb
Definition: ncm_device.c:96
uint8_t itf_num
Definition: ncm_device.c:87
uint8_t rhport
Definition: ncm_device.c:89
enum ncm_interface_t::@54 notification_xmit_state
xmit_ntb_t * xmit_glue_ntb
Definition: ncm_device.c:104
tusb_control_request_t header
Definition: ncm.h:159
@ TUSB_DIR_IN
Definition: tusb_types.h:67
@ TUSB_REQ_SET_INTERFACE
Definition: tusb_types.h:133
@ TUSB_REQ_GET_INTERFACE
Definition: tusb_types.h:132
@ TUSB_CLASS_CDC_DATA
Definition: tusb_types.h:169
static TU_ATTR_ALWAYS_INLINE uint8_t tu_desc_len(void const *desc)
Definition: tusb_types.h:542
xfer_result_t
Definition: tusb_types.h:236
@ TUSB_REQ_RCPT_INTERFACE
Definition: tusb_types.h:152
@ TUSB_XFER_BULK
Definition: tusb_types.h:61
struct TU_ATTR_PACKED tusb_desc_endpoint_t
USB Endpoint Descriptor.
@ CONTROL_STAGE_SETUP
Definition: tusb_types.h:268
static TU_ATTR_ALWAYS_INLINE uint8_t tu_desc_type(void const *desc)
Definition: tusb_types.h:537
@ TUSB_REQ_TYPE_STANDARD
Definition: tusb_types.h:144
@ TUSB_REQ_TYPE_CLASS
Definition: tusb_types.h:145
struct TU_ATTR_PACKED tusb_desc_interface_t
USB Interface Descriptor.
static TU_ATTR_ALWAYS_INLINE uint8_t const * tu_desc_next(void const *desc)
Definition: tusb_types.h:531
@ TUSB_DESC_CS_INTERFACE
Definition: tusb_types.h:114
@ TUSB_DESC_ENDPOINT
Definition: tusb_types.h:97
@ TUSB_DESC_INTERFACE
Definition: tusb_types.h:96
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
Definition: usbd.c:1309
bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep)
Definition: usbd.c:1277
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
Definition: usbd.c:1376
bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const *p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t *ep_out, uint8_t *ep_in)
Definition: usbd.c:1238
bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const *request, void *buffer, uint16_t len)
Definition: usbd_control.c:111
bool tud_control_status(uint8_t rhport, tusb_control_request_t const *request)
Definition: usbd_control.c:81
CFG_TUH_MEM_ALIGN tusb_control_request_t request
Definition: usbh.c:259
volatile uint8_t stage
Definition: usbh.c:265