summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
Diffstat (limited to 'src/class')
-rw-r--r--src/class/cdc/cdc_host.c169
-rw-r--r--src/class/dfu/dfu_device.c6
-rw-r--r--src/class/midi/midi_device.c102
-rw-r--r--src/class/midi/midi_device.h12
-rw-r--r--src/class/msc/msc_device.c6
-rw-r--r--src/class/mtp/mtp_device.h2
-rw-r--r--src/class/video/video_device.h3
7 files changed, 210 insertions, 90 deletions
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index f19c4a327..8f6dd7200 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -127,7 +127,7 @@ static bool acm_set_control_line_state(cdch_interface_t *p_cdc, tuh_xfer_cb_
static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
-static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
+static bool ftdi_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void ftdi_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static bool ftdi_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ftdi_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
@@ -216,82 +216,84 @@ typedef struct {
#define DRIVER_NAME_DECLARE(_str)
#endif
+// clang-format off
// Note driver list must be in the same order as SERIAL_DRIVER enum
static const cdch_serial_driver_t serial_drivers[] = {
{
- .vid_pid_list = NULL,
- .vid_pid_count = 0,
- .open = acm_open,
- .process_set_config = acm_process_set_config,
- .request_complete = acm_internal_control_complete,
- .set_control_line_state = acm_set_control_line_state,
- .set_baudrate = acm_set_line_coding,
- .set_data_format = acm_set_line_coding,
- .set_line_coding = acm_set_line_coding,
- DRIVER_NAME_DECLARE("ACM")
+ .vid_pid_list = NULL,
+ .vid_pid_count = 0,
+ .open = acm_open,
+ .process_set_config = acm_process_set_config,
+ .request_complete = acm_internal_control_complete,
+ .set_control_line_state = acm_set_control_line_state,
+ .set_baudrate = acm_set_line_coding,
+ .set_data_format = acm_set_line_coding,
+ .set_line_coding = acm_set_line_coding,
+ DRIVER_NAME_DECLARE("ACM")
},
#if CFG_TUH_CDC_FTDI
{
- .vid_pid_list = ftdi_vid_pid_list,
- .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
- .open = ftdi_open,
- .process_set_config = ftdi_proccess_set_config,
- .request_complete = ftdi_internal_control_complete,
- .set_control_line_state = ftdi_set_modem_ctrl,
- .set_baudrate = ftdi_set_baudrate,
- .set_data_format = ftdi_set_data_format,
- .set_line_coding = NULL, // 2 stage set line coding
- DRIVER_NAME_DECLARE("FTDI")
+ .vid_pid_list = ftdi_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
+ .open = ftdi_open,
+ .process_set_config = ftdi_process_set_config,
+ .request_complete = ftdi_internal_control_complete,
+ .set_control_line_state = ftdi_set_modem_ctrl,
+ .set_baudrate = ftdi_set_baudrate,
+ .set_data_format = ftdi_set_data_format,
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("FTDI")
},
#endif
#if CFG_TUH_CDC_CP210X
{
- .vid_pid_list = cp210x_vid_pid_list,
- .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
- .open = cp210x_open,
- .process_set_config = cp210x_process_set_config,
- .request_complete = cp210x_internal_control_complete,
- .set_control_line_state = cp210x_set_modem_ctrl,
- .set_baudrate = cp210x_set_baudrate,
- .set_data_format = cp210x_set_data_format,
- .set_line_coding = NULL, // 2 stage set line coding
- DRIVER_NAME_DECLARE("CP210x")
+ .vid_pid_list = cp210x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
+ .open = cp210x_open,
+ .process_set_config = cp210x_process_set_config,
+ .request_complete = cp210x_internal_control_complete,
+ .set_control_line_state = cp210x_set_modem_ctrl,
+ .set_baudrate = cp210x_set_baudrate,
+ .set_data_format = cp210x_set_data_format,
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("CP210x")
},
#endif
#if CFG_TUH_CDC_CH34X
{
- .vid_pid_list = ch34x_vid_pid_list,
- .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
- .open = ch34x_open,
- .process_set_config = ch34x_process_set_config,
- .request_complete = ch34x_internal_control_complete,
+ .vid_pid_list = ch34x_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
+ .open = ch34x_open,
+ .process_set_config = ch34x_process_set_config,
+ .request_complete = ch34x_internal_control_complete,
- .set_control_line_state = ch34x_set_modem_ctrl,
- .set_baudrate = ch34x_set_baudrate,
- .set_data_format = ch34x_set_data_format,
- .set_line_coding = NULL, // 2 stage set line coding
- DRIVER_NAME_DECLARE("CH34x")
+ .set_control_line_state = ch34x_set_modem_ctrl,
+ .set_baudrate = ch34x_set_baudrate,
+ .set_data_format = ch34x_set_data_format,
+ .set_line_coding = NULL, // 2 stage set line coding
+ DRIVER_NAME_DECLARE("CH34x")
},
#endif
#if CFG_TUH_CDC_PL2303
{
- .vid_pid_list = pl2303_vid_pid_list,
- .vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list),
- .open = pl2303_open,
- .process_set_config = pl2303_process_set_config,
- .request_complete = pl2303_internal_control_complete,
- .set_control_line_state = pl2303_set_modem_ctrl,
- .set_baudrate = pl2303_set_line_coding,
- .set_data_format = pl2303_set_line_coding,
- .set_line_coding = pl2303_set_line_coding,
- DRIVER_NAME_DECLARE("PL2303")
+ .vid_pid_list = pl2303_vid_pid_list,
+ .vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list),
+ .open = pl2303_open,
+ .process_set_config = pl2303_process_set_config,
+ .request_complete = pl2303_internal_control_complete,
+ .set_control_line_state = pl2303_set_modem_ctrl,
+ .set_baudrate = pl2303_set_line_coding,
+ .set_data_format = pl2303_set_line_coding,
+ .set_line_coding = pl2303_set_line_coding,
+ DRIVER_NAME_DECLARE("PL2303")
}
#endif
};
+// clang-format on
TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch");
@@ -761,7 +763,8 @@ uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *i
for (size_t i = 0; i < driver->vid_pid_count; i++) {
if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
const uint16_t drv_len = driver->open(daddr, itf_desc, max_len);
- TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, drv_len > 0 ? "OK" : "FAILED");
+ TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name,
+ drv_len > 0 ? "OK" : "FAILED");
return drv_len;
}
}
@@ -773,35 +776,16 @@ uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *i
return 0;
}
-bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
- tusb_control_request_t request;
- request.wIndex = tu_htole16((uint16_t) itf_num);
- uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
- cdch_interface_t *p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
- TU_LOG_CDC(p_cdc, "set config");
-
- // fake transfer to kick-off process_set_config()
- tuh_xfer_t xfer;
- xfer.daddr = daddr;
- xfer.result = XFER_RESULT_SUCCESS;
- xfer.setup = &request;
- xfer.user_data = 0; // initial state 0
- cdch_process_set_config(&xfer);
-
- return true;
-}
-
static void set_config_complete(cdch_interface_t *p_cdc, bool success) {
if (success) {
const uint8_t idx = get_idx_by_ptr(p_cdc);
- p_cdc->mounted = true;
+ p_cdc->mounted = true;
tuh_cdc_mount_cb(idx);
// Prepare for incoming data
tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
} else {
// clear the interface entry
- p_cdc->daddr = 0;
+ p_cdc->daddr = 0;
p_cdc->bInterfaceNumber = 0;
}
@@ -810,6 +794,33 @@ static void set_config_complete(cdch_interface_t *p_cdc, bool success) {
usbh_driver_set_config_complete(p_cdc->daddr, p_cdc->bInterfaceNumber + itf_offset);
}
+bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
+ const uint8_t idx = tuh_cdc_itf_get_index(daddr, itf_num);
+ cdch_interface_t *p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
+ TU_LOG_CDC(p_cdc, "set config");
+
+ // fake transfer to kick-off process_set_config()
+ tusb_control_request_t request;
+ request.wIndex = tu_htole16((uint16_t)itf_num);
+
+ tuh_xfer_t xfer;
+ xfer.daddr = daddr;
+ xfer.ep_addr = 0;
+ xfer.result = XFER_RESULT_SUCCESS;
+ xfer.setup = &request;
+ xfer.complete_cb = NULL;
+ xfer.buffer = NULL;
+ xfer.user_data = 0; // initial state 0
+
+ const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid];
+ if (!driver->process_set_config(p_cdc, &xfer)) {
+ set_config_complete(p_cdc, false);
+ }
+
+ return true;
+}
+
static void cdch_process_set_config(tuh_xfer_t *xfer) {
cdch_interface_t *p_cdc = get_itf_by_xfer(xfer);
TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
@@ -1215,22 +1226,14 @@ static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc,
return drv_len;
}
-static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
+static bool ftdi_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS);
const uintptr_t state = xfer->user_data;
switch (state) {
// from here sequence overtaken from Linux Kernel function ftdi_port_probe()
case CONFIG_FTDI_DETERMINE_TYPE:
// determine type
- if (p_cdc->bInterfaceNumber == 0) {
- TU_ASSERT(ftdi_determine_type(p_cdc));
- } else {
- // other interfaces have same type as interface 0
- uint8_t const idx_itf0 = tuh_cdc_itf_get_index(xfer->daddr, 0);
- cdch_interface_t const *p_cdc_itf0 = get_itf(idx_itf0);
- TU_ASSERT(p_cdc_itf0);
- p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type;
- }
+ TU_ASSERT(ftdi_determine_type(p_cdc));
TU_ATTR_FALLTHROUGH;
case CONFIG_FTDI_WRITE_LATENCY:
diff --git a/src/class/dfu/dfu_device.c b/src/class/dfu/dfu_device.c
index d3cc53918..a09c53b7e 100644
--- a/src/class/dfu/dfu_device.c
+++ b/src/class/dfu/dfu_device.c
@@ -327,7 +327,7 @@ bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control
default:
if (stage == CONTROL_STAGE_SETUP) {
- return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
+ return reply_getstatus(rhport, request, (dfu_state_t) _dfu_ctx.state, (dfu_status_t) _dfu_ctx.status, 0);
}
break;
}
@@ -376,7 +376,7 @@ static bool process_download_get_status(uint8_t rhport, uint8_t stage, const tus
timeout = 0;
}
- return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
+ return reply_getstatus(rhport, request, next_state, (dfu_status_t) _dfu_ctx.status, timeout);
} else if (stage == CONTROL_STAGE_ACK) {
if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_DNBUSY;
@@ -405,7 +405,7 @@ static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, const tus
timeout = 0;
}
- return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
+ return reply_getstatus(rhport, request, next_state, (dfu_status_t) _dfu_ctx.status, timeout);
} else if (stage == CONTROL_STAGE_ACK) {
if (_dfu_ctx.flashing_in_progress) {
_dfu_ctx.state = DFU_MANIFEST;
diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c
index 023a81595..a49cd725b 100644
--- a/src/class/midi/midi_device.c
+++ b/src/class/midi/midi_device.c
@@ -168,6 +168,108 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, ui
return total_read;
}
+// Note: this function shares stream->buffer with tud_midi_n_stream_read().
+// Do not mix calls to both functions on the same interface.
+uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num, void *buffer, uint32_t bufsize) {
+ TU_VERIFY(p_cable_num != NULL && buffer != NULL && bufsize > 0, 0);
+
+ midid_interface_t *p_midi = &_midid_itf[itf];
+ midi_driver_stream_t *stream = &p_midi->stream_read;
+ tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
+
+ uint8_t *buf8 = (uint8_t *)buffer;
+ uint32_t total_read = 0;
+
+ // Initialize to invalid cable so callers can detect "no data" even when
+ // the return value is 0.
+ *p_cable_num = 0xff;
+
+ // If there are leftover bytes from a previous partial read, return them first
+ if (stream->total > 0) {
+ *p_cable_num = (stream->buffer[0] >> 4) & 0x0f;
+ const uint8_t count = (uint8_t)tu_min32((uint32_t)(stream->total - stream->index), bufsize);
+ TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count));
+
+ total_read += count;
+ stream->index += count;
+ buf8 += count;
+ bufsize -= count;
+
+ if (stream->total == stream->index) {
+ stream->index = 0;
+ stream->total = 0;
+ }
+
+ if (bufsize == 0) {
+ return total_read;
+ }
+ }
+
+ while (bufsize > 0) {
+ // Peek at next packet header to get cable number without consuming
+ uint8_t one_byte;
+ if (!tu_edpt_stream_peek(ep_str, &one_byte)) {
+ break;
+ }
+
+ const uint8_t next_cable = (one_byte >> 4) & 0x0f;
+
+ // Stop if cable changed (covers both leftover-originated reads and
+ // freshly consumed packets — total_read > 0 in either case)
+ if (total_read > 0 && next_cable != *p_cable_num) {
+ break;
+ }
+ *p_cable_num = next_cable;
+
+ // Consume the packet
+ if (!tud_midi_n_packet_read(itf, stream->buffer)) {
+ break;
+ }
+
+ const uint8_t code_index = stream->buffer[0] & 0x0f;
+ uint8_t msg_bytes;
+
+ // MIDI 1.0 Table 4-1: Code Index Number Classifications
+ switch (code_index) {
+ case MIDI_CIN_MISC:
+ case MIDI_CIN_CABLE_EVENT:
+ // Reserved and unused, skip this packet
+ continue;
+
+ case MIDI_CIN_SYSEX_END_1BYTE:
+ case MIDI_CIN_1BYTE_DATA:
+ msg_bytes = 1;
+ break;
+
+ case MIDI_CIN_SYSCOM_2BYTE:
+ case MIDI_CIN_SYSEX_END_2BYTE:
+ case MIDI_CIN_PROGRAM_CHANGE:
+ case MIDI_CIN_CHANNEL_PRESSURE:
+ msg_bytes = 2;
+ break;
+
+ default:
+ msg_bytes = 3;
+ break;
+ }
+
+ const uint8_t count = (uint8_t)tu_min32((uint32_t)msg_bytes, bufsize);
+ TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1, count));
+
+ total_read += count;
+ buf8 += count;
+ bufsize -= count;
+
+ if (count < msg_bytes) {
+ // Output buffer full, save remaining for next call
+ stream->total = msg_bytes;
+ stream->index = count;
+ }
+ }
+
+ return total_read;
+}
+
bool tud_midi_n_packet_read(uint8_t itf, uint8_t packet[4]) {
midid_interface_t *p_midi = &_midid_itf[itf];
tu_edpt_stream_t *ep_str = &p_midi->ep_stream.rx;
diff --git a/src/class/midi/midi_device.h b/src/class/midi/midi_device.h
index ddbc2f9f0..b80ad544a 100644
--- a/src/class/midi/midi_device.h
+++ b/src/class/midi/midi_device.h
@@ -66,6 +66,13 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num);
// Read byte stream (legacy)
uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void *buffer, uint32_t bufsize);
+// Read byte stream with cable demultiplexing: returns the cable number of the
+// data that was read. Reads from a single cable per call; stops when the next
+// packet belongs to a different cable so the caller can dispatch per-cable.
+// Note: shares internal state with tud_midi_n_stream_read(); do not mix both
+// on the same interface.
+uint32_t tud_midi_n_demux_stream_read(uint8_t itf, uint8_t *p_cable_num, void *buffer, uint32_t bufsize);
+
// Write byte Stream (legacy)
uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize);
@@ -97,6 +104,11 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_midi_stream_read(void *buffer,
}
TU_ATTR_ALWAYS_INLINE static inline uint32_t
+tud_midi_demux_stream_read(uint8_t *p_cable_num, void *buffer, uint32_t bufsize) {
+ return tud_midi_n_demux_stream_read(0, p_cable_num, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t
tud_midi_stream_write(uint8_t cable_num, const uint8_t *buffer, uint32_t bufsize) {
return tud_midi_n_stream_write(0, cable_num, buffer, bufsize);
}
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 15bfafc35..3766e3a25 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -646,7 +646,11 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
break;
}
- TU_ASSERT(prepare_cbw(p_msc));
+ if (!usbd_edpt_stalled(rhport, p_msc->ep_out)) {
+ TU_ASSERT(prepare_cbw(p_msc));
+ } else {
+ p_msc->stage = MSC_STAGE_CMD;
+ }
} else {
// Any xfer ended here is considered unknown error, ignore it
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
diff --git a/src/class/mtp/mtp_device.h b/src/class/mtp/mtp_device.h
index a33f1dc08..6cce7efbb 100644
--- a/src/class/mtp/mtp_device.h
+++ b/src/class/mtp/mtp_device.h
@@ -18,7 +18,7 @@
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN0
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h
index f14555e4f..2750bb2fb 100644
--- a/src/class/video/video_device.h
+++ b/src/class/video/video_device.h
@@ -99,8 +99,7 @@ int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
* @param[in] stm_idx Destination streaming interface index
* @param[out] payload_buf Payload storage buffer (target buffer for requested data)
* @param[in] payload_size Size of payload_buf (requested data size)
- * @param[in] offset Current byte offset relative to given bufsize from tud_video_n_frame_xfer (framesize)
- * @return video_error_code_t */
+ * @param[in] offset Current byte offset relative to given bufsize from tud_video_n_frame_xfer (framesize) */
void tud_video_prepare_payload_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, tud_video_payload_request_t* request);
//--------------------------------------------------------------------+