blob: 8f87a6dca6637011c99a469f238843848d7bd5f0 [file] [log] [blame]
Austin Schuh41baf202022-01-01 14:33:40 -08001/*
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 */
25
26#include <strings.h>
27#include <stdlib.h> /* atoi */
28#include "tusb.h"
29#include "bsp/board.h"
30#include "main.h"
31
32#if (CFG_TUD_USBTMC_ENABLE_488)
33static usbtmc_response_capabilities_488_t const
34#else
35static usbtmc_response_capabilities_t const
36#endif
37tud_usbtmc_app_capabilities =
38{
39 .USBTMC_status = USBTMC_STATUS_SUCCESS,
40 .bcdUSBTMC = USBTMC_VERSION,
41 .bmIntfcCapabilities =
42 {
43 .listenOnly = 0,
44 .talkOnly = 0,
45 .supportsIndicatorPulse = 1
46 },
47 .bmDevCapabilities = {
48 .canEndBulkInOnTermChar = 0
49 },
50
51#if (CFG_TUD_USBTMC_ENABLE_488)
52 .bcdUSB488 = USBTMC_488_VERSION,
53 .bmIntfcCapabilities488 =
54 {
55 .supportsTrigger = 1,
56 .supportsREN_GTL_LLO = 0,
57 .is488_2 = 1
58 },
59 .bmDevCapabilities488 =
60 {
61 .SCPI = 1,
62 .SR1 = 0,
63 .RL1 = 0,
64 .DT1 =0,
65 }
66#endif
67};
68
69#define IEEE4882_STB_QUESTIONABLE (0x08u)
70#define IEEE4882_STB_MAV (0x10u)
71#define IEEE4882_STB_SER (0x20u)
72#define IEEE4882_STB_SRQ (0x40u)
73
74static const char idn[] = "TinyUSB,ModelNumber,SerialNumber,FirmwareVer123456\r\n";
75//static const char idn[] = "TinyUSB,ModelNumber,SerialNumber,FirmwareVer and a bunch of other text to make it longer than a packet, perhaps? lets make it three transfers...\n";
76static volatile uint8_t status;
77
78// 0=not query, 1=queried, 2=delay,set(MAV), 3=delay 4=ready?
79// (to simulate delay)
80static volatile uint16_t queryState = 0;
81static volatile uint32_t queryDelayStart;
82static volatile uint32_t bulkInStarted;
83static volatile uint32_t idnQuery;
84
85static uint32_t resp_delay = 125u; // Adjustable delay, to allow for better testing
86static size_t buffer_len;
87static size_t buffer_tx_ix; // for transmitting using multiple transfers
88static uint8_t buffer[225]; // A few packets long should be enough.
89
90
91static usbtmc_msg_dev_dep_msg_in_header_t rspMsg = {
92 .bmTransferAttributes =
93 {
94 .EOM = 1,
95 .UsingTermChar = 0
96 }
97};
98
99void tud_usbtmc_open_cb(uint8_t interface_id)
100{
101 (void)interface_id;
102 tud_usbtmc_start_bus_read();
103}
104
105#if (CFG_TUD_USBTMC_ENABLE_488)
106usbtmc_response_capabilities_488_t const *
107#else
108usbtmc_response_capabilities_t const *
109#endif
110tud_usbtmc_get_capabilities_cb()
111{
112 return &tud_usbtmc_app_capabilities;
113}
114
115
116bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg) {
117 (void)msg;
118 // Let trigger set the SRQ
119 status |= IEEE4882_STB_SRQ;
120 return true;
121}
122
123bool tud_usbtmc_msgBulkOut_start_cb(usbtmc_msg_request_dev_dep_out const * msgHeader)
124{
125 (void)msgHeader;
126 buffer_len = 0;
127 if(msgHeader->TransferSize > sizeof(buffer))
128 {
129
130 return false;
131 }
132 return true;
133}
134
135bool tud_usbtmc_msg_data_cb(void *data, size_t len, bool transfer_complete)
136{
137 // If transfer isn't finished, we just ignore it (for now)
138
139 if(len + buffer_len < sizeof(buffer))
140 {
141 memcpy(&(buffer[buffer_len]), data, len);
142 buffer_len += len;
143 }
144 else
145 {
146 return false; // buffer overflow!
147 }
148 queryState = transfer_complete;
149 idnQuery = 0;
150
151 if(transfer_complete && (len >=4) && !strncasecmp("*idn?",data,4))
152 {
153 idnQuery = 1;
154 }
155 if(transfer_complete && !strncasecmp("delay ",data,5))
156 {
157 queryState = 0;
158 int d = atoi((char*)data + 5);
159 if(d > 10000)
160 d = 10000;
161 if(d<0)
162 d=0;
163 resp_delay = (uint32_t)d;
164 }
165 tud_usbtmc_start_bus_read();
166 return true;
167}
168
169bool tud_usbtmc_msgBulkIn_complete_cb()
170{
171 if((buffer_tx_ix == buffer_len) || idnQuery) // done
172 {
173 status &= (uint8_t)~(IEEE4882_STB_MAV); // clear MAV
174 queryState = 0;
175 bulkInStarted = 0;
176 buffer_tx_ix = 0;
177 }
178 tud_usbtmc_start_bus_read();
179
180 return true;
181}
182
183static unsigned int msgReqLen;
184
185bool tud_usbtmc_msgBulkIn_request_cb(usbtmc_msg_request_dev_dep_in const * request)
186{
187 rspMsg.header.MsgID = request->header.MsgID,
188 rspMsg.header.bTag = request->header.bTag,
189 rspMsg.header.bTagInverse = request->header.bTagInverse;
190 msgReqLen = request->TransferSize;
191
192#ifdef xDEBUG
193 uart_tx_str_sync("MSG_IN_DATA: Requested!\r\n");
194#endif
195 if(queryState == 0 || (buffer_tx_ix == 0))
196 {
197 TU_ASSERT(bulkInStarted == 0);
198 bulkInStarted = 1;
199
200 // > If a USBTMC interface receives a Bulk-IN request prior to receiving a USBTMC command message
201 // that expects a response, the device must NAK the request (*not stall*)
202 }
203 else
204 {
205 size_t txlen = tu_min32(buffer_len-buffer_tx_ix,msgReqLen);
206 tud_usbtmc_transmit_dev_msg_data(&buffer[buffer_tx_ix], txlen,
207 (buffer_tx_ix+txlen) == buffer_len, false);
208 buffer_tx_ix += txlen;
209 }
210 // Always return true indicating not to stall the EP.
211 return true;
212}
213
214void usbtmc_app_task_iter(void) {
215 switch(queryState) {
216 case 0:
217 break;
218 case 1:
219 queryDelayStart = board_millis();
220 queryState = 2;
221 break;
222 case 2:
223 if( (board_millis() - queryDelayStart) > resp_delay) {
224 queryDelayStart = board_millis();
225 queryState=3;
226 status |= 0x10u; // MAV
227 status |= 0x40u; // SRQ
228 }
229 break;
230 case 3:
231 if( (board_millis() - queryDelayStart) > resp_delay) {
232 queryState = 4;
233 }
234 break;
235 case 4: // time to transmit;
236 if(bulkInStarted && (buffer_tx_ix == 0)) {
237 if(idnQuery)
238 {
239 tud_usbtmc_transmit_dev_msg_data(idn, tu_min32(sizeof(idn)-1,msgReqLen),true,false);
240 queryState = 0;
241 bulkInStarted = 0;
242 }
243 else
244 {
245 buffer_tx_ix = tu_min32(buffer_len,msgReqLen);
246 tud_usbtmc_transmit_dev_msg_data(buffer, buffer_tx_ix, buffer_tx_ix == buffer_len, false);
247 }
248 // MAV is cleared in the transfer complete callback.
249 }
250 break;
251 default:
252 TU_ASSERT(false,);
253 return;
254 }
255}
256
257bool tud_usbtmc_initiate_clear_cb(uint8_t *tmcResult)
258{
259 *tmcResult = USBTMC_STATUS_SUCCESS;
260 queryState = 0;
261 bulkInStarted = false;
262 status = 0;
263 return true;
264}
265
266bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp)
267{
268 queryState = 0;
269 bulkInStarted = false;
270 status = 0;
271 buffer_tx_ix = 0u;
272 buffer_len = 0u;
273 rsp->USBTMC_status = USBTMC_STATUS_SUCCESS;
274 rsp->bmClear.BulkInFifoBytes = 0u;
275 return true;
276}
277bool tud_usbtmc_initiate_abort_bulk_in_cb(uint8_t *tmcResult)
278{
279 bulkInStarted = 0;
280 *tmcResult = USBTMC_STATUS_SUCCESS;
281 return true;
282}
283bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp)
284{
285 (void)rsp;
286 tud_usbtmc_start_bus_read();
287 return true;
288}
289
290bool tud_usbtmc_initiate_abort_bulk_out_cb(uint8_t *tmcResult)
291{
292 *tmcResult = USBTMC_STATUS_SUCCESS;
293 return true;
294
295}
296bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp)
297{
298 (void)rsp;
299 tud_usbtmc_start_bus_read();
300 return true;
301}
302
303void tud_usbtmc_bulkIn_clearFeature_cb(void)
304{
305}
306void tud_usbtmc_bulkOut_clearFeature_cb(void)
307{
308 tud_usbtmc_start_bus_read();
309}
310
311// Return status byte, but put the transfer result status code in the rspResult argument.
312uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult)
313{
314 uint8_t old_status = status;
315 status = (uint8_t)(status & ~(IEEE4882_STB_SRQ)); // clear SRQ
316
317 *tmcResult = USBTMC_STATUS_SUCCESS;
318 // Increment status so that we see different results on each read...
319
320 return old_status;
321}
322
323bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult)
324{
325 (void)msg;
326 led_indicator_pulse();
327 *tmcResult = USBTMC_STATUS_SUCCESS;
328 return true;
329}