Merge 03d9dcc4c2
into fd5bb6e5db
This commit is contained in:
commit
7859bfdc44
|
@ -26,3 +26,4 @@ family_add_subdirectory(uac2_headset)
|
|||
family_add_subdirectory(usbtmc)
|
||||
family_add_subdirectory(video_capture)
|
||||
family_add_subdirectory(webusb_serial)
|
||||
family_add_subdirectory(ch341_serial)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
|
||||
|
||||
# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
|
||||
family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
project(${PROJECT})
|
||||
|
||||
# Checks this example is valid for the family and initializes the project
|
||||
family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
add_executable(${PROJECT})
|
||||
|
||||
# Example source
|
||||
target_sources(${PROJECT} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
|
||||
)
|
||||
|
||||
# Example include
|
||||
target_include_directories(${PROJECT} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
# Configure compilation flags and libraries for the example... see the corresponding function
|
||||
# in hw/bsp/FAMILY/family.cmake for details.
|
||||
family_configure_device_example(${PROJECT})
|
|
@ -0,0 +1,12 @@
|
|||
include ../../../tools/top.mk
|
||||
include ../../make.mk
|
||||
|
||||
INC += \
|
||||
src \
|
||||
$(TOP)/hw \
|
||||
|
||||
# Example source
|
||||
EXAMPLE_SOURCE += $(wildcard src/*.c)
|
||||
SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
|
||||
|
||||
include ../../rules.mk
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* 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 IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "bsp/board.h"
|
||||
#include "tusb.h"
|
||||
|
||||
// Change this to 1 and event callback will be logged to stdout
|
||||
#define CH341_LOG_EVENTS 0
|
||||
|
||||
#if (CH341_LOG_EVENTS)
|
||||
static const char * _par_str[] =
|
||||
{
|
||||
"N",
|
||||
"O",
|
||||
"E",
|
||||
"M",
|
||||
"S"
|
||||
};
|
||||
#endif
|
||||
|
||||
//------------- prototypes -------------//
|
||||
static void ch341_task(void);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
|
||||
// init device stack on configured roothub port
|
||||
tud_init(BOARD_TUD_RHPORT);
|
||||
|
||||
while (1)
|
||||
{
|
||||
tud_task(); // tinyusb device task
|
||||
ch341_task();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Echo back to terminal
|
||||
static void echo_serial_port( uint8_t buf[], uint32_t count)
|
||||
{
|
||||
tud_ch341_write(buf, count);
|
||||
tud_ch341_write_flush();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USB CH341
|
||||
//--------------------------------------------------------------------+
|
||||
static void ch341_task(void)
|
||||
{
|
||||
// connected() The serial port has been opened atleast once
|
||||
// Will continue to return true even after the serial port is closed.
|
||||
if ( tud_ch341_connected())
|
||||
{
|
||||
if ( tud_ch341_available() )
|
||||
{
|
||||
uint8_t buf[64];
|
||||
|
||||
uint32_t count = tud_ch341_read(buf, sizeof(buf));
|
||||
|
||||
// echo back to terminal
|
||||
echo_serial_port(buf, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when DTR/RTS changes
|
||||
void tud_ch341_line_state_cb(ch341_line_state_t line_state)
|
||||
{
|
||||
#if (CH341_LOG_EVENTS)
|
||||
printf("DTR=%u, RTS=%u\r\n",
|
||||
line_state & CH341_LINE_STATE_DTR_ACTIVE ? 1 : 0,
|
||||
line_state & CH341_LINE_STATE_RTS_ACTIVE ? 1 : 0);
|
||||
#else
|
||||
(void)(line_state);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Invoked when line coding changes
|
||||
void tud_ch341_line_coding_cb(ch341_line_coding_t const* p_line_coding)
|
||||
{
|
||||
#if (CH341_LOG_EVENTS)
|
||||
printf("BITRATE=%lu (%s%u%u) RX:%s TX:%s\r\n",
|
||||
(unsigned long)p_line_coding->bit_rate,
|
||||
_par_str[p_line_coding->parity],
|
||||
p_line_coding->data_bits,
|
||||
p_line_coding->stop_bits ? 2 : 1,
|
||||
p_line_coding->rx_en ? "ON":"OFF",
|
||||
p_line_coding->tx_en ? "ON":"OFF");
|
||||
#else
|
||||
(void)(p_line_coding);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Invoked when a break signal is received
|
||||
void tud_ch341_send_break_cb(bool is_break_active)
|
||||
{
|
||||
#if (CH341_LOG_EVENTS)
|
||||
printf("RCV BREAK=%s\r\n", is_break_active ? "ON" : "OFF");
|
||||
#else
|
||||
(void)(is_break_active);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* 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 IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Board Specific Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_TUD_RHPORT
|
||||
#define BOARD_TUD_RHPORT 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
#ifndef BOARD_TUD_MAX_SPEED
|
||||
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by board.mk
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#error CFG_TUSB_MCU must be defined
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_DEBUG
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#endif
|
||||
|
||||
// Enable Device stack
|
||||
#define CFG_TUD_ENABLED 1
|
||||
|
||||
// Default is max speed that hardware controller could support with on-chip PHY
|
||||
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
|
||||
|
||||
#ifndef CFG_TUSB_RHPORT0_MODE
|
||||
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 0
|
||||
#define CFG_TUD_MSC 0
|
||||
#define CFG_TUD_HID 0
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
#define CFG_TUD_CH341 1
|
||||
|
||||
// CH341 Endpoint max packet sizes (should not change)
|
||||
#define CFG_TUD_CH341_EP_RX_MAX_PACKET (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_CH341_EP_TX_MAX_PACKET CFG_TUD_CH341_EP_RX_MAX_PACKET
|
||||
#define CFG_TUD_CH341_EP_TXNOTIFY_MAX_PACKET (8)
|
||||
|
||||
// CH341 buffer size for TX and RX data (must be an interval of max packet size)
|
||||
// more is faster
|
||||
#define CFG_TUD_CH341_FIFO_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
* Portions Copyright (c) 2022 Travis Robinson (libusbdotnet@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* 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 IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
// QinHeng Electronics
|
||||
#define USB_VID 0x1A86
|
||||
// CH341 serial converter
|
||||
#define USB_PID 0x7523
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Generic Descriptor Templates
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define TUD_GEN_INTERFACE_DESCRIPTOR(_itfnum, _altsetting, _num_endpoints, _itfclass, _itfsubclass, _itfprotocol, _itfstridx) \
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, _altsetting, _num_endpoints, _itfclass, _itfsubclass, _itfprotocol, _itfstridx
|
||||
|
||||
#define TUD_GEN_BULK_EP_DESCRIPTOR(_epnum, _epsize) \
|
||||
7, TUSB_DESC_ENDPOINT, _epnum, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
#define TUD_GEN_INTERRUPT_EP_DESCRIPTOR(_epnum, _epsize, _interval) \
|
||||
7, TUSB_DESC_ENDPOINT, _epnum, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _interval
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// atleast 0x0200 required for HS
|
||||
.bcdUSB = 0x0200,
|
||||
#else
|
||||
// this is what's in a genuine CH340G's descriptor
|
||||
.bcdUSB = 0x0110,
|
||||
#endif
|
||||
.bDeviceClass = 0xFF,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = USB_VID,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0263, // CH340G
|
||||
|
||||
// A CH340G only has a product string. All other string indexes are 0.
|
||||
// The driver doesn't seen to care so we will specify all of them.
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void)
|
||||
{
|
||||
return (uint8_t const *) &desc_device;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
enum
|
||||
{
|
||||
ITF_NUM_CH341,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
// interface + 3 endpoints
|
||||
#define TUD_CH341_DESC_LEN (9+7+7+7)
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CH341_DESC_LEN)
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
|
||||
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
|
||||
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
|
||||
#define EPNUM_CH341_0_NOTIF 0x81
|
||||
#define EPNUM_CH341_0_OUT 0x02
|
||||
#define EPNUM_CH341_0_IN 0x82
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X
|
||||
// SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT
|
||||
// e.g EP1 OUT & EP1 IN cannot exist together
|
||||
#define EPNUM_CH341_0_NOTIF 0x81
|
||||
#define EPNUM_CH341_0_OUT 0x02
|
||||
#define EPNUM_CH341_0_IN 0x83
|
||||
#else
|
||||
#define EPNUM_CH341_0_NOTIF 0x81
|
||||
#define EPNUM_CH341_0_OUT 0x02
|
||||
#define EPNUM_CH341_0_IN 0x82
|
||||
#endif
|
||||
|
||||
uint8_t const desc_fs_configuration[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 100),
|
||||
TUD_GEN_INTERFACE_DESCRIPTOR(0, 0, 3, 0xFF, 1, 2 , 0),
|
||||
TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_IN, CFG_TUD_CH341_EP_TX_MAX_PACKET),
|
||||
TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_OUT, CFG_TUD_CH341_EP_RX_MAX_PACKET),
|
||||
TUD_GEN_INTERRUPT_EP_DESCRIPTOR(EPNUM_CH341_0_NOTIF, 8, 1)
|
||||
};
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
|
||||
// TEST: Im not sure if the ch341 windows driver will allow high speed descriptors.
|
||||
// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
|
||||
|
||||
uint8_t const desc_hs_configuration[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 100),
|
||||
TUD_GEN_INTERFACE_DESCRIPTOR(0, 0, 3, 0xFF, 1, 2 , 0),
|
||||
TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_IN, CFG_TUD_CH341_EP_TX_MAX_PACKET),
|
||||
TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_OUT, CFG_TUD_CH341_EP_RX_MAX_PACKET),
|
||||
TUD_GEN_INTERRUPT_EP_DESCRIPTOR(EPNUM_CH341_0_NOTIF, 8, 1)
|
||||
};
|
||||
|
||||
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
|
||||
tusb_desc_device_qualifier_t const desc_device_qualifier =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
|
||||
.bDeviceClass = 0xFF,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0x00
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
|
||||
// device_qualifier descriptor describes information about a high-speed capable device that would
|
||||
// change if the device were operating at the other speed. If not highspeed capable stall this request.
|
||||
uint8_t const* tud_descriptor_device_qualifier_cb(void)
|
||||
{
|
||||
return (uint8_t const*) &desc_device_qualifier;
|
||||
}
|
||||
|
||||
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
|
||||
uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void) index; // for multiple configurations
|
||||
|
||||
// if link speed is high return fullspeed config, and vice versa
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration;
|
||||
}
|
||||
|
||||
#endif // highspeed
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void) index; // for multiple configurations
|
||||
|
||||
#if TUD_OPT_HIGH_SPEED
|
||||
// Although we are highspeed, host may be fullspeed.
|
||||
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
|
||||
#else
|
||||
return desc_fs_configuration;
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const* string_desc_arr [] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"CH341 serial converter", // 2: Product
|
||||
"123456", // 3: Serials, should use chip ID
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32];
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
(void) langid;
|
||||
|
||||
uint8_t chr_count;
|
||||
|
||||
if ( index == 0)
|
||||
{
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
}else
|
||||
{
|
||||
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||
|
||||
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
|
||||
|
||||
const char* str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = strlen(str);
|
||||
if ( chr_count > 31 ) chr_count = 31;
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for(uint8_t i=0; i<chr_count; i++)
|
||||
{
|
||||
_desc_str[1+i] = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
|
||||
|
||||
return _desc_str;
|
||||
}
|
|
@ -39,7 +39,8 @@ SRC_C += \
|
|||
src/class/net/ncm_device.c \
|
||||
src/class/usbtmc/usbtmc_device.c \
|
||||
src/class/video/video_device.c \
|
||||
src/class/vendor/vendor_device.c
|
||||
src/class/vendor/vendor_device.c \
|
||||
src/class/ch341/ch341_device.c
|
||||
|
||||
# TinyUSB stack include
|
||||
INC += $(TOP)/src
|
||||
|
|
|
@ -79,6 +79,7 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
|
|||
${TOP}/src/class/usbtmc/usbtmc_device.c
|
||||
${TOP}/src/class/vendor/vendor_device.c
|
||||
${TOP}/src/class/video/video_device.c
|
||||
${TOP}/src/class/ch341/ch341_device.c
|
||||
)
|
||||
|
||||
#------------------------------------
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
* Portions Copyright (c) 2022 Travis Robinson (libusbdotnet@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* 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 IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup group_class
|
||||
* \defgroup ClassDriver_CH341 Communication Device Class (CH341)
|
||||
* Currently only Abstract Control Model subclass is supported
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_CH341_H__
|
||||
#define _TUSB_CH341_H__
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \defgroup ClassDriver_CH341_Common Common Definitions
|
||||
* @{ */
|
||||
|
||||
/// CH341 Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In)
|
||||
typedef enum
|
||||
{
|
||||
CH341_PIPE_DATA_IN, ///< Data in pipe
|
||||
CH341_PIPE_DATA_OUT, ///< Data out pipe
|
||||
CH341_PIPE_NOTIFICATION, ///< Notification pipe
|
||||
CH341_PIPE_ERROR, ///< Invalid Pipe ID
|
||||
} ch341_pipeid_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t bit_rate;
|
||||
uint8_t stop_bits; ///< 0: 1 stop bit - 2: 2 stop bits
|
||||
uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space
|
||||
uint8_t data_bits; ///< can be 5, 6, 7, 8
|
||||
bool tx_en;
|
||||
bool rx_en;
|
||||
} ch341_line_coding_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CH341_LINE_STATE_DTR_ACTIVE = (1 << 0),
|
||||
CH341_LINE_STATE_RTS_ACTIVE = (1 << 1),
|
||||
} ch341_line_state_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CH341_MODEM_STATE_CTS_ACTIVE = 0x01,
|
||||
CH341_MODEM_STATE_DSR_ACTIVE = 0x02,
|
||||
CH341_MODEM_STATE_RNG_ACTIVE = 0x04,
|
||||
CH341_MODEM_STATE_DCD_ACTIVE = 0x08,
|
||||
|
||||
CH341_MODEM_STATE_ALL = 0x0F,
|
||||
} ch341_modem_state_t;
|
||||
|
||||
// The CH34x doesn't use any structures so we don't wave anything to pack
|
||||
// Start of all packed definitions for compiler without per-type packed
|
||||
/*
|
||||
TU_ATTR_PACKED_BEGIN
|
||||
TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
|
||||
TU_ATTR_PACKED_END // End of all packed definitions
|
||||
TU_ATTR_BIT_FIELD_ORDER_END
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
|
@ -0,0 +1,854 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
* Portions Copyright (c) 2022 Travis Robinson (libusbdotnet@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* 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 IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_CH341)
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "ch341_device.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// CH341 defines directly from Linux host driver. The CH341 works basically
|
||||
// the same as a CH340G. In-fact, they share the same driver on Windows.
|
||||
// These are formatted and laid out much better than the older CH340.c so I'm
|
||||
// using them even though the descriptors are from a CH340G.
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define CH341_CLKRATE 48000000UL
|
||||
|
||||
/* flags for IO-Bits */
|
||||
#define CH341_BIT_RTS (1 << 6)
|
||||
#define CH341_BIT_DTR (1 << 5)
|
||||
|
||||
/******************************/
|
||||
/* interrupt pipe definitions */
|
||||
/******************************/
|
||||
/* always 4 interrupt bytes */
|
||||
/* first irq byte normally 0x08 */
|
||||
/* second irq byte base 0x7d + below */
|
||||
/* third irq byte base 0x94 + below */
|
||||
/* fourth irq byte normally 0xee */
|
||||
|
||||
/* second interrupt byte */
|
||||
#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */
|
||||
|
||||
/* status returned in third interrupt answer byte, inverted in data
|
||||
from irq */
|
||||
#define CH341_BIT_CTS 0x01
|
||||
#define CH341_BIT_DSR 0x02
|
||||
#define CH341_BIT_RI 0x04
|
||||
#define CH341_BIT_DCD 0x08
|
||||
#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
|
||||
|
||||
#define CH341_REQ_READ_VERSION 0x5F
|
||||
#define CH341_REQ_WRITE_REG 0x9A
|
||||
#define CH341_REQ_READ_REG 0x95
|
||||
#define CH341_REQ_SERIAL_INIT 0xA1
|
||||
#define CH341_REQ_MODEM_CTRL 0xA4
|
||||
|
||||
#define CH341_REG_BREAK 0x05
|
||||
#define CH341_REG_PRESCALER 0x12
|
||||
#define CH341_REG_DIVISOR 0x13
|
||||
#define CH341_REG_LCR 0x18
|
||||
#define CH341_REG_LCR2 0x25
|
||||
|
||||
// undocumented register. init val:0
|
||||
#define CH341_REG_0x0F 0x0F
|
||||
// undocumented register. init val:4
|
||||
#define CH341_REG_0x2C 0x2C
|
||||
// undocumented register. init val:0
|
||||
#define CH341_REG_0x27 0x27
|
||||
|
||||
#define CH341_REG_MCR_MSR 0x06
|
||||
#define CH341_REG_MCR_MSR2 0x07
|
||||
|
||||
#define CH341_NBREAK_BITS 0x01
|
||||
|
||||
#define CH341_LCR_ENABLE_RX 0x80
|
||||
#define CH341_LCR_ENABLE_TX 0x40
|
||||
#define CH341_LCR_MARK_SPACE 0x20
|
||||
#define CH341_LCR_PAR_EVEN 0x10
|
||||
#define CH341_LCR_ENABLE_PAR 0x08
|
||||
#define CH341_LCR_STOP_BITS_2 0x04
|
||||
#define CH341_LCR_CS8 0x03
|
||||
#define CH341_LCR_CS7 0x02
|
||||
#define CH341_LCR_CS6 0x01
|
||||
#define CH341_LCR_CS5 0x00
|
||||
|
||||
// The CH340G stores it's data in 8 bit registers.
|
||||
// REG_DIDX is used as our indicies to emulate these.
|
||||
typedef enum
|
||||
{
|
||||
REG_DIDX_BREAK,
|
||||
REG_DIDX_PRESCALER,
|
||||
REG_DIDX_DIVISOR,
|
||||
REG_DIDX_LCR,
|
||||
REG_DIDX_LCR2,
|
||||
REG_DIDX_MCR_MSR,
|
||||
REG_DIDX_MCR_MSR2,
|
||||
REG_DIDX_0x0F,
|
||||
REG_DIDX_0x2C,
|
||||
REG_DIDX_0x27,
|
||||
|
||||
REG_DIDX_MAX,
|
||||
}REG_DIDX;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_notif;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
|
||||
// There is no way to sense if the serial port is closed without using DTR/RTS.
|
||||
// This driver does not make any assumptions as to how the user might want to
|
||||
// use signals hence we do not do this.
|
||||
bool connected;
|
||||
bool line_coding_changed;
|
||||
bool line_state_changed;
|
||||
bool break_state_changed;
|
||||
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
char wanted_char;
|
||||
ch341_line_coding_t line_coding;
|
||||
ch341_line_state_t line_state;
|
||||
|
||||
// FIFO
|
||||
tu_fifo_t rx_ff;
|
||||
tu_fifo_t tx_ff;
|
||||
tu_fifo_t txnotify_ff;
|
||||
|
||||
uint8_t rx_ff_buf[CFG_TUD_CH341_FIFO_SIZE];
|
||||
uint8_t tx_ff_buf[CFG_TUD_CH341_FIFO_SIZE];
|
||||
uint8_t txnotify_ff_buf[CFG_TUD_CH341_EP_TXNOTIFY_MAX_PACKET];
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
osal_mutex_def_t rx_ff_mutex;
|
||||
osal_mutex_def_t tx_ff_mutex;
|
||||
osal_mutex_def_t txnotify_ff_mutex;
|
||||
#endif
|
||||
|
||||
// Endpoint 0 Transfer buffer
|
||||
// Note that we are NOT using CFG_TUD_ENDPOINT0_SIZE here! This is because
|
||||
// this is merely a temporary buffer we use to send data to the host and
|
||||
// we never send more than 2 bytes.
|
||||
CFG_TUSB_MEM_ALIGN uint8_t ep0_in_buf[4];
|
||||
|
||||
// Endpoint Transfer buffer
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CH341_EP_RX_MAX_PACKET];
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CH341_EP_TX_MAX_PACKET];
|
||||
|
||||
uint8_t register_data[REG_DIDX_MAX];
|
||||
|
||||
}ch341d_interface_t;
|
||||
|
||||
|
||||
#define ITF_MEM_RESET_SIZE offsetof(ch341d_interface_t, wanted_char)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// The CH341 cannot coexist with other interfaces so there can be only 1.
|
||||
// This is because it sends vendor class control messages directly to the
|
||||
// device asnd uses wIndex of the control packet to transfer vendor
|
||||
// specific data.
|
||||
CFG_TUSB_MEM_SECTION static ch341d_interface_t _ch341d_itf[1];
|
||||
|
||||
|
||||
static const uint32_t ch341_known_baud_rates[] = { 50, 75, 100, 110, 150, 300, 600, 900, 1200, 1800, 2400, 3600, 4800, 9600, 14400, 19200, 28800, 33600, 38400, 56000, 57600, 76800, 115200, 128000, 153600, 230400, 460800, 921600, 1500000, 2000000};
|
||||
|
||||
static inline void ch341_decode_bit_rate(ch341d_interface_t *p_ch341)
|
||||
{
|
||||
uint16_t baud_div = 0x100 - p_ch341->register_data[REG_DIDX_DIVISOR];
|
||||
uint8_t baud_fact = (p_ch341->register_data[REG_DIDX_PRESCALER] >> 2) & 0x01;
|
||||
uint8_t baud_ps = p_ch341->register_data[REG_DIDX_PRESCALER] & 0x03;
|
||||
uint32_t calc_bit_rate = (uint32_t)(CH341_CLKRATE / ((1 << (12 - 3 * baud_ps - baud_fact)) * baud_div));
|
||||
int index;
|
||||
|
||||
for (index = 0; index < (int)((sizeof(ch341_known_baud_rates) / sizeof(uint32_t))); index++)
|
||||
{
|
||||
int max_diff = (int)((ch341_known_baud_rates[index] * 2) / 1000);
|
||||
if (calc_bit_rate >= ch341_known_baud_rates[index] - max_diff && calc_bit_rate <= ch341_known_baud_rates[index] + max_diff)
|
||||
{
|
||||
p_ch341->line_coding.bit_rate = ch341_known_baud_rates[index];
|
||||
return;
|
||||
}
|
||||
if (calc_bit_rate < ch341_known_baud_rates[index])
|
||||
break;
|
||||
}
|
||||
p_ch341->line_coding.bit_rate = calc_bit_rate;
|
||||
}
|
||||
|
||||
static inline void ch341_decode_lcr(ch341d_interface_t *p_ch341)
|
||||
{
|
||||
// decode rx/tx enable
|
||||
uint8_t lcr = p_ch341->register_data[REG_DIDX_LCR];
|
||||
p_ch341->line_coding.tx_en = (lcr & CH341_LCR_ENABLE_TX) ? true : false;
|
||||
p_ch341->line_coding.rx_en = (lcr & CH341_LCR_ENABLE_RX) ? true : false;
|
||||
|
||||
// decode parity (0=None, 1=Odd, 2=Even, 3=Mark, 4=Space)
|
||||
if (lcr & CH341_LCR_ENABLE_PAR)
|
||||
{
|
||||
if ((lcr & (CH341_LCR_PAR_EVEN)) == 0)
|
||||
p_ch341->line_coding.parity = 1; // Odd
|
||||
else
|
||||
p_ch341->line_coding.parity = 2; // Even
|
||||
|
||||
if ((lcr & (CH341_LCR_MARK_SPACE)))
|
||||
p_ch341->line_coding.parity+=2;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_ch341->line_coding.parity = 0; // None
|
||||
}
|
||||
|
||||
// decode stop bits (0=1 stop bit, 2=2 stop bits)
|
||||
p_ch341->line_coding.stop_bits = (lcr & CH341_LCR_STOP_BITS_2) ? 2 : 0;
|
||||
|
||||
// decode data bits
|
||||
if ((lcr & 0x3) == CH341_LCR_CS5)
|
||||
p_ch341->line_coding.data_bits = 5;
|
||||
else if ((lcr & 0x3) == CH341_LCR_CS6)
|
||||
p_ch341->line_coding.data_bits = 6;
|
||||
else if ((lcr & 0x3) == CH341_LCR_CS7)
|
||||
p_ch341->line_coding.data_bits = 7;
|
||||
else
|
||||
p_ch341->line_coding.data_bits = 8;
|
||||
}
|
||||
|
||||
static inline ch341_line_state_t ch341_decode_mcr(ch341d_interface_t *p_ch341, uint16_t mcr)
|
||||
{
|
||||
p_ch341->register_data[REG_DIDX_MCR_MSR] = (p_ch341->register_data[REG_DIDX_MCR_MSR] & (~(CH341_BIT_DTR | CH341_BIT_RTS))) | (mcr & (CH341_BIT_DTR | CH341_BIT_RTS));
|
||||
|
||||
ch341_line_state_t line_state = 0;
|
||||
if (mcr & CH341_BIT_DTR)
|
||||
line_state &= ~CH341_LINE_STATE_DTR_ACTIVE;
|
||||
else
|
||||
line_state |= CH341_LINE_STATE_DTR_ACTIVE;
|
||||
|
||||
if (mcr & CH341_BIT_RTS)
|
||||
line_state &= ~CH341_LINE_STATE_RTS_ACTIVE;
|
||||
else
|
||||
line_state |= CH341_LINE_STATE_RTS_ACTIVE;
|
||||
|
||||
return line_state;
|
||||
}
|
||||
|
||||
static void ch341_write_regs(ch341d_interface_t *p_ch341, uint16_t wValue, uint16_t wIndex)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
uint8_t reg = wValue & 0xFF;
|
||||
uint8_t regval = wIndex & 0xFF;
|
||||
switch (reg)
|
||||
{
|
||||
case CH341_REG_BREAK:
|
||||
p_ch341->register_data[REG_DIDX_BREAK] = regval;
|
||||
p_ch341->break_state_changed = true;
|
||||
break;
|
||||
case CH341_REG_DIVISOR:
|
||||
p_ch341->register_data[REG_DIDX_DIVISOR] = regval;
|
||||
p_ch341->line_coding_changed = true;
|
||||
break;
|
||||
case CH341_REG_LCR:
|
||||
p_ch341->register_data[REG_DIDX_LCR] = regval;
|
||||
p_ch341->line_coding_changed = true;
|
||||
break;
|
||||
case CH341_REG_LCR2:
|
||||
p_ch341->register_data[REG_DIDX_LCR2] = regval;
|
||||
break;
|
||||
case CH341_REG_PRESCALER:
|
||||
regval &= 0x7;
|
||||
p_ch341->register_data[REG_DIDX_PRESCALER] = regval;
|
||||
p_ch341->line_coding_changed = true;
|
||||
break;
|
||||
case CH341_REG_0x0F:
|
||||
p_ch341->register_data[REG_DIDX_0x0F] = regval;
|
||||
break;
|
||||
case CH341_REG_0x27:
|
||||
p_ch341->register_data[REG_DIDX_0x27] = regval;
|
||||
break;
|
||||
case CH341_REG_0x2C:
|
||||
p_ch341->register_data[REG_DIDX_0x2C] = regval;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
wValue >>= 8;
|
||||
wIndex >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void ch341_read_regs(ch341d_interface_t *p_ch341, uint8_t* data, uint16_t wValue)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
uint8_t reg = wValue & 0xFF;
|
||||
switch (reg)
|
||||
{
|
||||
case CH341_REG_BREAK:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_BREAK];
|
||||
break;
|
||||
case CH341_REG_DIVISOR:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_DIVISOR];
|
||||
break;
|
||||
case CH341_REG_LCR:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_LCR];
|
||||
break;
|
||||
case CH341_REG_LCR2:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_LCR2];
|
||||
break;
|
||||
case CH341_REG_PRESCALER:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_PRESCALER];
|
||||
break;
|
||||
case CH341_REG_0x0F:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_0x0F];
|
||||
break;
|
||||
case CH341_REG_0x27:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_0x27];
|
||||
break;
|
||||
case CH341_REG_0x2C:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_0x2C];
|
||||
break;
|
||||
case CH341_REG_MCR_MSR:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_MCR_MSR];
|
||||
break;
|
||||
case CH341_REG_MCR_MSR2:
|
||||
data[i] = p_ch341->register_data[REG_DIDX_MCR_MSR2];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
wValue >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void _prep_out_transaction (ch341d_interface_t* p_ch341)
|
||||
{
|
||||
uint8_t const rhport = BOARD_TUD_RHPORT;
|
||||
uint16_t available = tu_fifo_remaining(&p_ch341->rx_ff);
|
||||
|
||||
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||
// TODO Actually we can still carry out the transfer, keeping count of received bytes
|
||||
// and slowly move it to the FIFO when read().
|
||||
// This pre-check reduces endpoint claiming
|
||||
TU_VERIFY(available >= sizeof(p_ch341->epout_buf), );
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbd_edpt_claim(rhport, p_ch341->ep_out), );
|
||||
|
||||
// fifo can be changed before endpoint is claimed
|
||||
available = tu_fifo_remaining(&p_ch341->rx_ff);
|
||||
|
||||
if ( available >= sizeof(p_ch341->epout_buf) )
|
||||
{
|
||||
usbd_edpt_xfer(rhport, p_ch341->ep_out, p_ch341->epout_buf, sizeof(p_ch341->epout_buf));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Release endpoint since we don't make any transfer
|
||||
usbd_edpt_release(rhport, p_ch341->ep_out);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
bool tud_ch341_connected(void)
|
||||
{
|
||||
return tud_ready() && _ch341d_itf[0].connected;
|
||||
}
|
||||
|
||||
ch341_line_state_t tud_ch341_get_line_state (void)
|
||||
{
|
||||
return _ch341d_itf[0].line_state;
|
||||
}
|
||||
|
||||
uint32_t tud_ch341_set_modem_state(ch341_modem_state_t modem_states)
|
||||
{
|
||||
ch341d_interface_t *p_ch341 = &_ch341d_itf[0];
|
||||
uint8_t buffer[4];
|
||||
|
||||
modem_states &= CH341_MODEM_STATE_ALL;
|
||||
|
||||
// FIXME? I beleive these signals are all active=low. I can only test the CTS line with
|
||||
// my CH340G breakout board and I know that with nothing connected or the CTS line shorted
|
||||
// to positive, the value transferred is 0xF (all 1's). With the CTS line shorted to ground,
|
||||
// the value is 0xE (all 1's except CTS)
|
||||
modem_states = (~modem_states) & CH341_MODEM_STATE_ALL;
|
||||
|
||||
p_ch341->register_data[REG_DIDX_MCR_MSR] = (p_ch341->register_data[REG_DIDX_MCR_MSR] & 0xF0) | (modem_states);
|
||||
return 1;
|
||||
|
||||
buffer[0] = 0x08;
|
||||
|
||||
// This is sometimes 0x3F and other times 0xBF but I beleieve that's because when I short
|
||||
// CTS to ground there is no de-bounce so it flickers.
|
||||
buffer[1] = 0x3F;
|
||||
|
||||
buffer[2] = 0x90 | modem_states;
|
||||
|
||||
// Register MCR_MSR2 is presumably for future use as it's always 0xEE but I beleive it to be
|
||||
// for additional mcr/msr status.
|
||||
buffer[3] = p_ch341->register_data[REG_DIDX_MCR_MSR2];
|
||||
|
||||
// write fifo
|
||||
uint16_t ret = tu_fifo_write_n(&p_ch341->txnotify_ff, buffer, 4);
|
||||
|
||||
if (ret == 4)
|
||||
{
|
||||
// start transfer
|
||||
ret = tud_ch341_notify_flush();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ch341_modem_state_t tud_ch341_get_modem_state(void)
|
||||
{
|
||||
ch341d_interface_t *p_ch341 = &_ch341d_itf[0];
|
||||
ch341_modem_state_t modem_states = p_ch341->register_data[REG_DIDX_MCR_MSR];
|
||||
modem_states = (~modem_states) & CH341_MODEM_STATE_ALL;
|
||||
|
||||
return modem_states;
|
||||
}
|
||||
|
||||
void tud_ch341_get_line_coding (ch341_line_coding_t* coding)
|
||||
{
|
||||
(*coding) = _ch341d_itf[0].line_coding;
|
||||
}
|
||||
|
||||
void tud_ch341_set_wanted_char (char wanted)
|
||||
{
|
||||
_ch341d_itf[0].wanted_char = wanted;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// READ API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_ch341_available(void)
|
||||
{
|
||||
return tu_fifo_count(&_ch341d_itf[0].rx_ff);
|
||||
}
|
||||
|
||||
uint32_t tud_ch341_read(void* buffer, uint32_t bufsize)
|
||||
{
|
||||
ch341d_interface_t* p_ch341 = &_ch341d_itf[0];
|
||||
uint32_t num_read = tu_fifo_read_n(&p_ch341->rx_ff, buffer, bufsize);
|
||||
_prep_out_transaction(p_ch341);
|
||||
return num_read;
|
||||
}
|
||||
|
||||
bool tud_ch341_peek(uint8_t* chr)
|
||||
{
|
||||
return tu_fifo_peek(&_ch341d_itf[0].rx_ff, chr);
|
||||
}
|
||||
|
||||
void tud_ch341_read_flush (void)
|
||||
{
|
||||
ch341d_interface_t* p_ch341 = &_ch341d_itf[0];
|
||||
tu_fifo_clear(&p_ch341->rx_ff);
|
||||
_prep_out_transaction(p_ch341);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// WRITE API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_ch341_write(void const* buffer, uint32_t bufsize)
|
||||
{
|
||||
ch341d_interface_t* p_ch341 = &_ch341d_itf[0];
|
||||
uint16_t ret = tu_fifo_write_n(&p_ch341->tx_ff, buffer, bufsize);
|
||||
|
||||
// flush if queue more than packet size
|
||||
if (tu_fifo_count(&p_ch341->tx_ff) >= sizeof(p_ch341->epin_buf))
|
||||
{
|
||||
tud_ch341_write_flush();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t tud_ch341_write_flush (void)
|
||||
{
|
||||
ch341d_interface_t* p_ch341 = &_ch341d_itf[0];
|
||||
|
||||
// Skip if usb is not ready yet
|
||||
TU_VERIFY( tud_ready(), 0 );
|
||||
|
||||
// No data to send
|
||||
if ( !tu_fifo_count(&p_ch341->tx_ff) ) return 0;
|
||||
|
||||
uint8_t const rhport = BOARD_TUD_RHPORT;
|
||||
|
||||
// Claim the endpoint
|
||||
TU_VERIFY( usbd_edpt_claim(rhport, p_ch341->ep_in), 0 );
|
||||
|
||||
// Pull data from FIFO
|
||||
uint16_t const count = tu_fifo_read_n(&p_ch341->tx_ff, p_ch341->epin_buf, sizeof(p_ch341->epin_buf));
|
||||
|
||||
if ( count )
|
||||
{
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_ch341->ep_in, p_ch341->epin_buf, count), 0 );
|
||||
return count;
|
||||
}else
|
||||
{
|
||||
// Release endpoint since we don't make any transfer
|
||||
// Note: data is dropped if terminal is not connected
|
||||
usbd_edpt_release(rhport, p_ch341->ep_in);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
uint32_t tud_ch341_notify_flush(void)
|
||||
{
|
||||
ch341d_interface_t *p_ch341 = &_ch341d_itf[0];
|
||||
|
||||
// Skip if usb is not ready yet
|
||||
TU_VERIFY(tud_ready(), 0);
|
||||
|
||||
// No data to send
|
||||
if (!tu_fifo_count(&p_ch341->txnotify_ff))
|
||||
return 0;
|
||||
|
||||
uint8_t const rhport = BOARD_TUD_RHPORT;
|
||||
|
||||
// Claim the endpoint
|
||||
TU_VERIFY(usbd_edpt_claim(rhport, p_ch341->ep_notif), 0);
|
||||
|
||||
// Pull data from FIFO
|
||||
uint16_t const count = tu_fifo_read_n(&p_ch341->txnotify_ff, p_ch341->txnotify_ff_buf, sizeof(p_ch341->txnotify_ff_buf));
|
||||
|
||||
if (count)
|
||||
{
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_ch341->ep_notif, p_ch341->txnotify_ff_buf, count), 0);
|
||||
return count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Release endpoint since we don't make any transfer
|
||||
// Note: data is dropped if terminal is not connected
|
||||
usbd_edpt_release(rhport, p_ch341->ep_notif);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tud_ch341_write_available (void)
|
||||
{
|
||||
return tu_fifo_remaining(&_ch341d_itf[0].tx_ff);
|
||||
}
|
||||
|
||||
bool tud_ch341_write_clear (void)
|
||||
{
|
||||
return tu_fifo_clear(&_ch341d_itf[0].tx_ff);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void ch341d_init(void)
|
||||
{
|
||||
tu_memclr(_ch341d_itf, sizeof(_ch341d_itf));
|
||||
|
||||
ch341d_interface_t* p_ch341 = &_ch341d_itf[0];
|
||||
|
||||
p_ch341->wanted_char = -1;
|
||||
|
||||
// default line coding is : stop bit = 1, parity = none, data bits = 8
|
||||
p_ch341->line_coding.bit_rate = 115200;
|
||||
p_ch341->line_coding.stop_bits = 0;
|
||||
p_ch341->line_coding.parity = 0;
|
||||
p_ch341->line_coding.data_bits = 8;
|
||||
p_ch341->line_coding.rx_en = false;
|
||||
p_ch341->line_coding.tx_en = false;
|
||||
|
||||
p_ch341->line_state = 0;
|
||||
|
||||
p_ch341->register_data[REG_DIDX_0x0F] = 0xD2;
|
||||
p_ch341->register_data[REG_DIDX_0x27] = 0x00;
|
||||
p_ch341->register_data[REG_DIDX_0x2C] = 0x0B;
|
||||
p_ch341->register_data[REG_DIDX_BREAK] = 0xBF;
|
||||
p_ch341->register_data[REG_DIDX_DIVISOR] = 0xCC;
|
||||
p_ch341->register_data[REG_DIDX_LCR] = 0xC3;
|
||||
p_ch341->register_data[REG_DIDX_LCR2] = 0x00;
|
||||
p_ch341->register_data[REG_DIDX_PRESCALER] = 0x03;
|
||||
p_ch341->register_data[REG_DIDX_MCR_MSR] = 0xFF;
|
||||
p_ch341->register_data[REG_DIDX_MCR_MSR2] = 0xEE;
|
||||
|
||||
// Config RX fifo
|
||||
tu_fifo_config(&p_ch341->rx_ff, p_ch341->rx_ff_buf, TU_ARRAY_SIZE(p_ch341->rx_ff_buf), 1, false);
|
||||
// Config TX fifo
|
||||
tu_fifo_config(&p_ch341->tx_ff, p_ch341->tx_ff_buf, TU_ARRAY_SIZE(p_ch341->tx_ff_buf), 1, false);
|
||||
// Config TX NOTIFY fifo
|
||||
tu_fifo_config(&p_ch341->txnotify_ff, p_ch341->txnotify_ff_buf, TU_ARRAY_SIZE(p_ch341->txnotify_ff_buf), 1, false);
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
tu_fifo_config_mutex(&p_ch341->rx_ff, NULL, osal_mutex_create(&p_ch341->rx_ff_mutex));
|
||||
tu_fifo_config_mutex(&p_ch341->tx_ff, osal_mutex_create(&p_ch341->tx_ff_mutex), NULL);
|
||||
tu_fifo_config_mutex(&p_ch341->txnotify_ff, osal_mutex_create(&p_ch341->txnotify_ff_mutex), NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ch341d_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
ch341d_interface_t* p_ch341 = &_ch341d_itf[0];
|
||||
|
||||
tu_memclr(p_ch341, ITF_MEM_RESET_SIZE);
|
||||
tu_fifo_clear(&p_ch341->rx_ff);
|
||||
tu_fifo_clear(&p_ch341->tx_ff);
|
||||
tu_fifo_clear(&p_ch341->txnotify_ff);
|
||||
tu_fifo_set_overwritable(&p_ch341->tx_ff, false);
|
||||
tu_fifo_set_overwritable(&p_ch341->txnotify_ff, false);
|
||||
}
|
||||
|
||||
uint16_t ch341d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||
{
|
||||
// CH340G interface class, subclass, and protocol
|
||||
TU_VERIFY(0xFF == itf_desc->bInterfaceClass &&
|
||||
0x01 == itf_desc->bInterfaceSubClass &&
|
||||
0x02 == itf_desc->bInterfaceProtocol, 0);
|
||||
|
||||
ch341d_interface_t * p_ch341 = &_ch341d_itf[0];
|
||||
|
||||
//------------- CH341 Interface -------------//
|
||||
p_ch341->itf_num = itf_desc->bInterfaceNumber;
|
||||
|
||||
uint16_t drv_len = sizeof(tusb_desc_interface_t);
|
||||
uint8_t const * p_desc = tu_desc_next( itf_desc );
|
||||
while (p_desc && drv_len < max_len)
|
||||
{
|
||||
if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc))
|
||||
{
|
||||
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *)p_desc;
|
||||
if (desc_ep->bmAttributes.xfer == TUSB_XFER_BULK)
|
||||
{
|
||||
if (p_ch341->ep_in == 0)
|
||||
{
|
||||
// Open endpoint pair
|
||||
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_ch341->ep_out, &p_ch341->ep_in), 0);
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
}
|
||||
else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT)
|
||||
{
|
||||
if (p_ch341->ep_notif == 0)
|
||||
{
|
||||
// Open notification endpoint
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
|
||||
p_ch341->ep_notif = desc_ep->bEndpointAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
// Prepare for incoming data
|
||||
_prep_out_transaction(p_ch341);
|
||||
|
||||
return drv_len;
|
||||
}
|
||||
|
||||
// The CH341 driver sends vendor requests only. we will pipe these into the ch341d_control_xfer_cb so it can handle everything
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return ch341d_control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
// Invoked when a control transfer occurred on an interface of this class
|
||||
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||
// return false to stall control endpoint (e.g unsupported request)
|
||||
bool ch341d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
{
|
||||
|
||||
ch341d_interface_t* p_ch341 = &_ch341d_itf[0];
|
||||
|
||||
if ((request->bmRequestType_bit.type) == TUSB_REQ_TYPE_VENDOR && ((request->bmRequestType_bit.recipient) == TUSB_REQ_RCPT_DEVICE))
|
||||
{
|
||||
switch(request->bRequest)
|
||||
{
|
||||
case CH341_REQ_READ_VERSION: // (0x5F)
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
p_ch341->ep0_in_buf[0] = 0x31;
|
||||
p_ch341->ep0_in_buf[1] = 0;
|
||||
tud_control_xfer(rhport, request, p_ch341->ep0_in_buf, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case CH341_REQ_READ_REG: // (0x95)
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
ch341_read_regs(p_ch341, p_ch341->ep0_in_buf, request->wValue);
|
||||
tud_control_xfer(rhport, request, p_ch341->ep0_in_buf, 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case CH341_REQ_WRITE_REG: // (0x9A)
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
tud_control_status(rhport, request);
|
||||
}
|
||||
else if (stage == CONTROL_STAGE_ACK)
|
||||
{
|
||||
ch341_write_regs(p_ch341, request->wValue, request->wIndex);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CH341_REQ_SERIAL_INIT: // (0xA1)
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
tud_control_status(rhport, request);
|
||||
}
|
||||
else if (stage == CONTROL_STAGE_ACK)
|
||||
{
|
||||
// wValue = LCR/LCR2
|
||||
// wIndex = BAUDDIV/PRESCALAR
|
||||
if (request->wValue && request->wIndex)
|
||||
{
|
||||
ch341_write_regs(p_ch341, CH341_REG_LCR << 8 | CH341_REG_LCR2, request->wValue);
|
||||
ch341_write_regs(p_ch341, CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER, request->wIndex);
|
||||
p_ch341->line_state_changed = true;
|
||||
p_ch341->connected = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CH341_REQ_MODEM_CTRL: // (0xA4)
|
||||
if (stage == CONTROL_STAGE_SETUP)
|
||||
{
|
||||
tud_control_status(rhport, request);
|
||||
}
|
||||
else if (stage == CONTROL_STAGE_ACK)
|
||||
{
|
||||
p_ch341->line_state = ch341_decode_mcr(p_ch341, request->wValue);
|
||||
p_ch341->line_state_changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_ch341->line_state_changed)
|
||||
{
|
||||
p_ch341->line_state_changed = false;
|
||||
if (tud_ch341_line_state_cb)
|
||||
tud_ch341_line_state_cb(p_ch341->line_state);
|
||||
}
|
||||
if (p_ch341->line_coding_changed)
|
||||
{
|
||||
p_ch341->line_coding_changed = false;
|
||||
ch341_decode_bit_rate(p_ch341);
|
||||
ch341_decode_lcr(p_ch341);
|
||||
if (tud_ch341_line_coding_cb)
|
||||
tud_ch341_line_coding_cb(&p_ch341->line_coding);
|
||||
}
|
||||
if (p_ch341->break_state_changed)
|
||||
{
|
||||
p_ch341->break_state_changed = false;
|
||||
if (tud_ch341_send_break_cb)
|
||||
tud_ch341_send_break_cb(p_ch341->register_data[REG_DIDX_BREAK]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ch341d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
(void) result;
|
||||
|
||||
ch341d_interface_t* p_ch341 = &_ch341d_itf[0];
|
||||
|
||||
// Received new data
|
||||
if ( ep_addr == p_ch341->ep_out )
|
||||
{
|
||||
tu_fifo_write_n(&p_ch341->rx_ff, &p_ch341->epout_buf, xferred_bytes);
|
||||
|
||||
// Check for wanted char and invoke callback if needed
|
||||
if ( tud_ch341_rx_wanted_cb && (((signed char) p_ch341->wanted_char) != -1) )
|
||||
{
|
||||
for ( uint32_t i = 0; i < xferred_bytes; i++ )
|
||||
{
|
||||
if ( (p_ch341->wanted_char == p_ch341->epout_buf[i]) && !tu_fifo_empty(&p_ch341->rx_ff) )
|
||||
{
|
||||
tud_ch341_rx_wanted_cb(p_ch341->wanted_char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// invoke receive callback (if there is still data)
|
||||
if (tud_ch341_rx_cb && !tu_fifo_empty(&p_ch341->rx_ff) ) tud_ch341_rx_cb();
|
||||
|
||||
// prepare for OUT transaction
|
||||
_prep_out_transaction(p_ch341);
|
||||
}
|
||||
|
||||
// Data sent to host, we continue to fetch from tx fifo to send.
|
||||
// Note: This will cause incorrect baudrate set in line coding.
|
||||
// Though maybe the baudrate is not really important !!!
|
||||
if ( ep_addr == p_ch341->ep_in )
|
||||
{
|
||||
// invoke transmit callback to possibly refill tx fifo
|
||||
if ( tud_ch341_tx_complete_cb ) tud_ch341_tx_complete_cb();
|
||||
|
||||
if ( 0 == tud_ch341_write_flush() )
|
||||
{
|
||||
// If there is no data left, a ZLP should be sent if
|
||||
// xferred_bytes is multiple of EP Packet size and not zero
|
||||
if (!tu_fifo_count(&p_ch341->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (CFG_TUD_CH341_EP_TX_MAX_PACKET-1))) )
|
||||
{
|
||||
if ( usbd_edpt_claim(rhport, p_ch341->ep_in) )
|
||||
{
|
||||
usbd_edpt_xfer(rhport, p_ch341->ep_in, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
* Portions Copyright (c) 2022 Travis Robinson (libusbdotnet@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* 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 IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CH341_DEVICE_H_
|
||||
#define _TUSB_CH341_DEVICE_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "ch341.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TR 06-09-22 NOTE: ///////////////////////////////////////////////////////////
|
||||
// I've left the "_n_" functions in to maintain some API compatibility with the
|
||||
// CDC implementation but there is no way to have more than one CH341 interface
|
||||
// due to the nature of how host and device communicate.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
#ifndef CFG_TUD_CH341_EP_RX_MAX_PACKET
|
||||
#define CFG_TUD_CH341_EP_RX_MAX_PACKET (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_CH341_EP_TX_MAX_PACKET
|
||||
#define CFG_TUD_CH341_EP_TX_MAX_PACKET CFG_TUD_CH341_EP_RX_MAX_PACKET
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_CH341_EP_TXNOTIFY_MAX_PACKET
|
||||
#define CFG_TUD_CH341_EP_TXNOTIFY_MAX_PACKET (8)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_CH341_FIFO_SIZE
|
||||
#define CFG_TUD_CH341_FIFO_SIZE CFG_TUD_CH341_EP_RX_MAX_PACKET
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \addtogroup CH341_Serial Serial
|
||||
* @{
|
||||
* \defgroup CH341_Serial_Device Device
|
||||
* @{ */
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Single Port)
|
||||
// The CH341 is a vendor class device that sends it's requests directly
|
||||
// to the device, hence there can be only 1 CH341 interface per device.
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if terminal is connected to this port
|
||||
bool tud_ch341_connected (void);
|
||||
|
||||
// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
|
||||
ch341_line_state_t tud_ch341_get_line_state (void);
|
||||
|
||||
// Get current line encoding: bit rate, stop bits parity etc ..
|
||||
void tud_ch341_get_line_coding (ch341_line_coding_t* coding);
|
||||
|
||||
// Set special character that will trigger tud_ch341_rx_wanted_cb() callback on receiving
|
||||
void tud_ch341_set_wanted_char (char wanted);
|
||||
|
||||
// Get the number of bytes available for reading
|
||||
uint32_t tud_ch341_available (void);
|
||||
|
||||
// Read received bytes
|
||||
uint32_t tud_ch341_read (void* buffer, uint32_t bufsize);
|
||||
|
||||
// Read a byte, return -1 if there is none
|
||||
static inline
|
||||
int32_t tud_ch341_read_char (void);
|
||||
|
||||
// Clear the received FIFO
|
||||
void tud_ch341_read_flush (void);
|
||||
|
||||
// Get a byte from FIFO at the specified position without removing it
|
||||
bool tud_ch341_peek (uint8_t* ui8);
|
||||
|
||||
// Write bytes to TX FIFO, data may remain in the FIFO for a while
|
||||
uint32_t tud_ch341_write (void const* buffer, uint32_t bufsize);
|
||||
|
||||
// Write a byte
|
||||
static inline
|
||||
uint32_t tud_ch341_write_char (char ch);
|
||||
|
||||
// Write a null-terminated string
|
||||
static inline
|
||||
uint32_t tud_ch341_write_str (char const* str);
|
||||
|
||||
// Force sending data if possible, return number of forced bytes
|
||||
uint32_t tud_ch341_write_flush (void);
|
||||
|
||||
// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation.
|
||||
uint32_t tud_ch341_write_available (void);
|
||||
|
||||
// Clear the transmit FIFO
|
||||
bool tud_ch341_write_clear (void);
|
||||
|
||||
// Send line events to host. (IE: CTS, DSR, RI, DCD)
|
||||
uint32_t tud_ch341_set_modem_state(ch341_modem_state_t modem_states);
|
||||
|
||||
// Returns the current state of the modem. (IE: CTS, DSR, RI, DCD)
|
||||
ch341_modem_state_t tud_ch341_get_modem_state(void);
|
||||
|
||||
// Force sending notify data if possible, return number of forced bytes
|
||||
uint32_t tud_ch341_notify_flush(void);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API (weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when received new data
|
||||
TU_ATTR_WEAK void tud_ch341_rx_cb(void);
|
||||
|
||||
// Invoked when received `wanted_char`
|
||||
TU_ATTR_WEAK void tud_ch341_rx_wanted_cb(char wanted_char);
|
||||
|
||||
// Invoked when space becomes available in TX buffer
|
||||
TU_ATTR_WEAK void tud_ch341_tx_complete_cb(void);
|
||||
|
||||
// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
|
||||
TU_ATTR_WEAK void tud_ch341_line_state_cb(ch341_line_state_t line_state);
|
||||
|
||||
// Invoked when line coding is change via SET_LINE_CODING
|
||||
TU_ATTR_WEAK void tud_ch341_line_coding_cb(ch341_line_coding_t const* p_line_coding);
|
||||
|
||||
// Invoked when received send break
|
||||
TU_ATTR_WEAK void tud_ch341_send_break_cb(bool is_break_active);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Inline functions
|
||||
//--------------------------------------------------------------------+
|
||||
static inline int32_t tud_ch341_read_char (void)
|
||||
{
|
||||
uint8_t ch;
|
||||
return tud_ch341_read(&ch, 1) ? (int32_t) ch : -1;
|
||||
}
|
||||
|
||||
static inline uint32_t tud_ch341_write_char (char ch)
|
||||
{
|
||||
return tud_ch341_write((void const*)&ch, 1);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_ch341_write_str (char const* str)
|
||||
{
|
||||
return tud_ch341_write((uint8_t const*)str, strlen(str));
|
||||
}
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL USBD-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
void ch341d_init (void);
|
||||
void ch341d_reset (uint8_t rhport);
|
||||
uint16_t ch341d_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool ch341d_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
bool ch341d_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CH341_DEVICE_H_ */
|
|
@ -233,6 +233,18 @@ static usbd_class_driver_t const _usbd_driver[] =
|
|||
.sof = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_CH341
|
||||
{
|
||||
DRIVER_NAME("CH341")
|
||||
.init = ch341d_init,
|
||||
.reset = ch341d_reset,
|
||||
.open = ch341d_open,
|
||||
.control_xfer_cb = ch341d_control_xfer_cb,
|
||||
.xfer_cb = ch341d_xfer_cb,
|
||||
.sof = NULL
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) };
|
||||
|
|
|
@ -113,6 +113,10 @@
|
|||
#if CFG_TUD_BTH
|
||||
#include "class/bth/bth_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_CH341
|
||||
#include "class/ch341/ch341_device.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -352,6 +352,10 @@ typedef int make_iso_compilers_happy;
|
|||
#define CFG_TUD_BTH 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_CH341
|
||||
#define CFG_TUD_CH341 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_ECM_RNDIS
|
||||
#ifdef CFG_TUD_NET
|
||||
#warning "CFG_TUD_NET is renamed to CFG_TUD_ECM_RNDIS"
|
||||
|
|
Loading…
Reference in New Issue