Open FFBoard
Open source force feedback firmware
usbtmc_device.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Nathan Conrad
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/*
28 * This library is not fully reentrant, though it is reentrant from the view
29 * of either the application layer or the USB stack. Due to its locking,
30 * it is not safe to call its functions from interrupts.
31 *
32 * The one exception is that its functions may not be called from the application
33 * until the USB stack is initialized. This should not be a problem since the
34 * device shouldn't be sending messages until it receives a request from the
35 * host.
36 */
37
38
39/*
40 * In the case of single-CPU "no OS", this task is never preempted other than by
41 * interrupts, and the USBTMC code isn't called by interrupts, so all is OK. For "no OS",
42 * the mutex structure's main effect is to disable the USB interrupts.
43 * With an OS, this class driver uses the OSAL to perform locking. The code uses a single lock
44 * and does not call outside of this class with a lock held, so deadlocks won't happen.
45 */
46
47//Limitations:
48// "vendor-specific" commands are not handled.
49// Dealing with "termchar" must be handled by the application layer,
50// though additional error checking is does in this module.
51// talkOnly and listenOnly are NOT supported. They're not permitted
52// in USB488, anyway.
53
54/* Supported:
55 *
56 * Notification pulse
57 * Trigger
58 * Read status byte (both by interrupt endpoint and control message)
59 *
60 */
61
62
63// TODO:
64// USBTMC 3.2.2 error conditions not strictly followed
65// No local lock-out, REN, or GTL.
66// Clear message available status byte at the correct time? (488 4.3.1.3)
67// Ability to defer status byte transmission
68// Transmission of status byte in response to USB488 SRQ condition
69
70#include "tusb_option.h"
71
72#if (CFG_TUD_ENABLED && CFG_TUD_USBTMC)
73
74#include "device/usbd.h"
75#include "device/usbd_pvt.h"
76
77#include "usbtmc_device.h"
78
79#ifdef xDEBUG
80#include "uart_util.h"
81tu_static char logMsg[150];
82#endif
83
84// Buffer size must be an exact multiple of the max packet size for both
85// bulk (up to 64 bytes for FS, 512 bytes for HS). In addation, this driver
86// imposes a minimum buffer size of 32 bytes.
87#define USBTMCD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
88
89// Interrupt endpoint buffer size, default to 2 bytes as USB488 specification.
90#ifndef CFG_TUD_USBTMC_INT_EP_SIZE
91#define CFG_TUD_USBTMC_INT_EP_SIZE 2
92#endif
93
94/*
95 * The state machine does not allow simultaneous reading and writing. This is
96 * consistent with USBTMC.
97 */
98
99typedef enum
100{
101 STATE_CLOSED, // Endpoints have not yet been opened since USB reset
102 STATE_NAK, // Bulk-out endpoint is in NAK state.
103 STATE_IDLE, // Bulk-out endpoint is waiting for CMD.
104 STATE_RCV, // Bulk-out is receiving DEV_DEP message
110 STATE_ABORTING_BULK_IN_SHORTED, // aborting, and short packet has been queued for transmission
111 STATE_ABORTING_BULK_IN_ABORTED, // aborting, and short packet has been transmitted
115
116#if (CFG_TUD_USBTMC_ENABLE_488)
118#else
120#endif
121
122
123typedef struct
124{
126
127 uint8_t itf_id;
128 uint8_t rhport;
129 uint8_t ep_bulk_in;
130 uint8_t ep_bulk_out;
131 uint8_t ep_int_in;
134 // IN buffer is only used for first packet, not the remainder
135 // in order to deal with prepending header
136 CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_BUFFER_SIZE];
137 // OUT buffer receives one packet at a time
138 CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE];
139 // Buffer int msg to ensure alignment and placement correctness
140 CFG_TUSB_MEM_ALIGN uint8_t ep_int_in_buf[CFG_TUD_USBTMC_INT_EP_SIZE];
141
142 uint32_t transfer_size_remaining; // also used for requested length for bulk IN.
143 uint32_t transfer_size_sent; // To keep track of data bytes that have been queued in FIFO (not header bytes)
144
145 uint8_t lastBulkOutTag; // used for aborts (mostly)
146 uint8_t lastBulkInTag; // used for aborts (mostly)
147
148 uint8_t const * devInBuffer; // pointer to application-layer used for transmissions
149
152
153CFG_TUD_MEM_SECTION tu_static usbtmc_interface_state_t usbtmc_state =
154{
155 .itf_id = 0xFF,
156};
157
158// We need all headers to fit in a single packet in this implementation, 32 bytes will fit all standard USBTMC headers
159TU_VERIFY_STATIC(USBTMCD_BUFFER_SIZE >= 32u,"USBTMC dev buffer size too small");
160
161static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len);
162static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen);
163
164#ifndef NDEBUG
165tu_static uint8_t termChar;
166#endif
167
168tu_static uint8_t termCharRequested = false;
169
170#if OSAL_MUTEX_REQUIRED
171static OSAL_MUTEX_DEF(usbtmcLockBuffer);
172#endif
174
175// Our own private lock, mostly for the state variable.
176#define criticalEnter() do { (void) osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0)
177#define criticalLeave() do { (void) osal_mutex_unlock(usbtmcLock); } while (0)
178
180{
181 bool ret = true;
182 criticalEnter();
184 if (oldState == expectedState)
185 {
186 usbtmc_state.state = newState;
187 }
188 else
189 {
190 ret = false;
191 }
192 criticalLeave();
193 return ret;
194}
195
196// called from app
197// We keep a reference to the buffer, so it MUST not change until the app is
198// notified that the transfer is complete.
199// length of data is specified in the hdr.
200
201// We can't just send the whole thing at once because we need to concatanate the
202// header with the data.
204 const void * data, size_t len,
205 bool endOfMessage,
206 bool usingTermChar)
207{
208 const unsigned int txBufLen = sizeof(usbtmc_state.ep_bulk_in_buf);
209
210#ifndef NDEBUG
211 TU_ASSERT(len > 0u);
212 TU_ASSERT(len <= usbtmc_state.transfer_size_remaining);
213 TU_ASSERT(usbtmc_state.transfer_size_sent == 0u);
214 if(usingTermChar)
215 {
217 TU_ASSERT(termCharRequested);
218 TU_ASSERT(((uint8_t const*)data)[len-1u] == termChar);
219 }
220#endif
221
224 tu_varclr(hdr);
227 hdr->header.bTagInverse = (uint8_t)~(usbtmc_state.lastBulkInTag);
228 hdr->TransferSize = len;
229 hdr->bmTransferAttributes.EOM = endOfMessage;
230 hdr->bmTransferAttributes.UsingTermChar = usingTermChar;
231
232 // Copy in the header
233 const size_t headerLen = sizeof(*hdr);
234 const size_t dataLen = ((headerLen + hdr->TransferSize) <= txBufLen) ?
235 len : (txBufLen - headerLen);
236 const size_t packetLen = headerLen + dataLen;
237
238 memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + headerLen, data, dataLen);
241 usbtmc_state.devInBuffer = (uint8_t const*) data + (dataLen);
242
243 bool stateChanged =
245 TU_VERIFY(stateChanged);
247 return true;
248}
249
250bool tud_usbtmc_transmit_notification_data(const void * data, size_t len)
251{
252#ifndef NDEBUG
253 TU_ASSERT(len > 0);
254 TU_ASSERT(usbtmc_state.ep_int_in != 0);
255#endif
257
260 return true;
261}
262
264{
266#ifndef NDEBUG
267# if CFG_TUD_USBTMC_ENABLE_488
269 TU_ASSERT(&tud_usbtmc_msg_trigger_cb != NULL,);
270 }
271 // Per USB488 spec: table 8
274# endif
276 TU_ASSERT(&tud_usbtmc_indicator_pulse_cb != NULL,);
277 }
278#endif
279
280 usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
281}
282
283bool usbtmcd_deinit(void) {
284 #if OSAL_MUTEX_REQUIRED
286 #endif
287 return true;
288}
289
290uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
291{
292 (void)rhport;
293
294 uint16_t drv_len;
295 uint8_t const * p_desc;
296 uint8_t found_endpoints = 0;
297
298 TU_VERIFY(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS , 0);
299 TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_USBTMC_APP_SUBCLASS, 0);
300
301#ifndef NDEBUG
302 // Only 2 or 3 endpoints are allowed for USBTMC.
303 TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3), 0);
304#endif
305
306 TU_ASSERT(usbtmc_state.state == STATE_CLOSED, 0);
307
308 // Interface
309 drv_len = 0u;
310 p_desc = (uint8_t const *) itf_desc;
311
313 usbtmc_state.rhport = rhport;
314
315 while (found_endpoints < itf_desc->bNumEndpoints && drv_len <= max_len)
316 {
317 if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
318 {
319 tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *)p_desc;
320 switch(ep_desc->bmAttributes.xfer) {
321 case TUSB_XFER_BULK:
322 // Ensure buffer is an exact multiple of the maxPacketSize
323 TU_ASSERT((USBTMCD_BUFFER_SIZE % tu_edpt_packet_size(ep_desc)) == 0, 0);
325 {
328 } else {
331 }
332
333 break;
335#ifndef NDEBUG
336 TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN, 0);
337 TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
338#endif
340 break;
341 default:
342 TU_ASSERT(false, 0);
343 }
344 TU_ASSERT( usbd_edpt_open(rhport, ep_desc), 0);
345 found_endpoints++;
346 }
347
348 drv_len += tu_desc_len(p_desc);
349 p_desc = tu_desc_next(p_desc);
350 }
351
352 // bulk endpoints are required, but interrupt IN is optional
353#ifndef NDEBUG
354 TU_ASSERT(usbtmc_state.ep_bulk_in != 0, 0);
355 TU_ASSERT(usbtmc_state.ep_bulk_out != 0, 0);
356 if (itf_desc->bNumEndpoints == 2)
357 {
358 TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
359 }
360 else if (itf_desc->bNumEndpoints == 3)
361 {
362 TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
363 }
364#if (CFG_TUD_USBTMC_ENABLE_488)
367 {
368 TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
369 }
370#endif
371#endif
374
375 return drv_len;
376}
377// Tell USBTMC class to set its bulk-in EP to ACK so that it can
378// receive USBTMC commands.
379// Returns false if it was already in an ACK state or is busy
380// processing a command (such as a clear). Returns true if it was
381// in the NAK state and successfully transitioned to the ACK wait
382// state.
384{
386 switch(oldState)
387 {
388 // These may transition to IDLE
389 case STATE_NAK:
391 TU_VERIFY(atomicChangeState(oldState, STATE_IDLE));
392 break;
393 // When receiving, let it remain receiving
394 case STATE_RCV:
395 break;
396 default:
397 return false;
398 }
400 return true;
401}
402
403void usbtmcd_reset_cb(uint8_t rhport)
404{
405 (void)rhport;
407
408 criticalEnter();
409 tu_varclr(&usbtmc_state);
410 usbtmc_state.capabilities = capabilities;
411 usbtmc_state.itf_id = 0xFFu;
412 criticalLeave();
413}
414
415static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len)
416{
417 (void)rhport;
418 // return true upon failure, as we can assume error is being handled elsewhere.
419 TU_VERIFY(atomicChangeState(STATE_IDLE, STATE_RCV), true);
421
422 // must be a header, should have been confirmed before calling here.
425 TU_VERIFY(tud_usbtmc_msgBulkOut_start_cb(msg));
426
427 TU_VERIFY(handle_devMsgOut(rhport, (uint8_t*)data + sizeof(*msg), len - sizeof(*msg), len));
429 return true;
430}
431
432static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen)
433{
434 (void)rhport;
435 // return true upon failure, as we can assume error is being handled elsewhere.
436 TU_VERIFY(usbtmc_state.state == STATE_RCV,true);
437
438 bool shortPacket = (packetLen < usbtmc_state.ep_bulk_out_wMaxPacketSize);
439
440 // Packet is to be considered complete when we get enough data or at a short packet.
441 bool atEnd = false;
442 if(len >= usbtmc_state.transfer_size_remaining || shortPacket)
443 {
444 atEnd = true;
446 }
447
449
452
453 // App may (should?) call the wait_for_bus() command at this point
454 if(!tud_usbtmc_msg_data_cb(data, len, atEnd))
455 {
456 // TODO: Go to an error state upon failure other than just stalling the EP?
457 return false;
458 }
459
460
461 return true;
462}
463
464static bool handle_devMsgIn(void *data, size_t len)
465{
466 TU_VERIFY(len == sizeof(usbtmc_msg_request_dev_dep_in));
468 bool stateChanged = atomicChangeState(STATE_IDLE, STATE_TX_REQUESTED);
469 TU_VERIFY(stateChanged);
473
475
476#ifndef NDEBUG
477 termChar = msg->TermChar;
478#endif
479
482
483 TU_VERIFY(tud_usbtmc_msgBulkIn_request_cb(msg));
484 return true;
485}
486
487bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
488{
489 TU_VERIFY(result == XFER_RESULT_SUCCESS);
490 //uart_tx_str_sync("TMC XFER CB\r\n");
492 return true; /* I think we can ignore everything here */
493 }
494
495 if(ep_addr == usbtmc_state.ep_bulk_out)
496 {
497 usbtmc_msg_generic_t *msg = NULL;
498
499 switch(usbtmc_state.state)
500 {
501 case STATE_IDLE:
502 {
503 TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t));
505 uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse);
506 TU_VERIFY(msg->header.bTag == invInvTag);
507 TU_VERIFY(msg->header.bTag != 0x00);
508
509 switch(msg->header.MsgID) {
511 if(!handle_devMsgOutStart(rhport, msg, xferred_bytes))
512 {
514 return false;
515 }
516 break;
517
519 TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
520 break;
521
522#if (CFG_TUD_USBTMC_ENABLE_488)
524 // Spec says we halt the EP if we didn't declare we support it.
526 TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg));
527
528 break;
529#endif
532 default:
534 return false;
535 }
536 return true;
537 }
538 case STATE_RCV:
539 if(!handle_devMsgOut(rhport, usbtmc_state.ep_bulk_out_buf, xferred_bytes, xferred_bytes))
540 {
542 return false;
543 }
544 return true;
545
547 // Should be stalled by now, shouldn't have received a packet.
548 return false;
549
555 default:
556 return false;
557 }
558 }
559 else if(ep_addr == usbtmc_state.ep_bulk_in)
560 {
561 switch(usbtmc_state.state) {
562 case STATE_TX_SHORTED:
565 break;
566
569 {
570 // Copy buffer to ensure alignment correctness
572 TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in,
577 }
578 else // last packet
579 {
580 size_t packetLen = usbtmc_state.transfer_size_remaining;
585 TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen) );
586 if(((packetLen % usbtmc_state.ep_bulk_in_wMaxPacketSize) != 0) || (packetLen == 0 ))
587 {
589 }
590 }
591 return true;
592
594 // need to send short packet (ZLP?)
595 TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
597 return true;
598
600 /* Done. :)*/
602 return true;
603
604 default:
605 TU_ASSERT(false);
606 }
607 }
608 else if (ep_addr == usbtmc_state.ep_int_in) {
611 }
612 return true;
613 }
614 return false;
615}
616
617// Invoked when a control transfer occurred on an interface of this class
618// Driver response accordingly to the request and the transfer stage (setup/data/ack)
619// return false to stall control endpoint (e.g unsupported request)
620bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
621{
622 // nothing to do with DATA and ACK stage
623 if ( stage != CONTROL_STAGE_SETUP ) return true;
624
625 uint8_t tmcStatusCode = USBTMC_STATUS_FAILED;
626#if (CFG_TUD_USBTMC_ENABLE_488)
627 uint8_t bTag;
628#endif
629
634 {
635 uint32_t ep_addr = (request->wIndex);
636
637 // At this point, a transfer MAY be in progress. Based on USB spec, when clearing bulk EP HALT,
638 // the EP transfer buffer needs to be cleared and DTOG needs to be reset, even if
639 // the EP is not halted. The only USBD API interface to do this is to stall and then un-stall the EP.
640 if(ep_addr == usbtmc_state.ep_bulk_out)
641 {
642 criticalEnter();
643 usbd_edpt_stall(rhport, (uint8_t)ep_addr);
644 usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr);
645 usbtmc_state.state = STATE_NAK; // USBD core has placed EP in NAK state for us
646 criticalLeave();
648 }
649 else if (ep_addr == usbtmc_state.ep_bulk_in)
650 {
651 usbd_edpt_stall(rhport, (uint8_t)ep_addr);
652 usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr);
654 }
655 else if ((usbtmc_state.ep_int_in != 0) && (ep_addr == usbtmc_state.ep_int_in))
656 {
657 // Clearing interrupt in EP
658 usbd_edpt_stall(rhport, (uint8_t)ep_addr);
659 usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr);
660 }
661 else
662 {
663 return false;
664 }
665 return true;
666 }
667
668 // Otherwise, we only handle class requests.
670 {
671 return false;
672 }
673
674 // Verification that we own the interface is unneeded since it's been routed to us specifically.
675
676 switch(request->bRequest)
677 {
678 // USBTMC required requests
680 {
683 };
684 TU_VERIFY(request->bmRequestType == 0xA2); // in,class,interface
685 TU_VERIFY(request->wLength == sizeof(rsp));
686 TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
687
688 // wValue is the requested bTag to abort
690 {
692 }
693 else if(usbtmc_state.lastBulkOutTag == (request->wValue & 0x7Fu))
694 {
696 }
697 else
698 {
700 // Check if we've queued a short packet
701 criticalEnter();
703 criticalLeave();
706 }
707 TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
708 return true;
709 }
710
712 {
715 .NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent
716 };
717 TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
718 TU_VERIFY(request->wLength == sizeof(rsp));
719 TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
720 TU_VERIFY(tud_usbtmc_check_abort_bulk_out_cb(&rsp));
721 TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
722 return true;
723 }
724
726 {
729 };
730 TU_VERIFY(request->bmRequestType == 0xA2); // in,class,interface
731 TU_VERIFY(request->wLength == sizeof(rsp));
732 TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_in);
733 // wValue is the requested bTag to abort
736 {
739 // Check if we've queued a short packet
740 criticalEnter();
743 criticalLeave();
745 {
746 // Send short packet, nothing is in the buffer yet
747 TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
749 }
751 }
753 { // FIXME: Unsure how to check if the OUT endpoint fifo is non-empty....
755 }
756 else
757 {
759 }
760 TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
761 return true;
762 }
763
765 {
766 TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
767 TU_VERIFY(request->wLength == 8u);
768
770 {
772 .bmAbortBulkIn =
773 {
775 },
776 .NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent,
777 };
778 TU_VERIFY(tud_usbtmc_check_abort_bulk_in_cb(&rsp));
779 criticalEnter();
780 switch(usbtmc_state.state)
781 {
785 break;
789 break;
790 default:
791 break;
792 }
793 criticalLeave();
794 TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
795
796 return true;
797 }
798
800 {
801 TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
802 TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
803 // After receiving an INITIATE_CLEAR request, the device must Halt the Bulk-OUT endpoint, queue the
804 // control endpoint response shown in Table 31, and clear all input buffers and output buffers.
807 criticalEnter();
809 criticalLeave();
810 TU_VERIFY(tud_usbtmc_initiate_clear_cb(&tmcStatusCode));
811 TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode,sizeof(tmcStatusCode)));
812 return true;
813 }
814
816 {
817 TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
818 usbtmc_get_clear_status_rsp_t clearStatusRsp = {0};
819 TU_VERIFY(request->wLength == sizeof(clearStatusRsp));
820
822 {
823 // Stuff stuck in TX buffer?
824 clearStatusRsp.bmClear.BulkInFifoBytes = 1;
825 clearStatusRsp.USBTMC_status = USBTMC_STATUS_PENDING;
826 }
827 else
828 {
829 // Let app check if it's clear
830 TU_VERIFY(tud_usbtmc_check_clear_cb(&clearStatusRsp));
831 }
832 if(clearStatusRsp.USBTMC_status == USBTMC_STATUS_SUCCESS)
833 {
834 criticalEnter();
836 criticalLeave();
837 }
838 TU_VERIFY(tud_control_xfer(rhport, request, (void*)&clearStatusRsp,sizeof(clearStatusRsp)));
839 return true;
840 }
841
843 {
844 TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
845 TU_VERIFY(request->wLength == sizeof(*(usbtmc_state.capabilities)));
846 TU_VERIFY(tud_control_xfer(rhport, request, (void*)(uintptr_t) usbtmc_state.capabilities, sizeof(*usbtmc_state.capabilities)));
847 return true;
848 }
849 // USBTMC Optional Requests
850
851 case USBTMC_bREQUEST_INDICATOR_PULSE: // Optional
852 {
853 TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
854 TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
856 TU_VERIFY(tud_usbtmc_indicator_pulse_cb(request, &tmcStatusCode));
857 TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode, sizeof(tmcStatusCode)));
858 return true;
859 }
860#if (CFG_TUD_USBTMC_ENABLE_488)
861
862 // USB488 required requests
864 {
866 TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
867 TU_VERIFY(request->wLength == sizeof(rsp)); // in,class,interface
868
869 bTag = request->wValue & 0x7F;
870 TU_VERIFY(request->bmRequestType == 0xA1);
871 TU_VERIFY((request->wValue & (~0x7F)) == 0u); // Other bits are required to be zero (USB488v1.0 Table 11)
872 TU_VERIFY(bTag >= 0x02 && bTag <= 127);
873 TU_VERIFY(request->wIndex == usbtmc_state.itf_id);
874 TU_VERIFY(request->wLength == 0x0003);
875 rsp.bTag = (uint8_t)bTag;
876 if(usbtmc_state.ep_int_in != 0)
877 {
878 rsp.statusByte = 0x00; // Use interrupt endpoint, instead. Must be 0x00 (USB488v1.0 4.3.1.2)
880 {
882 }
883 else
884 {
887 {
888 .bNotify1 = {
889 .one = 1,
890 .bTag = bTag & 0x7Fu,
891 },
892 .StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status))
893 };
894 // Must be queued before control request response sent (USB488v1.0 4.3.1.2)
895 usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&intMsg, sizeof(intMsg));
896 }
897 }
898 else
899 {
901 }
902 TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp, sizeof(rsp)));
903 return true;
904 }
905 // USB488 optional requests
909 {
910 TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
911 return false;
912 }
913#endif
914
915 default:
916 return false;
917 }
918}
919
920#endif /* CFG_TUD_TSMC */
static struct @612 data
static TU_ATTR_ALWAYS_INLINE bool osal_mutex_delete(osal_mutex_t mutex_hdl)
SemaphoreHandle_t osal_mutex_t
Definition: osal_freertos.h:54
static TU_ATTR_ALWAYS_INLINE osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef)
static void * memcpy(void *dst, const void *src, size_t n)
Definition: ringbuffer.c:8
uint8_t UsingTermChar
Support TermChar && Request.TermCharEnabled && last char in transfer is TermChar.
Definition: usbtmc.h:109
unsigned int supportsIndicatorPulse
Definition: usbtmc.h:228
unsigned int BulkInFifoBytes
Has queued data or a short packet that is queued.
Definition: usbtmc.h:245
unsigned int TermCharEnabled
"The Bulk-IN transfer must terminate on the specified TermChar."; CAPABILITIES must list TermChar
Definition: usbtmc.h:92
unsigned int canEndBulkInOnTermChar
Definition: usbtmc.h:232
unsigned int EOM
EOM set on last byte.
Definition: usbtmc.h:78
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint8_t USBTMC_status
usbtmc_status_enum
Definition: usbtmc.h:220
uint8_t bNotify1
Definition: usbtmc.h:325
uint8_t MsgID
Message type ID (usbtmc_msgid_enum)
Definition: usbtmc.h:59
struct TU_ATTR_PACKED::TU_ATTR_PACKED bmDevCapabilities
uint8_t TermChar
Definition: usbtmc.h:94
uint16_t wValue
Definition: audio.h:934
struct TU_ATTR_PACKED::@16::TU_ATTR_PACKED bmRequestType_bit
uint8_t bmAttributes
See: audio_clock_source_attribute_t.
Definition: audio.h:672
uint8_t statusByte
Definition: usbtmc.h:318
struct TU_ATTR_PACKED::TU_ATTR_PACKED bmDevCapabilities488
uint16_t wLength
Definition: audio.h:840
struct TU_ATTR_PACKED::TU_ATTR_PACKED bmIntfcCapabilities488
uint32_t TransferSize
Transfer size; LSB first.
Definition: usbtmc.h:75
struct TU_ATTR_PACKED::TU_ATTR_PACKED bmIntfcCapabilities
struct TU_ATTR_PACKED::TU_ATTR_PACKED bmClear
uint16_t wIndex
Definition: audio.h:943
uint8_t iInterface
Index of string descriptor describing this interface.
Definition: tusb_types.h:352
uint8_t bInterfaceClass
Class code (assigned by the USB-IF).
Definition: tusb_types.h:349
usbtmc_msg_header_t header
Header.
Definition: usbtmc.h:67
uint8_t bTagInverse
Complement of the tag.
Definition: usbtmc.h:61
uint8_t bTag
Transfer ID 1<=bTag<=255.
Definition: usbtmc.h:60
struct TU_ATTR_PACKED::TU_ATTR_PACKED bmTransferAttributes
uint8_t bEndpointAddress
Definition: video.h:306
uint8_t bInterfaceSubClass
Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bInterfaceCla...
Definition: tusb_types.h:350
uint8_t bmRequestType
Definition: audio.h:828
uint8_t bRequest
Request type audio_cs_req_t.
Definition: audio.h:831
uint8_t bNumEndpoints
Number of endpoints used by this interface (excluding endpoint zero). If this value is zero,...
Definition: tusb_types.h:348
uint8_t bInterfaceNumber
Number of this interface. Zero-based value identifying the index in the array of concurrent interface...
Definition: tusb_types.h:346
CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_BUFFER_SIZE]
CFG_TUSB_MEM_ALIGN uint8_t ep_int_in_buf[CFG_TUD_USBTMC_INT_EP_SIZE]
usbtmc_capabilities_specific_t const * capabilities
uint8_t const * devInBuffer
CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE]
volatile usbtmcd_state_enum state
static TU_ATTR_ALWAYS_INLINE uint32_t tu_min32(uint32_t x, uint32_t y)
Definition: tusb_common.h:156
static TU_ATTR_ALWAYS_INLINE int tu_memcpy_s(void *dest, size_t destsz, const void *src, size_t count)
Definition: tusb_common.h:114
@ TUSB_DIR_IN
Definition: tusb_types.h:67
@ TUSB_REQ_CLEAR_FEATURE
Definition: tusb_types.h:123
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
@ XFER_RESULT_SUCCESS
Definition: tusb_types.h:237
@ TUSB_REQ_RCPT_ENDPOINT
Definition: tusb_types.h:153
@ DESC_OFFSET_TYPE
Definition: tusb_types.h:247
static TU_ATTR_ALWAYS_INLINE uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const *desc_ep)
Definition: tusb_types.h:515
@ TUSB_XFER_INTERRUPT
Definition: tusb_types.h:62
@ TUSB_XFER_BULK
Definition: tusb_types.h:61
TU_ATTR_PACKED_END TU_ATTR_BIT_FIELD_ORDER_END static TU_ATTR_ALWAYS_INLINE tusb_dir_t tu_edpt_dir(uint8_t addr)
Definition: tusb_types.h:502
@ CONTROL_STAGE_SETUP
Definition: tusb_types.h:268
@ TUSB_REQ_TYPE_STANDARD
Definition: tusb_types.h:144
@ TUSB_REQ_TYPE_CLASS
Definition: tusb_types.h:145
static TU_ATTR_ALWAYS_INLINE uint8_t const * tu_desc_next(void const *desc)
Definition: tusb_types.h:531
@ TUSB_DESC_ENDPOINT
Definition: tusb_types.h:97
@ TUSB_REQ_FEATURE_EDPT_HALT
Definition: tusb_types.h:138
void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
Definition: usbd.c:1398
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
Definition: usbd.c:1309
void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
Definition: usbd.c:1385
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 tud_control_xfer(uint8_t rhport, tusb_control_request_t const *request, void *buffer, uint16_t len)
Definition: usbd_control.c:111
CFG_TUH_MEM_ALIGN tusb_control_request_t request
Definition: usbh.c:259
volatile uint8_t stage
Definition: usbh.c:265
@ USBTMC_STATUS_SUCCESS
Definition: usbtmc.h:204
@ USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS
Definition: usbtmc.h:207
@ USBTMC_STATUS_PENDING
Definition: usbtmc.h:205
@ USBTMC_STATUS_FAILED
Definition: usbtmc.h:206
@ USB488_STATUS_INTERRUPT_IN_BUSY
Definition: usbtmc.h:212
@ USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT
Definition: usbtmc.h:51
@ USBTMC_MSGID_USB488_TRIGGER
Definition: usbtmc.h:53
@ USBTMC_MSGID_DEV_DEP_MSG_OUT
Definition: usbtmc.h:49
@ USBTMC_MSGID_DEV_DEP_MSG_IN
Definition: usbtmc.h:50
@ USBTMC_MSGID_VENDOR_SPECIFIC_IN
Definition: usbtmc.h:52
@ USBTMC_bREQUEST_INDICATOR_PULSE
Definition: usbtmc.h:176
@ USB488_bREQUEST_GO_TO_LOCAL
Definition: usbtmc.h:181
@ USBTMC_bREQUEST_GET_CAPABILITIES
Definition: usbtmc.h:174
@ USBTMC_bREQUEST_CHECK_CLEAR_STATUS
Definition: usbtmc.h:173
@ USB488_bREQUEST_REN_CONTROL
Definition: usbtmc.h:180
@ USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT
Definition: usbtmc.h:168
@ USB488_bREQUEST_LOCAL_LOCKOUT
Definition: usbtmc.h:182
@ USBTMC_bREQUEST_INITIATE_CLEAR
Definition: usbtmc.h:172
@ USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS
Definition: usbtmc.h:169
@ USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN
Definition: usbtmc.h:170
@ USB488_bREQUEST_READ_STATUS_BYTE
Definition: usbtmc.h:179
@ USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS
Definition: usbtmc.h:171
static bool handle_devMsgIn(void *data, size_t len)
static OSAL_MUTEX_DEF(usbtmcLockBuffer)
static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen)
usbtmcd_state_enum
@ STATE_TX_SHORTED
@ STATE_ABORTING_BULK_OUT
@ STATE_NAK
@ STATE_IDLE
@ STATE_ABORTING_BULK_IN_SHORTED
@ STATE_TX_REQUESTED
@ STATE_ABORTING_BULK_IN_ABORTED
@ STATE_NUM_STATES
@ STATE_RCV
@ STATE_TX_INITIATED
@ STATE_CLOSED
@ STATE_CLEARING
@ STATE_ABORTING_BULK_IN
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
tu_static uint8_t termCharRequested
TU_VERIFY_STATIC(USBTMCD_BUFFER_SIZE >=32u,"USBTMC dev buffer size too small")
bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState)
tu_static char logMsg[150]
Definition: usbtmc_device.c:81
bool tud_usbtmc_transmit_dev_msg_data(const void *data, size_t len, bool endOfMessage, bool usingTermChar)
static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len)
void usbtmcd_init_cb(void)
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
bool usbtmcd_deinit(void)
void usbtmcd_reset_cb(uint8_t rhport)
osal_mutex_t usbtmcLock
tu_static uint8_t termChar
bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
bool tud_usbtmc_transmit_notification_data(const void *data, size_t len)
bool tud_usbtmc_start_bus_read(void)
usbtmc_response_capabilities_488_t usbtmc_capabilities_specific_t
CFG_TUD_MEM_SECTION tu_static usbtmc_interface_state_t usbtmc_state
bool tud_usbtmc_initiate_abort_bulk_in_cb(uint8_t *tmcResult)
TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const *msg, uint8_t *tmcResult)
TU_ATTR_WEAK bool tud_usbtmc_notification_complete_cb(void)
void tud_usbtmc_open_cb(uint8_t interface_id)
bool tud_usbtmc_msgBulkOut_start_cb(usbtmc_msg_request_dev_dep_out const *msgHeader)
bool tud_usbtmc_msg_data_cb(void *data, size_t len, bool transfer_complete)
bool tud_usbtmc_msgBulkIn_complete_cb(void)
TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t *msg)
bool tud_usbtmc_initiate_abort_bulk_out_cb(uint8_t *tmcResult)
bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp)
bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp)
void tud_usbtmc_bulkIn_clearFeature_cb(void)
bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp)
bool tud_usbtmc_initiate_clear_cb(uint8_t *tmcResult)
void tud_usbtmc_bulkOut_clearFeature_cb(void)
uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult)
usbtmc_response_capabilities_488_t const * tud_usbtmc_get_capabilities_cb(void)
bool tud_usbtmc_msgBulkIn_request_cb(usbtmc_msg_request_dev_dep_in const *request)