mirror of
https://github.com/hrvach/deskhop.git
synced 2024-11-24 16:54:18 +01:00
DeskHop v0.61 - Promoted RC to Stable
- Single unified firmware binary - Improved support for NKRO keyboards - Report mode default for keyboard port - Improved consumer control parser for variable data types (media keys should be better supported) - System control forwarding - Improved HID parser - Web UI to configure instead of keyboard shortcuts - Firmware upgrade while the device remains functional - Only one end will need upgrade, the other will get it automatically - No need to recompile to set most settings - Improved UART routines to use DMA, more reliable link - Fixed a bunch of bugs and issues
This commit is contained in:
parent
be645f7596
commit
1415c1d31a
@ -1,11 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
|
||||
set(VERSION_MAJOR 00)
|
||||
set(VERSION_MINOR 139)
|
||||
|
||||
set(PICO_SDK_FETCH_FROM_GIT off)
|
||||
set(PICO_BOARD=pico)
|
||||
set(PICO_SDK_PATH ${CMAKE_CURRENT_LIST_DIR}/pico-sdk)
|
||||
|
||||
include(pico_sdk_import.cmake)
|
||||
set(CMAKE_C_FLAGS "-Ofast -Wall -mcpu=cortex-m0plus -mtune=cortex-m0plus -funroll-loops")
|
||||
set(CMAKE_C_FLAGS "-Ofast -Wall -mcpu=cortex-m0plus -mtune=cortex-m0plus -fstack-usage")
|
||||
|
||||
set(PICO_COPY_TO_RAM 1)
|
||||
|
||||
@ -36,22 +39,27 @@ target_include_directories(Pico-PIO-USB PRIVATE ${PICO_PIO_USB_DIR})
|
||||
set(COMMON_SOURCES
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/usb_descriptors.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/defaults.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/constants.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/protocol.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/hid_parser.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/hid_report.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/utils.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/handlers.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/setup.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/keyboard.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/mouse.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/tasks.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/led.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/uart.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/usb.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/main.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/ramdisk.c
|
||||
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
|
||||
${PICO_TINYUSB_PATH}/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
|
||||
)
|
||||
|
||||
set(COMMON_INCLUDES
|
||||
${CMAKE_CURRENT_LIST_DIR}/src
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/include
|
||||
${PICO_PIO_USB_DIR}/src
|
||||
)
|
||||
|
||||
@ -61,6 +69,7 @@ set(COMMON_LINK_LIBRARIES
|
||||
hardware_uart
|
||||
hardware_gpio
|
||||
hardware_pio
|
||||
hardware_dma
|
||||
|
||||
tinyusb_device
|
||||
tinyusb_host
|
||||
@ -68,30 +77,45 @@ set(COMMON_LINK_LIBRARIES
|
||||
Pico-PIO-USB
|
||||
)
|
||||
|
||||
# Pico A - Keyboard (board_role = 0)
|
||||
# B - Mouse (board_role = 1)
|
||||
set(binary deskhop)
|
||||
|
||||
set(binaries board_A board_B)
|
||||
set(DISK_ASM "${CMAKE_CURRENT_LIST_DIR}/disk/disk.S")
|
||||
set(DISK_BIN "${CMAKE_CURRENT_LIST_DIR}/disk/disk.img")
|
||||
|
||||
foreach(board_role RANGE 0 1)
|
||||
list (GET binaries ${board_role} binary)
|
||||
set_property(SOURCE ${DISK_ASM} APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp")
|
||||
|
||||
add_executable(${binary})
|
||||
add_executable(${binary} ${DISK_ASM})
|
||||
|
||||
target_sources(${binary} PUBLIC ${COMMON_SOURCES})
|
||||
target_compile_definitions(${binary} PRIVATE BOARD_ROLE=${board_role} PIO_USB_USE_TINYUSB=1 PIO_USB_DP_PIN_DEFAULT=14)
|
||||
target_sources(${binary} PUBLIC ${COMMON_SOURCES})
|
||||
target_compile_definitions(${binary}
|
||||
PRIVATE
|
||||
PIO_USB_USE_TINYUSB=1
|
||||
PIO_USB_DP_PIN_DEFAULT=14
|
||||
# Uncomment to enable debug uart:
|
||||
# DH_DEBUG=1
|
||||
__disk_file_path__="${DISK_BIN}"
|
||||
)
|
||||
|
||||
target_include_directories(${binary} PUBLIC ${COMMON_INCLUDES})
|
||||
target_link_libraries(${binary} PUBLIC ${COMMON_LINK_LIBRARIES})
|
||||
target_include_directories(${binary} PUBLIC ${COMMON_INCLUDES})
|
||||
target_link_libraries(${binary} PUBLIC ${COMMON_LINK_LIBRARIES})
|
||||
|
||||
pico_enable_stdio_usb(${binary} 0)
|
||||
pico_add_extra_outputs(${binary})
|
||||
pico_enable_stdio_usb(${binary} 0)
|
||||
pico_enable_stdio_uart(${binary} 0)
|
||||
|
||||
target_link_options(${binary} PRIVATE
|
||||
pico_add_extra_outputs(${binary})
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${binary} POST_BUILD
|
||||
COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/crc32.py ${binary}.bin ${binary}.crc ${VERSION_MAJOR}${VERSION_MINOR}
|
||||
COMMAND ${CMAKE_OBJCOPY} --update-section .section_metadata=${binary}.crc ${binary}.elf
|
||||
COMMAND ${CMAKE_OBJCOPY} -O binary ${binary}.elf ${binary}.bin
|
||||
COMMAND ${CMAKE_BINARY_DIR}/elf2uf2/elf2uf2 ${binary}.elf ${binary}.uf2
|
||||
COMMENT "Update CRC32 section to match the actual binary"
|
||||
)
|
||||
|
||||
target_link_options(${binary} PRIVATE
|
||||
-Xlinker
|
||||
--print-memory-usage
|
||||
)
|
||||
)
|
||||
|
||||
pico_set_linker_script(${binary} ${CMAKE_SOURCE_DIR}/memory_map.ld)
|
||||
|
||||
endforeach()
|
||||
pico_set_linker_script(${binary} ${CMAKE_SOURCE_DIR}/memory_map.ld)
|
||||
|
@ -32,6 +32,8 @@ int pio_usb_set_out_data(endpoint_t *ep, const uint8_t *buffer, uint8_t len);
|
||||
// Misc functions
|
||||
int pio_usb_kbd_set_leds(usb_device_t *device, uint8_t port, uint8_t value);
|
||||
|
||||
extern int dh_debug_printf(const char *format, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -414,7 +414,7 @@ bool pio_usb_host_send_setup(uint8_t root_idx, uint8_t device_address,
|
||||
uint8_t const setup_packet[8]) {
|
||||
endpoint_t *ep = _find_ep(root_idx, device_address, 0);
|
||||
if (!ep) {
|
||||
printf("cannot find ep 0x00\r\n");
|
||||
dh_debug_printf("cannot find ep 0x00\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -430,7 +430,7 @@ bool pio_usb_host_endpoint_transfer(uint8_t root_idx, uint8_t device_address,
|
||||
uint16_t buflen) {
|
||||
endpoint_t *ep = _find_ep(root_idx, device_address, ep_address);
|
||||
if (!ep) {
|
||||
printf("no endpoint 0x%02X\r\n", ep_address);
|
||||
dh_debug_printf("no endpoint 0x%02X\r\n", ep_address);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -449,7 +449,7 @@ bool pio_usb_host_endpoint_abort_transfer(uint8_t root_idx, uint8_t device_addre
|
||||
uint8_t ep_address) {
|
||||
endpoint_t *ep = _find_ep(root_idx, device_address, ep_address);
|
||||
if (!ep) {
|
||||
printf("no endpoint 0x%02X\r\n", ep_address);
|
||||
dh_debug_printf("no endpoint 0x%02X\r\n", ep_address);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -679,13 +679,13 @@ static int __no_inline_not_in_flash_func(control_out_protocol)(
|
||||
}
|
||||
|
||||
if (time_us_64() - start_time >= timeout) {
|
||||
printf("control out[timeout]\n");
|
||||
dh_debug_printf("control out[timeout]\n");
|
||||
res = -2;
|
||||
} else if (pipe->operation == CONTROL_ERROR) {
|
||||
printf("control out[error]\n");
|
||||
dh_debug_printf("control out[error]\n");
|
||||
res = -1;
|
||||
} else if (pipe->operation == CONTROL_COMPLETE) {
|
||||
printf("control out[complete]\n");
|
||||
dh_debug_printf("control out[complete]\n");
|
||||
res = 0;
|
||||
}
|
||||
pipe->operation = CONTROL_NONE;
|
||||
@ -723,13 +723,13 @@ static int __no_inline_not_in_flash_func(control_in_protocol)(
|
||||
}
|
||||
|
||||
if (time_us_64() - start_time >= timeout) {
|
||||
printf("control in[timeout]\n");
|
||||
dh_debug_printf("control in[timeout]\n");
|
||||
res = -2;
|
||||
} else if (pipe->operation == CONTROL_ERROR) {
|
||||
printf("control in[error]\n");
|
||||
dh_debug_printf("control in[error]\n");
|
||||
res = -1;
|
||||
} else if (pipe->operation == CONTROL_COMPLETE) {
|
||||
printf("control in[complete]\n");
|
||||
dh_debug_printf("control in[complete]\n");
|
||||
res = 0;
|
||||
}
|
||||
pipe->operation = CONTROL_NONE;
|
||||
@ -763,18 +763,18 @@ static int get_hub_port_status(usb_device_t *device, uint8_t port,
|
||||
static int initialize_hub(usb_device_t *device) {
|
||||
uint8_t rx_buffer[16];
|
||||
int res = 0;
|
||||
printf("USB Hub detected\n");
|
||||
dh_debug_printf("USB Hub detected\n");
|
||||
usb_setup_packet_t get_hub_desc_request = GET_HUB_DESCRPTOR_REQUEST;
|
||||
control_in_protocol(device, (uint8_t *)&get_hub_desc_request,
|
||||
sizeof(get_hub_desc_request), rx_buffer, 8);
|
||||
const hub_descriptor_t *desc = (hub_descriptor_t *)rx_buffer;
|
||||
uint8_t port_num = desc->port_num;
|
||||
|
||||
printf("\tTurn on port powers\n");
|
||||
dh_debug_printf("\tTurn on port powers\n");
|
||||
for (int idx = 0; idx < port_num; idx++) {
|
||||
res = set_hub_feature(device, idx, HUB_SET_PORT_POWER);
|
||||
if (res != 0) {
|
||||
printf("\tFailed to turn on ports\n");
|
||||
dh_debug_printf("\tFailed to turn on ports\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -837,7 +837,7 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
uint8_t idx_product = desc->product;
|
||||
uint8_t idx_serial = desc->serial;
|
||||
|
||||
printf("Enumerating %04x:%04x, class:%d, address:%d\n", device->vid,
|
||||
dh_debug_printf("Enumerating %04x:%04x, class:%d, address:%d\n", device->vid,
|
||||
device->pid, device->device_class, address);
|
||||
|
||||
usb_setup_packet_t set_address_request = SET_ADDRESS_REQ_DEFAULT;
|
||||
@ -862,9 +862,9 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
if (idx_manufacture != 0) {
|
||||
res = get_string_descriptor(device, idx_manufacture, rx_buffer, str);
|
||||
if (res == 0) {
|
||||
printf("Manufacture:%s\n", str);
|
||||
dh_debug_printf("Manufacture:%s\n", str);
|
||||
} else {
|
||||
printf("Failed to get string descriptor (Manufacture)\n");
|
||||
dh_debug_printf("Failed to get string descriptor (Manufacture)\n");
|
||||
}
|
||||
stdio_flush();
|
||||
}
|
||||
@ -872,9 +872,9 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
if (idx_product != 0) {
|
||||
res = get_string_descriptor(device, idx_product, rx_buffer, str);
|
||||
if (res == 0) {
|
||||
printf("Product:%s\n", str);
|
||||
dh_debug_printf("Product:%s\n", str);
|
||||
} else {
|
||||
printf("Failed to get string descriptor (Product)\n");
|
||||
dh_debug_printf("Failed to get string descriptor (Product)\n");
|
||||
}
|
||||
stdio_flush();
|
||||
}
|
||||
@ -882,9 +882,9 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
if (idx_serial != 0) {
|
||||
res = get_string_descriptor(device, idx_serial, rx_buffer, str);
|
||||
if (res == 0) {
|
||||
printf("Serial:%s\n", str);
|
||||
dh_debug_printf("Serial:%s\n", str);
|
||||
} else {
|
||||
printf("Failed to get string descriptor (Serial)\n");
|
||||
dh_debug_printf("Failed to get string descriptor (Serial)\n");
|
||||
}
|
||||
stdio_flush();
|
||||
}
|
||||
@ -943,7 +943,7 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
case DESC_TYPE_INTERFACE: {
|
||||
const interface_descriptor_t *d =
|
||||
(const interface_descriptor_t *)descriptor;
|
||||
printf(
|
||||
dh_debug_printf(
|
||||
"inum:%d, altsetting:%d, numep:%d, iclass:%d, isubclass:%d, "
|
||||
"iprotcol:%d, iface:%d\n",
|
||||
d->inum, d->altsetting, d->numep, d->iclass, d->isubclass,
|
||||
@ -954,7 +954,7 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
case DESC_TYPE_ENDPOINT: {
|
||||
const endpoint_descriptor_t *d =
|
||||
(const endpoint_descriptor_t *)descriptor;
|
||||
printf("\t\t\tepaddr:0x%02x, attr:%d, size:%d, interval:%d\n",
|
||||
dh_debug_printf("\t\t\tepaddr:0x%02x, attr:%d, size:%d, interval:%d\n",
|
||||
d->epaddr, d->attr, d->max_size[0] | (d->max_size[1] << 8),
|
||||
d->interval);
|
||||
|
||||
@ -983,13 +983,13 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
ep->need_pre = !device->is_root && !device->is_fullspeed;
|
||||
ep->is_tx = (d->epaddr & 0x80) ? false : true;
|
||||
} else {
|
||||
printf("No empty EP\n");
|
||||
dh_debug_printf("No empty EP\n");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DESC_TYPE_HID: {
|
||||
const hid_descriptor_t *d = (const hid_descriptor_t *)descriptor;
|
||||
printf(
|
||||
dh_debug_printf(
|
||||
"\tbcdHID:%x.%x, country:%d, desc num:%d, desc_type:%d, "
|
||||
"desc_size:%d\n",
|
||||
d->bcd_hid[1], d->bcd_hid[0], d->contry_code, d->num_desc,
|
||||
@ -1011,11 +1011,11 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
control_in_protocol(
|
||||
device, (uint8_t *)&get_hid_report_descrpitor_request,
|
||||
sizeof(get_hid_report_descrpitor_request), rx_buffer, desc_len);
|
||||
printf("\t\tReport descriptor:");
|
||||
dh_debug_printf("\t\tReport descriptor:");
|
||||
for (int i = 0; i < desc_len; i++) {
|
||||
printf("%02x ", device->control_pipe.rx_buffer[i]);
|
||||
dh_debug_printf("%02x ", device->control_pipe.rx_buffer[i]);
|
||||
}
|
||||
printf("\n");
|
||||
dh_debug_printf("\n");
|
||||
stdio_flush();
|
||||
|
||||
} break;
|
||||
@ -1044,7 +1044,7 @@ static int enumerate_device(usb_device_t *device, uint8_t address) {
|
||||
}
|
||||
|
||||
static void device_disconnect(usb_device_t *device) {
|
||||
printf("Disconnect device %d\n", device->address);
|
||||
dh_debug_printf("Disconnect device %d\n", device->address);
|
||||
for (int port = 0; port < PIO_USB_HUB_PORT_CNT; port++) {
|
||||
if (device->child_devices[port] != 0) {
|
||||
device_disconnect(&pio_usb_device[device->child_devices[port]]);
|
||||
@ -1086,7 +1086,7 @@ static int assign_new_device_to_port(usb_device_t *hub_device, uint8_t port, boo
|
||||
pio_usb_device[idx].connected = true;
|
||||
pio_usb_device[idx].is_fullspeed = !is_ls;
|
||||
pio_usb_device[idx].event = EVENT_CONNECT;
|
||||
printf("Assign device %d to %d-%d\n", idx, hub_device->address, port);
|
||||
dh_debug_printf("Assign device %d to %d-%d\n", idx, hub_device->address, port);
|
||||
|
||||
endpoint_descriptor_t ep0_desc = {
|
||||
sizeof(endpoint_descriptor_t), DESC_TYPE_ENDPOINT, 0x00, 0x00, { 0x08, 0x00 }, 0x00
|
||||
@ -1097,7 +1097,7 @@ static int assign_new_device_to_port(usb_device_t *hub_device, uint8_t port, boo
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Failed to assign device\n");
|
||||
dh_debug_printf("Failed to assign device\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -1114,22 +1114,22 @@ static void __no_inline_not_in_flash_func(process_hub_event)(
|
||||
hub_port_status_t status;
|
||||
int res = get_hub_port_status(device, port, &status);
|
||||
if (res != 0) {
|
||||
printf("Failed to get port%d-%d status\n", device->address, port);
|
||||
dh_debug_printf("Failed to get port%d-%d status\n", device->address, port);
|
||||
continue;
|
||||
}
|
||||
printf("port%d-%d status:%d %d\n", device->address, port,
|
||||
dh_debug_printf("port%d-%d status:%d %d\n", device->address, port,
|
||||
status.port_change, status.port_status);
|
||||
|
||||
if (status.port_change & HUB_CHANGE_PORT_CONNECTION) {
|
||||
if (status.port_status & HUB_STAT_PORT_CONNECTION) {
|
||||
printf("new device on port %d, reset port\n", port);
|
||||
dh_debug_printf("new device on port %d, reset port\n", port);
|
||||
if (device->child_devices[port] != 0) {
|
||||
printf("device is already assigned. disconnect previous\n");
|
||||
dh_debug_printf("device is already assigned. disconnect previous\n");
|
||||
device_disconnect(&pio_usb_device[device->child_devices[port]]);
|
||||
}
|
||||
|
||||
if (device->root->addr0_exists) {
|
||||
printf("Address 0 already exists\n");
|
||||
dh_debug_printf("Address 0 already exists\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1137,17 +1137,17 @@ static void __no_inline_not_in_flash_func(process_hub_event)(
|
||||
set_hub_feature(device, port, HUB_SET_PORT_RESET);
|
||||
device->root->addr0_exists = true;
|
||||
} else {
|
||||
printf("No vacant in device pool\n");
|
||||
dh_debug_printf("No vacant in device pool\n");
|
||||
}
|
||||
} else {
|
||||
printf("device removed from port %d\n", port);
|
||||
dh_debug_printf("device removed from port %d\n", port);
|
||||
if (device->child_devices[port] != 0) {
|
||||
device_disconnect(&pio_usb_device[device->child_devices[port]]);
|
||||
}
|
||||
}
|
||||
clear_hub_feature(device, port, HUB_CLR_PORT_CONNECTION);
|
||||
} else if (status.port_change & HUB_CHANGE_PORT_RESET) {
|
||||
printf("reset port %d complete\n", port);
|
||||
dh_debug_printf("reset port %d complete\n", port);
|
||||
res = clear_hub_feature(device, port, HUB_CLR_PORT_RESET);
|
||||
if (res == 0) {
|
||||
assign_new_device_to_port(device, port,
|
||||
@ -1164,7 +1164,7 @@ static void __no_inline_not_in_flash_func(process_hub_event)(
|
||||
void __no_inline_not_in_flash_func(pio_usb_host_task)(void) {
|
||||
for (int root_idx = 0; root_idx < PIO_USB_ROOT_PORT_CNT; root_idx++) {
|
||||
if (pio_usb_root_port[root_idx].event == EVENT_CONNECT) {
|
||||
printf("Root %d connected\n", root_idx);
|
||||
dh_debug_printf("Root %d connected\n", root_idx);
|
||||
int dev_idx = device_pool_vacant();
|
||||
if (dev_idx >= 0) {
|
||||
on_device_connect(&pio_port[0], &pio_usb_root_port[root_idx], dev_idx);
|
||||
@ -1172,7 +1172,7 @@ void __no_inline_not_in_flash_func(pio_usb_host_task)(void) {
|
||||
}
|
||||
pio_usb_root_port[root_idx].event = EVENT_NONE;
|
||||
} else if (pio_usb_root_port[root_idx].event == EVENT_DISCONNECT) {
|
||||
printf("Root %d disconnected\n", root_idx);
|
||||
dh_debug_printf("Root %d disconnected\n", root_idx);
|
||||
pio_usb_host_close_device(
|
||||
root_idx, pio_usb_root_port[root_idx].root_device->address);
|
||||
pio_usb_root_port[root_idx].root_device->connected = false;
|
||||
@ -1187,7 +1187,7 @@ void __no_inline_not_in_flash_func(pio_usb_host_task)(void) {
|
||||
|
||||
if (device->event == EVENT_CONNECT) {
|
||||
device->event = EVENT_NONE;
|
||||
printf("Device %d Connected\n", idx);
|
||||
dh_debug_printf("Device %d Connected\n", idx);
|
||||
int res = enumerate_device(device, idx + 1);
|
||||
if (res == 0) {
|
||||
device->enumerated = true;
|
||||
@ -1198,7 +1198,7 @@ void __no_inline_not_in_flash_func(pio_usb_host_task)(void) {
|
||||
}
|
||||
|
||||
if (res != 0) {
|
||||
printf("Enumeration failed(%d)\n", res);
|
||||
dh_debug_printf("Enumeration failed(%d)\n", res);
|
||||
// retry
|
||||
if (device->is_root) {
|
||||
device->root->event = EVENT_DISCONNECT;
|
||||
@ -1210,7 +1210,7 @@ void __no_inline_not_in_flash_func(pio_usb_host_task)(void) {
|
||||
}
|
||||
} else if (device->event == EVENT_DISCONNECT) {
|
||||
device->event = EVENT_NONE;
|
||||
printf("Disconnect\n");
|
||||
dh_debug_printf("Disconnect\n");
|
||||
device_disconnect(device);
|
||||
} else if (device->event == EVENT_HUB_PORT_CHANGE) {
|
||||
process_hub_event(device);
|
||||
|
121
README.md
121
README.md
@ -52,7 +52,13 @@ It also remembers the LED state for each computer, so you can pick up exactly ho
|
||||
|
||||
## How to build
|
||||
|
||||
To avoid version mismatch and reported path issues when building, the project now bundles minimal pico sdk and tinyusb.
|
||||
To avoid version mismatch and reported path issues when building, as well as to save you from having to download a large SDK, the project now bundles minimal pico sdk and tinyusb.
|
||||
|
||||
On a Debian/Ubuntu systems, make sure to install these:
|
||||
|
||||
```
|
||||
sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
|
||||
```
|
||||
|
||||
You should be able to build by running:
|
||||
|
||||
@ -61,19 +67,21 @@ cmake -S . -B build
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
## Using pre-built images
|
||||
additionally, to rebuild web UI check webconfig/ and execute ```./render.py```, you'll need jinja2 installed.
|
||||
|
||||
Alternatively, you can use the [pre-built images](binaries/). Take the Pico board that goes to slot A on the PCB and hold the on-board button while connecting the cable.
|
||||
To rebuild the disk, check disk/ folder and run ```./create.sh```, tweak to your system if needed. You'll need **dosfstools** (to provide mkdosfs),
|
||||
|
||||
It should appear as a USB drive on your system. Copy the corresponding board_A.uf2 file there and repeat the same with B.
|
||||
## Using a pre-built image
|
||||
|
||||
Alternatively, you can use the [pre-built images](https://github.com/hrvach/deskhop/releases). Since version 0.6 there is only a single universal image. You need the .uf2 file which you simply copy to the device in one of the following ways:
|
||||
|
||||
## Upgrading firmware
|
||||
|
||||
Option 1 - Open the case, hold the button while connecting each Pico and copy the right uf2 to it.
|
||||
**Option 1** - (firmware 0.6 and later) Put the device in "config mode" by simultaneously pressing **left shift + right shift + c + o**. Device your keyboard is plugged into will reboot and turn into a USB drive called "DESKHOP". All you need to do is copy the .uf2 file to it. Once image is verified, device will flash and reboot, then proceed to upgrade the other board. During this operation the led will blink. Once it's done, it will write flash and reboot, completing the operation.
|
||||
|
||||
Option 2 - Switch a board to BOOTSEL mode by using a special key combination (listed below).
|
||||
_Note_ - This is not an actual generic USB drive, you can't use it to copy files to it.
|
||||
|
||||
This will make the corresponding Pico board enter the bootloader upgrade mode and act as USB flash drive. Now you can drag-and-drop the .uf2 file to it (you might need to plug in your mouse directly).
|
||||
**Option 2** - Using the ROM bootloader - hold the on-board button while connecting each Pico and copy the uf2 to the flash drive that appears. Images later than 0.6 support holding the button without having to fiddle around the power supply, but the "hold button while plugging" should always work, regardless of device state.
|
||||
|
||||
## Misc features
|
||||
|
||||
@ -94,19 +102,17 @@ Supposedly built in to prevent computer from entering standby, but truth be told
|
||||
|
||||
Potential usage example - I have a buggy USB dock that won't resume video from standby, so not allowing it to sleep can be a handy workaround.
|
||||
|
||||
![Image](img/screensaver.gif)
|
||||
|
||||
## Hardware
|
||||
|
||||
[The circuit](schematics/DeskHop_v1.1.pdf) is based on two Raspberry Pi Pico boards, chosen because they are cheap (4.10 € / pc), can be hand soldered and most suppliers have them in stock.
|
||||
|
||||
The Picos are connected using UART and separated by an Analog Devices ADuM1201 dual-channel digital isolator (~3€) or a much cheaper, faster and pin-compatible TI ISO7721DR (~1.5€).
|
||||
The Picos are connected using UART and separated by an Analog Devices ADuM1201 dual-channel digital isolator (~3€) or a much cheaper, faster and pin-compatible TI ISO7721DR (~1.5€) which is the preferred choice.
|
||||
|
||||
While they normally don't have support for dual USB, thanks to an [amazing project](https://github.com/sekigon-gonnoc/Pico-PIO-USB) where USB is implemented using the programmable IO wizardry found in RP2040, there is support for it to act both as an USB host and device.
|
||||
|
||||
## PCB [updated]
|
||||
|
||||
To keep things as simple as possible for DIY builds, the traces were kept on one side and the number of parts kept to a theoretical minimum.
|
||||
To keep things as simple as possible for DIY builds, the traces were kept on one side and the number of parts kept to a minimum.
|
||||
|
||||
![Image](img/plocica2.png)
|
||||
|
||||
@ -193,11 +199,13 @@ The standard process to do that is using isopropyl alcohol and an old toothbrush
|
||||
|
||||
## Usage guide
|
||||
|
||||
### Keyboard shortcuts
|
||||
### Keyboard shortcuts - (fw versions 0.6+)
|
||||
|
||||
_Firmware upgrade_
|
||||
- ```Right Shift + F12 + Left Shift + A``` - put board A in FW upgrade mode
|
||||
- ```Right Shift + F12 + Left Shift + B``` - put board B in FW upgrade mode
|
||||
_Config_
|
||||
- ```Left Shift + Right Shift + C + O``` - enter config mode
|
||||
- ```Right Shift + F12 + D``` - remove flash config
|
||||
- ```Right Shift + F12 + Y``` - save screen switch offset
|
||||
- ```Right Shift + F12 + S``` - turn on/off screensaver option
|
||||
|
||||
_Usage_
|
||||
- ```Right CTRL + Right ALT``` - Toggle slower mouse mode
|
||||
@ -205,20 +213,6 @@ _Usage_
|
||||
- ```Right ALT + Right Shift + L``` - Lock both outputs at once (set output OS before, see shortcuts below)
|
||||
- ```Caps Lock``` - Switch between outputs
|
||||
|
||||
_Config_
|
||||
- ```Right Shift + F12 + D``` - remove flash config
|
||||
- ```Right Shift + F12 + Y``` - save screen switch offset
|
||||
- ```Right Shift + F12 + S``` - turn on/off screensaver option
|
||||
|
||||
_Number of outputs_
|
||||
- ```Right Shift + Backspace + 1``` - set number of screens to 1 on current active output
|
||||
- ```Right Shift + Backspace + 2``` - set number of screens to 2 on current active output
|
||||
|
||||
_Set operating systems_
|
||||
- ```Right Shift + Backspace + 7``` - set os to Linux on current active output
|
||||
- ```Right Shift + Backspace + 8``` - set os to Windows on current active output
|
||||
- ```Right Shift + Backspace + 9``` - set os to MacOS on current active output
|
||||
|
||||
### Switch cursor height calibration
|
||||
|
||||
This step is not required, but it can be handy if your screens are not perfectly aligned or differ in size. The objective is to have the mouse pointer come out at exactly the same height.
|
||||
@ -229,16 +223,46 @@ Just park your mouse on the LARGER screen at the height of the smaller/lower scr
|
||||
|
||||
Repeat for the bottom border (if it's above the larger screen's border). This will get saved to flash and it should keep this calibration value from now on.
|
||||
|
||||
|
||||
### Multiple screens per output
|
||||
|
||||
Windows and Mac have issues with multiple screens and absolute positioning, so workarounds are needed (still experimental). Your main screens need to be in the middle, and secondary screen(s) on the edges. Move the mouse to the screen with multiple desktops and press *right shift + backspace + 2* if you have 2 desktops and *right shift + backspace + 7, 8 or 9* depending on your OS (Linux, Windows, Mac).
|
||||
Windows and Mac have issues with multiple screens and absolute positioning, so workarounds are needed (still experimental). There is a better workaround under construction, but for now you have to set the operating system for each output and number of screens.
|
||||
|
||||
Your main screens need to be in the middle, and secondary screen(s) on the edges. To configure the actual options, open the web configuration page for your device.
|
||||
|
||||
![Image](img/deskhop-scr.png)
|
||||
|
||||
### Other configuration
|
||||
### Web configuration mode
|
||||
|
||||
Mouse speed can now be configured per output screen and per axis. If you have multiple displays under Linux, your X speed might be too fast, so you need to configure it in user_config.h and rebuild. In the future, this will be configurable without having to do that.
|
||||
Starting with fw 0.6, an improved configuration mode is introduced. To configure your device, follow these instructions:
|
||||
|
||||
1. Press Left Shift + Right Shift + C + O - your device will reboot and enter configuration mode (on the side your keyboard is plugged into). LED will keep blinking during the configuration session.
|
||||
2. A new USB drive will appear named "DESKHOP" with a single file, config.htm
|
||||
3. Open that file with Chromium / Chrome. Unfortunately FF is not supported right now, since they avoid implementing WebHID.
|
||||
4. Click connect and allow deskhop device to pair.
|
||||
|
||||
![Image](img/connect-dialog.png)
|
||||
|
||||
5. Configure the options as you wish and click save to write to device.
|
||||
6. Click "exit" in the menu to leave configuration mode for added safety.
|
||||
|
||||
![Image](img/config-page-big.png)
|
||||
|
||||
Q: Why not simply create a nice online web page like Via instead of dealing with this weird USB drive thing?
|
||||
A: Loading javascript from a random online location that interacts with your input devices is a potential security risk. The configuration web page is local only and nothing is ever loaded externally.
|
||||
|
||||
<details closed>
|
||||
<summary>Linux doesn't see device? Click here.</summary>
|
||||
|
||||
Q: Chromium on Linux doesn't work.
|
||||
A: You probably need to tweak /dev permissions or create a corresponding udev rules file and make sure your user is in the right group, like so:
|
||||
|
||||
/etc/udev/rules.d/99-deskhop.rules
|
||||
```
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="c000", GROUP="plugdev", MODE="0660"
|
||||
```
|
||||
</details><br />
|
||||
|
||||
Please note the config web page is not "weird" because of deliberate obfuscation - it's self-decompressing due to very limited storage. Entire source is 100% open and a part of this repo. You are encouraged to rebuild everything yourself.
|
||||
|
||||
### Functional verification
|
||||
|
||||
@ -251,14 +275,16 @@ Do this test by first plugging the keyboard on one side and then on the other. I
|
||||
Some features are missing on purpose, despite the fact it would make the device easier to use or simpler to configure. Here is a quick breakdown of these decisions:
|
||||
|
||||
- There is no copy-paste or *any* information sharing between systems. This prevents information leakage.
|
||||
- No webhid device management or any inbound connectivity from the output computers, with the only exception of standard keyboard LED on/off messages, hard limited to 1 byte of data.
|
||||
- No webhid device management without explicit user consent. No inbound connectivity from the output computers, with the only exception of standard keyboard LED on/off messages and hard limited to 1 byte of data.
|
||||
- No FW upgrade triggering from the outputs. Only explicit and deliberate user action through a special keyboard shortcut may do that.
|
||||
- No plugged-in keyboard/mouse custom endpoints are exposed or information forwarded towards these devices. Their potential vulnerabilities are effectively firewalled from the computer.
|
||||
- No input history is allowed to be retained.
|
||||
- No device-initiated keystrokes, for any reason. Only thing that comes out is what you type/trigger.
|
||||
- Outputs are physically separated and galvanically isolated with a minimal isolation voltage of 2kV.
|
||||
- All packets exchanged between devices are of fixed length, no code is transferred and no raw config exchange of any kind can take place.
|
||||
- There is no bluetooth or wifi, networking, Internet access, usb drives etc.
|
||||
- All packets exchanged between devices are of fixed length, config options transferred are limited to a short list. Most options are read-only. Cross-device firmware upgrades can be disabled.
|
||||
- There is no bluetooth or wifi, networking, Internet access, etc.
|
||||
- No connected computer is considered trusted under any circumstances.
|
||||
- Configuration mode is automatically disabled after a period of inactivity.
|
||||
- Entirety of the code is open source, without any binary blobs and thoroughly commented to explain its purpose. I encourage you to never trust anyone and always make sure you know what you are running by doing a manual audit.
|
||||
|
||||
This still doesn't guarantee anything, but I believe it makes a reasonable set of ground rules to keep you safe and protected.
|
||||
@ -279,7 +305,7 @@ This still doesn't guarantee anything, but I believe it makes a reasonable set o
|
||||
|
||||
[UPDATE] It seems you can order it in QTY of 1 (for either PCB, assembled PCB or a fully assembled device) from [Elecrow if you follow this link](https://www.elecrow.com/deskhop-fast-desktop-switching.html)
|
||||
|
||||
*I **don't want to take any commission** on this - the only goal is to provide an alternative for people who don't feel confident enough to assemble the boards themselves.*
|
||||
[UPDATE2] - I never asked Elecrow for anything, but a couple of days ago they offered to sponsor the project with a small budget that will be used for future board prototyping. Since my goal is to create a better board with more than 2 outputs etc, I believe prototyping services might be beneficial to the project.
|
||||
|
||||
4. When the active screen is changed via the mouse, does the keyboard follow (and vice versa)?
|
||||
|
||||
@ -287,7 +313,7 @@ This still doesn't guarantee anything, but I believe it makes a reasonable set o
|
||||
|
||||
5. Will this work with keyboard/mouse combo dongles, like the Logitech Unifying receiver?
|
||||
|
||||
*Not tested yet, but the latest version might actually work (please provide feedback).*
|
||||
It should work. After a recent FW update, support for combo receivers should be much better.
|
||||
|
||||
6. Will this work with wireless mice and keyboards that have separate wireless receivers (one for the mouse, another for the keyboard)?
|
||||
|
||||
@ -309,12 +335,12 @@ There are several software alternatives you can use if that works in your partic
|
||||
|
||||
## Shortcomings
|
||||
|
||||
- Windows 10 broke HID absolute coordinates behavior in KB5003637, so you can't use more than 1 screen on Windows (mouse will stay on the main screen). There is an experimental workaround.
|
||||
- Windows 10 broke HID absolute coordinates behavior in KB5003637, so you can't use more than 1 screen on Windows (mouse will stay on the main screen). There is an experimental workaround with a better one on the way.
|
||||
- Code needs cleanup, some refactoring etc.
|
||||
- Not tested with a wide variety of devices, I don't know how it will work with your hardware. There is a reasonable chance things might not work out-of-the-box.
|
||||
- Advanced keyboards (with knobs, extra buttons or sliders) will probably face issues where this additional hardware doesn't work.
|
||||
- Super-modern mice with 300 buttons might see some buttons not work as expected.
|
||||
- NOTE: Both computers need to be connected and provide power to the USB for this to work (as each board gets powered by the computer it plugs into). Many desktops and laptops will provide power even when shut down nowadays. If you need to run with one board fully disconnected, you should be able to use a USB hub to plug both keyboard and mouse to a single port.
|
||||
- NOTE: **Both computers need to be connected and provide power to the USB for this to work** (as each board gets powered by the computer it plugs into). Many desktops and laptops will provide power even when shut down nowadays. If you need to run with one board fully disconnected, you should be able to use a USB hub to plug both keyboard and mouse to a single port.
|
||||
- MacOS has issues with more than one screens, latest firmware offers an experimental workaround that fixes it.
|
||||
|
||||
## Progress
|
||||
@ -322,14 +348,10 @@ There are several software alternatives you can use if that works in your partic
|
||||
So, what's the deal with all the enthusiasm? I can't believe it - please allow me to thank you all! I've never expected this kind of interest in a simple personal project, so the initial features are pretty basic (just like my cooking skills) and mostly cover my own usecase. Stay tuned for firmware updates that should bring wider device compatibility, more features and less bugs. As this is a hobby project, I appreciate your understanding for being time-constrained and promise to do the best I can.
|
||||
|
||||
Planned features:
|
||||
- ~~Proper TinyUSB host integration~~ (done)
|
||||
- ~~HID report protocol parsing, not just boot protocol~~ (mostly done)
|
||||
- ~~Support for unified dongle receivers~~
|
||||
- ~~Support for USB hubs and single-sided operation~~
|
||||
- ~~Configurable screens (done)~~
|
||||
- ~~Permament configuration stored in flash~~
|
||||
- Better support for keyboards with knobs and mice with mickeys
|
||||
- Unified firmware for both Picos
|
||||
- Better workarounds for multiscreen windows and macos
|
||||
- Transparent / Gaming mode
|
||||
- Support for more than 2 outputs
|
||||
- Improvements on the configuration UI
|
||||
- ... and more!
|
||||
|
||||
Working on a *lite* version which provides basic functionality with just a single Pico W board, lowering the cost even further and enabling you to try it out even with no added hardware or PCB.
|
||||
@ -341,13 +363,14 @@ Mouse polling should now work at 1000 Hz (the dips in the graph is my arm hurtin
|
||||
## Sponsor / donate
|
||||
|
||||
I'm NOT doing this for profit or any other reason except to try and help people by creating a better working environment for everyone.
|
||||
|
||||
I have, however, decided to accept donations for a single purpose only - to buy some keyboards/mice with media keys, buttons, nkro and other weird stuff people reported issues with in order to fix bugs, improve the state of the project and provide a better user experience overall.
|
||||
|
||||
Having said that, if you want to support the project, you can use this link:
|
||||
|
||||
[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate/?hosted_button_id=4RDC7JY5FNC78)
|
||||
|
||||
Thank you!
|
||||
Please allow me to thank everyone who helped or considering it, this already helped me expand support for a range of devices. Many thanks!
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
13
disk/create.sh
Executable file
13
disk/create.sh
Executable file
@ -0,0 +1,13 @@
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
dd if=/dev/zero of=fat.img bs=2M count=1
|
||||
|
||||
mkdosfs -F12 -n DESKHOP -i 0 fat.img
|
||||
|
||||
sudo mount -o loop,x-mount.mkdir -t vfat fat.img /mnt/disk/
|
||||
sudo cp ../webconfig/config.htm /mnt/disk/config.htm
|
||||
sudo umount /mnt/disk
|
||||
|
||||
dd if=fat.img of=disk.img bs=512 count=128
|
||||
rm fat.img
|
10
disk/disk.S
Normal file
10
disk/disk.S
Normal file
@ -0,0 +1,10 @@
|
||||
.section .section_disk,"a"
|
||||
.global _disk_start
|
||||
|
||||
_disk_start:
|
||||
.incbin __disk_file_path__
|
||||
.global _disk_end
|
||||
|
||||
_disk_end:
|
||||
|
||||
|
BIN
disk/disk.img
Normal file
BIN
disk/disk.img
Normal file
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 182 KiB |
@ -21,16 +21,33 @@
|
||||
__stack (== StackTop)
|
||||
*/
|
||||
|
||||
/* Total image is 256 kB, consisting of:
|
||||
Executable = 188 kB
|
||||
FAT disk image = 64 kB
|
||||
Firmware metadata = 4 kB (contains checksum)
|
||||
*/
|
||||
|
||||
__FLASH_LEN = 188k;
|
||||
__DISK_IMAGE_LEN = 64k;
|
||||
__METADATA_LEN = 4k;
|
||||
__TOTAL_IMAGE_LENGTH = 256k;
|
||||
|
||||
__CONFIG_STORAGE_LEN = 4k;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k - __CONFIG_STORAGE_LEN
|
||||
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __FLASH_LEN
|
||||
DISK_IMAGE(rw) : ORIGIN = 0x10000000 + __FLASH_LEN, LENGTH = __DISK_IMAGE_LEN
|
||||
FW_METADATA(rw) : ORIGIN = 0x10000000 + (__TOTAL_IMAGE_LENGTH - __METADATA_LEN), LENGTH = __METADATA_LEN
|
||||
FW_STAGING(rw) : ORIGIN = 0x10000000 + __TOTAL_IMAGE_LENGTH, LENGTH = __TOTAL_IMAGE_LENGTH
|
||||
|
||||
FLASH_CONFIG(rw) : ORIGIN = 0x10000000 + (2048k - __CONFIG_STORAGE_LEN), LENGTH = __CONFIG_STORAGE_LEN
|
||||
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
|
||||
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
|
||||
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
|
||||
}
|
||||
PROVIDE(_staging_metadata = ORIGIN(FW_STAGING) + (__TOTAL_IMAGE_LENGTH - __METADATA_LEN));
|
||||
PROVIDE(_firmware_metadata = ORIGIN(FW_METADATA));
|
||||
|
||||
ENTRY(_entry_point)
|
||||
|
||||
@ -42,6 +59,7 @@ SECTIONS
|
||||
*/
|
||||
|
||||
.flash_begin : {
|
||||
ADDR_FW_RUNNING = .;
|
||||
__flash_binary_start = .;
|
||||
} > FLASH
|
||||
|
||||
@ -180,6 +198,7 @@ SECTIONS
|
||||
|
||||
.uninitialized_data (NOLOAD): {
|
||||
. = ALIGN(4);
|
||||
__udata_end__ = .;
|
||||
*(.uninitialized_data*)
|
||||
} > RAM
|
||||
|
||||
@ -217,10 +236,35 @@ SECTIONS
|
||||
__HeapLimit = .;
|
||||
} > RAM
|
||||
|
||||
/* Configuration flash section (4k in size, end of flash) */
|
||||
/* Store web configuration utility HTML */
|
||||
.section_disk : {
|
||||
ADDR_DISK_IMAGE = .;
|
||||
KEEP(*(.section_disk))
|
||||
} > DISK_IMAGE
|
||||
|
||||
.section_config : {
|
||||
"ADDR_CONFIG" = .;
|
||||
/* Firmware metadata section (4k in size, contains version, checksum etc.) */
|
||||
.section_metadata : {
|
||||
ADDR_FW_METADATA = .;
|
||||
KEEP(*(.section_metadata));
|
||||
} > FW_METADATA
|
||||
|
||||
/* Firmware staging section (256k in size, near the end of flash) */
|
||||
.section_staging : {
|
||||
ADDR_FW_STAGING = .;
|
||||
KEEP(*(.section_staging))
|
||||
} > FW_STAGING
|
||||
|
||||
/* Just padding so we can have a nice, consistently-sized .bin file */
|
||||
.fill : {
|
||||
FILL(0x00);
|
||||
. = ORIGIN(FW_METADATA) + LENGTH(FW_METADATA) - 1;
|
||||
BYTE(0x00)
|
||||
___ROM_AT = .;
|
||||
} > FW_METADATA
|
||||
|
||||
/* Configuration flash section (4k in size, end of flash) */
|
||||
.section_config (NOLOAD) : {
|
||||
ADDR_CONFIG = .;
|
||||
} > FLASH_CONFIG
|
||||
|
||||
/* .stack*_dummy section doesn't contains any symbols. It is only
|
||||
@ -245,6 +289,14 @@ SECTIONS
|
||||
__flash_binary_end = .;
|
||||
} > FLASH
|
||||
|
||||
.pad : {
|
||||
/* This section will be filled with zeroes */
|
||||
FILL(0x00)
|
||||
. = ADDR_DISK_IMAGE - __flash_binary_end - 1;
|
||||
BYTE(0x00)
|
||||
KEEP(*(.pad))
|
||||
} > FLASH
|
||||
|
||||
/* stack limit is poorly named, but historically is maximum heap ptr */
|
||||
__StackLimit = ORIGIN(RAM) + LENGTH(RAM);
|
||||
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
|
||||
|
@ -108,6 +108,28 @@ TU_ATTR_USED int sys_read (int fhdl, char *buf, size_t count) {
|
||||
// st->st_mode = S_IFCHR;
|
||||
//}
|
||||
|
||||
// Clang use picolibc
|
||||
#if defined(__clang__)
|
||||
static int cl_putc(char c, FILE *f) {
|
||||
(void) f;
|
||||
return sys_write(0, &c, 1);
|
||||
}
|
||||
|
||||
static int cl_getc(FILE* f) {
|
||||
(void) f;
|
||||
char c;
|
||||
return sys_read(0, &c, 1) > 0 ? c : -1;
|
||||
}
|
||||
|
||||
static FILE __stdio = FDEV_SETUP_STREAM(cl_putc, cl_getc, NULL, _FDEV_SETUP_RW);
|
||||
FILE *const stdin = &__stdio;
|
||||
__strong_reference(stdin, stdout);
|
||||
__strong_reference(stdin, stderr);
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Board API
|
||||
//--------------------------------------------------------------------+
|
||||
int board_getchar(void) {
|
||||
char c;
|
||||
return (sys_read(0, &c, 1) > 0) ? (int) c : (-1);
|
||||
|
@ -32,10 +32,30 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tusb.h"
|
||||
|
||||
#if CFG_TUSB_OS == OPT_OS_FREERTOS
|
||||
#if TUP_MCU_ESPRESSIF
|
||||
// ESP-IDF need "freertos/" prefix in include path.
|
||||
// CFG_TUSB_OS_INC_PATH should be defined accordingly.
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
#else
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
#include "queue.h"
|
||||
#include "task.h"
|
||||
#include "timers.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Define the default baudrate
|
||||
#ifndef CFG_BOARD_UART_BAUDRATE
|
||||
#define CFG_BOARD_UART_BAUDRATE 115200 ///< Default baud rate
|
||||
@ -99,6 +119,7 @@ static inline uint32_t board_millis(void) {
|
||||
|
||||
#elif CFG_TUSB_OS == OPT_OS_CUSTOM
|
||||
// Implement your own board_millis() in any of .c file
|
||||
uint32_t board_millis(void);
|
||||
|
||||
#else
|
||||
#error "board_millis() is not implemented for this OS"
|
||||
@ -121,6 +142,7 @@ static inline size_t board_usb_get_serial(uint16_t desc_str1[], size_t max_chars
|
||||
uint8_t uid[16] TU_ATTR_ALIGNED(4);
|
||||
size_t uid_len;
|
||||
|
||||
// TODO work with make, but not working with esp32s3 cmake
|
||||
if ( board_get_unique_id ) {
|
||||
uid_len = board_get_unique_id(uid, sizeof(uid));
|
||||
}else {
|
||||
|
@ -47,7 +47,7 @@
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX, OPT_MCU_LPC54XXX, OPT_MCU_LPC55XX, OPT_MCU_MCXN9)
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L)
|
||||
#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L, OPT_MCU_KINETIS_K)
|
||||
#include "fsl_device_registers.h"
|
||||
|
||||
#elif CFG_TUSB_MCU == OPT_MCU_NRF5X
|
||||
|
@ -6,12 +6,33 @@ include(CMakePrintHelpers)
|
||||
set(TOP "${CMAKE_CURRENT_LIST_DIR}/../..")
|
||||
get_filename_component(TOP ${TOP} ABSOLUTE)
|
||||
|
||||
# Default to gcc
|
||||
#-------------------------------------------------------------
|
||||
# Toolchain
|
||||
# Can be changed via -DTOOLCHAIN=gcc|iar or -DCMAKE_C_COMPILER=
|
||||
#-------------------------------------------------------------
|
||||
# Detect toolchain based on CMAKE_C_COMPILER
|
||||
if (DEFINED CMAKE_C_COMPILER)
|
||||
string(FIND ${CMAKE_C_COMPILER} "iccarm" IS_IAR)
|
||||
string(FIND ${CMAKE_C_COMPILER} "clang" IS_CLANG)
|
||||
string(FIND ${CMAKE_C_COMPILER} "gcc" IS_GCC)
|
||||
|
||||
if (NOT IS_IAR EQUAL -1)
|
||||
set(TOOLCHAIN iar)
|
||||
elseif (NOT IS_CLANG EQUAL -1)
|
||||
set(TOOLCHAIN clang)
|
||||
elseif (NOT IS_GCC EQUAL -1)
|
||||
set(TOOLCHAIN gcc)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# default to gcc
|
||||
if (NOT DEFINED TOOLCHAIN)
|
||||
set(TOOLCHAIN gcc)
|
||||
endif ()
|
||||
|
||||
# FAMILY not defined, try to detect it from BOARD
|
||||
#-------------------------------------------------------------
|
||||
# FAMILY and BOARD
|
||||
#-------------------------------------------------------------
|
||||
if (NOT DEFINED FAMILY)
|
||||
if (NOT DEFINED BOARD)
|
||||
message(FATAL_ERROR "You must set a FAMILY variable for the build (e.g. rp2040, espressif).
|
||||
@ -74,22 +95,35 @@ set(WARNING_FLAGS_GNU
|
||||
|
||||
set(WARNING_FLAGS_IAR "")
|
||||
|
||||
#-------------------------------------------------------------
|
||||
# Functions
|
||||
#-------------------------------------------------------------
|
||||
|
||||
# Filter example based on only.txt and skip.txt
|
||||
function(family_filter RESULT DIR)
|
||||
get_filename_component(DIR ${DIR} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if (EXISTS "${DIR}/only.txt")
|
||||
file(READ "${DIR}/only.txt" ONLYS)
|
||||
# Replace newlines with semicolon so that it is treated as a list by CMake
|
||||
string(REPLACE "\n" ";" ONLYS_LINES ${ONLYS})
|
||||
if (EXISTS "${DIR}/skip.txt")
|
||||
file(STRINGS "${DIR}/skip.txt" SKIPS_LINES)
|
||||
foreach(MCU IN LISTS FAMILY_MCUS)
|
||||
# For each line in only.txt
|
||||
foreach(_line ${SKIPS_LINES})
|
||||
# If mcu:xxx exists for this mcu then skip
|
||||
if (${_line} STREQUAL "mcu:${MCU}" OR ${_line} STREQUAL "board:${BOARD}" OR ${_line} STREQUAL "family:${FAMILY}")
|
||||
set(${RESULT} 0 PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endif ()
|
||||
|
||||
# For each mcu
|
||||
if (EXISTS "${DIR}/only.txt")
|
||||
file(STRINGS "${DIR}/only.txt" ONLYS_LINES)
|
||||
foreach(MCU IN LISTS FAMILY_MCUS)
|
||||
# For each line in only.txt
|
||||
foreach(_line ${ONLYS_LINES})
|
||||
# If mcu:xxx exists for this mcu or board:xxx then include
|
||||
if (${_line} STREQUAL "mcu:${MCU}" OR ${_line} STREQUAL "board:${BOARD}")
|
||||
if (${_line} STREQUAL "mcu:${MCU}" OR ${_line} STREQUAL "board:${BOARD}" OR ${_line} STREQUAL "family:${FAMILY}")
|
||||
set(${RESULT} 1 PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
@ -98,29 +132,8 @@ function(family_filter RESULT DIR)
|
||||
|
||||
# Didn't find it in only file so don't build
|
||||
set(${RESULT} 0 PARENT_SCOPE)
|
||||
|
||||
elseif (EXISTS "${DIR}/skip.txt")
|
||||
file(READ "${DIR}/skip.txt" SKIPS)
|
||||
# Replace newlines with semicolon so that it is treated as a list by CMake
|
||||
string(REPLACE "\n" ";" SKIPS_LINES ${SKIPS})
|
||||
|
||||
# For each mcu
|
||||
foreach(MCU IN LISTS FAMILY_MCUS)
|
||||
# For each line in only.txt
|
||||
foreach(_line ${SKIPS_LINES})
|
||||
# If mcu:xxx exists for this mcu then skip
|
||||
if (${_line} STREQUAL "mcu:${MCU}")
|
||||
set(${RESULT} 0 PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# Didn't find in skip file so build
|
||||
set(${RESULT} 1 PARENT_SCOPE)
|
||||
else()
|
||||
|
||||
# Didn't find skip or only file so build
|
||||
# only.txt not exist so build
|
||||
set(${RESULT} 1 PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
@ -206,12 +219,12 @@ function(family_configure_common TARGET RTOS)
|
||||
if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0)
|
||||
target_link_options(${TARGET} PUBLIC "LINKER:--no-warn-rwx-segments")
|
||||
endif ()
|
||||
endif()
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "IAR")
|
||||
elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
target_link_options(${TARGET} PUBLIC "LINKER:-Map=$<TARGET_FILE:${TARGET}>.map")
|
||||
elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
|
||||
target_link_options(${TARGET} PUBLIC "LINKER:--map=$<TARGET_FILE:${TARGET}>.map")
|
||||
endif()
|
||||
|
||||
|
||||
# ETM Trace option
|
||||
if (TRACE_ETM STREQUAL "1")
|
||||
target_compile_definitions(${TARGET} PUBLIC TRACE_ETM)
|
||||
@ -226,7 +239,7 @@ function(family_configure_common TARGET RTOS)
|
||||
if (NOT TARGET segger_rtt)
|
||||
add_library(segger_rtt STATIC ${TOP}/lib/SEGGER_RTT/RTT/SEGGER_RTT.c)
|
||||
target_include_directories(segger_rtt PUBLIC ${TOP}/lib/SEGGER_RTT/RTT)
|
||||
#target_compile_definitions(segger_rtt PUBLIC SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL)
|
||||
# target_compile_definitions(segger_rtt PUBLIC SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL)
|
||||
endif()
|
||||
target_link_libraries(${TARGET} PUBLIC segger_rtt)
|
||||
endif ()
|
||||
@ -372,6 +385,10 @@ function(family_flash_jlink TARGET)
|
||||
set(JLINKEXE JLinkExe)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED JLINK_IF)
|
||||
set(JLINK_IF swd)
|
||||
endif ()
|
||||
|
||||
file(GENERATE
|
||||
OUTPUT $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.jlink
|
||||
CONTENT "halt
|
||||
@ -383,7 +400,7 @@ exit"
|
||||
|
||||
add_custom_target(${TARGET}-jlink
|
||||
DEPENDS ${TARGET}
|
||||
COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} -if swd -JTAGConf -1,-1 -speed auto -CommandFile $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.jlink
|
||||
COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} -if ${JLINK_IF} -JTAGConf -1,-1 -speed auto -CommandFile $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.jlink
|
||||
)
|
||||
endfunction()
|
||||
|
||||
@ -402,21 +419,36 @@ endfunction()
|
||||
|
||||
|
||||
# Add flash openocd target
|
||||
function(family_flash_openocd TARGET CLI_OPTIONS)
|
||||
function(family_flash_openocd TARGET)
|
||||
if (NOT DEFINED OPENOCD)
|
||||
set(OPENOCD openocd)
|
||||
endif ()
|
||||
|
||||
separate_arguments(CLI_OPTIONS_LIST UNIX_COMMAND ${CLI_OPTIONS})
|
||||
if (NOT DEFINED OPENOCD_OPTION2)
|
||||
set(OPENOCD_OPTION2 "")
|
||||
endif ()
|
||||
|
||||
separate_arguments(OPTION_LIST UNIX_COMMAND ${OPENOCD_OPTION})
|
||||
separate_arguments(OPTION_LIST2 UNIX_COMMAND ${OPENOCD_OPTION2})
|
||||
|
||||
# note skip verify since it has issue with rp2040
|
||||
add_custom_target(${TARGET}-openocd
|
||||
DEPENDS ${TARGET}
|
||||
COMMAND ${OPENOCD} ${CLI_OPTIONS_LIST} -c "program $<TARGET_FILE:${TARGET}> reset exit"
|
||||
COMMAND ${OPENOCD} ${OPTION_LIST} -c "program $<TARGET_FILE:${TARGET}> reset" ${OPTION_LIST2} -c exit
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# Add flash openocd-wch target
|
||||
# compiled from https://github.com/hathach/riscv-openocd-wch or https://github.com/dragonlock2/miscboards/blob/main/wch/SDK/riscv-openocd.tar.xz
|
||||
function(family_flash_openocd_wch TARGET)
|
||||
if (NOT DEFINED OPENOCD)
|
||||
set(OPENOCD $ENV{HOME}/app/riscv-openocd-wch/src/openocd)
|
||||
endif ()
|
||||
|
||||
family_flash_openocd(${TARGET})
|
||||
endfunction()
|
||||
|
||||
# Add flash pycod target
|
||||
function(family_flash_pyocd TARGET)
|
||||
if (NOT DEFINED PYOC)
|
||||
@ -430,6 +462,18 @@ function(family_flash_pyocd TARGET)
|
||||
endfunction()
|
||||
|
||||
|
||||
# Add flash teensy_cli target
|
||||
function(family_flash_teensy TARGET)
|
||||
if (NOT DEFINED TEENSY_CLI)
|
||||
set(TEENSY_CLI teensy_loader_cli)
|
||||
endif ()
|
||||
|
||||
add_custom_target(${TARGET}-teensy
|
||||
DEPENDS ${TARGET}
|
||||
COMMAND ${TEENSY_CLI} --mcu=${TEENSY_MCU} -w -s $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.hex
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# Add flash using NXP's LinkServer (redserver)
|
||||
# https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/linkserver-for-microcontrollers:LINKERSERVER
|
||||
function(family_flash_nxplink TARGET)
|
||||
@ -460,6 +504,21 @@ function(family_flash_dfu_util TARGET OPTION)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(family_flash_msp430flasher TARGET)
|
||||
if (NOT DEFINED MSP430Flasher)
|
||||
set(MSP430FLASHER MSP430Flasher)
|
||||
endif ()
|
||||
|
||||
# set LD_LIBRARY_PATH to find libmsp430.so (directory containing MSP430Flasher)
|
||||
find_program(MSP430FLASHER_PATH MSP430Flasher)
|
||||
get_filename_component(MSP430FLASHER_PARENT_DIR "${MSP430FLASHER_PATH}" DIRECTORY)
|
||||
add_custom_target(${TARGET}-msp430flasher
|
||||
DEPENDS ${TARGET}
|
||||
COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=${MSP430FLASHER_PARENT_DIR}
|
||||
${MSP430FLASHER} -w $<TARGET_FILE_DIR:${TARGET}>/${TARGET}.hex -z [VCC]
|
||||
)
|
||||
endfunction()
|
||||
|
||||
#----------------------------------
|
||||
# Family specific
|
||||
#----------------------------------
|
||||
|
@ -21,6 +21,7 @@ if (NOT PICO_TINYUSB_PATH)
|
||||
endif()
|
||||
|
||||
if (NOT TINYUSB_OPT_OS)
|
||||
message("Setting OPT_OS_PICO")
|
||||
set(TINYUSB_OPT_OS OPT_OS_PICO)
|
||||
endif()
|
||||
|
||||
@ -71,6 +72,7 @@ target_sources(tinyusb_device_base INTERFACE
|
||||
${TOP}/src/device/usbd_control.c
|
||||
${TOP}/src/class/cdc/cdc_device.c
|
||||
${TOP}/src/class/hid/hid_device.c
|
||||
${TOP}/src/class/msc/msc_device.c
|
||||
)
|
||||
|
||||
#------------------------------------
|
||||
@ -82,10 +84,7 @@ target_sources(tinyusb_host_base INTERFACE
|
||||
${TOP}/src/portable/raspberrypi/rp2040/rp2040_usb.c
|
||||
${TOP}/src/host/usbh.c
|
||||
${TOP}/src/host/hub.c
|
||||
${TOP}/src/class/cdc/cdc_host.c
|
||||
${TOP}/src/class/hid/hid_host.c
|
||||
${TOP}/src/class/msc/msc_host.c
|
||||
${TOP}/src/class/vendor/vendor_host.c
|
||||
)
|
||||
|
||||
# Sometimes have to do host specific actions in mostly common functions
|
||||
@ -122,6 +121,8 @@ target_link_libraries(tinyusb_bsp INTERFACE pico_unique_id)
|
||||
# tinyusb_additions will hold our extra settings for examples
|
||||
add_library(tinyusb_additions INTERFACE)
|
||||
|
||||
|
||||
message("Setting PICO workarounds")
|
||||
target_compile_definitions(tinyusb_additions INTERFACE
|
||||
PICO_RP2040_USB_DEVICE_ENUMERATION_FIX=1
|
||||
PICO_RP2040_USB_DEVICE_UFRAME_FIX=1
|
||||
@ -307,7 +308,6 @@ function(suppress_tinyusb_warnings)
|
||||
${PICO_TINYUSB_PATH}/src/device/usbd_control.c
|
||||
${PICO_TINYUSB_PATH}/src/host/usbh.c
|
||||
${PICO_TINYUSB_PATH}/src/class/cdc/cdc_device.c
|
||||
${PICO_TINYUSB_PATH}/src/class/cdc/cdc_host.c
|
||||
${PICO_TINYUSB_PATH}/src/class/hid/hid_device.c
|
||||
${PICO_TINYUSB_PATH}/src/class/hid/hid_host.c
|
||||
${PICO_TINYUSB_PATH}/src/class/audio/audio_device.c
|
||||
@ -360,9 +360,6 @@ function(suppress_tinyusb_warnings)
|
||||
set_source_files_properties(
|
||||
${PICO_TINYUSB_PATH}/src/class/cdc/cdc_device.c
|
||||
COMPILE_FLAGS "-Wno-unreachable-code")
|
||||
set_source_files_properties(
|
||||
${PICO_TINYUSB_PATH}/src/class/cdc/cdc_host.c
|
||||
COMPILE_FLAGS "-Wno-unreachable-code-fallthrough")
|
||||
set_source_files_properties(
|
||||
${PICO_TINYUSB_PATH}/lib/fatfs/source/ff.c
|
||||
PROPERTIES
|
||||
|
@ -13,14 +13,22 @@ function(add_tinyusb TARGET)
|
||||
# device
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/device/usbd_control.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/audio/audio_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/dfu/dfu_rt_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/midi/midi_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/msc/msc_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ecm_rndis_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/net/ncm_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/usbtmc/usbtmc_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_device.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/video/video_device.c
|
||||
# host
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/host/usbh.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/host/hub.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/cdc/cdc_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/hid/hid_host.c
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/class/vendor/vendor_host.c
|
||||
# typec
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/typec/usbc.c
|
||||
)
|
||||
@ -30,7 +38,7 @@ function(add_tinyusb TARGET)
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../lib/networking
|
||||
)
|
||||
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
-Wall
|
||||
-Wextra
|
||||
|
@ -136,8 +136,7 @@ typedef enum{
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// Communication Interface Management Element Request Codes
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
|
||||
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
|
||||
CDC_REQUEST_SET_COMM_FEATURE = 0x02,
|
||||
@ -180,39 +179,38 @@ typedef enum
|
||||
CDC_REQUEST_GET_ATM_VC_STATISTICS = 0x53,
|
||||
|
||||
CDC_REQUEST_MDLM_SEMANTIC_MODEL = 0x60,
|
||||
}cdc_management_request_t;
|
||||
} cdc_management_request_t;
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
CDC_CONTROL_LINE_STATE_DTR = 0x01,
|
||||
CDC_CONTROL_LINE_STATE_RTS = 0x02,
|
||||
};
|
||||
} cdc_control_line_state_t;
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
CDC_LINE_CODING_STOP_BITS_1 = 0, // 1 bit
|
||||
CDC_LINE_CODING_STOP_BITS_1_5 = 1, // 1.5 bits
|
||||
CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits
|
||||
};
|
||||
} cdc_line_coding_stopbits_t;
|
||||
|
||||
// TODO Backward compatible for typos. Maybe removed in the future release
|
||||
#define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1
|
||||
#define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5
|
||||
#define CDC_LINE_CONDING_STOP_BITS_2 CDC_LINE_CODING_STOP_BITS_2
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
CDC_LINE_CODING_PARITY_NONE = 0,
|
||||
CDC_LINE_CODING_PARITY_ODD = 1,
|
||||
CDC_LINE_CODING_PARITY_EVEN = 2,
|
||||
CDC_LINE_CODING_PARITY_MARK = 3,
|
||||
CDC_LINE_CODING_PARITY_SPACE = 4,
|
||||
};
|
||||
} cdc_line_coding_parity_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Management Element Notification (Notification Endpoint)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// 6.3 Notification Codes
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
CDC_NOTIF_NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
|
||||
CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
|
||||
CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08,
|
||||
|
@ -43,10 +43,7 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
enum
|
||||
{
|
||||
BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
};
|
||||
#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -176,9 +173,11 @@ uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
|
||||
uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
|
||||
|
||||
// flush if queue more than packet size
|
||||
// may need to suppress -Wunreachable-code since most of the time CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
|
||||
if ( (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(&p_cdc->tx_ff)) )
|
||||
{
|
||||
if ( tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
|
||||
#if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
|
||||
|| tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size
|
||||
#endif
|
||||
) {
|
||||
tud_cdc_n_write_flush(itf);
|
||||
}
|
||||
|
||||
@ -253,11 +252,39 @@ void cdcd_init(void)
|
||||
// In this way, the most current data is prioritized.
|
||||
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
|
||||
|
||||
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex));
|
||||
tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL);
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
|
||||
osal_mutex_t mutex_wr = osal_mutex_create(&p_cdc->tx_ff_mutex);
|
||||
TU_ASSERT(mutex_rd != NULL && mutex_wr != NULL, );
|
||||
|
||||
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, mutex_rd);
|
||||
tu_fifo_config_mutex(&p_cdc->tx_ff, mutex_wr, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool cdcd_deinit(void) {
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
for(uint8_t i=0; i<CFG_TUD_CDC; i++) {
|
||||
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
|
||||
osal_mutex_t mutex_rd = p_cdc->rx_ff.mutex_rd;
|
||||
osal_mutex_t mutex_wr = p_cdc->tx_ff.mutex_wr;
|
||||
|
||||
if (mutex_rd) {
|
||||
osal_mutex_delete(mutex_rd);
|
||||
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, NULL);
|
||||
}
|
||||
|
||||
if (mutex_wr) {
|
||||
osal_mutex_delete(mutex_wr);
|
||||
tu_fifo_config_mutex(&p_cdc->tx_ff, NULL, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cdcd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
@ -268,7 +295,9 @@ void cdcd_reset(uint8_t rhport)
|
||||
|
||||
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
|
||||
tu_fifo_clear(&p_cdc->rx_ff);
|
||||
#if !CFG_TUD_CDC_PERSISTENT_TX_BUFF
|
||||
tu_fifo_clear(&p_cdc->tx_ff);
|
||||
#endif
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,12 @@
|
||||
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#endif
|
||||
|
||||
// By default the TX fifo buffer is cleared on connect / bus reset.
|
||||
// Enable this to persist any data in the fifo instead.
|
||||
#ifndef CFG_TUD_CDC_PERSISTENT_TX_BUFF
|
||||
#define CFG_TUD_CDC_PERSISTENT_TX_BUFF (0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -247,6 +253,7 @@ static inline bool tud_cdc_write_clear(void)
|
||||
// INTERNAL USBD-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
void cdcd_init (void);
|
||||
bool cdcd_deinit (void);
|
||||
void cdcd_reset (uint8_t rhport);
|
||||
uint16_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool cdcd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,204 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CDC_HOST_H_
|
||||
#define _TUSB_CDC_HOST_H_
|
||||
|
||||
#include "cdc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Set Line Control state on enumeration/mounted: DTR ( bit 0), RTS (bit 1)
|
||||
#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
|
||||
#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0
|
||||
#endif
|
||||
|
||||
// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
|
||||
//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM
|
||||
//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
|
||||
//#endif
|
||||
|
||||
// RX FIFO size
|
||||
#ifndef CFG_TUH_CDC_RX_BUFSIZE
|
||||
#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX
|
||||
#endif
|
||||
|
||||
// RX Endpoint size
|
||||
#ifndef CFG_TUH_CDC_RX_EPSIZE
|
||||
#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX
|
||||
#endif
|
||||
|
||||
// TX FIFO size
|
||||
#ifndef CFG_TUH_CDC_TX_BUFSIZE
|
||||
#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX
|
||||
#endif
|
||||
|
||||
// TX Endpoint size
|
||||
#ifndef CFG_TUH_CDC_TX_EPSIZE
|
||||
#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get Interface index from device address + interface number
|
||||
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
|
||||
uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num);
|
||||
|
||||
// Get Interface information
|
||||
// return true if index is correct and interface is currently mounted
|
||||
bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
|
||||
|
||||
// Check if a interface is mounted
|
||||
bool tuh_cdc_mounted(uint8_t idx);
|
||||
|
||||
// Get current DTR status
|
||||
bool tuh_cdc_get_dtr(uint8_t idx);
|
||||
|
||||
// Get current RTS status
|
||||
bool tuh_cdc_get_rts(uint8_t idx);
|
||||
|
||||
// Check if interface is connected (DTR active)
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx)
|
||||
{
|
||||
return tuh_cdc_get_dtr(idx);
|
||||
}
|
||||
|
||||
// Get local (saved/cached) version of line coding.
|
||||
// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding()
|
||||
// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined.
|
||||
// NOTE: This function does not make any USB transfer request to device.
|
||||
bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Write API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get the number of bytes available for writing
|
||||
uint32_t tuh_cdc_write_available(uint8_t idx);
|
||||
|
||||
// Write to cdc interface
|
||||
uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize);
|
||||
|
||||
// Force sending data if possible, return number of forced bytes
|
||||
uint32_t tuh_cdc_write_flush(uint8_t idx);
|
||||
|
||||
// Clear the transmit FIFO
|
||||
bool tuh_cdc_write_clear(uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Read API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get the number of bytes available for reading
|
||||
uint32_t tuh_cdc_read_available(uint8_t idx);
|
||||
|
||||
// Read from cdc interface
|
||||
uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize);
|
||||
|
||||
// Get a byte from RX FIFO without removing it
|
||||
bool tuh_cdc_peek(uint8_t idx, uint8_t* ch);
|
||||
|
||||
// Clear the received FIFO
|
||||
bool tuh_cdc_read_clear (uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Endpoint (Request) API
|
||||
// Each Function will make a USB control transfer request to/from device
|
||||
// - If complete_cb is provided, the function will return immediately and invoke
|
||||
// the callback when request is complete.
|
||||
// - If complete_cb is NULL, the function will block until request is complete.
|
||||
// - In this case, user_data should be pointed to xfer_result_t to hold the transfer result.
|
||||
// - The function will return true if transfer is successful, false otherwise.
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Request to Set Control Line State: DTR (bit 0), RTS (bit 1)
|
||||
bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to set baudrate
|
||||
bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Set Line Coding (ACM only)
|
||||
// Should only use if you don't work with serial devices such as FTDI/CP210x
|
||||
bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Request to Get Line Coding (ACM only)
|
||||
// Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and
|
||||
// CFG_TUH_CDC_LINE_CODING_ON_ENUM is not defined
|
||||
// bool tuh_cdc_get_line_coding(uint8_t idx, cdc_line_coding_t* coding);
|
||||
|
||||
// Connect by set both DTR, RTS
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Disconnect by clear both DTR, RTS
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CDC APPLICATION CALLBACKS
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when a device with CDC interface is mounted
|
||||
// idx is index of cdc interface in the internal pool.
|
||||
TU_ATTR_WEAK extern void tuh_cdc_mount_cb(uint8_t idx);
|
||||
|
||||
// Invoked when a device with CDC interface is unmounted
|
||||
TU_ATTR_WEAK extern void tuh_cdc_umount_cb(uint8_t idx);
|
||||
|
||||
// Invoked when received new data
|
||||
TU_ATTR_WEAK extern void tuh_cdc_rx_cb(uint8_t idx);
|
||||
|
||||
// Invoked when a TX is complete and therefore space becomes available in TX buffer
|
||||
TU_ATTR_WEAK extern void tuh_cdc_tx_complete_cb(uint8_t idx);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void cdch_init (void);
|
||||
bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
void cdch_close (uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CDC_HOST_H_ */
|
@ -1,301 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup ClassDriver_CDC Communication Device Class (CDC)
|
||||
* \defgroup CDC_RNDIS Remote Network Driver Interface Specification (RNDIS)
|
||||
* @{
|
||||
* \defgroup CDC_RNDIS_Common Common Definitions
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_CDC_RNDIS_H_
|
||||
#define _TUSB_CDC_RNDIS_H_
|
||||
|
||||
#include "cdc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __CC_ARM
|
||||
#pragma diag_suppress 66 // Suppress Keil warnings #66-D: enumeration value is out of "int" range
|
||||
#endif
|
||||
|
||||
/// RNDIS Message Types
|
||||
typedef enum
|
||||
{
|
||||
RNDIS_MSG_PACKET = 0x00000001UL, ///< The host and device use this to send network data to one another.
|
||||
|
||||
RNDIS_MSG_INITIALIZE = 0x00000002UL, ///< Sent by the host to initialize the device.
|
||||
RNDIS_MSG_INITIALIZE_CMPLT = 0x80000002UL, ///< Device response to an initialize message.
|
||||
|
||||
RNDIS_MSG_HALT = 0x00000003UL, ///< Sent by the host to halt the device. This does not have a response. It is optional for the device to send this message to the host.
|
||||
|
||||
RNDIS_MSG_QUERY = 0x00000004UL, ///< Sent by the host to send a query OID.
|
||||
RNDIS_MSG_QUERY_CMPLT = 0x80000004UL, ///< Device response to a query OID.
|
||||
|
||||
RNDIS_MSG_SET = 0x00000005UL, ///< Sent by the host to send a set OID.
|
||||
RNDIS_MSG_SET_CMPLT = 0x80000005UL, ///< Device response to a set OID.
|
||||
|
||||
RNDIS_MSG_RESET = 0x00000006UL, ///< Sent by the host to perform a soft reset on the device.
|
||||
RNDIS_MSG_RESET_CMPLT = 0x80000006UL, ///< Device response to reset message.
|
||||
|
||||
RNDIS_MSG_INDICATE_STATUS = 0x00000007UL, ///< Sent by the device to indicate its status or an error when an unrecognized message is received.
|
||||
|
||||
RNDIS_MSG_KEEP_ALIVE = 0x00000008UL, ///< During idle periods, sent every few seconds by the host to check that the device is still responsive. It is optional for the device to send this message to check if the host is active.
|
||||
RNDIS_MSG_KEEP_ALIVE_CMPLT = 0x80000008UL ///< The device response to a keepalivemessage. The host can respond with this message to a keepalive message from the device when the device implements the optional KeepAliveTimer.
|
||||
}rndis_msg_type_t;
|
||||
|
||||
/// RNDIS Message Status Values
|
||||
typedef enum
|
||||
{
|
||||
RNDIS_STATUS_SUCCESS = 0x00000000UL, ///< Success
|
||||
RNDIS_STATUS_FAILURE = 0xC0000001UL, ///< Unspecified error
|
||||
RNDIS_STATUS_INVALID_DATA = 0xC0010015UL, ///< Invalid data error
|
||||
RNDIS_STATUS_NOT_SUPPORTED = 0xC00000BBUL, ///< Unsupported request error
|
||||
RNDIS_STATUS_MEDIA_CONNECT = 0x4001000BUL, ///< Device is connected to a network medium.
|
||||
RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000CUL ///< Device is disconnected from the medium.
|
||||
}rndis_msg_status_t;
|
||||
|
||||
#ifdef __CC_ARM
|
||||
#pragma diag_default 66 // return Keil 66 to normal severity
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MESSAGE STRUCTURE
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- Initialize -------------//
|
||||
/// \brief Initialize Message
|
||||
/// \details This message MUST be sent by the host to initialize the device.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message type, must be \ref RNDIS_MSG_INITIALIZE
|
||||
uint32_t length ; ///< Message length in bytes, must be 0x18
|
||||
uint32_t request_id ; ///< A 32-bit integer value, generated by the host, used to match the host's sent request to the response from the device.
|
||||
uint32_t major_version ; ///< The major version of the RNDIS Protocol implemented by the host.
|
||||
uint32_t minor_version ; ///< The minor version of the RNDIS Protocol implemented by the host
|
||||
uint32_t max_xfer_size ; ///< The maximum size, in bytes, of any single bus data transfer that the host expects to receive from the device.
|
||||
}rndis_msg_initialize_t;
|
||||
|
||||
/// \brief Initialize Complete Message
|
||||
/// \details This message MUST be sent by the device in response to an initialize message.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_INITIALIZE_CMPLT
|
||||
uint32_t length ; ///< Message length in bytes, must be 0x30
|
||||
uint32_t request_id ; ///< A 32-bit integer value from \a request_id field of the \ref rndis_msg_initialize_t to which this message is a response.
|
||||
uint32_t status ; ///< The initialization status of the device, has value from \ref rndis_msg_status_t
|
||||
uint32_t major_version ; ///< the highest-numbered RNDIS Protocol version supported by the device.
|
||||
uint32_t minor_version ; ///< the highest-numbered RNDIS Protocol version supported by the device.
|
||||
uint32_t device_flags ; ///< MUST be set to 0x000000010. Other values are reserved for future use.
|
||||
uint32_t medium ; ///< is 0x00 for RNDIS_MEDIUM_802_3
|
||||
uint32_t max_packet_per_xfer ; ///< The maximum number of concatenated \ref RNDIS_MSG_PACKET messages that the device can handle in a single bus transfer to it. This value MUST be at least 1.
|
||||
uint32_t max_xfer_size ; ///< The maximum size, in bytes, of any single bus data transfer that the device expects to receive from the host.
|
||||
uint32_t packet_alignment_factor ; ///< The byte alignment the device expects for each RNDIS message that is part of a multimessage transfer to it. The value is specified as an exponent of 2; for example, the host uses 2<SUP>{PacketAlignmentFactor}</SUP> as the alignment value.
|
||||
uint32_t reserved[2] ;
|
||||
} rndis_msg_initialize_cmplt_t;
|
||||
|
||||
//------------- Query -------------//
|
||||
/// \brief Query Message
|
||||
/// \details This message MUST be sent by the host to query an OID.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_QUERY
|
||||
uint32_t length ; ///< Message length in bytes, including the header and the \a oid_buffer
|
||||
uint32_t request_id ; ///< A 32-bit integer value, generated by the host, used to match the host's sent request to the response from the device.
|
||||
uint32_t oid ; ///< The integer value of the host operating system-defined identifier, for the parameter of the device being queried for.
|
||||
uint32_t buffer_length ; ///< The length, in bytes, of the input data required for the OID query. This MUST be set to 0 when there is no input data associated with the OID.
|
||||
uint32_t buffer_offset ; ///< The offset, in bytes, from the beginning of \a request_id field where the input data for the query is located in the message. This value MUST be set to 0 when there is no input data associated with the OID.
|
||||
uint32_t reserved ;
|
||||
uint8_t oid_buffer[] ; ///< Flexible array contains the input data supplied by the host, required for the OID query request processing by the device, as per the host NDIS specification.
|
||||
} rndis_msg_query_t, rndis_msg_set_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(rndis_msg_query_t) == 28, "Make sure flexible array member does not affect layout");
|
||||
|
||||
/// \brief Query Complete Message
|
||||
/// \details This message MUST be sent by the device in response to a query OID message.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_QUERY_CMPLT
|
||||
uint32_t length ; ///< Message length in bytes, including the header and the \a oid_buffer
|
||||
uint32_t request_id ; ///< A 32-bit integer value from \a request_id field of the \ref rndis_msg_query_t to which this message is a response.
|
||||
uint32_t status ; ///< The status of processing for the query request, has value from \ref rndis_msg_status_t.
|
||||
uint32_t buffer_length ; ///< The length, in bytes, of the data in the response to the query. This MUST be set to 0 when there is no OIDInputBuffer.
|
||||
uint32_t buffer_offset ; ///< The offset, in bytes, from the beginning of \a request_id field where the response data for the query is located in the message. This MUST be set to 0 when there is no \ref oid_buffer.
|
||||
uint8_t oid_buffer[] ; ///< Flexible array member contains the response data to the OID query request as specified by the host.
|
||||
} rndis_msg_query_cmplt_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(rndis_msg_query_cmplt_t) == 24, "Make sure flexible array member does not affect layout");
|
||||
|
||||
//------------- Reset -------------//
|
||||
/// \brief Reset Message
|
||||
/// \details This message MUST be sent by the host to perform a soft reset on the device.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_RESET
|
||||
uint32_t length ; ///< Message length in bytes, MUST be 0x06
|
||||
uint32_t reserved ;
|
||||
} rndis_msg_reset_t;
|
||||
|
||||
/// \brief Reset Complete Message
|
||||
/// \details This message MUST be sent by the device in response to a reset message.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_RESET_CMPLT
|
||||
uint32_t length ; ///< Message length in bytes, MUST be 0x10
|
||||
uint32_t status ; ///< The status of processing for the \ref rndis_msg_reset_t, has value from \ref rndis_msg_status_t.
|
||||
uint32_t addressing_reset ; ///< This field indicates whether the addressing information, which is the multicast address list or packet filter, has been lost during the reset operation. This MUST be set to 0x00000001 if the device requires that the host to resend addressing information or MUST be set to zero otherwise.
|
||||
} rndis_msg_reset_cmplt_t;
|
||||
|
||||
//typedef struct {
|
||||
// uint32_t type;
|
||||
// uint32_t length;
|
||||
// uint32_t status;
|
||||
// uint32_t buffer_length;
|
||||
// uint32_t buffer_offset;
|
||||
// uint32_t diagnostic_status; // optional
|
||||
// uint32_t diagnostic_error_offset; // optional
|
||||
// uint32_t status_buffer[0]; // optional
|
||||
//} rndis_msg_indicate_status_t;
|
||||
|
||||
/// \brief Keep Alive Message
|
||||
/// \details This message MUST be sent by the host to check that device is still responsive. It is optional for the device to send this message to check if the host is active
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type
|
||||
uint32_t length ; ///< Message length in bytes, MUST be 0x10
|
||||
uint32_t request_id ;
|
||||
} rndis_msg_keep_alive_t, rndis_msg_halt_t;
|
||||
|
||||
/// \brief Set Complete Message
|
||||
/// \brief This message MUST be sent in response to a the request message
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type
|
||||
uint32_t length ; ///< Message length in bytes, MUST be 0x10
|
||||
uint32_t request_id ; ///< must be the same as requesting message
|
||||
uint32_t status ; ///< The status of processing for the request message request by the device to which this message is the response.
|
||||
} rndis_msg_set_cmplt_t, rndis_msg_keep_alive_cmplt_t;
|
||||
|
||||
/// \brief Packet Data Message
|
||||
/// \brief This message MUST be used by the host and the device to send network data to one another.
|
||||
typedef struct {
|
||||
uint32_t type ; ///< Message Type, must be \ref RNDIS_MSG_PACKET
|
||||
uint32_t length ; ///< Message length in bytes, The total length of this RNDIS message including the header, payload, and padding.
|
||||
uint32_t data_offset ; ///< Specifies the offset, in bytes, from the start of this \a data_offset field of this message to the start of the data. This MUST be an integer multiple of 4.
|
||||
uint32_t data_length ; ///< Specifies the number of bytes in the payload of this message.
|
||||
uint32_t out_of_band_data_offet ; ///< Specifies the offset, in bytes, of the first out-of-band data record from the start of the DataOffset field in this message. MUST be an integer multiple of 4 when out-of-band data is present or set to 0 otherwise. When there are multiple out-ofband data records, each subsequent record MUST immediately follow the previous out-of-band data record.
|
||||
uint32_t out_of_band_data_length ; ///< Specifies, in bytes, the total length of the out-of-band data.
|
||||
uint32_t num_out_of_band_data_elements ; ///< Specifies the number of out-of-band records in this message.
|
||||
uint32_t per_packet_info_offset ; ///< Specifies the offset, in bytes, of the start of per-packet-info data record from the start of the \a data_offset field in this message. MUST be an integer multiple of 4 when per-packet-info data record is present or MUST be set to 0 otherwise. When there are multiple per-packet-info data records, each subsequent record MUST immediately follow the previous record.
|
||||
uint32_t per_packet_info_length ; ///< Specifies, in bytes, the total length of per-packetinformation contained in this message.
|
||||
uint32_t reserved[2] ;
|
||||
uint32_t payload[0] ; ///< Network data contained in this message.
|
||||
|
||||
// uint8_t padding[0]
|
||||
// Additional bytes of zeros added at the end of the message to comply with
|
||||
// the internal and external padding requirements. Internal padding SHOULD be as per the
|
||||
// specification of the out-of-band data record and per-packet-info data record. The external
|
||||
//padding size SHOULD be determined based on the PacketAlignmentFactor field specification
|
||||
//in REMOTE_NDIS_INITIALIZE_CMPLT message by the device, when multiple
|
||||
//REMOTE_NDIS_PACKET_MSG messages are bundled together in a single bus-native message.
|
||||
//In this case, all but the very last REMOTE_NDIS_PACKET_MSG MUST respect the
|
||||
//PacketAlignmentFactor field.
|
||||
|
||||
// rndis_msg_packet_t [0] : (optional) more packet if multiple packet per bus transaction is supported
|
||||
} rndis_msg_packet_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t size ; ///< Length, in bytes, of this header and appended data and padding. This value MUST be an integer multiple of 4.
|
||||
uint32_t type ; ///< MUST be as per host operating system specification.
|
||||
uint32_t offset ; ///< The byte offset from the beginning of this record to the beginning of data.
|
||||
uint32_t data[0] ; ///< Flexible array contains data
|
||||
} rndis_msg_out_of_band_data_t, rndis_msg_per_packet_info_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// NDIS Object ID
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/// NDIS Object ID
|
||||
typedef enum
|
||||
{
|
||||
//------------- General Required OIDs -------------//
|
||||
RNDIS_OID_GEN_SUPPORTED_LIST = 0x00010101, ///< List of supported OIDs
|
||||
RNDIS_OID_GEN_HARDWARE_STATUS = 0x00010102, ///< Hardware status
|
||||
RNDIS_OID_GEN_MEDIA_SUPPORTED = 0x00010103, ///< Media types supported (encoded)
|
||||
RNDIS_OID_GEN_MEDIA_IN_USE = 0x00010104, ///< Media types in use (encoded)
|
||||
RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105, ///<
|
||||
RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106, ///< Maximum frame size in bytes
|
||||
RNDIS_OID_GEN_LINK_SPEED = 0x00010107, ///< Link speed in units of 100 bps
|
||||
RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108, ///< Transmit buffer space
|
||||
RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109, ///< Receive buffer space
|
||||
RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010A, ///< Minimum amount of storage, in bytes, that a single packet occupies in the transmit buffer space of the NIC
|
||||
RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010B, ///< Amount of storage, in bytes, that a single packet occupies in the receive buffer space of the NIC
|
||||
RNDIS_OID_GEN_VENDOR_ID = 0x0001010C, ///< Vendor NIC code
|
||||
RNDIS_OID_GEN_VENDOR_DESCRIPTION = 0x0001010D, ///< Vendor network card description
|
||||
RNDIS_OID_GEN_CURRENT_PACKET_FILTER = 0x0001010E, ///< Current packet filter (encoded)
|
||||
RNDIS_OID_GEN_CURRENT_LOOKAHEAD = 0x0001010F, ///< Current lookahead size in bytes
|
||||
RNDIS_OID_GEN_DRIVER_VERSION = 0x00010110, ///< NDIS version number used by the driver
|
||||
RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111, ///< Maximum total packet length in bytes
|
||||
RNDIS_OID_GEN_PROTOCOL_OPTIONS = 0x00010112, ///< Optional protocol flags (encoded)
|
||||
RNDIS_OID_GEN_MAC_OPTIONS = 0x00010113, ///< Optional NIC flags (encoded)
|
||||
RNDIS_OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114, ///< Whether the NIC is connected to the network
|
||||
RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115, ///< The maximum number of send packets the driver can accept per call to its MiniportSendPacketsfunction
|
||||
|
||||
//------------- General Optional OIDs -------------//
|
||||
RNDIS_OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116, ///< Vendor-assigned version number of the driver
|
||||
RNDIS_OID_GEN_SUPPORTED_GUIDS = 0x00010117, ///< The custom GUIDs (Globally Unique Identifier) supported by the miniport driver
|
||||
RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118, ///< List of network-layer addresses associated with the binding between a transport and the driver
|
||||
RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119, ///< Size of packets' additional headers
|
||||
RNDIS_OID_GEN_MEDIA_CAPABILITIES = 0x00010201, ///<
|
||||
RNDIS_OID_GEN_PHYSICAL_MEDIUM = 0x00010202, ///< Physical media supported by the miniport driver (encoded)
|
||||
|
||||
//------------- 802.3 Objects (Ethernet) -------------//
|
||||
RNDIS_OID_802_3_PERMANENT_ADDRESS = 0x01010101, ///< Permanent station address
|
||||
RNDIS_OID_802_3_CURRENT_ADDRESS = 0x01010102, ///< Current station address
|
||||
RNDIS_OID_802_3_MULTICAST_LIST = 0x01010103, ///< Current multicast address list
|
||||
RNDIS_OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104, ///< Maximum size of multicast address list
|
||||
} rndis_oid_type_t;
|
||||
|
||||
/// RNDIS Packet Filter Bits \ref RNDIS_OID_GEN_CURRENT_PACKET_FILTER.
|
||||
typedef enum
|
||||
{
|
||||
RNDIS_PACKET_TYPE_DIRECTED = 0x00000001, ///< Directed packets. Directed packets contain a destination address equal to the station address of the NIC.
|
||||
RNDIS_PACKET_TYPE_MULTICAST = 0x00000002, ///< Multicast address packets sent to addresses in the multicast address list.
|
||||
RNDIS_PACKET_TYPE_ALL_MULTICAST = 0x00000004, ///< All multicast address packets, not just the ones enumerated in the multicast address list.
|
||||
RNDIS_PACKET_TYPE_BROADCAST = 0x00000008, ///< Broadcast packets.
|
||||
RNDIS_PACKET_TYPE_SOURCE_ROUTING = 0x00000010, ///< All source routing packets. If the protocol driver sets this bit, the NDIS library attempts to act as a source routing bridge.
|
||||
RNDIS_PACKET_TYPE_PROMISCUOUS = 0x00000020, ///< Specifies all packets regardless of whether VLAN filtering is enabled or not and whether the VLAN identifier matches or not.
|
||||
RNDIS_PACKET_TYPE_SMT = 0x00000040, ///< SMT packets that an FDDI NIC receives.
|
||||
RNDIS_PACKET_TYPE_ALL_LOCAL = 0x00000080, ///< All packets sent by installed protocols and all packets indicated by the NIC that is identified by a given NdisBindingHandle.
|
||||
RNDIS_PACKET_TYPE_GROUP = 0x00001000, ///< Packets sent to the current group address.
|
||||
RNDIS_PACKET_TYPE_ALL_FUNCTIONAL = 0x00002000, ///< All functional address packets, not just the ones in the current functional address.
|
||||
RNDIS_PACKET_TYPE_FUNCTIONAL = 0x00004000, ///< Functional address packets sent to addresses included in the current functional address.
|
||||
RNDIS_PACKET_TYPE_MAC_FRAME = 0x00008000, ///< NIC driver frames that a Token Ring NIC receives.
|
||||
RNDIS_PACKET_TYPE_NO_LOCAL = 0x00010000,
|
||||
} rndis_packet_filter_type_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CDC_RNDIS_H_ */
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
@ -1,289 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUH_ENABLED && CFG_TUH_CDC && CFG_TUH_CDC_RNDIS)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "common/tusb_common.h"
|
||||
#include "cdc_host.h"
|
||||
#include "cdc_rndis_host.h"
|
||||
|
||||
#if 0 // TODO remove subtask related macros later
|
||||
// Sub Task
|
||||
#define OSAL_SUBTASK_BEGIN
|
||||
#define OSAL_SUBTASK_END return TUSB_ERROR_NONE;
|
||||
|
||||
#define STASK_RETURN(_error) return _error;
|
||||
#define STASK_INVOKE(_subtask, _status) (_status) = _subtask
|
||||
#define STASK_ASSERT(_cond) TU_VERIFY(_cond, TUSB_ERROR_OSAL_TASK_FAILED)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
#define RNDIS_MSG_PAYLOAD_MAX (1024*4)
|
||||
|
||||
CFG_TUH_MEM_SECTION static uint8_t msg_notification[CFG_TUH_DEVICE_MAX][8];
|
||||
CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static uint8_t msg_payload[RNDIS_MSG_PAYLOAD_MAX];
|
||||
|
||||
static rndish_data_t rndish_data[CFG_TUH_DEVICE_MAX];
|
||||
|
||||
// TODO Microsoft requires message length for any get command must be at least 4096 bytes
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
static tusb_error_t rndis_body_subtask(void);
|
||||
static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
|
||||
uint8_t * p_mess, uint32_t mess_length,
|
||||
uint8_t *p_response );
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_error_t tusbh_cdc_rndis_get_mac_addr(uint8_t dev_addr, uint8_t mac_address[6])
|
||||
{
|
||||
TU_ASSERT( tusbh_cdc_rndis_is_mounted(dev_addr), TUSB_ERROR_CDCH_DEVICE_NOT_MOUNTED);
|
||||
TU_VERIFY( mac_address, TUSB_ERROR_INVALID_PARA);
|
||||
|
||||
memcpy(mac_address, rndish_data[dev_addr-1].mac_address, 6);
|
||||
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// IMPLEMENTATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// To enable the TASK_ASSERT style (quick return on false condition) in a real RTOS, a task must act as a wrapper
|
||||
// and is used mainly to call subtasks. Within a subtask return statement can be called freely, the task with
|
||||
// forever loop cannot have any return at all.
|
||||
OSAL_TASK_FUNCTION(cdch_rndis_task) (void* param;)
|
||||
{
|
||||
OSAL_TASK_BEGIN
|
||||
rndis_body_subtask();
|
||||
OSAL_TASK_END
|
||||
}
|
||||
|
||||
static tusb_error_t rndis_body_subtask(void)
|
||||
{
|
||||
static uint8_t relative_addr;
|
||||
|
||||
OSAL_SUBTASK_BEGIN
|
||||
|
||||
for (relative_addr = 0; relative_addr < CFG_TUH_DEVICE_MAX; relative_addr++)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
osal_task_delay(100);
|
||||
|
||||
OSAL_SUBTASK_END
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// RNDIS-CDC Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void rndish_init(void)
|
||||
{
|
||||
tu_memclr(rndish_data, sizeof(rndish_data_t)*CFG_TUH_DEVICE_MAX);
|
||||
|
||||
//------------- Task creation -------------//
|
||||
|
||||
//------------- semaphore creation for notification pipe -------------//
|
||||
for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
|
||||
{
|
||||
rndish_data[i].sem_notification_hdl = osal_semaphore_create( OSAL_SEM_REF(rndish_data[i].semaphore_notification) );
|
||||
}
|
||||
}
|
||||
|
||||
void rndish_close(uint8_t dev_addr)
|
||||
{
|
||||
osal_semaphore_reset( rndish_data[dev_addr-1].sem_notification_hdl );
|
||||
// tu_memclr(&rndish_data[dev_addr-1], sizeof(rndish_data_t)); TODO need to move semaphore & its handle out before memclr
|
||||
}
|
||||
|
||||
|
||||
static rndis_msg_initialize_t const msg_init =
|
||||
{
|
||||
.type = RNDIS_MSG_INITIALIZE,
|
||||
.length = sizeof(rndis_msg_initialize_t),
|
||||
.request_id = 1, // TODO should use some magic number
|
||||
.major_version = 1,
|
||||
.minor_version = 0,
|
||||
.max_xfer_size = 0x4000 // TODO mimic windows
|
||||
};
|
||||
|
||||
static rndis_msg_query_t const msg_query_permanent_addr =
|
||||
{
|
||||
.type = RNDIS_MSG_QUERY,
|
||||
.length = sizeof(rndis_msg_query_t)+6,
|
||||
.request_id = 1,
|
||||
.oid = RNDIS_OID_802_3_PERMANENT_ADDRESS,
|
||||
.buffer_length = 6,
|
||||
.buffer_offset = 20,
|
||||
};
|
||||
|
||||
static rndis_msg_set_t const msg_set_packet_filter =
|
||||
{
|
||||
.type = RNDIS_MSG_SET,
|
||||
.length = sizeof(rndis_msg_set_t)+4,
|
||||
.request_id = 1,
|
||||
.oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
|
||||
.buffer_length = 4,
|
||||
.buffer_offset = 20,
|
||||
};
|
||||
|
||||
tusb_error_t rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
|
||||
{
|
||||
tusb_error_t error;
|
||||
|
||||
OSAL_SUBTASK_BEGIN
|
||||
|
||||
//------------- Message Initialize -------------//
|
||||
memcpy(msg_payload, &msg_init, sizeof(rndis_msg_initialize_t));
|
||||
STASK_INVOKE(
|
||||
send_message_get_response_subtask( dev_addr, p_cdc,
|
||||
msg_payload, sizeof(rndis_msg_initialize_t),
|
||||
msg_payload),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
// TODO currently not support multiple data packets per xfer
|
||||
rndis_msg_initialize_cmplt_t * const p_init_cmpt = (rndis_msg_initialize_cmplt_t *) msg_payload;
|
||||
STASK_ASSERT(p_init_cmpt->type == RNDIS_MSG_INITIALIZE_CMPLT && p_init_cmpt->status == RNDIS_STATUS_SUCCESS &&
|
||||
p_init_cmpt->max_packet_per_xfer == 1 && p_init_cmpt->max_xfer_size <= RNDIS_MSG_PAYLOAD_MAX);
|
||||
rndish_data[dev_addr-1].max_xfer_size = p_init_cmpt->max_xfer_size;
|
||||
|
||||
//------------- Message Query 802.3 Permanent Address -------------//
|
||||
memcpy(msg_payload, &msg_query_permanent_addr, sizeof(rndis_msg_query_t));
|
||||
tu_memclr(msg_payload + sizeof(rndis_msg_query_t), 6); // 6 bytes for MAC address
|
||||
|
||||
STASK_INVOKE(
|
||||
send_message_get_response_subtask( dev_addr, p_cdc,
|
||||
msg_payload, sizeof(rndis_msg_query_t) + 6,
|
||||
msg_payload),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
rndis_msg_query_cmplt_t * const p_query_cmpt = (rndis_msg_query_cmplt_t *) msg_payload;
|
||||
STASK_ASSERT(p_query_cmpt->type == RNDIS_MSG_QUERY_CMPLT && p_query_cmpt->status == RNDIS_STATUS_SUCCESS);
|
||||
memcpy(rndish_data[dev_addr-1].mac_address, msg_payload + 8 + p_query_cmpt->buffer_offset, 6);
|
||||
|
||||
//------------- Set OID_GEN_CURRENT_PACKET_FILTER to (DIRECTED | MULTICAST | BROADCAST) -------------//
|
||||
memcpy(msg_payload, &msg_set_packet_filter, sizeof(rndis_msg_set_t));
|
||||
tu_memclr(msg_payload + sizeof(rndis_msg_set_t), 4); // 4 bytes for filter flags
|
||||
((rndis_msg_set_t*) msg_payload)->oid_buffer[0] = (RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_MULTICAST | RNDIS_PACKET_TYPE_BROADCAST);
|
||||
|
||||
STASK_INVOKE(
|
||||
send_message_get_response_subtask( dev_addr, p_cdc,
|
||||
msg_payload, sizeof(rndis_msg_set_t) + 4,
|
||||
msg_payload),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
rndis_msg_set_cmplt_t * const p_set_cmpt = (rndis_msg_set_cmplt_t *) msg_payload;
|
||||
STASK_ASSERT(p_set_cmpt->type == RNDIS_MSG_SET_CMPLT && p_set_cmpt->status == RNDIS_STATUS_SUCCESS);
|
||||
|
||||
tusbh_cdc_rndis_mounted_cb(dev_addr);
|
||||
|
||||
OSAL_SUBTASK_END
|
||||
}
|
||||
|
||||
void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes)
|
||||
{
|
||||
if ( pipehandle_is_equal(pipe_hdl, p_cdc->pipe_notification) )
|
||||
{
|
||||
osal_semaphore_post( rndish_data[pipe_hdl.dev_addr-1].sem_notification_hdl );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL & HELPER
|
||||
//--------------------------------------------------------------------+
|
||||
static tusb_error_t send_message_get_response_subtask( uint8_t dev_addr, cdch_data_t *p_cdc,
|
||||
uint8_t * p_mess, uint32_t mess_length,
|
||||
uint8_t *p_response)
|
||||
{
|
||||
tusb_error_t error;
|
||||
|
||||
OSAL_SUBTASK_BEGIN
|
||||
|
||||
//------------- Send RNDIS Control Message -------------//
|
||||
STASK_INVOKE(
|
||||
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
|
||||
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND, 0, p_cdc->interface_number,
|
||||
mess_length, p_mess),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
//------------- waiting for Response Available notification -------------//
|
||||
(void) usbh_edpt_xfer(p_cdc->pipe_notification, msg_notification[dev_addr-1], 8);
|
||||
osal_semaphore_wait(rndish_data[dev_addr-1].sem_notification_hdl, OSAL_TIMEOUT_NORMAL, &error);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
STASK_ASSERT(msg_notification[dev_addr-1][0] == 1);
|
||||
|
||||
//------------- Get RNDIS Message Initialize Complete -------------//
|
||||
STASK_INVOKE(
|
||||
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
|
||||
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE, 0, p_cdc->interface_number,
|
||||
RNDIS_MSG_PAYLOAD_MAX, p_response),
|
||||
error
|
||||
);
|
||||
if ( TUSB_ERROR_NONE != error ) STASK_RETURN(error);
|
||||
|
||||
OSAL_SUBTASK_END
|
||||
}
|
||||
|
||||
//static tusb_error_t send_process_msg_initialize_subtask(uint8_t dev_addr, cdch_data_t *p_cdc)
|
||||
//{
|
||||
// tusb_error_t error;
|
||||
//
|
||||
// OSAL_SUBTASK_BEGIN
|
||||
//
|
||||
// *((rndis_msg_initialize_t*) msg_payload) = (rndis_msg_initialize_t)
|
||||
// {
|
||||
// .type = RNDIS_MSG_INITIALIZE,
|
||||
// .length = sizeof(rndis_msg_initialize_t),
|
||||
// .request_id = 1, // TODO should use some magic number
|
||||
// .major_version = 1,
|
||||
// .minor_version = 0,
|
||||
// .max_xfer_size = 0x4000 // TODO mimic windows
|
||||
// };
|
||||
//
|
||||
//
|
||||
//
|
||||
// OSAL_SUBTASK_END
|
||||
//}
|
||||
#endif
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup CDC_RNDIS
|
||||
* \defgroup CDC_RNSID_Host Host
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_CDC_RNDIS_HOST_H_
|
||||
#define _TUSB_CDC_RNDIS_HOST_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "host/usbh.h"
|
||||
#include "cdc_rndis.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL RNDIS-CDC Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
OSAL_SEM_DEF(semaphore_notification);
|
||||
osal_semaphore_handle_t sem_notification_hdl; // used to wait on notification pipe
|
||||
uint32_t max_xfer_size; // got from device's msg initialize complete
|
||||
uint8_t mac_address[6];
|
||||
}rndish_data_t;
|
||||
|
||||
void rndish_init(void);
|
||||
bool rndish_open_subtask(uint8_t dev_addr, cdch_data_t *p_cdc);
|
||||
void rndish_xfer_isr(cdch_data_t *p_cdc, pipe_handle_t pipe_hdl, xfer_result_t event, uint32_t xferred_bytes);
|
||||
void rndish_close(uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CDC_RNDIS_HOST_H_ */
|
||||
|
||||
/** @} */
|
Binary file not shown.
@ -300,6 +300,19 @@ typedef struct TU_ATTR_PACKED
|
||||
int8_t pan; // using AC Pan
|
||||
} hid_mouse_report_t;
|
||||
|
||||
|
||||
// Absolute Mouse: same as the Standard (relative) Mouse Report but
|
||||
// with int16_t instead of int8_t for X and Y coordinates.
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */
|
||||
int16_t x; /**< Current x position of the mouse. */
|
||||
int16_t y; /**< Current y position of the mouse. */
|
||||
int8_t wheel; /**< Current delta wheel movement on the mouse. */
|
||||
int8_t pan; // using AC Pan
|
||||
} hid_abs_mouse_report_t;
|
||||
|
||||
|
||||
/// Standard Mouse Buttons Bitmap
|
||||
typedef enum
|
||||
{
|
||||
@ -515,7 +528,54 @@ typedef enum
|
||||
#define HID_KEY_CLEAR_AGAIN 0xA2
|
||||
#define HID_KEY_CRSEL_PROPS 0xA3
|
||||
#define HID_KEY_EXSEL 0xA4
|
||||
// RESERVED 0xA5-DF
|
||||
// RESERVED 0xA5-AF
|
||||
#define HID_KEY_KEYPAD_00 0xB0
|
||||
#define HID_KEY_KEYPAD_000 0xB1
|
||||
#define HID_KEY_THOUSANDS_SEPARATOR 0xB2
|
||||
#define HID_KEY_DECIMAL_SEPARATOR 0xB3
|
||||
#define HID_KEY_CURRENCY_UNIT 0xB4
|
||||
#define HID_KEY_CURRENCY_SUBUNIT 0xB5
|
||||
#define HID_KEY_KEYPAD_LEFT_PARENTHESIS 0xB6
|
||||
#define HID_KEY_KEYPAD_RIGHT_PARENTHESIS 0xB7
|
||||
#define HID_KEY_KEYPAD_LEFT_BRACE 0xB8
|
||||
#define HID_KEY_KEYPAD_RIGHT_BRACE 0xB9
|
||||
#define HID_KEY_KEYPAD_TAB 0xBA
|
||||
#define HID_KEY_KEYPAD_BACKSPACE 0xBB
|
||||
#define HID_KEY_KEYPAD_A 0xBC
|
||||
#define HID_KEY_KEYPAD_B 0xBD
|
||||
#define HID_KEY_KEYPAD_C 0xBE
|
||||
#define HID_KEY_KEYPAD_D 0xBF
|
||||
#define HID_KEY_KEYPAD_E 0xC0
|
||||
#define HID_KEY_KEYPAD_F 0xC1
|
||||
#define HID_KEY_KEYPAD_XOR 0xC2
|
||||
#define HID_KEY_KEYPAD_CARET 0xC3
|
||||
#define HID_KEY_KEYPAD_PERCENT 0xC4
|
||||
#define HID_KEY_KEYPAD_LESS_THAN 0xC5
|
||||
#define HID_KEY_KEYPAD_GREATER_THAN 0xC6
|
||||
#define HID_KEY_KEYPAD_AMPERSAND 0xC7
|
||||
#define HID_KEY_KEYPAD_DOUBLE_AMPERSAND 0xC8
|
||||
#define HID_KEY_KEYPAD_VERTICAL_BAR 0xC9
|
||||
#define HID_KEY_KEYPAD_DOUBLE_VERTICAL_BAR 0xCA
|
||||
#define HID_KEY_KEYPAD_COLON 0xCB
|
||||
#define HID_KEY_KEYPAD_HASH 0xCC
|
||||
#define HID_KEY_KEYPAD_SPACE 0xCD
|
||||
#define HID_KEY_KEYPAD_AT 0xCE
|
||||
#define HID_KEY_KEYPAD_EXCLAMATION 0xCF
|
||||
#define HID_KEY_KEYPAD_MEMORY_STORE 0xD0
|
||||
#define HID_KEY_KEYPAD_MEMORY_RECALL 0xD1
|
||||
#define HID_KEY_KEYPAD_MEMORY_CLEAR 0xD2
|
||||
#define HID_KEY_KEYPAD_MEMORY_ADD 0xD3
|
||||
#define HID_KEY_KEYPAD_MEMORY_SUBTRACT 0xD4
|
||||
#define HID_KEY_KEYPAD_MEMORY_MULTIPLY 0xD5
|
||||
#define HID_KEY_KEYPAD_MEMORY_DIVIDE 0xD6
|
||||
#define HID_KEY_KEYPAD_PLUS_MINUS 0xD7
|
||||
#define HID_KEY_KEYPAD_CLEAR 0xD8
|
||||
#define HID_KEY_KEYPAD_CLEAR_ENTRY 0xD9
|
||||
#define HID_KEY_KEYPAD_BINARY 0xDA
|
||||
#define HID_KEY_KEYPAD_OCTAL 0xDB
|
||||
#define HID_KEY_KEYPAD_DECIMAL_2 0xDC
|
||||
#define HID_KEY_KEYPAD_HEXADECIMAL 0xDD
|
||||
// RESERVED 0xDE-DF
|
||||
#define HID_KEY_CONTROL_LEFT 0xE0
|
||||
#define HID_KEY_SHIFT_LEFT 0xE1
|
||||
#define HID_KEY_ALT_LEFT 0xE2
|
||||
@ -701,6 +761,7 @@ enum {
|
||||
HID_USAGE_PAGE_UNICODE = 0x10,
|
||||
HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14,
|
||||
HID_USAGE_PAGE_MEDICAL = 0x40,
|
||||
HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION = 0x59,
|
||||
HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83
|
||||
HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
|
||||
HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
|
||||
@ -788,8 +849,7 @@ enum {
|
||||
|
||||
/// HID Usage Table: Consumer Page (0x0C)
|
||||
/// Only contains controls that supported by Windows (whole list is too long)
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
// Generic Control
|
||||
HID_USAGE_CONSUMER_CONTROL = 0x0001,
|
||||
|
||||
@ -845,9 +905,45 @@ enum
|
||||
HID_USAGE_CONSUMER_AC_PAN = 0x0238,
|
||||
};
|
||||
|
||||
/// HID Usage Table - Lighting And Illumination Page (0x59)
|
||||
enum {
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY = 0x01,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT = 0x02,
|
||||
HID_USAGE_LIGHTING_LAMP_COUNT = 0x03,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS = 0x04,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS = 0x05,
|
||||
HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS = 0x06,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_KIND = 0x07,
|
||||
HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS = 0x08,
|
||||
HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT = 0x20,
|
||||
HID_USAGE_LIGHTING_LAMP_ID = 0x21,
|
||||
HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT = 0x22,
|
||||
HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS = 0x23,
|
||||
HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS = 0x24,
|
||||
HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS = 0x25,
|
||||
HID_USAGE_LIGHTING_LAMP_PURPOSES = 0x26,
|
||||
HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS = 0x27,
|
||||
HID_USAGE_LIGHTING_RED_LEVEL_COUNT = 0x28,
|
||||
HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT = 0x29,
|
||||
HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT = 0x2A,
|
||||
HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT = 0x2B,
|
||||
HID_USAGE_LIGHTING_IS_PROGRAMMABLE = 0x2C,
|
||||
HID_USAGE_LIGHTING_INPUT_BINDING = 0x2D,
|
||||
HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT = 0x50,
|
||||
HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL = 0x51,
|
||||
HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL = 0x52,
|
||||
HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL = 0x53,
|
||||
HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL = 0x54,
|
||||
HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS = 0x55,
|
||||
HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT = 0x60,
|
||||
HID_USAGE_LIGHTING_LAMP_ID_START = 0x61,
|
||||
HID_USAGE_LIGHTING_LAMP_ID_END = 0x62,
|
||||
HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT = 0x70,
|
||||
HID_USAGE_LIGHTING_AUTONOMOUS_MODE = 0x71,
|
||||
};
|
||||
|
||||
/// HID Usage Table: FIDO Alliance Page (0xF1D0)
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
HID_USAGE_FIDO_U2FHID = 0x01, // U2FHID usage for top-level collection
|
||||
HID_USAGE_FIDO_DATA_IN = 0x20, // Raw IN data report
|
||||
HID_USAGE_FIDO_DATA_OUT = 0x21 // Raw OUT data report
|
||||
|
@ -39,23 +39,23 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out; // optional Out endpoint
|
||||
uint8_t itf_protocol; // Boot mouse or keyboard
|
||||
|
||||
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||
uint8_t idle_rate; // up to application to handle idle rate
|
||||
uint16_t report_desc_len;
|
||||
CFG_TUSB_MEM_ALIGN uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||
CFG_TUSB_MEM_ALIGN uint8_t idle_rate; // up to application to handle idle rate
|
||||
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
|
||||
CFG_TUSB_MEM_ALIGN uint8_t ctrl_buf[CFG_TUD_HID_EP_BUFSIZE];
|
||||
|
||||
// TODO save hid descriptor since host can specifically request this after enumeration
|
||||
// Note: HID descriptor may be not available from application after enumeration
|
||||
tusb_hid_descriptor_hid_t const * hid_descriptor;
|
||||
tusb_hid_descriptor_hid_t const *hid_descriptor;
|
||||
} hidd_interface_t;
|
||||
|
||||
CFG_TUD_MEM_SECTION tu_static hidd_interface_t _hidd_itf[CFG_TUD_HID];
|
||||
@ -63,9 +63,9 @@ CFG_TUD_MEM_SECTION tu_static hidd_interface_t _hidd_itf[CFG_TUD_HID];
|
||||
/*------------- Helpers -------------*/
|
||||
static inline uint8_t get_index_by_itfnum(uint8_t itf_num)
|
||||
{
|
||||
for (uint8_t i=0; i < CFG_TUD_HID; i++ )
|
||||
{
|
||||
if ( itf_num == _hidd_itf[i].itf_num ) return i;
|
||||
for (uint8_t i = 0; i < CFG_TUD_HID; i++) {
|
||||
if (itf_num == _hidd_itf[i].itf_num)
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
@ -81,37 +81,29 @@ bool tud_hid_n_ready(uint8_t instance)
|
||||
return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(rhport, ep_in);
|
||||
}
|
||||
|
||||
bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_t len)
|
||||
bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, uint16_t len)
|
||||
{
|
||||
uint8_t const rhport = 0;
|
||||
hidd_interface_t * p_hid = &_hidd_itf[instance];
|
||||
hidd_interface_t *p_hid = &_hidd_itf[instance];
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
|
||||
TU_VERIFY(usbd_edpt_claim(rhport, p_hid->ep_in));
|
||||
|
||||
// prepare data
|
||||
if (report_id)
|
||||
{
|
||||
if (report_id) {
|
||||
p_hid->epin_buf[0] = report_id;
|
||||
TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf+1, CFG_TUD_HID_EP_BUFSIZE-1, report, len));
|
||||
TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf + 1, CFG_TUD_HID_EP_BUFSIZE - 1, report, len));
|
||||
len++;
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf, CFG_TUD_HID_EP_BUFSIZE, report, len));
|
||||
}
|
||||
|
||||
return usbd_edpt_xfer(rhport, p_hid->ep_in, p_hid->epin_buf, len);
|
||||
}
|
||||
|
||||
uint8_t tud_hid_n_interface_protocol(uint8_t instance)
|
||||
{
|
||||
return _hidd_itf[instance].itf_protocol;
|
||||
}
|
||||
uint8_t tud_hid_n_interface_protocol(uint8_t instance) { return _hidd_itf[instance].itf_protocol; }
|
||||
|
||||
uint8_t tud_hid_n_get_protocol(uint8_t instance)
|
||||
{
|
||||
return _hidd_itf[instance].protocol_mode;
|
||||
}
|
||||
uint8_t tud_hid_n_get_protocol(uint8_t instance) { return _hidd_itf[instance].protocol_mode; }
|
||||
|
||||
bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
|
||||
{
|
||||
@ -120,22 +112,18 @@ bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modi
|
||||
report.modifier = modifier;
|
||||
report.reserved = 0;
|
||||
|
||||
if ( keycode )
|
||||
{
|
||||
if (keycode) {
|
||||
memcpy(report.keycode, keycode, sizeof(report.keycode));
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
tu_memclr(report.keycode, 6);
|
||||
}
|
||||
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id,
|
||||
uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
|
||||
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
|
||||
{
|
||||
hid_mouse_report_t report =
|
||||
{
|
||||
hid_mouse_report_t report = {
|
||||
.buttons = buttons,
|
||||
.x = x,
|
||||
.y = y,
|
||||
@ -146,10 +134,21 @@ bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id,
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
|
||||
int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
|
||||
hid_gamepad_report_t report =
|
||||
{
|
||||
bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal)
|
||||
{
|
||||
hid_abs_mouse_report_t report = {
|
||||
.buttons = buttons,
|
||||
.x = x,
|
||||
.y = y,
|
||||
.wheel = vertical,
|
||||
.pan = horizontal
|
||||
};
|
||||
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||
}
|
||||
|
||||
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons)
|
||||
{
|
||||
hid_gamepad_report_t report = {
|
||||
.x = x,
|
||||
.y = y,
|
||||
.z = z,
|
||||
@ -171,59 +170,59 @@ void hidd_init(void)
|
||||
hidd_reset(0);
|
||||
}
|
||||
|
||||
bool hidd_deinit(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void hidd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
(void)rhport;
|
||||
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
|
||||
}
|
||||
|
||||
uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
|
||||
{
|
||||
uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||
{
|
||||
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
|
||||
|
||||
// len = interface + hid + n*endpoints
|
||||
uint16_t const drv_len =
|
||||
(uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
|
||||
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
uint16_t const drv_len = (uint16_t)(sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_ASSERT(max_len >= drv_len, 0);
|
||||
|
||||
// Find available interface
|
||||
hidd_interface_t * p_hid = NULL;
|
||||
hidd_interface_t *p_hid = NULL;
|
||||
uint8_t hid_id;
|
||||
for(hid_id=0; hid_id<CFG_TUD_HID; hid_id++)
|
||||
{
|
||||
if ( _hidd_itf[hid_id].ep_in == 0 )
|
||||
{
|
||||
for (hid_id = 0; hid_id < CFG_TUD_HID; hid_id++) {
|
||||
if (_hidd_itf[hid_id].ep_in == 0) {
|
||||
p_hid = &_hidd_itf[hid_id];
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_ASSERT(p_hid, 0);
|
||||
|
||||
uint8_t const *p_desc = (uint8_t const *) desc_itf;
|
||||
uint8_t const *p_desc = (uint8_t const *)desc_itf;
|
||||
|
||||
//------------- HID descriptor -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
TU_ASSERT(HID_DESC_TYPE_HID == tu_desc_type(p_desc), 0);
|
||||
p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
|
||||
p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *)p_desc;
|
||||
|
||||
//------------- Endpoint Descriptor -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0);
|
||||
|
||||
if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
|
||||
if (desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT)
|
||||
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
|
||||
|
||||
p_hid->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
|
||||
p_hid->itf_num = desc_itf->bInterfaceNumber;
|
||||
|
||||
// Use offsetof to avoid pointer to the odd/misaligned address
|
||||
p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
|
||||
p_hid->report_desc_len = tu_unaligned_read16((uint8_t const *)p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
|
||||
|
||||
// Prepare for output endpoint
|
||||
if (p_hid->ep_out)
|
||||
{
|
||||
if ( !usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)) )
|
||||
{
|
||||
if (p_hid->ep_out) {
|
||||
if (!usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))) {
|
||||
TU_LOG_FAILED();
|
||||
TU_BREAKPOINT();
|
||||
}
|
||||
@ -235,104 +234,86 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint1
|
||||
// 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 hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||
|
||||
uint8_t const hid_itf = get_index_by_itfnum((uint8_t) request->wIndex);
|
||||
uint8_t const hid_itf = get_index_by_itfnum((uint8_t)request->wIndex);
|
||||
TU_VERIFY(hid_itf < CFG_TUD_HID);
|
||||
|
||||
hidd_interface_t* p_hid = &_hidd_itf[hid_itf];
|
||||
hidd_interface_t *p_hid = &_hidd_itf[hid_itf];
|
||||
|
||||
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
|
||||
{
|
||||
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
|
||||
//------------- STD Request -------------//
|
||||
if ( stage == CONTROL_STAGE_SETUP )
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
uint8_t const desc_type = tu_u16_high(request->wValue);
|
||||
//uint8_t const desc_index = tu_u16_low (request->wValue);
|
||||
// uint8_t const desc_index = tu_u16_low (request->wValue);
|
||||
|
||||
if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
|
||||
{
|
||||
if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID) {
|
||||
TU_VERIFY(p_hid->hid_descriptor);
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void*)(uintptr_t) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
|
||||
}
|
||||
else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
|
||||
{
|
||||
uint8_t const * desc_report = tud_hid_descriptor_report_cb(hid_itf);
|
||||
tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_report, p_hid->report_desc_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
TU_VERIFY(tud_control_xfer(rhport, request, (void *)(uintptr_t)p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
|
||||
} else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT) {
|
||||
uint8_t const *desc_report = tud_hid_descriptor_report_cb(hid_itf);
|
||||
tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_report, p_hid->report_desc_len);
|
||||
} else {
|
||||
return false; // stall unsupported request
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
|
||||
{
|
||||
} else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
|
||||
//------------- Class Specific Request -------------//
|
||||
switch( request->bRequest )
|
||||
{
|
||||
switch (request->bRequest) {
|
||||
case HID_REQ_CONTROL_GET_REPORT:
|
||||
if ( stage == CONTROL_STAGE_SETUP )
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
uint8_t const report_type = tu_u16_high(request->wValue);
|
||||
uint8_t const report_id = tu_u16_low(request->wValue);
|
||||
|
||||
uint8_t* report_buf = p_hid->epin_buf;
|
||||
uint8_t *report_buf = p_hid->ctrl_buf;
|
||||
uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
|
||||
|
||||
uint16_t xferlen = 0;
|
||||
|
||||
// If host request a specific Report ID, add ID to as 1 byte of response
|
||||
if ( (report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1) )
|
||||
{
|
||||
if ((report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1)) {
|
||||
*report_buf++ = report_id;
|
||||
req_len--;
|
||||
|
||||
xferlen++;
|
||||
}
|
||||
|
||||
xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
|
||||
TU_ASSERT( xferlen > 0 );
|
||||
xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t)report_type, report_buf, req_len);
|
||||
TU_ASSERT(xferlen > 0);
|
||||
|
||||
tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
|
||||
tud_control_xfer(rhport, request, p_hid->ctrl_buf, xferlen);
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_SET_REPORT:
|
||||
if ( stage == CONTROL_STAGE_SETUP )
|
||||
{
|
||||
TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
|
||||
tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
|
||||
}
|
||||
else if ( stage == CONTROL_STAGE_ACK )
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
TU_VERIFY(request->wLength <= sizeof(p_hid->ctrl_buf));
|
||||
tud_control_xfer(rhport, request, p_hid->ctrl_buf, request->wLength);
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
uint8_t const report_type = tu_u16_high(request->wValue);
|
||||
uint8_t const report_id = tu_u16_low(request->wValue);
|
||||
|
||||
uint8_t const* report_buf = p_hid->epout_buf;
|
||||
uint8_t const *report_buf = p_hid->ctrl_buf;
|
||||
uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
|
||||
|
||||
// If host request a specific Report ID, extract report ID in buffer before invoking callback
|
||||
if ( (report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0]) )
|
||||
{
|
||||
if ((report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0])) {
|
||||
report_buf++;
|
||||
report_len--;
|
||||
}
|
||||
|
||||
tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, report_len);
|
||||
tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t)report_type, report_buf, report_len);
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_SET_IDLE:
|
||||
if ( stage == CONTROL_STAGE_SETUP )
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
p_hid->idle_rate = tu_u16_high(request->wValue);
|
||||
if ( tud_hid_set_idle_cb )
|
||||
{
|
||||
if (tud_hid_set_idle_cb) {
|
||||
// stall request if callback return false
|
||||
TU_VERIFY( tud_hid_set_idle_cb( hid_itf, p_hid->idle_rate) );
|
||||
TU_VERIFY(tud_hid_set_idle_cb(hid_itf, p_hid->idle_rate));
|
||||
}
|
||||
|
||||
tud_control_status(rhport, request);
|
||||
@ -340,39 +321,33 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_GET_IDLE:
|
||||
if ( stage == CONTROL_STAGE_SETUP )
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
// TODO idle rate of report
|
||||
tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_GET_PROTOCOL:
|
||||
if ( stage == CONTROL_STAGE_SETUP )
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_REQ_CONTROL_SET_PROTOCOL:
|
||||
if ( stage == CONTROL_STAGE_SETUP )
|
||||
{
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
tud_control_status(rhport, request);
|
||||
}
|
||||
else if ( stage == CONTROL_STAGE_ACK )
|
||||
{
|
||||
p_hid->protocol_mode = (uint8_t) request->wValue;
|
||||
if (tud_hid_set_protocol_cb)
|
||||
{
|
||||
} else if (stage == CONTROL_STAGE_ACK) {
|
||||
p_hid->protocol_mode = (uint8_t)request->wValue;
|
||||
if (tud_hid_set_protocol_cb) {
|
||||
tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: return false; // stall unsupported request
|
||||
default:
|
||||
return false; // stall unsupported request
|
||||
}
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
return false; // stall unsupported request
|
||||
}
|
||||
|
||||
@ -381,31 +356,43 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
|
||||
|
||||
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
(void) result;
|
||||
(void)result;
|
||||
|
||||
uint8_t instance = 0;
|
||||
hidd_interface_t * p_hid = _hidd_itf;
|
||||
hidd_interface_t *p_hid = _hidd_itf;
|
||||
|
||||
// Identify which interface to use
|
||||
for (instance = 0; instance < CFG_TUD_HID; instance++)
|
||||
{
|
||||
for (instance = 0; instance < CFG_TUD_HID; instance++) {
|
||||
p_hid = &_hidd_itf[instance];
|
||||
if ( (ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in) ) break;
|
||||
if ((ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in))
|
||||
break;
|
||||
}
|
||||
TU_ASSERT(instance < CFG_TUD_HID);
|
||||
|
||||
// Check if there was a problem
|
||||
if (XFER_RESULT_SUCCESS != result) { // Inform application about the issue
|
||||
if (tud_hid_report_fail_cb) {
|
||||
tud_hid_report_fail_cb(instance, ep_addr, (uint16_t)xferred_bytes);
|
||||
}
|
||||
|
||||
// Allow a new transfer to be received if issue happened on an OUT endpoint
|
||||
if (ep_addr == p_hid->ep_out) {
|
||||
// Prepare the OUT endpoint to be able to receive a new transfer
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sent report successfully
|
||||
if (ep_addr == p_hid->ep_in)
|
||||
{
|
||||
if (tud_hid_report_complete_cb)
|
||||
{
|
||||
tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint16_t) xferred_bytes);
|
||||
if (ep_addr == p_hid->ep_in) {
|
||||
if (tud_hid_report_complete_cb) {
|
||||
tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint16_t)xferred_bytes);
|
||||
}
|
||||
}
|
||||
// Received report
|
||||
else if (ep_addr == p_hid->ep_out)
|
||||
{
|
||||
tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, (uint16_t) xferred_bytes);
|
||||
// Received report successfully
|
||||
else if (ep_addr == p_hid->ep_out) {
|
||||
tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (uint16_t)xferred_bytes);
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,16 @@ bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modi
|
||||
// use template layout report as defined by hid_mouse_report_t
|
||||
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
|
||||
|
||||
// ABSOLUTE MOUSE: convenient helper to send absolute mouse report if application
|
||||
// use template layout report as defined by hid_abs_mouse_report_t
|
||||
bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal);
|
||||
|
||||
|
||||
static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal)
|
||||
{
|
||||
return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
|
||||
}
|
||||
|
||||
// Gamepad: convenient helper to send gamepad report if application
|
||||
// use template layout report TUD_HID_REPORT_DESC_GAMEPAD
|
||||
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
|
||||
@ -118,6 +128,8 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
|
||||
// Note: For composite reports, report[0] is report ID
|
||||
TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len);
|
||||
|
||||
// Invoked when a transfer wasn't successful
|
||||
TU_ATTR_WEAK void tud_hid_report_fail_cb(uint8_t instance, uint8_t ep_addr, uint16_t len);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Inline Functions
|
||||
@ -266,6 +278,55 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Absolute Mouse Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||
/* Report ID if any */\
|
||||
__VA_ARGS__ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
|
||||
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||
HID_USAGE_MIN ( 1 ) ,\
|
||||
HID_USAGE_MAX ( 5 ) ,\
|
||||
HID_LOGICAL_MIN ( 0 ) ,\
|
||||
HID_LOGICAL_MAX ( 1 ) ,\
|
||||
/* Left, Right, Middle, Backward, Forward buttons */ \
|
||||
HID_REPORT_COUNT( 5 ) ,\
|
||||
HID_REPORT_SIZE ( 1 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* 3 bit padding */ \
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 3 ) ,\
|
||||
HID_INPUT ( HID_CONSTANT ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||
/* X, Y absolute position [0, 32767] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||
HID_LOGICAL_MIN ( 0x00 ) ,\
|
||||
HID_LOGICAL_MAX_N( 0x7FFF, 2 ) ,\
|
||||
HID_REPORT_SIZE ( 16 ) ,\
|
||||
HID_REPORT_COUNT ( 2 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||
/* Vertical wheel scroll [-127, 127] */ \
|
||||
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
|
||||
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||
HID_REPORT_COUNT( 1 ) ,\
|
||||
HID_REPORT_SIZE ( 8 ) ,\
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
|
||||
/* Horizontal wheel scroll [-127, 127] */ \
|
||||
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
|
||||
HID_LOGICAL_MIN ( 0x81 ), \
|
||||
HID_LOGICAL_MAX ( 0x7f ), \
|
||||
HID_REPORT_COUNT( 1 ), \
|
||||
HID_REPORT_SIZE ( 8 ), \
|
||||
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
|
||||
HID_COLLECTION_END , \
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// Consumer Control Report Descriptor Template
|
||||
#define TUD_HID_REPORT_DESC_CONSUMER(...) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\
|
||||
@ -402,15 +463,189 @@ static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
|
||||
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
// HID Lighting and Illumination Report Descriptor Template
|
||||
// - 1st parameter is report id (required)
|
||||
// Creates 6 report ids for lighting HID usages in the following order:
|
||||
// report_id+0: HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT
|
||||
// report_id+1: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT
|
||||
// report_id+2: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT
|
||||
// report_id+3: HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT
|
||||
// report_id+4: HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT
|
||||
// report_id+5: HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT
|
||||
#define TUD_HID_REPORT_DESC_LIGHTING(report_id) \
|
||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
|
||||
/* Lamp Array Attributes Report */ \
|
||||
HID_REPORT_ID (report_id ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_COUNT ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_KIND ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 2147483647, 3 ),\
|
||||
HID_REPORT_SIZE ( 32 ),\
|
||||
HID_REPORT_COUNT ( 5 ),\
|
||||
HID_FEATURE ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Attributes Request Report */ \
|
||||
HID_REPORT_ID ( report_id + 1 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Attributes Response Report */ \
|
||||
HID_REPORT_ID ( report_id + 2 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_PURPOSES ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 2147483647, 3 ),\
|
||||
HID_REPORT_SIZE ( 32 ),\
|
||||
HID_REPORT_COUNT ( 5 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_LEVEL_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_IS_PROGRAMMABLE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INPUT_BINDING ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 255, 2 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 6 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Multi-Update Report */ \
|
||||
HID_REPORT_ID ( report_id + 3 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_COUNT ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX ( 8 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 2 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 8 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 255, 2 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 32 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Range Update Report */ \
|
||||
HID_REPORT_ID ( report_id + 4 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX ( 8 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID_START ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ID_END ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 65535, 3 ),\
|
||||
HID_REPORT_SIZE ( 16 ),\
|
||||
HID_REPORT_COUNT ( 2 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX_N ( 255, 2 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 4 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
/* Lamp Array Control Report */ \
|
||||
HID_REPORT_ID ( report_id + 5 ) \
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT ),\
|
||||
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),\
|
||||
HID_USAGE ( HID_USAGE_LIGHTING_AUTONOMOUS_MODE ),\
|
||||
HID_LOGICAL_MIN ( 0 ),\
|
||||
HID_LOGICAL_MAX ( 1 ),\
|
||||
HID_REPORT_SIZE ( 8 ),\
|
||||
HID_REPORT_COUNT ( 1 ),\
|
||||
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||
HID_COLLECTION_END ,\
|
||||
HID_COLLECTION_END \
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidd_init (void);
|
||||
bool hidd_deinit (void);
|
||||
void hidd_reset (uint8_t rhport);
|
||||
uint16_t hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -39,17 +39,17 @@
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_HID_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
uint8_t daddr;
|
||||
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
bool mounted; // Enumeration is complete
|
||||
|
||||
uint8_t itf_protocol; // None, Keyboard, Mouse
|
||||
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||
@ -72,78 +72,57 @@ tu_static uint8_t _hidh_default_protocol = HID_PROTOCOL_BOOT;
|
||||
//--------------------------------------------------------------------+
|
||||
// Helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx) {
|
||||
TU_ASSERT(daddr > 0 && idx < CFG_TUH_HID, NULL);
|
||||
hidh_interface_t* p_hid = &_hidh_itf[idx];
|
||||
return (p_hid->daddr == daddr) ? p_hid : NULL;
|
||||
}
|
||||
|
||||
// Get instance ID by endpoint address
|
||||
static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr)
|
||||
{
|
||||
for ( uint8_t idx = 0; idx < CFG_TUH_HID; idx++ )
|
||||
{
|
||||
hidh_interface_t const * p_hid = &_hidh_itf[idx];
|
||||
|
||||
if ( p_hid->daddr == daddr &&
|
||||
(p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr) )
|
||||
{
|
||||
static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
|
||||
hidh_interface_t const* p_hid = &_hidh_itf[idx];
|
||||
if (p_hid->daddr == daddr &&
|
||||
(p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr)) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
static hidh_interface_t* find_new_itf(void)
|
||||
{
|
||||
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||
{
|
||||
static hidh_interface_t* find_new_itf(void) {
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr == 0) return &_hidh_itf[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interface API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t tuh_hid_itf_get_count(uint8_t daddr)
|
||||
{
|
||||
uint8_t tuh_hid_itf_get_count(uint8_t daddr) {
|
||||
uint8_t count = 0;
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||
{
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr == daddr) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_itf_get_total_count(void)
|
||||
{
|
||||
uint8_t tuh_hid_itf_get_total_count(void) {
|
||||
uint8_t count = 0;
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||
{
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
if (_hidh_itf[i].daddr != 0) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool tuh_hid_mounted(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
bool tuh_hid_mounted(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
return p_hid != NULL;
|
||||
TU_VERIFY(p_hid);
|
||||
return p_hid->mounted;
|
||||
}
|
||||
|
||||
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info)
|
||||
{
|
||||
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid && info);
|
||||
|
||||
@ -165,20 +144,16 @@ bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num)
|
||||
{
|
||||
for ( uint8_t idx = 0; idx < CFG_TUH_HID; idx++ )
|
||||
{
|
||||
hidh_interface_t const * p_hid = &_hidh_itf[idx];
|
||||
|
||||
if ( p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx;
|
||||
uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num) {
|
||||
for (uint8_t idx = 0; idx < CFG_TUH_HID; idx++) {
|
||||
hidh_interface_t const* p_hid = &_hidh_itf[idx];
|
||||
if (p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx;
|
||||
}
|
||||
|
||||
return TUSB_INDEX_INVALID_8;
|
||||
}
|
||||
|
||||
uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
return p_hid ? p_hid->itf_protocol : 0;
|
||||
}
|
||||
@ -186,29 +161,24 @@ uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx)
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
return p_hid ? p_hid->protocol_mode : 0;
|
||||
}
|
||||
|
||||
static void set_protocol_complete(tuh_xfer_t* xfer)
|
||||
{
|
||||
static void set_protocol_complete(tuh_xfer_t* xfer) {
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid, );
|
||||
TU_VERIFY(p_hid,);
|
||||
|
||||
if (XFER_RESULT_SUCCESS == xfer->result)
|
||||
{
|
||||
if (XFER_RESULT_SUCCESS == xfer->result) {
|
||||
p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue);
|
||||
}
|
||||
|
||||
if (tuh_hid_set_protocol_complete_cb)
|
||||
{
|
||||
if (tuh_hid_set_protocol_complete_cb) {
|
||||
tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode);
|
||||
}
|
||||
}
|
||||
@ -217,14 +187,12 @@ void tuh_hid_set_default_protocol(uint8_t protocol) {
|
||||
_hidh_default_protocol = protocol;
|
||||
}
|
||||
|
||||
static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
TU_LOG_DRV("HID Set Protocol = %d\r\n", protocol);
|
||||
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
@ -235,8 +203,7 @@ static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
@ -248,20 +215,61 @@ static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol,
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol)
|
||||
{
|
||||
bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid && p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE);
|
||||
|
||||
return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
|
||||
}
|
||||
|
||||
static void set_report_complete(tuh_xfer_t* xfer)
|
||||
{
|
||||
static void get_report_complete(tuh_xfer_t* xfer) {
|
||||
TU_LOG_DRV("HID Get Report complete\r\n");
|
||||
|
||||
if (tuh_hid_get_report_complete_cb) {
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
|
||||
|
||||
uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
|
||||
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
|
||||
|
||||
tuh_hid_get_report_complete_cb(xfer->daddr, idx, report_id, report_type,
|
||||
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
TU_LOG_DRV("HID Get Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_GET_REPORT,
|
||||
.wValue = tu_htole16(tu_u16(report_type, report_id)),
|
||||
.wIndex = tu_htole16((uint16_t) p_hid->itf_num),
|
||||
.wLength = len
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = report,
|
||||
.complete_cb = get_report_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
static void set_report_complete(tuh_xfer_t* xfer) {
|
||||
TU_LOG_DRV("HID Set Report complete\r\n");
|
||||
|
||||
if (tuh_hid_set_report_complete_cb)
|
||||
{
|
||||
if (tuh_hid_set_report_complete_cb) {
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
|
||||
|
||||
@ -273,29 +281,24 @@ static void set_report_complete(tuh_xfer_t* xfer)
|
||||
}
|
||||
}
|
||||
|
||||
bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len)
|
||||
{
|
||||
bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
TU_LOG_DRV("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_REPORT,
|
||||
.wValue = tu_htole16(tu_u16(report_type, report_id)),
|
||||
.wIndex = tu_htole16((uint16_t)p_hid->itf_num),
|
||||
.wIndex = tu_htole16((uint16_t) p_hid->itf_num),
|
||||
.wLength = len
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
@ -307,27 +310,24 @@ bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t r
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
// SET IDLE request, device can stall if not support this request
|
||||
TU_LOG_DRV("HID Set Idle \r\n");
|
||||
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_IDLE,
|
||||
.wValue = tu_htole16(idle_rate),
|
||||
.wIndex = tu_htole16((uint16_t)itf_num),
|
||||
.wIndex = tu_htole16((uint16_t) itf_num),
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
@ -344,68 +344,60 @@ static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, t
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if HID interface is ready to receive report
|
||||
bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx)
|
||||
{
|
||||
bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
return !usbh_edpt_busy(dev_addr, p_hid->ep_in);
|
||||
}
|
||||
|
||||
bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx)
|
||||
{
|
||||
bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY( usbh_edpt_claim(daddr, p_hid->ep_in) );
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_in));
|
||||
|
||||
if ( !usbh_edpt_xfer(daddr, p_hid->ep_in, p_hid->epin_buf, p_hid->epin_size) )
|
||||
{
|
||||
if (!usbh_edpt_xfer(daddr, p_hid->ep_in, p_hid->epin_buf, p_hid->epin_size)) {
|
||||
usbh_edpt_release(daddr, p_hid->ep_in);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx)
|
||||
{
|
||||
bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
return tuh_edpt_abort_xfer(dev_addr, p_hid->ep_in);
|
||||
}
|
||||
|
||||
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
return !usbh_edpt_busy(dev_addr, p_hid->ep_out);
|
||||
}
|
||||
|
||||
bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len)
|
||||
{
|
||||
bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len) {
|
||||
TU_LOG_DRV("HID Send Report %d\r\n", report_id);
|
||||
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
if (p_hid->ep_out == 0)
|
||||
{
|
||||
if (p_hid->ep_out == 0) {
|
||||
// This HID does not have an out endpoint (other than control)
|
||||
return false;
|
||||
}
|
||||
else if (len > CFG_TUH_HID_EPOUT_BUFSIZE ||
|
||||
(report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1)))
|
||||
{
|
||||
} else if (len > CFG_TUH_HID_EPOUT_BUFSIZE ||
|
||||
(report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1))) {
|
||||
// ep_out buffer is not large enough to hold contents
|
||||
return false;
|
||||
}
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY( usbh_edpt_claim(daddr, p_hid->ep_out) );
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_hid->ep_out));
|
||||
|
||||
if (report_id == 0)
|
||||
{
|
||||
if (report_id == 0) {
|
||||
// No report ID in transmission
|
||||
memcpy(&p_hid->epout_buf[0], report, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
p_hid->epout_buf[0] = report_id;
|
||||
memcpy(&p_hid->epout_buf[1], report, len);
|
||||
++len; // 1 more byte for report_id
|
||||
@ -413,8 +405,7 @@ bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const vo
|
||||
|
||||
TU_LOG3_MEM(p_hid->epout_buf, len, 2);
|
||||
|
||||
if ( !usbh_edpt_xfer(daddr, p_hid->ep_out, p_hid->epout_buf, len) )
|
||||
{
|
||||
if (!usbh_edpt_xfer(daddr, p_hid->ep_out, p_hid->epout_buf, len)) {
|
||||
usbh_edpt_release(daddr, p_hid->ep_out);
|
||||
return false;
|
||||
}
|
||||
@ -425,13 +416,17 @@ bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const vo
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidh_init(void)
|
||||
{
|
||||
bool hidh_init(void) {
|
||||
TU_LOG_DRV("sizeof(hidh_interface_t) = %u\r\n", sizeof(hidh_interface_t));
|
||||
tu_memclr(_hidh_itf, sizeof(_hidh_itf));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
bool hidh_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
|
||||
(void) result;
|
||||
|
||||
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||
@ -440,29 +435,26 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid);
|
||||
|
||||
if ( dir == TUSB_DIR_IN )
|
||||
{
|
||||
// TU_LOG_DRV(" Get Report callback (%u, %u)\r\n", daddr, idx);
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
TU_LOG_DRV(" Get Report callback (%u, %u)\r\n", daddr, idx);
|
||||
TU_LOG3_MEM(p_hid->epin_buf, xferred_bytes, 2);
|
||||
tuh_hid_report_received_cb(daddr, idx, p_hid->epin_buf, (uint16_t) xferred_bytes);
|
||||
}else
|
||||
{
|
||||
if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(daddr, idx, p_hid->epout_buf, (uint16_t) xferred_bytes);
|
||||
} else {
|
||||
if (tuh_hid_report_sent_cb) {
|
||||
tuh_hid_report_sent_cb(daddr, idx, p_hid->epout_buf, (uint16_t) xferred_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hidh_close(uint8_t daddr)
|
||||
{
|
||||
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||
{
|
||||
void hidh_close(uint8_t daddr) {
|
||||
for (uint8_t i = 0; i < CFG_TUH_HID; i++) {
|
||||
hidh_interface_t* p_hid = &_hidh_itf[i];
|
||||
if (p_hid->daddr == daddr)
|
||||
{
|
||||
if (p_hid->daddr == daddr) {
|
||||
TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i);
|
||||
if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
|
||||
p_hid->daddr = 0;
|
||||
if (tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
|
||||
tu_memclr(p_hid, sizeof(hidh_interface_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -471,25 +463,22 @@ void hidh_close(uint8_t daddr)
|
||||
// Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||
{
|
||||
bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
(void) max_len;
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
|
||||
|
||||
TU_LOG_DRV("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
|
||||
|
||||
// len = interface + hid + n*endpoints
|
||||
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
|
||||
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_ASSERT(max_len >= drv_len);
|
||||
|
||||
uint8_t const *p_desc = (uint8_t const *) desc_itf;
|
||||
uint8_t const* p_desc = (uint8_t const*) desc_itf;
|
||||
|
||||
//------------- HID descriptor -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
|
||||
tusb_hid_descriptor_hid_t const* desc_hid = (tusb_hid_descriptor_hid_t const*) p_desc;
|
||||
TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
|
||||
|
||||
hidh_interface_t* p_hid = find_new_itf();
|
||||
@ -498,26 +487,22 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_
|
||||
|
||||
//------------- Endpoint Descriptors -------------//
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
|
||||
|
||||
for(int i = 0; i < desc_itf->bNumEndpoints; i++)
|
||||
{
|
||||
for (int i = 0; i < desc_itf->bNumEndpoints; i++) {
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
|
||||
TU_ASSERT( tuh_edpt_open(daddr, desc_ep) );
|
||||
TU_ASSERT(tuh_edpt_open(daddr, desc_ep));
|
||||
|
||||
if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
|
||||
{
|
||||
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
|
||||
p_hid->ep_in = desc_ep->bEndpointAddress;
|
||||
p_hid->epin_size = tu_edpt_packet_size(desc_ep);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
p_hid->ep_out = desc_ep->bEndpointAddress;
|
||||
p_hid->epout_size = tu_edpt_packet_size(desc_ep);
|
||||
}
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
desc_ep = (tusb_desc_endpoint_t const*) p_desc;
|
||||
}
|
||||
|
||||
p_hid->itf_num = desc_itf->bInterfaceNumber;
|
||||
@ -528,8 +513,7 @@ bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_
|
||||
|
||||
// Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
|
||||
p_hid->protocol_mode = _hidh_default_protocol;
|
||||
if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass )
|
||||
{
|
||||
if (HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass) {
|
||||
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
|
||||
}
|
||||
|
||||
@ -550,8 +534,7 @@ enum {
|
||||
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len);
|
||||
static void process_set_config(tuh_xfer_t* xfer);
|
||||
|
||||
bool hidh_set_config(uint8_t daddr, uint8_t itf_num)
|
||||
{
|
||||
bool hidh_set_config(uint8_t daddr, uint8_t itf_num) {
|
||||
tusb_control_request_t request;
|
||||
request.wIndex = tu_htole16((uint16_t) itf_num);
|
||||
|
||||
@ -567,14 +550,12 @@ bool hidh_set_config(uint8_t daddr, uint8_t itf_num)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void process_set_config(tuh_xfer_t* xfer)
|
||||
{
|
||||
static void process_set_config(tuh_xfer_t* xfer) {
|
||||
// Stall is a valid response for SET_IDLE, sometime SET_PROTOCOL as well
|
||||
// therefore we could ignore its result
|
||||
if ( !(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE ||
|
||||
xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL) )
|
||||
{
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
|
||||
if (!(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE ||
|
||||
xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL)) {
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS,);
|
||||
}
|
||||
|
||||
uintptr_t const state = xfer->user_data;
|
||||
@ -583,18 +564,17 @@ static void process_set_config(tuh_xfer_t* xfer)
|
||||
|
||||
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid, );
|
||||
TU_VERIFY(p_hid,);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case CONFG_SET_IDLE:
|
||||
{
|
||||
switch (state) {
|
||||
case CONFG_SET_IDLE: {
|
||||
// Idle rate = 0 mean only report when there is changes
|
||||
const uint16_t idle_rate = 0;
|
||||
const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
|
||||
const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE)
|
||||
? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
|
||||
_hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFIG_SET_PROTOCOL:
|
||||
_hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC);
|
||||
@ -603,35 +583,35 @@ static void process_set_config(tuh_xfer_t* xfer)
|
||||
case CONFIG_GET_REPORT_DESC:
|
||||
// Get Report Descriptor if possible
|
||||
// using usbh enumeration buffer since report descriptor can be very long
|
||||
if( p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
|
||||
{
|
||||
if (p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE) {
|
||||
TU_LOG_DRV("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len);
|
||||
|
||||
// Driver is mounted without report descriptor
|
||||
config_driver_mount_complete(daddr, idx, NULL, 0);
|
||||
}else
|
||||
{
|
||||
tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0, usbh_get_enum_buf(), p_hid->report_desc_len, process_set_config, CONFIG_COMPLETE);
|
||||
} else {
|
||||
tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0,
|
||||
usbh_get_enum_buf(), p_hid->report_desc_len,
|
||||
process_set_config, CONFIG_COMPLETE);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONFIG_COMPLETE:
|
||||
{
|
||||
case CONFIG_COMPLETE: {
|
||||
uint8_t const* desc_report = usbh_get_enum_buf();
|
||||
uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
|
||||
|
||||
config_driver_mount_complete(daddr, idx, desc_report, desc_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len)
|
||||
{
|
||||
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len) {
|
||||
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||
TU_VERIFY(p_hid, );
|
||||
TU_VERIFY(p_hid,);
|
||||
p_hid->mounted = true;
|
||||
|
||||
// enumeration is complete
|
||||
if (tuh_hid_mount_cb) tuh_hid_mount_cb(daddr, idx, desc_report, desc_len);
|
||||
@ -644,21 +624,19 @@ static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t con
|
||||
// Report Descriptor Parser
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len)
|
||||
{
|
||||
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count,
|
||||
uint8_t const* desc_report, uint16_t desc_len) {
|
||||
// Report Item 6.2.2.2 USB HID 1.11
|
||||
union TU_ATTR_PACKED
|
||||
{
|
||||
union TU_ATTR_PACKED {
|
||||
uint8_t byte;
|
||||
struct TU_ATTR_PACKED
|
||||
{
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t size : 2;
|
||||
uint8_t type : 2;
|
||||
uint8_t tag : 4;
|
||||
};
|
||||
} header;
|
||||
|
||||
tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t));
|
||||
tu_memclr(report_info_arr, arr_count * sizeof(tuh_hid_report_info_t));
|
||||
|
||||
uint8_t report_num = 0;
|
||||
tuh_hid_report_info_t* info = report_info_arr;
|
||||
@ -668,9 +646,7 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
|
||||
// uint8_t ri_report_size = 0;
|
||||
|
||||
uint8_t ri_collection_depth = 0;
|
||||
|
||||
while(desc_len && report_num < arr_count)
|
||||
{
|
||||
while (desc_len && report_num < arr_count) {
|
||||
header.byte = *desc_report++;
|
||||
desc_len--;
|
||||
|
||||
@ -681,47 +657,42 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
|
||||
uint8_t const data8 = desc_report[0];
|
||||
|
||||
TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
|
||||
for(uint32_t i=0; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]);
|
||||
for (uint32_t i = 0; i < size; i++) TU_LOG(3, "%02X ", desc_report[i]);
|
||||
TU_LOG(3, "\r\n");
|
||||
|
||||
switch(type)
|
||||
{
|
||||
switch (type) {
|
||||
case RI_TYPE_MAIN:
|
||||
switch (tag)
|
||||
{
|
||||
switch (tag) {
|
||||
case RI_MAIN_INPUT: break;
|
||||
case RI_MAIN_OUTPUT: break;
|
||||
case RI_MAIN_FEATURE: break;
|
||||
|
||||
case RI_MAIN_COLLECTION:
|
||||
ri_collection_depth++;
|
||||
break;
|
||||
|
||||
case RI_MAIN_COLLECTION_END:
|
||||
ri_collection_depth--;
|
||||
if (ri_collection_depth == 0)
|
||||
{
|
||||
if (ri_collection_depth == 0) {
|
||||
info++;
|
||||
report_num++;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
default:break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RI_TYPE_GLOBAL:
|
||||
switch(tag)
|
||||
{
|
||||
switch (tag) {
|
||||
case RI_GLOBAL_USAGE_PAGE:
|
||||
// only take in account the "usage page" before REPORT ID
|
||||
if ( ri_collection_depth == 0 ) memcpy(&info->usage_page, desc_report, size);
|
||||
if (ri_collection_depth == 0) memcpy(&info->usage_page, desc_report, size);
|
||||
break;
|
||||
|
||||
case RI_GLOBAL_LOGICAL_MIN : break;
|
||||
case RI_GLOBAL_LOGICAL_MAX : break;
|
||||
case RI_GLOBAL_PHYSICAL_MIN : break;
|
||||
case RI_GLOBAL_PHYSICAL_MAX : break;
|
||||
case RI_GLOBAL_LOGICAL_MIN: break;
|
||||
case RI_GLOBAL_LOGICAL_MAX: break;
|
||||
case RI_GLOBAL_PHYSICAL_MIN: break;
|
||||
case RI_GLOBAL_PHYSICAL_MAX: break;
|
||||
|
||||
case RI_GLOBAL_REPORT_ID:
|
||||
info->report_id = data8;
|
||||
@ -735,32 +706,31 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
|
||||
// ri_report_count = data8;
|
||||
break;
|
||||
|
||||
case RI_GLOBAL_UNIT_EXPONENT : break;
|
||||
case RI_GLOBAL_UNIT : break;
|
||||
case RI_GLOBAL_PUSH : break;
|
||||
case RI_GLOBAL_POP : break;
|
||||
case RI_GLOBAL_UNIT_EXPONENT: break;
|
||||
case RI_GLOBAL_UNIT: break;
|
||||
case RI_GLOBAL_PUSH: break;
|
||||
case RI_GLOBAL_POP: break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
case RI_TYPE_LOCAL:
|
||||
switch(tag)
|
||||
{
|
||||
switch (tag) {
|
||||
case RI_LOCAL_USAGE:
|
||||
// only take in account the "usage" before starting REPORT ID
|
||||
if ( ri_collection_depth == 0 ) info->usage = data8;
|
||||
if (ri_collection_depth == 0) info->usage = data8;
|
||||
break;
|
||||
|
||||
case RI_LOCAL_USAGE_MIN : break;
|
||||
case RI_LOCAL_USAGE_MAX : break;
|
||||
case RI_LOCAL_DESIGNATOR_INDEX : break;
|
||||
case RI_LOCAL_DESIGNATOR_MIN : break;
|
||||
case RI_LOCAL_DESIGNATOR_MAX : break;
|
||||
case RI_LOCAL_STRING_INDEX : break;
|
||||
case RI_LOCAL_STRING_MIN : break;
|
||||
case RI_LOCAL_STRING_MAX : break;
|
||||
case RI_LOCAL_DELIMITER : break;
|
||||
case RI_LOCAL_USAGE_MIN: break;
|
||||
case RI_LOCAL_USAGE_MAX: break;
|
||||
case RI_LOCAL_DESIGNATOR_INDEX: break;
|
||||
case RI_LOCAL_DESIGNATOR_MIN: break;
|
||||
case RI_LOCAL_DESIGNATOR_MAX: break;
|
||||
case RI_LOCAL_STRING_INDEX: break;
|
||||
case RI_LOCAL_STRING_MIN: break;
|
||||
case RI_LOCAL_STRING_MAX: break;
|
||||
case RI_LOCAL_DELIMITER: break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
@ -773,9 +743,8 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
|
||||
desc_len -= size;
|
||||
}
|
||||
|
||||
for ( uint8_t i = 0; i < report_num; i++ )
|
||||
{
|
||||
info = report_info_arr+i;
|
||||
for (uint8_t i = 0; i < report_num; i++) {
|
||||
info = report_info_arr + i;
|
||||
TU_LOG_DRV("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include "hid.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -47,8 +47,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
uint8_t report_id;
|
||||
uint8_t usage;
|
||||
uint16_t usage_page;
|
||||
@ -86,7 +85,8 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Parse report descriptor into array of report_info struct and return number of reports.
|
||||
// For complicated report, application should write its own parser.
|
||||
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED;
|
||||
TU_ATTR_UNUSED uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count,
|
||||
uint8_t const* desc_report, uint16_t desc_len);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Control Endpoint API
|
||||
@ -105,9 +105,14 @@ void tuh_hid_set_default_protocol(uint8_t protocol);
|
||||
// This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE)
|
||||
bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
|
||||
|
||||
// Get Report using control endpoint
|
||||
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
|
||||
bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
|
||||
|
||||
// Set Report using control endpoint
|
||||
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
|
||||
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
|
||||
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
|
||||
void* report, uint16_t len);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interrupt Endpoint API
|
||||
@ -121,6 +126,9 @@ bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx);
|
||||
// - false if failed to queue the transfer e.g endpoint is busy
|
||||
bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Abort receiving report on Interrupt Endpoint
|
||||
bool tuh_hid_receive_abort(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
// Check if HID interface is ready to send report
|
||||
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx);
|
||||
|
||||
@ -149,6 +157,10 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* re
|
||||
// Invoked when sent report to device successfully via interrupt endpoint
|
||||
TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
|
||||
|
||||
// Invoked when Get Report to device via either control endpoint
|
||||
// len = 0 indicate there is error in the transfer e.g stalled response
|
||||
TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
|
||||
// Invoked when Sent Report to device via either control endpoint
|
||||
// len = 0 indicate there is error in the transfer e.g stalled response
|
||||
TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||
@ -159,11 +171,12 @@ TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void hidh_init (void);
|
||||
bool hidh_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||
bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
bool hidh_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void hidh_close (uint8_t dev_addr);
|
||||
bool hidh_init(void);
|
||||
bool hidh_deinit(void);
|
||||
bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len);
|
||||
bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num);
|
||||
bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
void hidh_close(uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_MSC_LOG_LEVEL
|
||||
|
||||
TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
|
||||
{
|
||||
@ -251,11 +251,15 @@ static inline void set_sense_medium_not_present(uint8_t lun)
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void mscd_init(void)
|
||||
{
|
||||
void mscd_init(void) {
|
||||
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
|
||||
}
|
||||
|
||||
bool mscd_deinit(void) {
|
||||
// nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
void mscd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
@ -685,6 +689,24 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
|
||||
}
|
||||
break;
|
||||
|
||||
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
|
||||
resplen = 0;
|
||||
|
||||
if (tud_msc_prevent_allow_medium_removal_cb)
|
||||
{
|
||||
scsi_prevent_allow_medium_removal_t const * prevent_allow = (scsi_prevent_allow_medium_removal_t const *) scsi_cmd;
|
||||
if ( !tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control) )
|
||||
{
|
||||
// Failed status response
|
||||
resplen = - 1;
|
||||
|
||||
// set default sense if not set by callback
|
||||
if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case SCSI_CMD_READ_CAPACITY_10:
|
||||
{
|
||||
uint32_t block_count;
|
||||
|
@ -131,6 +131,9 @@ TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
|
||||
|
||||
//Invoked when we receive the Prevent / Allow Medium Removal command
|
||||
TU_ATTR_WEAK bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control);
|
||||
|
||||
// Invoked when received REQUEST_SENSE
|
||||
TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize);
|
||||
|
||||
@ -150,6 +153,7 @@ TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void mscd_init (void);
|
||||
bool mscd_deinit (void);
|
||||
void mscd_reset (uint8_t rhport);
|
||||
uint16_t mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool mscd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request);
|
||||
|
@ -1,497 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUH_ENABLED && CFG_TUH_MSC
|
||||
|
||||
#include "host/usbh.h"
|
||||
#include "host/usbh_pvt.h"
|
||||
|
||||
#include "msc_host.h"
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
|
||||
#ifndef CFG_TUH_MSC_LOG_LEVEL
|
||||
#define CFG_TUH_MSC_LOG_LEVEL CFG_TUH_LOG_LEVEL
|
||||
#endif
|
||||
|
||||
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_MSC_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
enum {
|
||||
MSC_STAGE_IDLE = 0,
|
||||
MSC_STAGE_CMD,
|
||||
MSC_STAGE_DATA,
|
||||
MSC_STAGE_STATUS,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
|
||||
uint8_t max_lun;
|
||||
|
||||
volatile bool configured; // Receive SET_CONFIGURE
|
||||
volatile bool mounted; // Enumeration is complete
|
||||
|
||||
struct {
|
||||
uint32_t block_size;
|
||||
uint32_t block_count;
|
||||
} capacity[CFG_TUH_MSC_MAXLUN];
|
||||
|
||||
//------------- SCSI -------------//
|
||||
uint8_t stage;
|
||||
void* buffer;
|
||||
tuh_msc_complete_cb_t complete_cb;
|
||||
uintptr_t complete_arg;
|
||||
|
||||
CFG_TUH_MEM_ALIGN msc_cbw_t cbw;
|
||||
CFG_TUH_MEM_ALIGN msc_csw_t csw;
|
||||
} msch_interface_t;
|
||||
|
||||
CFG_TUH_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
|
||||
|
||||
// buffer used to read scsi information when mounted
|
||||
// largest response data currently is inquiry TODO Inquiry is not part of enum anymore
|
||||
CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
|
||||
static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
|
||||
|
||||
// FIXME potential nul reference
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline msch_interface_t* get_itf(uint8_t dev_addr) {
|
||||
return &_msch_itf[dev_addr - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC API
|
||||
//--------------------------------------------------------------------+
|
||||
uint8_t tuh_msc_get_maxlun(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->max_lun;
|
||||
}
|
||||
|
||||
uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->capacity[lun].block_count;
|
||||
}
|
||||
|
||||
uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->capacity[lun].block_size;
|
||||
}
|
||||
|
||||
bool tuh_msc_mounted(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->mounted;
|
||||
}
|
||||
|
||||
bool tuh_msc_ready(uint8_t dev_addr) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in) && !usbh_edpt_busy(dev_addr, p_msc->ep_out);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// PUBLIC API: SCSI COMMAND
|
||||
//--------------------------------------------------------------------+
|
||||
static inline void cbw_init(msc_cbw_t* cbw, uint8_t lun) {
|
||||
tu_memclr(cbw, sizeof(msc_cbw_t));
|
||||
cbw->signature = MSC_CBW_SIGNATURE;
|
||||
cbw->tag = 0x54555342; // TUSB
|
||||
cbw->lun = lun;
|
||||
}
|
||||
|
||||
bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(daddr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbh_edpt_claim(daddr, p_msc->ep_out));
|
||||
|
||||
p_msc->cbw = *cbw;
|
||||
p_msc->stage = MSC_STAGE_CMD;
|
||||
p_msc->buffer = data;
|
||||
p_msc->complete_cb = complete_cb;
|
||||
p_msc->complete_arg = arg;
|
||||
|
||||
if (!usbh_edpt_xfer(daddr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t))) {
|
||||
usbh_edpt_release(daddr, p_msc->ep_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_read_capacity10_t);
|
||||
cbw.command[0] = SCSI_CMD_READ_CAPACITY_10;
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->mounted);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_inquiry_t);
|
||||
|
||||
scsi_inquiry_t const cmd_inquiry = {
|
||||
.cmd_code = SCSI_CMD_INQUIRY,
|
||||
.alloc_length = sizeof(scsi_inquiry_resp_t)
|
||||
};
|
||||
memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = 0;
|
||||
cbw.dir = TUSB_DIR_OUT;
|
||||
cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
|
||||
cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
|
||||
cbw.command[1] = lun; // according to wiki TODO need verification
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void* response,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = 18; // TODO sense response
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_request_sense_t);
|
||||
|
||||
scsi_request_sense_t const cmd_request_sense = {
|
||||
.cmd_code = SCSI_CMD_REQUEST_SENSE,
|
||||
.alloc_length = 18
|
||||
};
|
||||
memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void* buffer, uint32_t lba, uint16_t block_count,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->mounted);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
|
||||
cbw.dir = TUSB_DIR_IN_MASK;
|
||||
cbw.cmd_len = sizeof(scsi_read10_t);
|
||||
|
||||
scsi_read10_t const cmd_read10 = {
|
||||
.cmd_code = SCSI_CMD_READ_10,
|
||||
.lba = tu_htonl(lba),
|
||||
.block_count = tu_htons(block_count)
|
||||
};
|
||||
memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb, arg);
|
||||
}
|
||||
|
||||
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const* buffer, uint32_t lba, uint16_t block_count,
|
||||
tuh_msc_complete_cb_t complete_cb, uintptr_t arg) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->mounted);
|
||||
|
||||
msc_cbw_t cbw;
|
||||
cbw_init(&cbw, lun);
|
||||
|
||||
cbw.total_bytes = block_count * p_msc->capacity[lun].block_size;
|
||||
cbw.dir = TUSB_DIR_OUT;
|
||||
cbw.cmd_len = sizeof(scsi_write10_t);
|
||||
|
||||
scsi_write10_t const cmd_write10 = {
|
||||
.cmd_code = SCSI_CMD_WRITE_10,
|
||||
.lba = tu_htonl(lba),
|
||||
.block_count = tu_htons(block_count)
|
||||
};
|
||||
memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
|
||||
|
||||
return tuh_msc_scsi_command(dev_addr, &cbw, (void*) (uintptr_t) buffer, complete_cb, arg);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// MSC interface Reset (not used now)
|
||||
bool tuh_msc_reset(uint8_t dev_addr) {
|
||||
tusb_control_request_t const new_request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = MSC_REQ_RESET,
|
||||
.wValue = 0,
|
||||
.wIndex = p_msc->itf_num,
|
||||
.wLength = 0
|
||||
};
|
||||
TU_ASSERT( usbh_control_xfer( dev_addr, &new_request, NULL ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// CLASS-USBH API
|
||||
//--------------------------------------------------------------------+
|
||||
void msch_init(void) {
|
||||
tu_memclr(_msch_itf, sizeof(_msch_itf));
|
||||
}
|
||||
|
||||
void msch_close(uint8_t dev_addr) {
|
||||
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX,);
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_VERIFY(p_msc->configured,);
|
||||
|
||||
TU_LOG_DRV(" MSCh close addr = %d\r\n", dev_addr);
|
||||
|
||||
// invoke Application Callback
|
||||
if (p_msc->mounted) {
|
||||
if (tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
|
||||
}
|
||||
|
||||
tu_memclr(p_msc, sizeof(msch_interface_t));
|
||||
}
|
||||
|
||||
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
msc_cbw_t const * cbw = &p_msc->cbw;
|
||||
msc_csw_t * csw = &p_msc->csw;
|
||||
|
||||
switch (p_msc->stage) {
|
||||
case MSC_STAGE_CMD:
|
||||
// Must be Command Block
|
||||
TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
|
||||
|
||||
if (cbw->total_bytes && p_msc->buffer) {
|
||||
// Data stage if any
|
||||
p_msc->stage = MSC_STAGE_DATA;
|
||||
uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
|
||||
TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
|
||||
} else {
|
||||
// Status stage
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_STAGE_DATA:
|
||||
// Status stage
|
||||
p_msc->stage = MSC_STAGE_STATUS;
|
||||
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
|
||||
break;
|
||||
|
||||
case MSC_STAGE_STATUS:
|
||||
// SCSI op is complete
|
||||
p_msc->stage = MSC_STAGE_IDLE;
|
||||
|
||||
if (p_msc->complete_cb) {
|
||||
tuh_msc_complete_data_t const cb_data = {
|
||||
.cbw = cbw,
|
||||
.csw = csw,
|
||||
.scsi_data = p_msc->buffer,
|
||||
.user_arg = p_msc->complete_arg
|
||||
};
|
||||
p_msc->complete_cb(dev_addr, &cb_data);
|
||||
}
|
||||
break;
|
||||
|
||||
// unknown state
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MSC Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
static void config_get_maxlun_complete(tuh_xfer_t* xfer);
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
|
||||
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const* desc_itf, uint16_t max_len) {
|
||||
(void) rhport;
|
||||
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
|
||||
MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
|
||||
|
||||
// msc driver length is fixed
|
||||
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) +
|
||||
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||
TU_ASSERT(drv_len <= max_len);
|
||||
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
tusb_desc_endpoint_t const* ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(desc_itf);
|
||||
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
|
||||
|
||||
if (TUSB_DIR_IN == tu_edpt_dir(ep_desc->bEndpointAddress)) {
|
||||
p_msc->ep_in = ep_desc->bEndpointAddress;
|
||||
} else {
|
||||
p_msc->ep_out = ep_desc->bEndpointAddress;
|
||||
}
|
||||
|
||||
ep_desc = (tusb_desc_endpoint_t const*) tu_desc_next(ep_desc);
|
||||
}
|
||||
|
||||
p_msc->itf_num = desc_itf->bInterfaceNumber;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool msch_set_config(uint8_t dev_addr, uint8_t itf_num) {
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
TU_ASSERT(p_msc->itf_num == itf_num);
|
||||
|
||||
p_msc->configured = true;
|
||||
|
||||
//------------- Get Max Lun -------------//
|
||||
TU_LOG_DRV("MSC Get Max Lun\r\n");
|
||||
tusb_control_request_t const request = {
|
||||
.bmRequestType_bit = {
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = MSC_REQ_GET_MAX_LUN,
|
||||
.wValue = 0,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 1
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = _msch_buffer,
|
||||
.complete_cb = config_get_maxlun_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void config_get_maxlun_complete(tuh_xfer_t* xfer) {
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
msch_interface_t* p_msc = get_itf(daddr);
|
||||
|
||||
// STALL means zero
|
||||
p_msc->max_lun = (XFER_RESULT_SUCCESS == xfer->result) ? _msch_buffer[0] : 0;
|
||||
p_msc->max_lun++; // MAX LUN is minus 1 by specs
|
||||
|
||||
TU_LOG_DRV(" Max LUN = %u\r\n", p_msc->max_lun);
|
||||
|
||||
// TODO multiple LUN support
|
||||
TU_LOG_DRV("SCSI Test Unit Ready\r\n");
|
||||
uint8_t const lun = 0;
|
||||
tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete, 0);
|
||||
}
|
||||
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
|
||||
if (csw->status == 0) {
|
||||
// Unit is ready, read its capacity
|
||||
TU_LOG_DRV("SCSI Read Capacity\r\n");
|
||||
tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer),
|
||||
config_read_capacity_complete, 0);
|
||||
} else {
|
||||
// Note: During enumeration, some device fails Test Unit Ready and require a few retries
|
||||
// with Request Sense to start working !!
|
||||
// TODO limit number of retries
|
||||
TU_LOG_DRV("SCSI Request Sense\r\n");
|
||||
TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, _msch_buffer, config_request_sense_complete, 0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
|
||||
TU_ASSERT(csw->status == 0);
|
||||
TU_ASSERT(tuh_msc_test_unit_ready(dev_addr, cbw->lun, config_test_unit_ready_complete, 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data) {
|
||||
msc_cbw_t const* cbw = cb_data->cbw;
|
||||
msc_csw_t const* csw = cb_data->csw;
|
||||
|
||||
TU_ASSERT(csw->status == 0);
|
||||
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
|
||||
// Capacity response field: Block size and Last LBA are both Big-Endian
|
||||
scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer);
|
||||
p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
|
||||
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
|
||||
|
||||
// Mark enumeration is complete
|
||||
p_msc->mounted = true;
|
||||
if (tuh_msc_mount_cb) tuh_msc_mount_cb(dev_addr);
|
||||
|
||||
// notify usbh that driver enumeration is complete
|
||||
usbh_driver_set_config_complete(dev_addr, p_msc->itf_num);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_MSC_HOST_H_
|
||||
#define TUSB_MSC_HOST_H_
|
||||
|
||||
#include "msc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifndef CFG_TUH_MSC_MAXLUN
|
||||
#define CFG_TUH_MSC_MAXLUN 4
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
msc_cbw_t const* cbw; // SCSI command
|
||||
msc_csw_t const* csw; // SCSI status
|
||||
void* scsi_data; // SCSI Data
|
||||
uintptr_t user_arg; // user argument
|
||||
}tuh_msc_complete_data_t;
|
||||
|
||||
typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Check if device supports MassStorage interface.
|
||||
// This function true after tuh_msc_mounted_cb() and false after tuh_msc_unmounted_cb()
|
||||
bool tuh_msc_mounted(uint8_t dev_addr);
|
||||
|
||||
// Check if the interface is currently ready or busy transferring data
|
||||
bool tuh_msc_ready(uint8_t dev_addr);
|
||||
|
||||
// Get Max Lun
|
||||
uint8_t tuh_msc_get_maxlun(uint8_t dev_addr);
|
||||
|
||||
// Get number of block
|
||||
uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun);
|
||||
|
||||
// Get block size in bytes
|
||||
uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
|
||||
|
||||
// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
// return true if success, false if there is already pending operation.
|
||||
bool tuh_msc_scsi_command(uint8_t daddr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Inquiry command
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Test Unit Ready command
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Request Sense 10 command
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
// Perform SCSI Read Capacity 10 command
|
||||
// Complete callback is invoked when SCSI op is complete.
|
||||
// Note: during enumeration, host stack already carried out this request. Application can retrieve capacity by
|
||||
// simply call tuh_msc_get_block_count() and tuh_msc_get_block_size()
|
||||
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||
|
||||
//------------- Application Callback -------------//
|
||||
|
||||
// Invoked when a device with MassStorage interface is mounted
|
||||
TU_ATTR_WEAK void tuh_msc_mount_cb(uint8_t dev_addr);
|
||||
|
||||
// Invoked when a device with MassStorage interface is unmounted
|
||||
TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void msch_init (void);
|
||||
bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||
bool msch_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
void msch_close (uint8_t dev_addr);
|
||||
bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,287 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUD_ENABLED && CFG_TUD_VENDOR)
|
||||
|
||||
#include "device/usbd.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
#include "vendor_device.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct
|
||||
{
|
||||
uint8_t itf_num;
|
||||
uint8_t ep_in;
|
||||
uint8_t ep_out;
|
||||
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
tu_fifo_t rx_ff;
|
||||
tu_fifo_t tx_ff;
|
||||
|
||||
uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
|
||||
uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
osal_mutex_def_t rx_ff_mutex;
|
||||
osal_mutex_def_t tx_ff_mutex;
|
||||
#endif
|
||||
|
||||
// Endpoint Transfer buffer
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
|
||||
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_VENDOR_EPSIZE];
|
||||
} vendord_interface_t;
|
||||
|
||||
CFG_TUD_MEM_SECTION tu_static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
|
||||
|
||||
#define ITF_MEM_RESET_SIZE offsetof(vendord_interface_t, rx_ff)
|
||||
|
||||
|
||||
bool tud_vendor_n_mounted (uint8_t itf)
|
||||
{
|
||||
return _vendord_itf[itf].ep_in && _vendord_itf[itf].ep_out;
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_available (uint8_t itf)
|
||||
{
|
||||
return tu_fifo_count(&_vendord_itf[itf].rx_ff);
|
||||
}
|
||||
|
||||
bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8)
|
||||
{
|
||||
return tu_fifo_peek(&_vendord_itf[itf].rx_ff, u8);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Read API
|
||||
//--------------------------------------------------------------------+
|
||||
static void _prep_out_transaction (vendord_interface_t* p_itf)
|
||||
{
|
||||
uint8_t const rhport = 0;
|
||||
|
||||
// claim endpoint
|
||||
TU_VERIFY(usbd_edpt_claim(rhport, p_itf->ep_out), );
|
||||
|
||||
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||
uint16_t max_read = tu_fifo_remaining(&p_itf->rx_ff);
|
||||
if ( max_read >= CFG_TUD_VENDOR_EPSIZE )
|
||||
{
|
||||
usbd_edpt_xfer(rhport, p_itf->ep_out, p_itf->epout_buf, CFG_TUD_VENDOR_EPSIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Release endpoint since we don't make any transfer
|
||||
usbd_edpt_release(rhport, p_itf->ep_out);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize)
|
||||
{
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
uint32_t num_read = tu_fifo_read_n(&p_itf->rx_ff, buffer, (uint16_t) bufsize);
|
||||
_prep_out_transaction(p_itf);
|
||||
return num_read;
|
||||
}
|
||||
|
||||
void tud_vendor_n_read_flush (uint8_t itf)
|
||||
{
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
tu_fifo_clear(&p_itf->rx_ff);
|
||||
_prep_out_transaction(p_itf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Write API
|
||||
//--------------------------------------------------------------------+
|
||||
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize)
|
||||
{
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
uint16_t ret = tu_fifo_write_n(&p_itf->tx_ff, buffer, (uint16_t) bufsize);
|
||||
|
||||
// flush if queue more than packet size
|
||||
if (tu_fifo_count(&p_itf->tx_ff) >= CFG_TUD_VENDOR_EPSIZE) {
|
||||
tud_vendor_n_write_flush(itf);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_write_flush (uint8_t itf)
|
||||
{
|
||||
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||
|
||||
// Skip if usb is not ready yet
|
||||
TU_VERIFY( tud_ready(), 0 );
|
||||
|
||||
// No data to send
|
||||
if ( !tu_fifo_count(&p_itf->tx_ff) ) return 0;
|
||||
|
||||
uint8_t const rhport = 0;
|
||||
|
||||
// Claim the endpoint
|
||||
TU_VERIFY( usbd_edpt_claim(rhport, p_itf->ep_in), 0 );
|
||||
|
||||
// Pull data from FIFO
|
||||
uint16_t const count = tu_fifo_read_n(&p_itf->tx_ff, p_itf->epin_buf, sizeof(p_itf->epin_buf));
|
||||
|
||||
if ( count )
|
||||
{
|
||||
TU_ASSERT( usbd_edpt_xfer(rhport, p_itf->ep_in, p_itf->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_itf->ep_in);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tud_vendor_n_write_available (uint8_t itf)
|
||||
{
|
||||
return tu_fifo_remaining(&_vendord_itf[itf].tx_ff);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void vendord_init(void)
|
||||
{
|
||||
tu_memclr(_vendord_itf, sizeof(_vendord_itf));
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
|
||||
{
|
||||
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||
|
||||
// config fifo
|
||||
tu_fifo_config(&p_itf->rx_ff, p_itf->rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, 1, false);
|
||||
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
|
||||
|
||||
#if CFG_FIFO_MUTEX
|
||||
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
|
||||
tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void vendord_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
|
||||
{
|
||||
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||
|
||||
tu_memclr(p_itf, ITF_MEM_RESET_SIZE);
|
||||
tu_fifo_clear(&p_itf->rx_ff);
|
||||
tu_fifo_clear(&p_itf->tx_ff);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
|
||||
{
|
||||
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
|
||||
|
||||
uint8_t const * p_desc = tu_desc_next(desc_itf);
|
||||
uint8_t const * desc_end = p_desc + max_len;
|
||||
|
||||
// Find available interface
|
||||
vendord_interface_t* p_vendor = NULL;
|
||||
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
|
||||
{
|
||||
if ( _vendord_itf[i].ep_in == 0 && _vendord_itf[i].ep_out == 0 )
|
||||
{
|
||||
p_vendor = &_vendord_itf[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
TU_VERIFY(p_vendor, 0);
|
||||
|
||||
p_vendor->itf_num = desc_itf->bInterfaceNumber;
|
||||
if (desc_itf->bNumEndpoints)
|
||||
{
|
||||
// skip non-endpoint descriptors
|
||||
while ( (TUSB_DESC_ENDPOINT != tu_desc_type(p_desc)) && (p_desc < desc_end) )
|
||||
{
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
// Open endpoint pair with usbd helper
|
||||
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in), 0);
|
||||
|
||||
p_desc += desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
|
||||
|
||||
// Prepare for incoming data
|
||||
if ( p_vendor->ep_out )
|
||||
{
|
||||
_prep_out_transaction(p_vendor);
|
||||
}
|
||||
|
||||
if ( p_vendor->ep_in ) tud_vendor_n_write_flush((uint8_t)(p_vendor - _vendord_itf));
|
||||
}
|
||||
|
||||
return (uint16_t) ((uintptr_t) p_desc - (uintptr_t) desc_itf);
|
||||
}
|
||||
|
||||
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) result;
|
||||
|
||||
uint8_t itf = 0;
|
||||
vendord_interface_t* p_itf = _vendord_itf;
|
||||
|
||||
for ( ; ; itf++, p_itf++)
|
||||
{
|
||||
if (itf >= TU_ARRAY_SIZE(_vendord_itf)) return false;
|
||||
|
||||
if ( ( ep_addr == p_itf->ep_out ) || ( ep_addr == p_itf->ep_in ) ) break;
|
||||
}
|
||||
|
||||
if ( ep_addr == p_itf->ep_out )
|
||||
{
|
||||
// Receive new data
|
||||
tu_fifo_write_n(&p_itf->rx_ff, p_itf->epout_buf, (uint16_t) xferred_bytes);
|
||||
|
||||
// Invoked callback if any
|
||||
if (tud_vendor_rx_cb) tud_vendor_rx_cb(itf);
|
||||
|
||||
_prep_out_transaction(p_itf);
|
||||
}
|
||||
else if ( ep_addr == p_itf->ep_in )
|
||||
{
|
||||
if (tud_vendor_tx_cb) tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
|
||||
// Send complete, try to send more if possible
|
||||
tud_vendor_n_write_flush(itf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_VENDOR_DEVICE_H_
|
||||
#define _TUSB_VENDOR_DEVICE_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifndef CFG_TUD_VENDOR_EPSIZE
|
||||
#define CFG_TUD_VENDOR_EPSIZE 64
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Multiple Interfaces)
|
||||
//--------------------------------------------------------------------+
|
||||
bool tud_vendor_n_mounted (uint8_t itf);
|
||||
|
||||
uint32_t tud_vendor_n_available (uint8_t itf);
|
||||
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
|
||||
bool tud_vendor_n_peek (uint8_t itf, uint8_t* ui8);
|
||||
void tud_vendor_n_read_flush (uint8_t itf);
|
||||
|
||||
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
|
||||
uint32_t tud_vendor_n_write_flush (uint8_t itf);
|
||||
uint32_t tud_vendor_n_write_available (uint8_t itf);
|
||||
|
||||
static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
|
||||
|
||||
// backward compatible
|
||||
#define tud_vendor_n_flush(itf) tud_vendor_n_write_flush(itf)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Single Port)
|
||||
//--------------------------------------------------------------------+
|
||||
static inline bool tud_vendor_mounted (void);
|
||||
static inline uint32_t tud_vendor_available (void);
|
||||
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize);
|
||||
static inline bool tud_vendor_peek (uint8_t* ui8);
|
||||
static inline void tud_vendor_read_flush (void);
|
||||
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize);
|
||||
static inline uint32_t tud_vendor_write_str (char const* str);
|
||||
static inline uint32_t tud_vendor_write_available (void);
|
||||
static inline uint32_t tud_vendor_write_flush (void);
|
||||
|
||||
// backward compatible
|
||||
#define tud_vendor_flush() tud_vendor_write_flush()
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API (weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when received new data
|
||||
TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf);
|
||||
// Invoked when last rx transfer finished
|
||||
TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Inline Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str)
|
||||
{
|
||||
return tud_vendor_n_write(itf, str, strlen(str));
|
||||
}
|
||||
|
||||
static inline bool tud_vendor_mounted (void)
|
||||
{
|
||||
return tud_vendor_n_mounted(0);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_vendor_available (void)
|
||||
{
|
||||
return tud_vendor_n_available(0);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
|
||||
{
|
||||
return tud_vendor_n_read(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
static inline bool tud_vendor_peek (uint8_t* ui8)
|
||||
{
|
||||
return tud_vendor_n_peek(0, ui8);
|
||||
}
|
||||
|
||||
static inline void tud_vendor_read_flush(void)
|
||||
{
|
||||
tud_vendor_n_read_flush(0);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
|
||||
{
|
||||
return tud_vendor_n_write(0, buffer, bufsize);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_vendor_write_flush (void)
|
||||
{
|
||||
return tud_vendor_n_write_flush(0);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_vendor_write_str (char const* str)
|
||||
{
|
||||
return tud_vendor_n_write_str(0, str);
|
||||
}
|
||||
|
||||
static inline uint32_t tud_vendor_write_available (void)
|
||||
{
|
||||
return tud_vendor_n_write_available(0);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void vendord_init(void);
|
||||
void vendord_reset(uint8_t rhport);
|
||||
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_VENDOR_DEVICE_H_ */
|
146
pico-sdk/lib/tinyusb/src/class/vendor/vendor_host.c
vendored
146
pico-sdk/lib/tinyusb/src/class/vendor/vendor_host.c
vendored
@ -1,146 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if (CFG_TUH_ENABLED && CFG_TUH_VENDOR)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INCLUDE
|
||||
//--------------------------------------------------------------------+
|
||||
#include "host/usbh.h"
|
||||
#include "vendor_host.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
custom_interface_info_t custom_interface[CFG_TUH_DEVICE_MAX];
|
||||
|
||||
static tusb_error_t cush_validate_paras(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length)
|
||||
{
|
||||
if ( !tusbh_custom_is_mounted(dev_addr, vendor_id, product_id) )
|
||||
{
|
||||
return TUSB_ERROR_DEVICE_NOT_READY;
|
||||
}
|
||||
|
||||
TU_ASSERT( p_buffer != NULL && length != 0, TUSB_ERROR_INVALID_PARA);
|
||||
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION API (need to check parameters)
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_error_t tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length)
|
||||
{
|
||||
TU_ASSERT_ERR( cush_validate_paras(dev_addr, vendor_id, product_id, p_buffer, length) );
|
||||
|
||||
if ( !hcd_pipe_is_idle(custom_interface[dev_addr-1].pipe_in) )
|
||||
{
|
||||
return TUSB_ERROR_INTERFACE_IS_BUSY;
|
||||
}
|
||||
|
||||
(void) usbh_edpt_xfer( custom_interface[dev_addr-1].pipe_in, p_buffer, length);
|
||||
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
|
||||
tusb_error_t tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void const * p_data, uint16_t length)
|
||||
{
|
||||
TU_ASSERT_ERR( cush_validate_paras(dev_addr, vendor_id, product_id, p_data, length) );
|
||||
|
||||
if ( !hcd_pipe_is_idle(custom_interface[dev_addr-1].pipe_out) )
|
||||
{
|
||||
return TUSB_ERROR_INTERFACE_IS_BUSY;
|
||||
}
|
||||
|
||||
(void) usbh_edpt_xfer( custom_interface[dev_addr-1].pipe_out, p_data, length);
|
||||
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH-CLASS API
|
||||
//--------------------------------------------------------------------+
|
||||
void cush_init(void)
|
||||
{
|
||||
tu_memclr(&custom_interface, sizeof(custom_interface_info_t) * CFG_TUH_DEVICE_MAX);
|
||||
}
|
||||
|
||||
tusb_error_t cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
|
||||
{
|
||||
// FIXME quick hack to test lpc1k custom class with 2 bulk endpoints
|
||||
uint8_t const *p_desc = (uint8_t const *) p_interface_desc;
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
//------------- Bulk Endpoints Descriptor -------------//
|
||||
for(uint32_t i=0; i<2; i++)
|
||||
{
|
||||
tusb_desc_endpoint_t const *p_endpoint = (tusb_desc_endpoint_t const *) p_desc;
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint->bDescriptorType, TUSB_ERROR_INVALID_PARA);
|
||||
|
||||
pipe_handle_t * p_pipe_hdl = ( p_endpoint->bEndpointAddress & TUSB_DIR_IN_MASK ) ?
|
||||
&custom_interface[dev_addr-1].pipe_in : &custom_interface[dev_addr-1].pipe_out;
|
||||
*p_pipe_hdl = usbh_edpt_open(dev_addr, p_endpoint, TUSB_CLASS_VENDOR_SPECIFIC);
|
||||
TU_ASSERT ( pipehandle_is_valid(*p_pipe_hdl), TUSB_ERROR_HCD_OPEN_PIPE_FAILED );
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
(*p_length) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
|
||||
return TUSB_ERROR_NONE;
|
||||
}
|
||||
|
||||
void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void cush_close(uint8_t dev_addr)
|
||||
{
|
||||
tusb_error_t err1, err2;
|
||||
custom_interface_info_t * p_interface = &custom_interface[dev_addr-1];
|
||||
|
||||
// TODO re-consider to check pipe valid before calling pipe_close
|
||||
if( pipehandle_is_valid( p_interface->pipe_in ) )
|
||||
{
|
||||
err1 = hcd_pipe_close( p_interface->pipe_in );
|
||||
}
|
||||
|
||||
if ( pipehandle_is_valid( p_interface->pipe_out ) )
|
||||
{
|
||||
err2 = hcd_pipe_close( p_interface->pipe_out );
|
||||
}
|
||||
|
||||
tu_memclr(p_interface, sizeof(custom_interface_info_t));
|
||||
|
||||
TU_ASSERT(err1 == TUSB_ERROR_NONE && err2 == TUSB_ERROR_NONE, (void) 0 );
|
||||
}
|
||||
|
||||
#endif
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_VENDOR_HOST_H_
|
||||
#define _TUSB_VENDOR_HOST_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
pipe_handle_t pipe_in;
|
||||
pipe_handle_t pipe_out;
|
||||
}custom_interface_info_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBH-CLASS DRIVER API
|
||||
//--------------------------------------------------------------------+
|
||||
static inline bool tusbh_custom_is_mounted(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id)
|
||||
{
|
||||
(void) vendor_id; // TODO check this later
|
||||
(void) product_id;
|
||||
// return (tusbh_device_get_mounted_class_flag(dev_addr) & TU_BIT(TUSB_CLASS_MAPPED_INDEX_END-1) ) != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tusbh_custom_read(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void * p_buffer, uint16_t length);
|
||||
bool tusbh_custom_write(uint8_t dev_addr, uint16_t vendor_id, uint16_t product_id, void const * p_data, uint16_t length);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void cush_init(void);
|
||||
bool cush_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length);
|
||||
void cush_isr(pipe_handle_t pipe_hdl, xfer_result_t event);
|
||||
void cush_close(uint8_t dev_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_VENDOR_HOST_H_ */
|
@ -37,6 +37,7 @@
|
||||
#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
|
||||
#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) )
|
||||
#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) )
|
||||
#define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d))
|
||||
|
||||
#define TU_U16(_high, _low) ((uint16_t) (((_high) << 8) | (_low)))
|
||||
#define TU_U16_HIGH(_u16) ((uint8_t) (((_u16) >> 8) & 0x00ff))
|
||||
@ -64,6 +65,7 @@
|
||||
// Standard Headers
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
@ -56,7 +56,7 @@
|
||||
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
#define TU_VERIFY_STATIC _Static_assert
|
||||
#elif defined(__CCRX__)
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(Line, __LINE__)[(const_expr) ? 1 : 0];
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(_verify_static_, _TU_COUNTER_)[(const_expr) ? 1 : 0];
|
||||
#else
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
|
||||
#endif
|
||||
|
@ -60,6 +60,7 @@ void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
|
||||
|
||||
static inline void tu_print_buf(uint8_t const* buf, uint32_t bufsize) {
|
||||
for(uint32_t i=0; i<bufsize; i++) tu_printf("%02X ", buf[i]);
|
||||
tu_printf("\r\n");
|
||||
}
|
||||
|
||||
// Log with Level
|
||||
@ -76,7 +77,7 @@ static inline void tu_print_buf(uint8_t const* buf, uint32_t bufsize) {
|
||||
#define TU_LOG1_MEM tu_print_mem
|
||||
#define TU_LOG1_BUF(_x, _n) tu_print_buf((uint8_t const*)(_x), _n)
|
||||
#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\r\n", (unsigned long) (_x) )
|
||||
#define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\r\n", (unsigned long) (_x) )
|
||||
#define TU_LOG1_HEX(_x) tu_printf(#_x " = 0x%lX\r\n", (unsigned long) (_x) )
|
||||
|
||||
// Log Level 2: Warn
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
|
@ -62,7 +62,9 @@ TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex)
|
||||
typedef enum
|
||||
{
|
||||
TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
|
||||
#endif
|
||||
} tu_fifo_copy_mode_t;
|
||||
|
||||
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
|
||||
@ -92,6 +94,7 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
|
||||
// Pull & Push
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
|
||||
// Code adapted from dcd_synopsys.c
|
||||
// TODO generalize with configurable 1 byte or 4 byte each read
|
||||
@ -140,6 +143,7 @@ static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t
|
||||
*reg_tx = tmp32;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// send one item to fifo WITHOUT updating write pointer
|
||||
static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
|
||||
@ -179,7 +183,7 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
|
||||
memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
case TU_FIFO_COPY_CST_FULL_WORDS:
|
||||
// Intended for hardware buffers from which it can be read word by word only
|
||||
if(n <= lin_count)
|
||||
@ -224,6 +228,8 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
|
||||
if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,7 +270,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr,
|
||||
memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
case TU_FIFO_COPY_CST_FULL_WORDS:
|
||||
if ( n <= lin_count )
|
||||
{
|
||||
@ -309,6 +315,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr,
|
||||
// Read data wrapped part
|
||||
if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
default: break;
|
||||
@ -726,10 +733,29 @@ uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
|
||||
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
|
||||
}
|
||||
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
/******************************************************************************/
|
||||
/*!
|
||||
@brief This function will read n elements from the array index specified by
|
||||
the read pointer and increment the read index.
|
||||
This function checks for an overflow and corrects read pointer if required.
|
||||
The dest address will not be incremented which is useful for writing to registers.
|
||||
|
||||
@param[in] f
|
||||
Pointer to the FIFO buffer to manipulate
|
||||
@param[in] buffer
|
||||
The pointer to data location
|
||||
@param[in] n
|
||||
Number of element that buffer can afford
|
||||
|
||||
@returns number of items read from the FIFO
|
||||
*/
|
||||
/******************************************************************************/
|
||||
uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
|
||||
{
|
||||
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
/*!
|
||||
@ -838,6 +864,7 @@ uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
|
||||
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
|
||||
}
|
||||
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
/******************************************************************************/
|
||||
/*!
|
||||
@brief This function will write n elements into the array index specified by
|
||||
@ -857,6 +884,7 @@ uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data,
|
||||
{
|
||||
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
/*!
|
||||
|
@ -102,10 +102,8 @@ extern "C" {
|
||||
* |
|
||||
* -------------------------
|
||||
* | R | 1 | 2 | W | 4 | 5 |
|
||||
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
uint8_t* buffer ; // buffer pointer
|
||||
uint16_t depth ; // max items
|
||||
|
||||
@ -124,16 +122,14 @@ typedef struct
|
||||
|
||||
} tu_fifo_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
uint16_t len_lin ; ///< linear length in item size
|
||||
uint16_t len_wrap ; ///< wrapped length in item size
|
||||
void * ptr_lin ; ///< linear part start pointer
|
||||
void * ptr_wrap ; ///< wrapped part start pointer
|
||||
} tu_fifo_buffer_info_t;
|
||||
|
||||
#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable) \
|
||||
{ \
|
||||
#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable){\
|
||||
.buffer = _buffer, \
|
||||
.depth = _depth, \
|
||||
.item_size = sizeof(_type), \
|
||||
@ -144,32 +140,31 @@ typedef struct
|
||||
uint8_t _name##_buf[_depth*sizeof(_type)]; \
|
||||
tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable)
|
||||
|
||||
|
||||
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
|
||||
bool tu_fifo_clear(tu_fifo_t *f);
|
||||
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex)
|
||||
{
|
||||
void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) {
|
||||
f->mutex_wr = wr_mutex;
|
||||
f->mutex_rd = rd_mutex;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex)
|
||||
|
||||
#endif
|
||||
|
||||
bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
|
||||
uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n);
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n);
|
||||
#endif
|
||||
|
||||
bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
|
||||
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
|
||||
#ifdef TUP_MEM_CONST_ADDR
|
||||
uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
|
||||
#endif
|
||||
|
||||
bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer);
|
||||
uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
|
||||
@ -182,8 +177,7 @@ bool tu_fifo_overflowed (tu_fifo_t* f);
|
||||
void tu_fifo_correct_read_pointer (tu_fifo_t* f);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
uint16_t tu_fifo_depth(tu_fifo_t* f)
|
||||
{
|
||||
uint16_t tu_fifo_depth(tu_fifo_t* f) {
|
||||
return f->depth;
|
||||
}
|
||||
|
||||
@ -198,7 +192,6 @@ void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
|
||||
void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info);
|
||||
void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -100,6 +100,13 @@
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MCXA15)
|
||||
// USB0 is chipidea FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS_MCX
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX)
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
@ -107,7 +114,7 @@
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L)
|
||||
#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L, OPT_MCU_KINETIS_K)
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS_KINETIS
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
@ -188,6 +195,7 @@
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F4)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
#define TUP_USBIP_DWC2_TEST_MODE
|
||||
|
||||
// For most mcu, FS has 4, HS has 6. TODO 446/469/479 HS has 9
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
@ -202,6 +210,7 @@
|
||||
// MCU with on-chip HS Phy
|
||||
#if defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F733xx)
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS
|
||||
#define TUP_USBIP_DWC2_TEST_MODE
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32H7)
|
||||
@ -270,6 +279,7 @@
|
||||
defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx)
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUP_USBIP_DWC2_TEST_MODE
|
||||
#else
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#endif
|
||||
@ -322,6 +332,9 @@
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2) && (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421))
|
||||
#error "MCUs are only supported with CFG_TUH_MAX3421 enabled"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Dialog
|
||||
//--------------------------------------------------------------------+
|
||||
@ -391,12 +404,24 @@
|
||||
|
||||
//------------- WCH -------------//
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32V307)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
// v307 support both FS and HS
|
||||
#define TUP_USBIP_WCH_USBHS
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // default to highspeed
|
||||
#define TUP_DCD_ENDPOINT_MAX (CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED ? 16 : 8)
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32F20X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUP_USBIP_WCH_USBHS
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // default to highspeed
|
||||
#define TUP_DCD_ENDPOINT_MAX (CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED ? 16 : 8)
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32V20X)
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@ -419,7 +444,7 @@
|
||||
#define TUP_MCU_MULTIPLE_CORE 0
|
||||
#endif
|
||||
|
||||
#ifndef TUP_DCD_ENDPOINT_MAX
|
||||
#if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED
|
||||
#warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8"
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#endif
|
||||
@ -434,4 +459,12 @@
|
||||
#define TU_ATTR_FAST_FUNC
|
||||
#endif
|
||||
|
||||
#if defined(TUP_USBIP_DWC2) || defined(TUP_USBIP_FSDEV)
|
||||
#define TUP_DCD_EDPT_ISO_ALLOC
|
||||
#endif
|
||||
|
||||
#if defined(TUP_USBIP_DWC2)
|
||||
#define TUP_MEM_CONST_ADDR
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -60,7 +60,7 @@ typedef struct {
|
||||
tu_fifo_t ff;
|
||||
|
||||
// mutex: read if ep rx, write if e tx
|
||||
OSAL_MUTEX_DEF(ff_mutex);
|
||||
OSAL_MUTEX_DEF(ff_mutexdef);
|
||||
|
||||
}tu_edpt_stream_t;
|
||||
|
||||
@ -87,15 +87,17 @@ bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex);
|
||||
// Endpoint Stream
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Init an stream, should only be called once
|
||||
// Init an endpoint stream
|
||||
bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable,
|
||||
void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize);
|
||||
|
||||
// Deinit an endpoint stream
|
||||
bool tu_edpt_stream_deinit(tu_edpt_stream_t* s);
|
||||
|
||||
// Open an stream for an endpoint
|
||||
// hwid is either device address (host mode) or rhport (device mode)
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_edpt_stream_open(tu_edpt_stream_t* s, uint8_t hwid, tusb_desc_endpoint_t const *desc_ep)
|
||||
{
|
||||
void tu_edpt_stream_open(tu_edpt_stream_t* s, uint8_t hwid, tusb_desc_endpoint_t const *desc_ep) {
|
||||
tu_fifo_clear(&s->ff);
|
||||
s->hwid = hwid;
|
||||
s->ep_addr = desc_ep->bEndpointAddress;
|
||||
@ -103,16 +105,14 @@ void tu_edpt_stream_open(tu_edpt_stream_t* s, uint8_t hwid, tusb_desc_endpoint_t
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
void tu_edpt_stream_close(tu_edpt_stream_t* s)
|
||||
{
|
||||
void tu_edpt_stream_close(tu_edpt_stream_t* s) {
|
||||
s->hwid = 0;
|
||||
s->ep_addr = 0;
|
||||
}
|
||||
|
||||
// Clear fifo
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool tu_edpt_stream_clear(tu_edpt_stream_t* s)
|
||||
{
|
||||
bool tu_edpt_stream_clear(tu_edpt_stream_t* s) {
|
||||
return tu_fifo_clear(&s->ff);
|
||||
}
|
||||
|
||||
@ -131,8 +131,7 @@ bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferr
|
||||
|
||||
// Get the number of bytes available for writing
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
uint32_t tu_edpt_stream_write_available(tu_edpt_stream_t* s)
|
||||
{
|
||||
uint32_t tu_edpt_stream_write_available(tu_edpt_stream_t* s) {
|
||||
return (uint32_t) tu_fifo_remaining(&s->ff);
|
||||
}
|
||||
|
||||
|
@ -24,12 +24,8 @@
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup group_usb_definitions
|
||||
* \defgroup USBDef_Type USB Types
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_TYPES_H_
|
||||
#define _TUSB_TYPES_H_
|
||||
#ifndef TUSB_TYPES_H_
|
||||
#define TUSB_TYPES_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@ -44,43 +40,38 @@
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
/// defined base on EHCI specs value for Endpoint Speed
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_SPEED_FULL = 0,
|
||||
TUSB_SPEED_LOW = 1,
|
||||
TUSB_SPEED_HIGH = 2,
|
||||
TUSB_SPEED_INVALID = 0xff,
|
||||
}tusb_speed_t;
|
||||
} tusb_speed_t;
|
||||
|
||||
/// defined base on USB Specs Endpoint's bmAttributes
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_XFER_CONTROL = 0 ,
|
||||
TUSB_XFER_ISOCHRONOUS ,
|
||||
TUSB_XFER_BULK ,
|
||||
TUSB_XFER_INTERRUPT
|
||||
}tusb_xfer_type_t;
|
||||
} tusb_xfer_type_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_DIR_OUT = 0,
|
||||
TUSB_DIR_IN = 1,
|
||||
|
||||
TUSB_DIR_IN_MASK = 0x80
|
||||
}tusb_dir_t;
|
||||
} tusb_dir_t;
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
TUSB_EPSIZE_BULK_FS = 64,
|
||||
TUSB_EPSIZE_BULK_HS= 512,
|
||||
TUSB_EPSIZE_BULK_HS = 512,
|
||||
|
||||
TUSB_EPSIZE_ISO_FS_MAX = 1023,
|
||||
TUSB_EPSIZE_ISO_HS_MAX = 1024,
|
||||
};
|
||||
|
||||
/// Isochronous End Point Attributes
|
||||
typedef enum
|
||||
{
|
||||
/// Isochronous Endpoint Attributes
|
||||
typedef enum {
|
||||
TUSB_ISO_EP_ATT_NO_SYNC = 0x00,
|
||||
TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04,
|
||||
TUSB_ISO_EP_ATT_ADAPTIVE = 0x08,
|
||||
@ -88,11 +79,10 @@ typedef enum
|
||||
TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point
|
||||
TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point
|
||||
TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback
|
||||
}tusb_iso_ep_attribute_t;
|
||||
} tusb_iso_ep_attribute_t;
|
||||
|
||||
/// USB Descriptor Types
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_DESC_DEVICE = 0x01,
|
||||
TUSB_DESC_CONFIGURATION = 0x02,
|
||||
TUSB_DESC_STRING = 0x03,
|
||||
@ -119,10 +109,9 @@ typedef enum
|
||||
|
||||
TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30,
|
||||
TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31
|
||||
}tusb_desc_type_t;
|
||||
} tusb_desc_type_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_REQ_GET_STATUS = 0 ,
|
||||
TUSB_REQ_CLEAR_FEATURE = 1 ,
|
||||
TUSB_REQ_RESERVED = 2 ,
|
||||
@ -136,25 +125,22 @@ typedef enum
|
||||
TUSB_REQ_GET_INTERFACE = 10 ,
|
||||
TUSB_REQ_SET_INTERFACE = 11 ,
|
||||
TUSB_REQ_SYNCH_FRAME = 12
|
||||
}tusb_request_code_t;
|
||||
} tusb_request_code_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_REQ_FEATURE_EDPT_HALT = 0,
|
||||
TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1,
|
||||
TUSB_REQ_FEATURE_TEST_MODE = 2
|
||||
}tusb_request_feature_selector_t;
|
||||
} tusb_request_feature_selector_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_REQ_TYPE_STANDARD = 0,
|
||||
TUSB_REQ_TYPE_CLASS,
|
||||
TUSB_REQ_TYPE_VENDOR,
|
||||
TUSB_REQ_TYPE_INVALID
|
||||
} tusb_request_type_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_REQ_RCPT_DEVICE =0,
|
||||
TUSB_REQ_RCPT_INTERFACE,
|
||||
TUSB_REQ_RCPT_ENDPOINT,
|
||||
@ -162,8 +148,7 @@ typedef enum
|
||||
} tusb_request_recipient_t;
|
||||
|
||||
// https://www.usb.org/defined-class-codes
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
TUSB_CLASS_UNSPECIFIED = 0 ,
|
||||
TUSB_CLASS_AUDIO = 1 ,
|
||||
TUSB_CLASS_CDC = 2 ,
|
||||
@ -187,26 +172,23 @@ typedef enum
|
||||
TUSB_CLASS_MISC = 0xEF ,
|
||||
TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE ,
|
||||
TUSB_CLASS_VENDOR_SPECIFIC = 0xFF
|
||||
}tusb_class_code_t;
|
||||
} tusb_class_code_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MISC_SUBCLASS_COMMON = 2
|
||||
}misc_subclass_type_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
MISC_PROTOCOL_IAD = 1
|
||||
}misc_protocol_type_t;
|
||||
} misc_protocol_type_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
APP_SUBCLASS_USBTMC = 0x03,
|
||||
APP_SUBCLASS_DFU_RUNTIME = 0x01
|
||||
} app_subclass_type_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
DEVICE_CAPABILITY_WIRELESS_USB = 0x01,
|
||||
DEVICE_CAPABILITY_USB20_EXTENSION = 0x02,
|
||||
DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03,
|
||||
@ -223,11 +205,11 @@ typedef enum
|
||||
DEVICE_CAPABILITY_AUTHENTICATION = 0x0E,
|
||||
DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F,
|
||||
DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10
|
||||
}device_capability_type_t;
|
||||
} device_capability_type_t;
|
||||
|
||||
enum {
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
|
||||
TUSB_DESC_CONFIG_ATT_SELF_POWERED = TU_BIT(6),
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5,
|
||||
TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1u << 6,
|
||||
};
|
||||
|
||||
#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2)
|
||||
@ -235,28 +217,25 @@ enum {
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
XFER_RESULT_SUCCESS = 0,
|
||||
XFER_RESULT_FAILED,
|
||||
XFER_RESULT_STALLED,
|
||||
XFER_RESULT_TIMEOUT,
|
||||
XFER_RESULT_INVALID
|
||||
}xfer_result_t;
|
||||
} xfer_result_t;
|
||||
|
||||
enum // TODO remove
|
||||
{
|
||||
// TODO remove
|
||||
enum {
|
||||
DESC_OFFSET_LEN = 0,
|
||||
DESC_OFFSET_TYPE = 1
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
INTERFACE_INVALID_NUMBER = 0xff
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00,
|
||||
MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01,
|
||||
MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02,
|
||||
@ -268,16 +247,14 @@ typedef enum
|
||||
MS_OS_20_FEATURE_VENDOR_REVISION = 0x08
|
||||
} microsoft_os_20_type_t;
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
CONTROL_STAGE_IDLE,
|
||||
CONTROL_STAGE_SETUP,
|
||||
CONTROL_STAGE_DATA,
|
||||
CONTROL_STAGE_ACK
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
enum {
|
||||
TUSB_INDEX_INVALID_8 = 0xFFu
|
||||
};
|
||||
|
||||
@ -290,15 +267,14 @@ TU_ATTR_PACKED_BEGIN
|
||||
TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
|
||||
/// USB Device Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
|
||||
uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
|
||||
uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H).
|
||||
|
||||
uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific.
|
||||
uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
|
||||
uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis.
|
||||
uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF).
|
||||
uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF).
|
||||
uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF).
|
||||
uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
|
||||
|
||||
uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
|
||||
@ -314,8 +290,7 @@ typedef struct TU_ATTR_PACKED
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct");
|
||||
|
||||
// USB Binary Device Object Store (BOS) Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned for this descriptor
|
||||
@ -325,8 +300,7 @@ typedef struct TU_ATTR_PACKED
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct");
|
||||
|
||||
/// USB Configuration Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration.
|
||||
@ -341,8 +315,7 @@ typedef struct TU_ATTR_PACKED
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct");
|
||||
|
||||
/// USB Interface Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type
|
||||
|
||||
@ -358,8 +331,7 @@ typedef struct TU_ATTR_PACKED
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct");
|
||||
|
||||
/// USB Endpoint Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; // Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; // ENDPOINT Descriptor Type
|
||||
|
||||
@ -379,8 +351,7 @@ typedef struct TU_ATTR_PACKED
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct");
|
||||
|
||||
/// USB Other Speed Configuration Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned
|
||||
@ -393,8 +364,7 @@ typedef struct TU_ATTR_PACKED
|
||||
} tusb_desc_other_speed_t;
|
||||
|
||||
/// USB Device Qualifier Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Device Qualifier Type
|
||||
uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00)
|
||||
@ -411,8 +381,7 @@ typedef struct TU_ATTR_PACKED
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct");
|
||||
|
||||
/// USB Interface Association Descriptor (IAD ECN)
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
|
||||
@ -426,17 +395,17 @@ typedef struct TU_ATTR_PACKED
|
||||
uint8_t iFunction ; ///< Index of the string descriptor describing the interface association.
|
||||
} tusb_desc_interface_assoc_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8, "size is not correct");
|
||||
|
||||
// USB String Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type
|
||||
uint16_t unicode_string[];
|
||||
} tusb_desc_string_t;
|
||||
|
||||
// USB Binary Device Object Store (BOS)
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType ;
|
||||
uint8_t bDevCapabilityType;
|
||||
@ -445,9 +414,8 @@ typedef struct TU_ATTR_PACKED
|
||||
uint8_t CapabilityData[];
|
||||
} tusb_desc_bos_platform_t;
|
||||
|
||||
// USB WebuSB URL Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
// USB WebUSB URL Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bScheme;
|
||||
@ -455,8 +423,7 @@ typedef struct TU_ATTR_PACKED
|
||||
} tusb_desc_webusb_url_t;
|
||||
|
||||
// DFU Functional Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
|
||||
@ -481,7 +448,7 @@ typedef struct TU_ATTR_PACKED
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct TU_ATTR_PACKED{
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
union {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
|
||||
@ -500,7 +467,6 @@ typedef struct TU_ATTR_PACKED{
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");
|
||||
|
||||
|
||||
TU_ATTR_PACKED_END // End of all packed definitions
|
||||
TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
@ -509,36 +475,25 @@ TU_ATTR_BIT_FIELD_ORDER_END
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get direction from Endpoint address
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) {
|
||||
return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
|
||||
}
|
||||
|
||||
// Get Endpoint number from address
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) {
|
||||
return (uint8_t)(addr & (~TUSB_DIR_IN_MASK));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) {
|
||||
return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep)
|
||||
{
|
||||
return tu_le16toh(desc_ep->wMaxPacketSize) & TU_GENMASK(10, 0);
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
|
||||
return tu_le16toh(desc_ep->wMaxPacketSize) & 0x7FF;
|
||||
}
|
||||
|
||||
#if CFG_TUSB_DEBUG
|
||||
TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir)
|
||||
{
|
||||
tu_static const char *str[] = {"out", "in"};
|
||||
return str[dir];
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) {
|
||||
tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
|
||||
return str[t];
|
||||
}
|
||||
@ -549,21 +504,18 @@ TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// return next descriptor
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) {
|
||||
uint8_t const* desc8 = (uint8_t const*) desc;
|
||||
return desc8 + desc8[DESC_OFFSET_LEN];
|
||||
}
|
||||
|
||||
// get descriptor type
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) {
|
||||
return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
|
||||
}
|
||||
|
||||
// get descriptor length
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) {
|
||||
return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
|
||||
}
|
||||
|
||||
@ -580,6 +532,4 @@ uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t b
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_TYPES_H_ */
|
||||
|
||||
/** @} */
|
||||
#endif // TUSB_TYPES_H_
|
||||
|
@ -76,14 +76,14 @@
|
||||
#endif
|
||||
|
||||
// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55
|
||||
#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__)
|
||||
#define TU_BREAKPOINT() do \
|
||||
{ \
|
||||
#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__) || \
|
||||
defined(__ARM7M__) || defined (__ARM7EM__) || defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__)
|
||||
#define TU_BREAKPOINT() do { \
|
||||
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
|
||||
if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
|
||||
} while(0)
|
||||
|
||||
#elif defined(__riscv)
|
||||
#elif defined(__riscv) && !TUP_MCU_ESPRESSIF
|
||||
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
|
||||
|
||||
#elif defined(_mips)
|
||||
|
@ -97,6 +97,14 @@ typedef struct TU_ATTR_ALIGNED(4) {
|
||||
};
|
||||
} dcd_event_t;
|
||||
|
||||
typedef enum {
|
||||
TEST_J = 1,
|
||||
TEST_K,
|
||||
TEST_SE0_NAK,
|
||||
TEST_PACKET,
|
||||
TEST_FORCE_ENABLE,
|
||||
} test_mode_t;
|
||||
|
||||
//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -122,6 +130,9 @@ void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) TU_ATTR_W
|
||||
// Initialize controller to device mode
|
||||
void dcd_init(uint8_t rhport);
|
||||
|
||||
// Deinitialize controller, unset device mode.
|
||||
bool dcd_deinit(uint8_t rhport);
|
||||
|
||||
// Interrupt Handler
|
||||
void dcd_int_handler(uint8_t rhport);
|
||||
|
||||
@ -146,6 +157,13 @@ void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
|
||||
// Enable/Disable Start-of-frame interrupt. Default is disabled
|
||||
void dcd_sof_enable(uint8_t rhport, bool en);
|
||||
|
||||
#if CFG_TUD_TEST_MODE
|
||||
// Check if the test mode is supported, returns true is test mode selector is supported
|
||||
bool dcd_check_test_mode_support(test_mode_t test_selector) TU_ATTR_WEAK;
|
||||
|
||||
// Put device into a test mode (needs power cycle to quit)
|
||||
void dcd_enter_test_mode(uint8_t rhport, test_mode_t test_selector) TU_ATTR_WEAK;
|
||||
#endif
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,9 +37,12 @@ extern "C" {
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Init device stack
|
||||
// Init device stack on roothub port
|
||||
bool tud_init (uint8_t rhport);
|
||||
|
||||
// Deinit device stack on roothub port
|
||||
bool tud_deinit(uint8_t rhport);
|
||||
|
||||
// Check if device stack is already initialized
|
||||
bool tud_inited(void);
|
||||
|
||||
@ -94,6 +97,9 @@ bool tud_disconnect(void);
|
||||
// Return false on unsupported MCUs
|
||||
bool tud_connect(void);
|
||||
|
||||
// Enable or disable the Start Of Frame callback support
|
||||
bool tud_sof_cb_enable(bool en);
|
||||
|
||||
// Carry out Data and Status stage of control transfer
|
||||
// - If len = 0, it is equivalent to sending status only
|
||||
// - If len > wLength : it will be truncated
|
||||
@ -149,6 +155,9 @@ TU_ATTR_WEAK void tud_resume_cb(void);
|
||||
// Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext()
|
||||
void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
|
||||
|
||||
// Invoked when a new (micro) frame started
|
||||
void tud_sof_cb(uint32_t frame_count);
|
||||
|
||||
// Invoked when received control request with VENDOR TYPE
|
||||
TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
|
||||
@ -218,8 +227,8 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\
|
||||
/* CDC Call */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\
|
||||
/* CDC ACM: support line request */\
|
||||
4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 2,\
|
||||
/* CDC ACM: support line request + send break */\
|
||||
4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 6,\
|
||||
/* CDC Union */\
|
||||
5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
|
||||
/* Endpoint Notification */\
|
||||
@ -393,6 +402,11 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
|
||||
|
||||
// For more channels, add definitions here
|
||||
|
||||
/* Standard AC Interrupt Endpoint Descriptor(4.8.2.1) */
|
||||
#define TUD_AUDIO_DESC_STD_AC_INT_EP_LEN 7
|
||||
#define TUD_AUDIO_DESC_STD_AC_INT_EP(_ep, _interval) \
|
||||
TUD_AUDIO_DESC_STD_AC_INT_EP_LEN, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(6), _interval
|
||||
|
||||
/* Standard AS Interface Descriptor(4.9.1) */
|
||||
#define TUD_AUDIO_DESC_STD_AS_INT_LEN 9
|
||||
#define TUD_AUDIO_DESC_STD_AS_INT(_itfnum, _altset, _nEPs, _stridx) \
|
||||
@ -421,7 +435,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
|
||||
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */
|
||||
#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN 7
|
||||
#define TUD_AUDIO_DESC_STD_AS_ISO_FB_EP(_ep, _interval) \
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_NO_SYNC | TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(4), _interval
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_FB_EP_LEN, TUSB_DESC_ENDPOINT, _ep, (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_NO_SYNC | (uint8_t)TUSB_ISO_EP_ATT_EXPLICIT_FB), U16_TO_U8S_LE(4), _interval
|
||||
|
||||
// AUDIO simple descriptor (UAC2) for 1 microphone input
|
||||
// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
|
||||
@ -468,7 +482,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
|
||||
|
||||
@ -517,7 +531,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000)
|
||||
|
||||
@ -565,7 +579,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
|
||||
/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\
|
||||
TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample),\
|
||||
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
|
||||
TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01),\
|
||||
/* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */\
|
||||
TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000),\
|
||||
/* Standard AS Isochronous Feedback Endpoint Descriptor(4.10.2.1) */\
|
||||
|
@ -23,8 +23,8 @@
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
#ifndef _TUSB_USBD_PVT_H_
|
||||
#define _TUSB_USBD_PVT_H_
|
||||
#ifndef TUSB_USBD_PVT_H_
|
||||
#define TUSB_USBD_PVT_H_
|
||||
|
||||
#include "osal/osal.h"
|
||||
#include "common/tusb_fifo.h"
|
||||
@ -35,16 +35,23 @@
|
||||
|
||||
#define TU_LOG_USBD(...) TU_LOG(CFG_TUD_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef enum {
|
||||
SOF_CONSUMER_USER = 0,
|
||||
SOF_CONSUMER_AUDIO,
|
||||
} sof_consumer_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct {
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
|
||||
char const* name;
|
||||
#endif
|
||||
|
||||
void (* init ) (void);
|
||||
bool (* deinit ) (void);
|
||||
void (* reset ) (uint8_t rhport);
|
||||
uint16_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t max_len);
|
||||
bool (* control_xfer_cb ) (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||
@ -110,7 +117,7 @@ bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr) {
|
||||
}
|
||||
|
||||
// Enable SOF interrupt
|
||||
void usbd_sof_enable(uint8_t rhport, bool en);
|
||||
void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Helper
|
||||
|
@ -125,11 +125,14 @@ bool hcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) TU_ATTR_W
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// optional hcd configuration, called by tuh_configure()
|
||||
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) TU_ATTR_WEAK;
|
||||
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param);
|
||||
|
||||
// Initialize controller to host mode
|
||||
bool hcd_init(uint8_t rhport);
|
||||
|
||||
// De-initialize controller
|
||||
bool hcd_deinit(uint8_t rhport);
|
||||
|
||||
// Interrupt Handler
|
||||
void hcd_int_handler(uint8_t rhport, bool in_isr);
|
||||
|
||||
|
@ -182,9 +182,13 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
|
||||
//--------------------------------------------------------------------+
|
||||
// CLASS-USBH API (don't require to verify parameters)
|
||||
//--------------------------------------------------------------------+
|
||||
void hub_init(void)
|
||||
{
|
||||
bool hub_init(void) {
|
||||
tu_memclr(hub_data, sizeof(hub_data));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_deinit(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
|
@ -187,16 +187,14 @@ bool hub_port_get_status (uint8_t hub_addr, uint8_t hub_port, void* resp,
|
||||
bool hub_edpt_status_xfer(uint8_t dev_addr);
|
||||
|
||||
// Reset a port
|
||||
static inline bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return hub_port_set_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Clear Reset Change
|
||||
static inline bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
|
||||
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data);
|
||||
}
|
||||
|
||||
@ -204,7 +202,8 @@ static inline bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_por
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void hub_init (void);
|
||||
bool hub_init (void);
|
||||
bool hub_deinit (void);
|
||||
bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
|
||||
bool hub_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||
bool hub_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -73,11 +73,25 @@ typedef struct {
|
||||
tusb_desc_interface_t desc;
|
||||
} tuh_itf_info_t;
|
||||
|
||||
// ConfigID for tuh_config()
|
||||
// ConfigID for tuh_configure()
|
||||
enum {
|
||||
TUH_CFGID_RPI_PIO_USB_CONFIGURATION = OPT_MCU_RP2040 << 8 // cfg_param: pio_usb_configuration_t
|
||||
TUH_CFGID_INVALID = 0,
|
||||
TUH_CFGID_RPI_PIO_USB_CONFIGURATION = 100, // cfg_param: pio_usb_configuration_t
|
||||
TUH_CFGID_MAX3421 = 200,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t max_nak; // max NAK per endpoint per frame
|
||||
uint8_t cpuctl; // R16: CPU Control Register
|
||||
uint8_t pinctl; // R17: Pin Control Register. FDUPSPI bit is ignored
|
||||
} tuh_configure_max3421_t;
|
||||
|
||||
typedef union {
|
||||
// For TUH_CFGID_RPI_PIO_USB_CONFIGURATION use pio_usb_configuration_t
|
||||
|
||||
tuh_configure_max3421_t max3421;
|
||||
} tuh_configure_param_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// APPLICATION CALLBACK
|
||||
//--------------------------------------------------------------------+
|
||||
@ -109,7 +123,11 @@ bool tuh_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param);
|
||||
// Init host stack
|
||||
bool tuh_init(uint8_t rhport);
|
||||
|
||||
// Deinit host stack on rhport
|
||||
bool tuh_deinit(uint8_t rhport);
|
||||
|
||||
// Check if host stack is already initialized with any roothub ports
|
||||
// To check if an rhport is initialized, use tuh_rhport_is_active()
|
||||
bool tuh_inited(void);
|
||||
|
||||
// Task function should be called in main/rtos loop, extended version of tuh_task()
|
||||
|
@ -36,6 +36,10 @@
|
||||
#endif
|
||||
|
||||
#define TU_LOG_USBH(...) TU_LOG(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
#define TU_LOG_MEM_USBH(...) TU_LOG_MEM(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
#define TU_LOG_BUF_USBH(...) TU_LOG_BUF(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
#define TU_LOG_INT_USBH(...) TU_LOG_INT(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
#define TU_LOG_HEX_USBH(...) TU_LOG_HEX(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
|
||||
|
||||
enum {
|
||||
USBH_EPSIZE_BULK_MAX = (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
|
||||
@ -46,11 +50,9 @@ enum {
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct {
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL
|
||||
char const* name;
|
||||
#endif
|
||||
|
||||
void (* const init )(void);
|
||||
bool (* const init )(void);
|
||||
bool (* const deinit )(void);
|
||||
bool (* const open )(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||
bool (* const set_config )(uint8_t dev_addr, uint8_t itf_num);
|
||||
bool (* const xfer_cb )(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||
|
@ -74,15 +74,18 @@ typedef void (*osal_task_func_t)( void * );
|
||||
// Should be implemented as static inline function in osal_port.h header
|
||||
/*
|
||||
osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
|
||||
bool osal_semaphore_delete(osal_semaphore_t semd_hdl);
|
||||
bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
|
||||
bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec);
|
||||
void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed
|
||||
|
||||
osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
|
||||
bool osal_mutex_delete(osal_mutex_t mutex_hdl)
|
||||
bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
|
||||
bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
|
||||
|
||||
osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
|
||||
bool osal_queue_delete(osal_queue_t qhdl);
|
||||
bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec);
|
||||
bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr);
|
||||
bool osal_queue_empty(osal_queue_t qhdl);
|
||||
|
@ -1,214 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_OSAL_FREERTOS_H_
|
||||
#define _TUSB_OSAL_FREERTOS_H_
|
||||
|
||||
// FreeRTOS Headers
|
||||
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,FreeRTOS.h)
|
||||
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,semphr.h)
|
||||
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,queue.h)
|
||||
#include TU_INCLUDE_PATH(CFG_TUSB_OS_INC_PATH,task.h)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
typedef StaticSemaphore_t osal_semaphore_def_t;
|
||||
typedef StaticSemaphore_t osal_mutex_def_t;
|
||||
#else
|
||||
// not used therefore defined to smallest possible type to save space
|
||||
typedef uint8_t osal_semaphore_def_t;
|
||||
typedef uint8_t osal_mutex_def_t;
|
||||
#endif
|
||||
|
||||
typedef SemaphoreHandle_t osal_semaphore_t;
|
||||
typedef SemaphoreHandle_t osal_mutex_t;
|
||||
typedef QueueHandle_t osal_queue_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t depth;
|
||||
uint16_t item_sz;
|
||||
void* buf;
|
||||
|
||||
#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
|
||||
char const* name;
|
||||
#endif
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
StaticQueue_t sq;
|
||||
#endif
|
||||
} osal_queue_def_t;
|
||||
|
||||
#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
|
||||
#define _OSAL_Q_NAME(_name) .name = #_name
|
||||
#else
|
||||
#define _OSAL_Q_NAME(_name)
|
||||
#endif
|
||||
|
||||
// _int_set is not used with an RTOS
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
static _type _name##_##buf[_depth];\
|
||||
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) };
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) {
|
||||
if ( msec == OSAL_TIMEOUT_WAIT_FOREVER ) return portMAX_DELAY;
|
||||
if ( msec == 0 ) return 0;
|
||||
|
||||
uint32_t ticks = pdMS_TO_TICKS(msec);
|
||||
|
||||
// configTICK_RATE_HZ is less than 1000 and 1 tick > 1 ms
|
||||
// we still need to delay at least 1 tick
|
||||
if ( ticks == 0 ) ticks = 1;
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
vTaskDelay(pdMS_TO_TICKS(msec));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t *semdef) {
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
return xSemaphoreCreateBinaryStatic(semdef);
|
||||
#else
|
||||
(void) semdef;
|
||||
return xSemaphoreCreateBinary();
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
if ( !in_isr ) {
|
||||
return xSemaphoreGive(sem_hdl) != 0;
|
||||
} else {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken);
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
|
||||
// not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7
|
||||
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
|
||||
#else
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
#endif
|
||||
|
||||
return res != 0;
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return xSemaphoreTake(sem_hdl, _osal_ms2tick(msec));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
|
||||
xQueueReset(sem_hdl);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API (priority inheritance)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
return xSemaphoreCreateMutexStatic(mdef);
|
||||
#else
|
||||
(void) mdef;
|
||||
return xSemaphoreCreateMutex();
|
||||
#endif
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return osal_semaphore_wait(mutex_hdl, msec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return xSemaphoreGive(mutex_hdl);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
osal_queue_t q;
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
q = xQueueCreateStatic(qdef->depth, qdef->item_sz, (uint8_t*) qdef->buf, &qdef->sq);
|
||||
#else
|
||||
q = xQueueCreate(qdef->depth, qdef->item_sz);
|
||||
#endif
|
||||
|
||||
#if defined(configQUEUE_REGISTRY_SIZE) && (configQUEUE_REGISTRY_SIZE>0)
|
||||
vQueueAddToRegistry(q, qdef->name);
|
||||
#endif
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
return xQueueReceive(qhdl, data, _osal_ms2tick(msec));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
|
||||
if ( !in_isr ) {
|
||||
return xQueueSendToBack(qhdl, data, OSAL_TIMEOUT_WAIT_FOREVER) != 0;
|
||||
} else {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
BaseType_t res = xQueueSendToBackFromISR(qhdl, data, &xHigherPriorityTaskWoken);
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
|
||||
// not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7 (IDF v5)
|
||||
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
|
||||
#else
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
#endif
|
||||
|
||||
return res != 0;
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
return uxQueueMessagesWaiting(qhdl) == 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef OSAL_MYNEWT_H_
|
||||
#define OSAL_MYNEWT_H_
|
||||
|
||||
#include "os/os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
|
||||
{
|
||||
os_time_delay( os_time_ms_to_ticks32(msec) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct os_sem osal_semaphore_def_t;
|
||||
typedef struct os_sem* osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
|
||||
{
|
||||
return (os_sem_init(semdef, 0) == OS_OK) ? (osal_semaphore_t) semdef : NULL;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
|
||||
{
|
||||
(void) in_isr;
|
||||
return os_sem_release(sem_hdl) == OS_OK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec)
|
||||
{
|
||||
uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? OS_TIMEOUT_NEVER : os_time_ms_to_ticks32(msec);
|
||||
return os_sem_pend(sem_hdl, ticks) == OS_OK;
|
||||
}
|
||||
|
||||
static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
|
||||
{
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API (priority inheritance)
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct os_mutex osal_mutex_def_t;
|
||||
typedef struct os_mutex* osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
|
||||
{
|
||||
return (os_mutex_init(mdef) == OS_OK) ? (osal_mutex_t) mdef : NULL;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec)
|
||||
{
|
||||
uint32_t const ticks = (msec == OSAL_TIMEOUT_WAIT_FOREVER) ? OS_TIMEOUT_NEVER : os_time_ms_to_ticks32(msec);
|
||||
return os_mutex_pend(mutex_hdl, ticks) == OS_OK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
|
||||
{
|
||||
return os_mutex_release(mutex_hdl) == OS_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
static _type _name##_##buf[_depth];\
|
||||
static struct os_event _name##_##evbuf[_depth];\
|
||||
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, .evbuf = _name##_##evbuf};\
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t depth;
|
||||
uint16_t item_sz;
|
||||
void* buf;
|
||||
void* evbuf;
|
||||
|
||||
struct os_mempool mpool;
|
||||
struct os_mempool epool;
|
||||
|
||||
struct os_eventq evq;
|
||||
}osal_queue_def_t;
|
||||
|
||||
typedef osal_queue_def_t* osal_queue_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
|
||||
{
|
||||
if ( OS_OK != os_mempool_init(&qdef->mpool, qdef->depth, qdef->item_sz, qdef->buf, "usbd queue") ) return NULL;
|
||||
if ( OS_OK != os_mempool_init(&qdef->epool, qdef->depth, sizeof(struct os_event), qdef->evbuf, "usbd evqueue") ) return NULL;
|
||||
|
||||
os_eventq_init(&qdef->evq);
|
||||
return (osal_queue_t) qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
|
||||
{
|
||||
(void) msec; // os_eventq_get() does not take timeout, always behave as msec = WAIT_FOREVER
|
||||
|
||||
struct os_event* ev;
|
||||
ev = os_eventq_get(&qhdl->evq);
|
||||
|
||||
memcpy(data, ev->ev_arg, qhdl->item_sz); // copy message
|
||||
os_memblock_put(&qhdl->mpool, ev->ev_arg); // put back mem block
|
||||
os_memblock_put(&qhdl->epool, ev); // put back ev block
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
|
||||
{
|
||||
(void) in_isr;
|
||||
|
||||
// get a block from mem pool for data
|
||||
void* ptr = os_memblock_get(&qhdl->mpool);
|
||||
if (!ptr) return false;
|
||||
memcpy(ptr, data, qhdl->item_sz);
|
||||
|
||||
// get a block from event pool to put into queue
|
||||
struct os_event* ev = (struct os_event*) os_memblock_get(&qhdl->epool);
|
||||
if (!ev)
|
||||
{
|
||||
os_memblock_put(&qhdl->mpool, ptr);
|
||||
return false;
|
||||
}
|
||||
tu_memclr(ev, sizeof(struct os_event));
|
||||
ev->ev_arg = ptr;
|
||||
|
||||
os_eventq_put(&qhdl->evq, ev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
|
||||
{
|
||||
return STAILQ_EMPTY(&qhdl->evq.evq_list);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OSAL_MYNEWT_H_ */
|
@ -54,6 +54,12 @@ TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
(void) semd_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
sem_hdl->count++;
|
||||
@ -90,6 +96,11 @@ TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_de
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
(void) mutex_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return osal_semaphore_wait(mutex_hdl, msec);
|
||||
}
|
||||
@ -143,6 +154,11 @@ TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_de
|
||||
return (osal_queue_t) qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
(void) qhdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
(void) msec; // not used, always behave as msec = 0
|
||||
|
||||
@ -164,7 +180,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void
|
||||
_osal_q_unlock(qhdl);
|
||||
}
|
||||
|
||||
TU_ASSERT(success);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,8 @@
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_OSAL_PICO_H_
|
||||
#define _TUSB_OSAL_PICO_H_
|
||||
#ifndef TUSB_OSAL_PICO_H_
|
||||
#define TUSB_OSAL_PICO_H_
|
||||
|
||||
#include "pico/time.h"
|
||||
#include "pico/sem.h"
|
||||
@ -33,42 +33,42 @@
|
||||
#include "pico/critical_section.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
sleep_ms(msec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Binary Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct semaphore osal_semaphore_def_t, *osal_semaphore_t;
|
||||
typedef struct semaphore osal_semaphore_def_t, * osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
|
||||
sem_init(semdef, 0, 255);
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
(void) semd_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
sem_release(sem_hdl);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return sem_acquire_timeout_ms(sem_hdl, msec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
|
||||
sem_reset(sem_hdl, 0);
|
||||
}
|
||||
|
||||
@ -76,21 +76,23 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t s
|
||||
// MUTEX API
|
||||
// Within tinyusb, mutex is never used in ISR context
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct mutex osal_mutex_def_t, *osal_mutex_t;
|
||||
typedef struct mutex osal_mutex_def_t, * osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
|
||||
mutex_init(mdef);
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
(void) mutex_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return mutex_enter_timeout_ms(mutex_hdl, msec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
mutex_exit(mutex_hdl);
|
||||
return true;
|
||||
}
|
||||
@ -100,8 +102,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
|
||||
//--------------------------------------------------------------------+
|
||||
#include "common/tusb_fifo.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
tu_fifo_t ff;
|
||||
struct critical_section critsec; // osal_queue may be used in IRQs, so need critical section
|
||||
} osal_queue_def_t;
|
||||
@ -115,60 +116,39 @@ typedef osal_queue_def_t* osal_queue_t;
|
||||
.ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \
|
||||
}
|
||||
|
||||
// lock queue by disable USB interrupt
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _osal_q_lock(osal_queue_t qhdl)
|
||||
{
|
||||
critical_section_enter_blocking(&qhdl->critsec);
|
||||
}
|
||||
|
||||
// unlock queue
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _osal_q_unlock(osal_queue_t qhdl)
|
||||
{
|
||||
critical_section_exit(&qhdl->critsec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
critical_section_init(&qdef->critsec);
|
||||
tu_fifo_clear(&qdef->ff);
|
||||
return (osal_queue_t) qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
osal_queue_def_t* qdef = (osal_queue_def_t*) qhdl;
|
||||
critical_section_deinit(&qdef->critsec);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
(void) msec; // not used, always behave as msec = 0
|
||||
|
||||
// TODO: revisit... docs say that mutexes are never used from IRQ context,
|
||||
// however osal_queue_recieve may be. therefore my assumption is that
|
||||
// the fifo mutex is not populated for queues used from an IRQ context
|
||||
//assert(!qhdl->ff.mutex);
|
||||
|
||||
_osal_q_lock(qhdl);
|
||||
critical_section_enter_blocking(&qhdl->critsec);
|
||||
bool success = tu_fifo_read(&qhdl->ff, data);
|
||||
_osal_q_unlock(qhdl);
|
||||
critical_section_exit(&qhdl->critsec);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
|
||||
{
|
||||
// TODO: revisit... docs say that mutexes are never used from IRQ context,
|
||||
// however osal_queue_recieve may be. therefore my assumption is that
|
||||
// the fifo mutex is not populated for queues used from an IRQ context
|
||||
//assert(!qhdl->ff.mutex);
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
|
||||
(void) in_isr;
|
||||
|
||||
_osal_q_lock(qhdl);
|
||||
critical_section_enter_blocking(&qhdl->critsec);
|
||||
bool success = tu_fifo_write(&qhdl->ff, data);
|
||||
_osal_q_unlock(qhdl);
|
||||
|
||||
TU_ASSERT(success);
|
||||
critical_section_exit(&qhdl->critsec);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
// TODO: revisit; whether this is true or not currently, tu_fifo_empty is a single
|
||||
// volatile read.
|
||||
|
||||
@ -178,7 +158,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_OSAL_PICO_H_ */
|
||||
#endif
|
||||
|
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 tfx2001 (2479727366@qq.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_OSAL_RTTHREAD_H_
|
||||
#define _TUSB_OSAL_RTTHREAD_H_
|
||||
|
||||
// RT-Thread Headers
|
||||
#include "rtthread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
|
||||
rt_thread_mdelay(msec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct rt_semaphore osal_semaphore_def_t;
|
||||
typedef rt_sem_t osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t
|
||||
osal_semaphore_create(osal_semaphore_def_t *semdef) {
|
||||
rt_sem_init(semdef, "tusb", 0, RT_IPC_FLAG_PRIO);
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
return rt_sem_release(sem_hdl) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return rt_sem_take(sem_hdl, rt_tick_from_millisecond((rt_int32_t) msec)) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
|
||||
rt_sem_control(sem_hdl, RT_IPC_CMD_RESET, 0);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API (priority inheritance)
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct rt_mutex osal_mutex_def_t;
|
||||
typedef rt_mutex_t osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
|
||||
rt_mutex_init(mdef, "tusb", RT_IPC_FLAG_PRIO);
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return rt_mutex_take(mutex_hdl, rt_tick_from_millisecond((rt_int32_t) msec)) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return rt_mutex_release(mutex_hdl) == RT_EOK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
static _type _name##_##buf[_depth]; \
|
||||
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf };
|
||||
|
||||
typedef struct {
|
||||
uint16_t depth;
|
||||
uint16_t item_sz;
|
||||
void *buf;
|
||||
|
||||
struct rt_messagequeue sq;
|
||||
} osal_queue_def_t;
|
||||
|
||||
typedef rt_mq_t osal_queue_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t *qdef) {
|
||||
rt_mq_init(&(qdef->sq), "tusb", qdef->buf, qdef->item_sz,
|
||||
qdef->item_sz * qdef->depth, RT_IPC_FLAG_PRIO);
|
||||
return &(qdef->sq);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void *data, uint32_t msec) {
|
||||
|
||||
rt_tick_t tick = rt_tick_from_millisecond((rt_int32_t) msec);
|
||||
return rt_mq_recv(qhdl, data, qhdl->msg_size, tick) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
|
||||
(void) in_isr;
|
||||
return rt_mq_send(qhdl, (void *)data, qhdl->msg_size) == RT_EOK;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
return (qhdl->entry) == 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_OSAL_RTTHREAD_H_ */
|
@ -1,170 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Tian Yunhao (t123yh)
|
||||
* 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.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_OSAL_RTX4_H_
|
||||
#define _TUSB_OSAL_RTX4_H_
|
||||
|
||||
#include <rtl.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
|
||||
{
|
||||
uint16_t hi = msec >> 16;
|
||||
uint16_t lo = msec;
|
||||
while (hi--) {
|
||||
os_dly_wait(0xFFFE);
|
||||
}
|
||||
os_dly_wait(lo);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t msec2wait(uint32_t msec) {
|
||||
if (msec == OSAL_TIMEOUT_WAIT_FOREVER)
|
||||
return 0xFFFF;
|
||||
else if (msec >= 0xFFFE)
|
||||
return 0xFFFE;
|
||||
else
|
||||
return msec;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef OS_SEM osal_semaphore_def_t;
|
||||
typedef OS_ID osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline OS_ID osal_semaphore_create(osal_semaphore_def_t* semdef) {
|
||||
os_sem_init(semdef, 0);
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
if ( !in_isr ) {
|
||||
os_sem_send(sem_hdl);
|
||||
} else {
|
||||
isr_sem_send(sem_hdl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
return os_sem_wait(sem_hdl, msec2wait(msec)) != OS_R_TMO;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API (priority inheritance)
|
||||
//--------------------------------------------------------------------+
|
||||
typedef OS_MUT osal_mutex_def_t;
|
||||
typedef OS_ID osal_mutex_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
|
||||
{
|
||||
os_mut_init(mdef);
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
|
||||
{
|
||||
return os_mut_wait(mutex_hdl, msec2wait(msec)) != OS_R_TMO;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
|
||||
{
|
||||
return os_mut_release(mutex_hdl) == OS_R_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// role device/host is used by OS NONE for mutex (disable usb isr) only
|
||||
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
os_mbx_declare(_name##__mbox, _depth); \
|
||||
_declare_box(_name##__pool, sizeof(_type), _depth); \
|
||||
osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .pool = _name##__pool, .mbox = _name##__mbox };
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t depth;
|
||||
uint16_t item_sz;
|
||||
U32* pool;
|
||||
U32* mbox;
|
||||
}osal_queue_def_t;
|
||||
|
||||
typedef osal_queue_def_t* osal_queue_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
|
||||
{
|
||||
os_mbx_init(qdef->mbox, (qdef->depth + 4) * 4);
|
||||
_init_box(qdef->pool, ((qdef->item_sz+3)/4)*(qdef->depth) + 3, qdef->item_sz);
|
||||
return qdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
|
||||
{
|
||||
void* buf;
|
||||
os_mbx_wait(qhdl->mbox, &buf, msec2wait(msec));
|
||||
memcpy(data, buf, qhdl->item_sz);
|
||||
_free_box(qhdl->pool, buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
|
||||
{
|
||||
void* buf = _alloc_box(qhdl->pool);
|
||||
memcpy(buf, data, qhdl->item_sz);
|
||||
if ( !in_isr )
|
||||
{
|
||||
os_mbx_send(qhdl->mbox, buf, 0xFFFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
isr_mbx_send(qhdl->mbox, buf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
|
||||
{
|
||||
return os_mbx_check(qhdl->mbox) == qhdl->depth;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -48,7 +48,7 @@
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
// Init these in dcd_init
|
||||
static uint8_t *next_buffer_ptr;
|
||||
static uint8_t* next_buffer_ptr;
|
||||
|
||||
// USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in.
|
||||
static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
|
||||
@ -56,46 +56,41 @@ static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
|
||||
// SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd
|
||||
static bool _sof_enable = false;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) {
|
||||
return &hw_endpoints[num][dir];
|
||||
}
|
||||
|
||||
static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) {
|
||||
uint8_t num = tu_edpt_number(ep_addr);
|
||||
tusb_dir_t dir = tu_edpt_dir(ep_addr);
|
||||
return hw_endpoint_get_by_num(num, dir);
|
||||
}
|
||||
|
||||
static void _hw_endpoint_alloc(struct hw_endpoint *ep, uint8_t transfer_type)
|
||||
{
|
||||
static void _hw_endpoint_alloc(struct hw_endpoint* ep, uint8_t transfer_type) {
|
||||
// size must be multiple of 64
|
||||
uint size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u;
|
||||
|
||||
// double buffered Bulk endpoint
|
||||
if ( transfer_type == TUSB_XFER_BULK )
|
||||
{
|
||||
if (transfer_type == TUSB_XFER_BULK) {
|
||||
size *= 2u;
|
||||
}
|
||||
|
||||
ep->hw_data_buf = next_buffer_ptr;
|
||||
next_buffer_ptr += size;
|
||||
|
||||
assert(((uintptr_t )next_buffer_ptr & 0b111111u) == 0);
|
||||
assert(((uintptr_t) next_buffer_ptr & 0b111111u) == 0);
|
||||
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
|
||||
hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
|
||||
|
||||
pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf);
|
||||
|
||||
// Fill in endpoint control register with buffer offset
|
||||
uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
|
||||
uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
|
||||
|
||||
*ep->endpoint_control = reg;
|
||||
}
|
||||
|
||||
static void _hw_endpoint_close(struct hw_endpoint *ep)
|
||||
{
|
||||
static void _hw_endpoint_close(struct hw_endpoint* ep) {
|
||||
// Clear hardware registers and then zero the struct
|
||||
// Clears endpoint enable
|
||||
*ep->endpoint_control = 0;
|
||||
@ -106,29 +101,25 @@ static void _hw_endpoint_close(struct hw_endpoint *ep)
|
||||
|
||||
// Reclaim buffer space if all endpoints are closed
|
||||
bool reclaim_buffers = true;
|
||||
for ( uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++ )
|
||||
{
|
||||
if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL || hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL)
|
||||
{
|
||||
for (uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++) {
|
||||
if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL ||
|
||||
hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL) {
|
||||
reclaim_buffers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reclaim_buffers)
|
||||
{
|
||||
if (reclaim_buffers) {
|
||||
next_buffer_ptr = &usb_dpram->epx_data[0];
|
||||
}
|
||||
}
|
||||
|
||||
static void hw_endpoint_close(uint8_t ep_addr)
|
||||
{
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
static void hw_endpoint_close(uint8_t ep_addr) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
_hw_endpoint_close(ep);
|
||||
}
|
||||
|
||||
static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type)
|
||||
{
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
|
||||
const uint8_t num = tu_edpt_number(ep_addr);
|
||||
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
|
||||
@ -143,35 +134,26 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t
|
||||
ep->transfer_type = transfer_type;
|
||||
|
||||
// Every endpoint has a buffer control register in dpram
|
||||
if ( dir == TUSB_DIR_IN )
|
||||
{
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
|
||||
}
|
||||
|
||||
// Clear existing buffer control state
|
||||
*ep->buffer_control = 0;
|
||||
|
||||
if ( num == 0 )
|
||||
{
|
||||
if (num == 0) {
|
||||
// EP0 has no endpoint control register because the buffer offsets are fixed
|
||||
ep->endpoint_control = NULL;
|
||||
|
||||
// Buffer offset is fixed (also double buffered)
|
||||
ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Set the endpoint control register (starts at EP1, hence num-1)
|
||||
if ( dir == TUSB_DIR_IN )
|
||||
{
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out;
|
||||
}
|
||||
|
||||
@ -180,31 +162,26 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t
|
||||
}
|
||||
}
|
||||
|
||||
static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
|
||||
{
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
hw_endpoint_xfer_start(ep, buffer, total_bytes);
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void)
|
||||
{
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers);
|
||||
uint bit = 1u;
|
||||
for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++)
|
||||
{
|
||||
if (remaining_buffers & bit)
|
||||
{
|
||||
for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) {
|
||||
if (remaining_buffers & bit) {
|
||||
// clear this in advance
|
||||
usb_hw_clear->buf_status = bit;
|
||||
|
||||
// IN transfer for even i, OUT transfer for odd i
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN);
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN);
|
||||
|
||||
// Continue xfer
|
||||
bool done = hw_endpoint_xfer_continue(ep);
|
||||
if (done)
|
||||
{
|
||||
if (done) {
|
||||
// Notify
|
||||
dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
@ -215,41 +192,52 @@ static void __tusb_irq_path_func(hw_handle_buff_status)(void)
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(void)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) {
|
||||
// If we have finished this transfer on EP0 set pid back to 1 for next
|
||||
// setup transfer. Also clear a stall in case
|
||||
uint8_t addrs[] = {0x0, 0x80};
|
||||
for (uint i = 0 ; i < TU_ARRAY_SIZE(addrs); i++)
|
||||
{
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]);
|
||||
for (uint8_t dir = 0; dir < 2; dir++) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir);
|
||||
if (ep->active) {
|
||||
// Abort any pending transfer from a prior control transfer per USB specs
|
||||
// Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1).
|
||||
// Which means we are not guaranteed to safely abort pending transfer on B0 and B1.
|
||||
uint32_t const abort_mask = (dir ? USB_EP_ABORT_EP0_IN_BITS : USB_EP_ABORT_EP0_OUT_BITS);
|
||||
if (rp2040_chip_version() >= 2) {
|
||||
usb_hw_set->abort = abort_mask;
|
||||
while ((usb_hw->abort_done & abort_mask) != abort_mask) {}
|
||||
}
|
||||
|
||||
_hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_DATA1_PID | USB_BUF_CTRL_SEL);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
|
||||
if (rp2040_chip_version() >= 2) {
|
||||
usb_hw_clear->abort_done = abort_mask;
|
||||
usb_hw_clear->abort = abort_mask;
|
||||
}
|
||||
}
|
||||
ep->next_pid = 1u;
|
||||
}
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(reset_non_control_endpoints)(void)
|
||||
{
|
||||
static void __tusb_irq_path_func(reset_non_control_endpoints)(void) {
|
||||
// Disable all non-control
|
||||
for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS-1; i++ )
|
||||
{
|
||||
for (uint8_t i = 0; i < USB_MAX_ENDPOINTS - 1; i++) {
|
||||
usb_dpram->ep_ctrl[i].in = 0;
|
||||
usb_dpram->ep_ctrl[i].out = 0;
|
||||
}
|
||||
|
||||
// clear non-control hw endpoints
|
||||
tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2*sizeof(hw_endpoint_t));
|
||||
tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t));
|
||||
|
||||
// reclaim buffer space
|
||||
next_buffer_ptr = &usb_dpram->epx_data[0];
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
{
|
||||
static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
|
||||
uint32_t const status = usb_hw->ints;
|
||||
uint32_t handled = 0;
|
||||
|
||||
if ( status & USB_INTF_DEV_SOF_BITS )
|
||||
{
|
||||
if (status & USB_INTF_DEV_SOF_BITS) {
|
||||
bool keep_sof_alive = false;
|
||||
|
||||
handled |= USB_INTF_DEV_SOF_BITS;
|
||||
@ -258,20 +246,17 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
// Errata 15 workaround for Device Bulk-In endpoint
|
||||
e15_last_sof = time_us_32();
|
||||
|
||||
for ( uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++ )
|
||||
{
|
||||
struct hw_endpoint * ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
|
||||
for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
|
||||
|
||||
// Active Bulk IN endpoint requires SOF
|
||||
if ( (ep->transfer_type == TUSB_XFER_BULK) && ep->active )
|
||||
{
|
||||
if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) {
|
||||
keep_sof_alive = true;
|
||||
|
||||
hw_endpoint_lock_update(ep, 1);
|
||||
|
||||
// Deferred enable?
|
||||
if ( ep->pending )
|
||||
{
|
||||
if (ep->pending) {
|
||||
ep->pending = 0;
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
@ -282,26 +267,24 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
#endif
|
||||
|
||||
// disable SOF interrupt if it is used for RESUME in remote wakeup
|
||||
if ( !keep_sof_alive && !_sof_enable ) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
|
||||
dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
|
||||
}
|
||||
|
||||
// xfer events are handled before setup req. So if a transfer completes immediately
|
||||
// before closing the EP, the events will be delivered in same order.
|
||||
if ( status & USB_INTS_BUFF_STATUS_BITS )
|
||||
{
|
||||
if (status & USB_INTS_BUFF_STATUS_BITS) {
|
||||
handled |= USB_INTS_BUFF_STATUS_BITS;
|
||||
hw_handle_buff_status();
|
||||
}
|
||||
|
||||
if ( status & USB_INTS_SETUP_REQ_BITS )
|
||||
{
|
||||
if (status & USB_INTS_SETUP_REQ_BITS) {
|
||||
handled |= USB_INTS_SETUP_REQ_BITS;
|
||||
uint8_t const * setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet);
|
||||
uint8_t const* setup = remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet);
|
||||
|
||||
// reset pid to both 1 (data and ack)
|
||||
reset_ep0_pid();
|
||||
reset_ep0();
|
||||
|
||||
// Pass setup packet to tiny usb
|
||||
dcd_event_setup_received(0, setup, true);
|
||||
@ -329,8 +312,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
#endif
|
||||
|
||||
// SE0 for 2.5 us or more (will last at least 10ms)
|
||||
if ( status & USB_INTS_BUS_RESET_BITS )
|
||||
{
|
||||
if (status & USB_INTS_BUS_RESET_BITS) {
|
||||
pico_trace("BUS RESET\r\n");
|
||||
|
||||
handled |= USB_INTS_BUS_RESET_BITS;
|
||||
@ -342,7 +324,7 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
|
||||
// Only run enumeration workaround if pull up is enabled
|
||||
if ( usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS ) rp2040_usb_device_enumeration_fix();
|
||||
if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) rp2040_usb_device_enumeration_fix();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -354,22 +336,19 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
* because without VBUS detection, it is impossible to tell the difference between
|
||||
* being disconnected and suspended.
|
||||
*/
|
||||
if ( status & USB_INTS_DEV_SUSPEND_BITS )
|
||||
{
|
||||
if (status & USB_INTS_DEV_SUSPEND_BITS) {
|
||||
handled |= USB_INTS_DEV_SUSPEND_BITS;
|
||||
dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
|
||||
}
|
||||
|
||||
if ( status & USB_INTS_DEV_RESUME_FROM_HOST_BITS )
|
||||
{
|
||||
if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS) {
|
||||
handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
|
||||
dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
|
||||
}
|
||||
|
||||
if ( status ^ handled )
|
||||
{
|
||||
if (status ^ handled) {
|
||||
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
|
||||
}
|
||||
}
|
||||
@ -390,10 +369,11 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void)
|
||||
#define PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY 0xff
|
||||
#endif
|
||||
|
||||
void dcd_init (uint8_t rhport)
|
||||
{
|
||||
void dcd_init(uint8_t rhport) {
|
||||
assert(rhport == 0);
|
||||
|
||||
TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version());
|
||||
|
||||
// Reset hardware to default state
|
||||
rp2040_usb_init();
|
||||
|
||||
@ -405,7 +385,7 @@ void dcd_init (uint8_t rhport)
|
||||
irq_add_shared_handler(USBCTRL_IRQ, dcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
|
||||
|
||||
// Init control endpoints
|
||||
tu_memclr(hw_endpoints[0], 2*sizeof(hw_endpoint_t));
|
||||
tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t));
|
||||
hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL);
|
||||
hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL);
|
||||
|
||||
@ -427,20 +407,30 @@ void dcd_init (uint8_t rhport)
|
||||
dcd_connect(rhport);
|
||||
}
|
||||
|
||||
void dcd_int_enable(__unused uint8_t rhport)
|
||||
{
|
||||
bool dcd_deinit(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
reset_non_control_endpoints();
|
||||
irq_remove_handler(USBCTRL_IRQ, dcd_rp2040_irq);
|
||||
|
||||
// reset usb hardware into initial state
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_int_enable(__unused uint8_t rhport) {
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, true);
|
||||
}
|
||||
|
||||
void dcd_int_disable(__unused uint8_t rhport)
|
||||
{
|
||||
void dcd_int_disable(__unused uint8_t rhport) {
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, false);
|
||||
}
|
||||
|
||||
void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr)
|
||||
{
|
||||
void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) {
|
||||
assert(rhport == 0);
|
||||
|
||||
// Can't set device address in hardware until status xfer has complete
|
||||
@ -448,8 +438,7 @@ void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr)
|
||||
hw_endpoint_xfer(0x80, NULL, 0);
|
||||
}
|
||||
|
||||
void dcd_remote_wakeup(__unused uint8_t rhport)
|
||||
{
|
||||
void dcd_remote_wakeup(__unused uint8_t rhport) {
|
||||
pico_info("dcd_remote_wakeup %d\n", rhport);
|
||||
assert(rhport == 0);
|
||||
|
||||
@ -460,100 +449,88 @@ void dcd_remote_wakeup(__unused uint8_t rhport)
|
||||
}
|
||||
|
||||
// disconnect by disabling internal pull-up resistor on D+/D-
|
||||
void dcd_disconnect(__unused uint8_t rhport)
|
||||
{
|
||||
void dcd_disconnect(__unused uint8_t rhport) {
|
||||
(void) rhport;
|
||||
usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
}
|
||||
|
||||
// connect by enabling internal pull-up resistor on D+/D-
|
||||
void dcd_connect(__unused uint8_t rhport)
|
||||
{
|
||||
void dcd_connect(__unused uint8_t rhport) {
|
||||
(void) rhport;
|
||||
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en)
|
||||
{
|
||||
void dcd_sof_enable(uint8_t rhport, bool en) {
|
||||
(void) rhport;
|
||||
|
||||
_sof_enable = en;
|
||||
|
||||
if (en)
|
||||
{
|
||||
if (en) {
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}else
|
||||
{
|
||||
}
|
||||
#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
else {
|
||||
// Don't clear immediately if the SOF workaround is in use.
|
||||
// The SOF handler will conditionally disable the interrupt.
|
||||
#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
|
||||
{
|
||||
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
|
||||
(void) rhport;
|
||||
|
||||
if ( request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
|
||||
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
|
||||
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
|
||||
request->bRequest == TUSB_REQ_SET_ADDRESS )
|
||||
{
|
||||
request->bRequest == TUSB_REQ_SET_ADDRESS) {
|
||||
usb_hw->dev_addr_ctrl = (uint8_t) request->wValue;
|
||||
}
|
||||
}
|
||||
|
||||
bool dcd_edpt_open (__unused uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
|
||||
{
|
||||
bool dcd_edpt_open(__unused uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
|
||||
assert(rhport == 0);
|
||||
hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_close_all (uint8_t rhport)
|
||||
{
|
||||
void dcd_edpt_close_all(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
// may need to use EP Abort
|
||||
reset_non_control_endpoints();
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
||||
{
|
||||
bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
|
||||
assert(rhport == 0);
|
||||
hw_endpoint_xfer(ep_addr, buffer, total_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport;
|
||||
|
||||
if ( tu_edpt_number(ep_addr) == 0 )
|
||||
{
|
||||
if (tu_edpt_number(ep_addr) == 0) {
|
||||
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
|
||||
usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
|
||||
usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS
|
||||
: USB_EP_STALL_ARM_EP0_OUT_BITS;
|
||||
}
|
||||
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
|
||||
// stall and clear current pending buffer
|
||||
// may need to use EP_ABORT
|
||||
_hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL);
|
||||
}
|
||||
|
||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport;
|
||||
|
||||
if (tu_edpt_number(ep_addr))
|
||||
{
|
||||
struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
if (tu_edpt_number(ep_addr)) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
|
||||
// clear stall also reset toggle to DATA0, ready for next transfer
|
||||
ep->next_pid = 0;
|
||||
@ -561,16 +538,13 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
|
||||
{
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport;
|
||||
|
||||
pico_trace("dcd_edpt_close %02x\r\n", ep_addr);
|
||||
hw_endpoint_close(ep_addr);
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport)
|
||||
{
|
||||
void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
dcd_rp2040_irq();
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ static void __tusb_irq_path_func(_handle_buff_status_bit)(uint bit, struct hw_en
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void)
|
||||
{
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status 0x%08x\n", remaining_buffers);
|
||||
pico_trace("buf_status 0x%08lx\n", remaining_buffers);
|
||||
|
||||
// Check EPX first
|
||||
uint bit = 0b1;
|
||||
@ -325,10 +325,8 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
|
||||
ep->wMaxPacketSize = wMaxPacketSize;
|
||||
ep->transfer_type = transfer_type;
|
||||
|
||||
pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, tu_edpt_number(ep->ep_addr),
|
||||
ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->transfer_type);
|
||||
pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, tu_edpt_number(ep->ep_addr),
|
||||
ep_dir_string[tu_edpt_dir(ep->ep_addr)], ep->hw_data_buf);
|
||||
pico_trace("hw_endpoint_init dev %d ep %02X xfer %d\n", ep->dev_addr, ep->ep_addr, ep->transfer_type);
|
||||
pico_trace("dev %d ep %02X setup buffer @ 0x%p\n", ep->dev_addr, ep->ep_addr, ep->hw_data_buf);
|
||||
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
|
||||
// Bits 0-5 should be 0
|
||||
assert(!(dpram_offset & 0b111111));
|
||||
@ -343,7 +341,7 @@ static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t
|
||||
ep_reg |= (uint32_t) ((bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB);
|
||||
}
|
||||
*ep->endpoint_control = ep_reg;
|
||||
pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg);
|
||||
pico_trace("endpoint control (0x%p) <- 0x%lx\n", ep->endpoint_control, ep_reg);
|
||||
ep->configured = true;
|
||||
|
||||
if ( ep != &epx )
|
||||
@ -411,6 +409,16 @@ bool hcd_init(uint8_t rhport)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hcd_deinit(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq);
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void hcd_port_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
|
@ -35,26 +35,18 @@
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTOTYPE
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Direction strings for debug
|
||||
const char *ep_dir_string[] = {
|
||||
"out",
|
||||
"in",
|
||||
};
|
||||
|
||||
static void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
|
||||
static void _hw_endpoint_xfer_sync(struct hw_endpoint* ep);
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
static bool e15_is_bulkin_ep(struct hw_endpoint *ep);
|
||||
static bool e15_is_critical_frame_period(struct hw_endpoint *ep);
|
||||
static bool e15_is_bulkin_ep(struct hw_endpoint* ep);
|
||||
static bool e15_is_critical_frame_period(struct hw_endpoint* ep);
|
||||
#else
|
||||
#define e15_is_bulkin_ep(x) (false)
|
||||
#define e15_is_critical_frame_period(x) (false)
|
||||
#endif
|
||||
|
||||
// if usb hardware is in host mode
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) {
|
||||
return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false;
|
||||
}
|
||||
|
||||
@ -62,8 +54,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void)
|
||||
// Implementation
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void rp2040_usb_init(void)
|
||||
{
|
||||
void rp2040_usb_init(void) {
|
||||
// Reset usb controller
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
@ -88,46 +79,33 @@ void rp2040_usb_init(void)
|
||||
TU_LOG2_INT(sizeof(hw_endpoint_t));
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint *ep)
|
||||
{
|
||||
void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) {
|
||||
ep->active = false;
|
||||
ep->remaining_len = 0;
|
||||
ep->xferred_len = 0;
|
||||
ep->user_buf = 0;
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask)
|
||||
{
|
||||
void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint* ep, uint32_t and_mask,
|
||||
uint32_t or_mask) {
|
||||
uint32_t value = 0;
|
||||
|
||||
if ( and_mask )
|
||||
{
|
||||
if (and_mask) {
|
||||
value = *ep->buffer_control & and_mask;
|
||||
}
|
||||
|
||||
if ( or_mask )
|
||||
{
|
||||
if (or_mask) {
|
||||
value |= or_mask;
|
||||
if ( or_mask & USB_BUF_CTRL_AVAIL )
|
||||
{
|
||||
if ( *ep->buffer_control & USB_BUF_CTRL_AVAIL )
|
||||
{
|
||||
panic("ep %d %s was already available", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
if (or_mask & USB_BUF_CTRL_AVAIL) {
|
||||
if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
|
||||
panic("ep %02X was already available", ep->ep_addr);
|
||||
}
|
||||
*ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
|
||||
// 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
|
||||
// 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control
|
||||
// Don't need delay in host mode as host is in charge
|
||||
#if !CFG_TUH_ENABLED
|
||||
__asm volatile (
|
||||
"b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1: b 1f\n"
|
||||
"1:\n"
|
||||
: : : "memory");
|
||||
#endif
|
||||
if ( !is_host_mode()) {
|
||||
busy_wait_at_least_cycles(12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,10 +113,9 @@ void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoi
|
||||
}
|
||||
|
||||
// prepare buffer, return buffer control
|
||||
static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
{
|
||||
static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
|
||||
uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize);
|
||||
ep->remaining_len = (uint16_t)(ep->remaining_len - buflen);
|
||||
ep->remaining_len = (uint16_t) (ep->remaining_len - buflen);
|
||||
|
||||
uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL;
|
||||
|
||||
@ -146,10 +123,9 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
|
||||
buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
|
||||
ep->next_pid ^= 1u;
|
||||
|
||||
if ( !ep->rx )
|
||||
{
|
||||
if (!ep->rx) {
|
||||
// Copy data from user buffer to hw buffer
|
||||
memcpy(ep->hw_data_buf + buf_id*64, ep->user_buf, buflen);
|
||||
memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
|
||||
ep->user_buf += buflen;
|
||||
|
||||
// Mark as full
|
||||
@ -159,8 +135,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
|
||||
// Is this the last buffer? Only really matters for host mode. Will trigger
|
||||
// the trans complete irq but also stop it polling. We only really care about
|
||||
// trans complete for setup packets being sent
|
||||
if (ep->remaining_len == 0)
|
||||
{
|
||||
if (ep->remaining_len == 0) {
|
||||
buf_ctrl |= USB_BUF_CTRL_LAST;
|
||||
}
|
||||
|
||||
@ -170,8 +145,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint *ep,
|
||||
}
|
||||
|
||||
// Prepare buffer control register value
|
||||
void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
{
|
||||
void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) {
|
||||
uint32_t ep_ctrl = *ep->endpoint_control;
|
||||
|
||||
// always compute and start with buffer 0
|
||||
@ -186,8 +160,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) ||
|
||||
(is_host && tu_edpt_number(ep->ep_addr) != 0);
|
||||
|
||||
if(ep->remaining_len && !force_single)
|
||||
{
|
||||
if (ep->remaining_len && !force_single) {
|
||||
// Use buffer 1 (double buffered) if there is still data
|
||||
// TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt)
|
||||
|
||||
@ -196,8 +169,7 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
// Set endpoint control double buffered bit if needed
|
||||
ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER;
|
||||
ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER;
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// Single buffered since 1 is enough
|
||||
ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
|
||||
ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
|
||||
@ -212,16 +184,12 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep)
|
||||
_hw_endpoint_buffer_control_set_value32(ep, buf_ctrl);
|
||||
}
|
||||
|
||||
void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len)
|
||||
{
|
||||
void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) {
|
||||
hw_endpoint_lock_update(ep, 1);
|
||||
|
||||
if ( ep->active )
|
||||
{
|
||||
if (ep->active) {
|
||||
// TODO: Is this acceptable for interrupt packets?
|
||||
TU_LOG(1, "WARN: starting new transfer on already active ep %d %s\r\n", tu_edpt_number(ep->ep_addr),
|
||||
ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
|
||||
TU_LOG(1, "WARN: starting new transfer on already active ep %02X\r\n", ep->ep_addr);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
}
|
||||
|
||||
@ -231,16 +199,13 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t to
|
||||
ep->active = true;
|
||||
ep->user_buf = buffer;
|
||||
|
||||
if ( e15_is_bulkin_ep(ep) )
|
||||
{
|
||||
if (e15_is_bulkin_ep(ep)) {
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}
|
||||
|
||||
if ( e15_is_critical_frame_period(ep) )
|
||||
{
|
||||
if (e15_is_critical_frame_period(ep)) {
|
||||
ep->pending = 1;
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
|
||||
@ -248,34 +213,30 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t to
|
||||
}
|
||||
|
||||
// sync endpoint buffer and return transferred bytes
|
||||
static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uint8_t buf_id)
|
||||
{
|
||||
static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
|
||||
uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
|
||||
if (buf_id) buf_ctrl = buf_ctrl >> 16;
|
||||
|
||||
uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
|
||||
|
||||
if ( !ep->rx )
|
||||
{
|
||||
if (!ep->rx) {
|
||||
// We are continuing a transfer here. If we are TX, we have successfully
|
||||
// sent some data can increase the length we have sent
|
||||
assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
|
||||
|
||||
ep->xferred_len = (uint16_t)(ep->xferred_len + xferred_bytes);
|
||||
}else
|
||||
{
|
||||
ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
|
||||
} else {
|
||||
// If we have received some data, so can increase the length
|
||||
// we have received AFTER we have copied it to the user buffer at the appropriate offset
|
||||
assert(buf_ctrl & USB_BUF_CTRL_FULL);
|
||||
|
||||
memcpy(ep->user_buf, ep->hw_data_buf + buf_id*64, xferred_bytes);
|
||||
ep->xferred_len = (uint16_t)(ep->xferred_len + xferred_bytes);
|
||||
memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
|
||||
ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
|
||||
ep->user_buf += xferred_bytes;
|
||||
}
|
||||
|
||||
// Short packet
|
||||
if (xferred_bytes < ep->wMaxPacketSize)
|
||||
{
|
||||
if (xferred_bytes < ep->wMaxPacketSize) {
|
||||
pico_trace(" Short packet on buffer %d with %u bytes\r\n", buf_id, xferred_bytes);
|
||||
// Reduce total length as this is last packet
|
||||
ep->remaining_len = 0;
|
||||
@ -284,8 +245,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint *ep, uin
|
||||
return xferred_bytes;
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep)
|
||||
{
|
||||
static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) {
|
||||
// Update hw endpoint struct with info from hardware
|
||||
// after a buff status interrupt
|
||||
|
||||
@ -296,14 +256,11 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep
|
||||
uint16_t buf0_bytes = sync_ep_buffer(ep, 0);
|
||||
|
||||
// sync buffer 1 if double buffered
|
||||
if ( (*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS )
|
||||
{
|
||||
if (buf0_bytes == ep->wMaxPacketSize)
|
||||
{
|
||||
if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) {
|
||||
if (buf0_bytes == ep->wMaxPacketSize) {
|
||||
// sync buffer 1 if not short packet
|
||||
sync_ep_buffer(ep, 1);
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// short packet on buffer 0
|
||||
// TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example
|
||||
// At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from
|
||||
@ -335,14 +292,12 @@ static void __tusb_irq_path_func(_hw_endpoint_xfer_sync) (struct hw_endpoint *ep
|
||||
}
|
||||
|
||||
// Returns true if transfer is complete
|
||||
bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep)
|
||||
{
|
||||
bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) {
|
||||
hw_endpoint_lock_update(ep, 1);
|
||||
|
||||
// Part way through a transfer
|
||||
if (!ep->active)
|
||||
{
|
||||
panic("Can't continue xfer on inactive ep %d %s", tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
if (!ep->active) {
|
||||
panic("Can't continue xfer on inactive ep %02X", ep->ep_addr);
|
||||
}
|
||||
|
||||
// Update EP struct from hardware state
|
||||
@ -350,21 +305,15 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep)
|
||||
|
||||
// Now we have synced our state with the hardware. Is there more data to transfer?
|
||||
// If we are done then notify tinyusb
|
||||
if (ep->remaining_len == 0)
|
||||
{
|
||||
pico_trace("Completed transfer of %d bytes on ep %d %s\r\n",
|
||||
ep->xferred_len, tu_edpt_number(ep->ep_addr), ep_dir_string[tu_edpt_dir(ep->ep_addr)]);
|
||||
if (ep->remaining_len == 0) {
|
||||
pico_trace("Completed transfer of %d bytes on ep %02X\r\n", ep->xferred_len, ep->ep_addr);
|
||||
// Notify caller we are done so it can notify the tinyusb stack
|
||||
hw_endpoint_lock_update(ep, -1);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( e15_is_critical_frame_period(ep) )
|
||||
{
|
||||
} else {
|
||||
if (e15_is_critical_frame_period(ep)) {
|
||||
ep->pending = 1;
|
||||
} else
|
||||
{
|
||||
} else {
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
}
|
||||
@ -399,16 +348,14 @@ bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep)
|
||||
volatile uint32_t e15_last_sof = 0;
|
||||
|
||||
// check if Errata 15 is needed for this endpoint i.e device bulk-in
|
||||
static bool __tusb_irq_path_func(e15_is_bulkin_ep) (struct hw_endpoint *ep)
|
||||
{
|
||||
static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint* ep) {
|
||||
return (!is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN &&
|
||||
ep->transfer_type == TUSB_XFER_BULK);
|
||||
}
|
||||
|
||||
// check if we need to apply Errata 15 workaround : i.e
|
||||
// Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame
|
||||
static bool __tusb_irq_path_func(e15_is_critical_frame_period) (struct hw_endpoint *ep)
|
||||
{
|
||||
static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) {
|
||||
TU_VERIFY(e15_is_bulkin_ep(ep));
|
||||
|
||||
/* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point.
|
||||
@ -419,11 +366,10 @@ static bool __tusb_irq_path_func(e15_is_critical_frame_period) (struct hw_endpoi
|
||||
if (delta < 800 || delta > 998) {
|
||||
return false;
|
||||
}
|
||||
TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(), e15_last_sof);
|
||||
TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(),
|
||||
e15_last_sof);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
#endif
|
||||
|
@ -4,11 +4,15 @@ TINYUSB_SRC_C += \
|
||||
src/common/tusb_fifo.c \
|
||||
src/device/usbd.c \
|
||||
src/device/usbd_control.c \
|
||||
src/class/msc/msc_device.c \
|
||||
src/typec/usbc.c \
|
||||
src/class/audio/audio_device.c \
|
||||
src/class/cdc/cdc_device.c \
|
||||
src/class/dfu/dfu_device.c \
|
||||
src/class/dfu/dfu_rt_device.c \
|
||||
src/class/hid/hid_device.c \
|
||||
src/class/usbtmc/usbtmc_device.c \
|
||||
src/host/usbh.c \
|
||||
src/host/hub.c \
|
||||
src/class/cdc/cdc_host.c \
|
||||
src/class/hid/hid_host.c \
|
||||
src/typec/usbc.c \
|
||||
|
@ -43,32 +43,30 @@
|
||||
// Public API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tusb_init(void)
|
||||
{
|
||||
#if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT)
|
||||
bool tusb_init(void) {
|
||||
#if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT)
|
||||
// init device stack CFG_TUSB_RHPORTx_MODE must be defined
|
||||
TU_ASSERT ( tud_init(TUD_OPT_RHPORT) );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT)
|
||||
#if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT)
|
||||
// init host stack CFG_TUSB_RHPORTx_MODE must be defined
|
||||
TU_ASSERT( tuh_init(TUH_OPT_RHPORT) );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tusb_inited(void)
|
||||
{
|
||||
bool tusb_inited(void) {
|
||||
bool ret = false;
|
||||
|
||||
#if CFG_TUD_ENABLED
|
||||
#if CFG_TUD_ENABLED
|
||||
ret = ret || tud_inited();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_ENABLED
|
||||
#if CFG_TUH_ENABLED
|
||||
ret = ret || tuh_inited();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -77,43 +75,35 @@ bool tusb_inited(void)
|
||||
// Descriptor helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1)
|
||||
{
|
||||
while(desc+1 < end)
|
||||
{
|
||||
if ( desc[1] == byte1 ) return desc;
|
||||
uint8_t const* tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1) {
|
||||
while (desc + 1 < end) {
|
||||
if (desc[1] == byte1) return desc;
|
||||
desc += desc[DESC_OFFSET_LEN];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2)
|
||||
{
|
||||
while(desc+2 < end)
|
||||
{
|
||||
if ( desc[1] == byte1 && desc[2] == byte2) return desc;
|
||||
uint8_t const* tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2) {
|
||||
while (desc + 2 < end) {
|
||||
if (desc[1] == byte1 && desc[2] == byte2) return desc;
|
||||
desc += desc[DESC_OFFSET_LEN];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3)
|
||||
{
|
||||
while(desc+3 < end)
|
||||
{
|
||||
uint8_t const* tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3) {
|
||||
while (desc + 3 < end) {
|
||||
if (desc[1] == byte1 && desc[2] == byte2 && desc[3] == byte3) return desc;
|
||||
desc += desc[DESC_OFFSET_LEN];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint Helper for both Host and Device stack
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex)
|
||||
{
|
||||
bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex) {
|
||||
(void) mutex;
|
||||
|
||||
// pre-check to help reducing mutex lock
|
||||
@ -122,111 +112,93 @@ bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex)
|
||||
|
||||
// can only claim the endpoint if it is not busy and not claimed yet.
|
||||
bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0);
|
||||
if (available)
|
||||
{
|
||||
if (available) {
|
||||
ep_state->claimed = 1;
|
||||
}
|
||||
|
||||
(void) osal_mutex_unlock(mutex);
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex)
|
||||
{
|
||||
bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) {
|
||||
(void) mutex;
|
||||
|
||||
(void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
||||
|
||||
// can only release the endpoint if it is claimed and not busy
|
||||
bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0);
|
||||
if (ret)
|
||||
{
|
||||
if (ret) {
|
||||
ep_state->claimed = 0;
|
||||
}
|
||||
|
||||
(void) osal_mutex_unlock(mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed)
|
||||
{
|
||||
bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed) {
|
||||
uint16_t const max_packet_size = tu_edpt_packet_size(desc_ep);
|
||||
TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size);
|
||||
|
||||
switch (desc_ep->bmAttributes.xfer)
|
||||
{
|
||||
case TUSB_XFER_ISOCHRONOUS:
|
||||
{
|
||||
switch (desc_ep->bmAttributes.xfer) {
|
||||
case TUSB_XFER_ISOCHRONOUS: {
|
||||
uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023);
|
||||
TU_ASSERT(max_packet_size <= spec_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TUSB_XFER_BULK:
|
||||
if (speed == TUSB_SPEED_HIGH)
|
||||
{
|
||||
if (speed == TUSB_SPEED_HIGH) {
|
||||
// Bulk highspeed must be EXACTLY 512
|
||||
TU_ASSERT(max_packet_size == 512);
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// TODO Bulk fullspeed can only be 8, 16, 32, 64
|
||||
TU_ASSERT(max_packet_size <= 64);
|
||||
}
|
||||
break;
|
||||
|
||||
case TUSB_XFER_INTERRUPT:
|
||||
{
|
||||
case TUSB_XFER_INTERRUPT: {
|
||||
uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 64);
|
||||
TU_ASSERT(max_packet_size <= spec_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len, uint8_t driver_id)
|
||||
{
|
||||
void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len,
|
||||
uint8_t driver_id) {
|
||||
uint8_t const* p_desc = (uint8_t const*) desc_itf;
|
||||
uint8_t const* desc_end = p_desc + desc_len;
|
||||
|
||||
while( p_desc < desc_end )
|
||||
{
|
||||
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
|
||||
{
|
||||
while (p_desc < desc_end) {
|
||||
if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
|
||||
uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress;
|
||||
|
||||
TU_LOG(2, " Bind EP %02x to driver id %u\r\n", ep_addr, driver_id);
|
||||
ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id;
|
||||
}
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len)
|
||||
{
|
||||
uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len) {
|
||||
uint8_t const* p_desc = (uint8_t const*) desc_itf;
|
||||
uint16_t len = 0;
|
||||
|
||||
while (itf_count--)
|
||||
{
|
||||
while (itf_count--) {
|
||||
// Next on interface desc
|
||||
len += tu_desc_len(desc_itf);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
while (len < max_len)
|
||||
{
|
||||
while (len < max_len) {
|
||||
// return on IAD regardless of itf count
|
||||
if ( tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION ) return len;
|
||||
|
||||
if ( (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) &&
|
||||
((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0 )
|
||||
{
|
||||
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION) {
|
||||
return len;
|
||||
}
|
||||
if ((tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) &&
|
||||
((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -243,9 +215,8 @@ uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf,
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable,
|
||||
void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize)
|
||||
{
|
||||
osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutex);
|
||||
void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize) {
|
||||
osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutexdef);
|
||||
(void) new_mutex;
|
||||
(void) is_tx;
|
||||
|
||||
@ -259,92 +230,82 @@ bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool ove
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tu_edpt_stream_deinit(tu_edpt_stream_t* s) {
|
||||
(void) s;
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
if (s->ff.mutex_wr) osal_mutex_delete(s->ff.mutex_wr);
|
||||
if (s->ff.mutex_rd) osal_mutex_delete(s->ff.mutex_rd);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool stream_claim(tu_edpt_stream_t* s)
|
||||
{
|
||||
if (s->is_host)
|
||||
{
|
||||
bool stream_claim(tu_edpt_stream_t* s) {
|
||||
if (s->is_host) {
|
||||
#if CFG_TUH_ENABLED
|
||||
return usbh_edpt_claim(s->daddr, s->ep_addr);
|
||||
#endif
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
#if CFG_TUD_ENABLED
|
||||
return usbd_edpt_claim(s->rhport, s->ep_addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool stream_xfer(tu_edpt_stream_t* s, uint16_t count)
|
||||
{
|
||||
if (s->is_host)
|
||||
{
|
||||
bool stream_xfer(tu_edpt_stream_t* s, uint16_t count) {
|
||||
if (s->is_host) {
|
||||
#if CFG_TUH_ENABLED
|
||||
return usbh_edpt_xfer(s->daddr, s->ep_addr, count ? s->ep_buf : NULL, count);
|
||||
#endif
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
#if CFG_TUD_ENABLED
|
||||
return usbd_edpt_xfer(s->rhport, s->ep_addr, count ? s->ep_buf : NULL, count);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
bool stream_release(tu_edpt_stream_t* s)
|
||||
{
|
||||
if (s->is_host)
|
||||
{
|
||||
bool stream_release(tu_edpt_stream_t* s) {
|
||||
if (s->is_host) {
|
||||
#if CFG_TUH_ENABLED
|
||||
return usbh_edpt_release(s->daddr, s->ep_addr);
|
||||
#endif
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
#if CFG_TUD_ENABLED
|
||||
return usbd_edpt_release(s->rhport, s->ep_addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Stream Write
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes)
|
||||
{
|
||||
bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes) {
|
||||
// ZLP condition: no pending data, last transferred bytes is multiple of packet size
|
||||
TU_VERIFY( !tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (s->ep_packetsize-1))) );
|
||||
|
||||
TU_VERIFY( stream_claim(s) );
|
||||
TU_ASSERT( stream_xfer(s, 0) );
|
||||
|
||||
TU_VERIFY(!tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (s->ep_packetsize - 1))));
|
||||
TU_VERIFY(stream_claim(s));
|
||||
TU_ASSERT(stream_xfer(s, 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s)
|
||||
{
|
||||
uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s) {
|
||||
// skip if no data
|
||||
TU_VERIFY( tu_fifo_count(&s->ff), 0 );
|
||||
TU_VERIFY(tu_fifo_count(&s->ff), 0);
|
||||
|
||||
// Claim the endpoint
|
||||
TU_VERIFY( stream_claim(s), 0 );
|
||||
TU_VERIFY(stream_claim(s), 0);
|
||||
|
||||
// Pull data from FIFO -> EP buf
|
||||
uint16_t const count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize);
|
||||
|
||||
if ( count )
|
||||
{
|
||||
TU_ASSERT( stream_xfer(s, count), 0 );
|
||||
if (count) {
|
||||
TU_ASSERT(stream_xfer(s, count), 0);
|
||||
return count;
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// Release endpoint since we don't make any transfer
|
||||
// Note: data is dropped if terminal is not connected
|
||||
stream_release(s);
|
||||
@ -352,16 +313,13 @@ uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize)
|
||||
{
|
||||
uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const* buffer, uint32_t bufsize) {
|
||||
TU_VERIFY(bufsize); // TODO support ZLP
|
||||
|
||||
uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize);
|
||||
|
||||
// flush if fifo has more than packet size or
|
||||
// in rare case: fifo depth is configured too small (which never reach packet size)
|
||||
if ( (tu_fifo_count(&s->ff) >= s->ep_packetsize) || (tu_fifo_depth(&s->ff) < s->ep_packetsize) )
|
||||
{
|
||||
if ((tu_fifo_count(&s->ff) >= s->ep_packetsize) || (tu_fifo_depth(&s->ff) < s->ep_packetsize)) {
|
||||
tu_edpt_stream_write_xfer(s);
|
||||
}
|
||||
|
||||
@ -371,9 +329,7 @@ uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t
|
||||
//--------------------------------------------------------------------+
|
||||
// Stream Read
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s)
|
||||
{
|
||||
uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s) {
|
||||
uint16_t available = tu_fifo_remaining(&s->ff);
|
||||
|
||||
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||
@ -388,25 +344,21 @@ uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s)
|
||||
// get available again since fifo can be changed before endpoint is claimed
|
||||
available = tu_fifo_remaining(&s->ff);
|
||||
|
||||
if ( available >= s->ep_packetsize )
|
||||
{
|
||||
if (available >= s->ep_packetsize) {
|
||||
// multiple of packet size limit by ep bufsize
|
||||
uint16_t count = (uint16_t) (available & ~(s->ep_packetsize -1));
|
||||
uint16_t count = (uint16_t) (available & ~(s->ep_packetsize - 1));
|
||||
count = tu_min16(count, s->ep_bufsize);
|
||||
|
||||
TU_ASSERT( stream_xfer(s, count), 0 );
|
||||
|
||||
TU_ASSERT(stream_xfer(s, count), 0);
|
||||
return count;
|
||||
}else
|
||||
{
|
||||
} else {
|
||||
// Release endpoint since we don't make any transfer
|
||||
stream_release(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize)
|
||||
{
|
||||
uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize) {
|
||||
uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t) bufsize);
|
||||
tu_edpt_stream_read_xfer(s);
|
||||
return num_read;
|
||||
@ -420,42 +372,35 @@ uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize
|
||||
#include <ctype.h>
|
||||
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
|
||||
|
||||
char const* const tu_str_speed[] = { "Full", "Low", "High" };
|
||||
char const* const tu_str_std_request[] =
|
||||
{
|
||||
"Get Status" ,
|
||||
"Clear Feature" ,
|
||||
"Reserved" ,
|
||||
"Set Feature" ,
|
||||
"Reserved" ,
|
||||
"Set Address" ,
|
||||
"Get Descriptor" ,
|
||||
"Set Descriptor" ,
|
||||
"Get Configuration" ,
|
||||
"Set Configuration" ,
|
||||
"Get Interface" ,
|
||||
"Set Interface" ,
|
||||
char const* const tu_str_speed[] = {"Full", "Low", "High"};
|
||||
char const* const tu_str_std_request[] = {
|
||||
"Get Status",
|
||||
"Clear Feature",
|
||||
"Reserved",
|
||||
"Set Feature",
|
||||
"Reserved",
|
||||
"Set Address",
|
||||
"Get Descriptor",
|
||||
"Set Descriptor",
|
||||
"Get Configuration",
|
||||
"Set Configuration",
|
||||
"Get Interface",
|
||||
"Set Interface",
|
||||
"Synch Frame"
|
||||
};
|
||||
|
||||
char const* const tu_str_xfer_result[] = {
|
||||
"OK", "FAILED", "STALLED", "TIMEOUT"
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static void dump_str_line(uint8_t const* buf, uint16_t count)
|
||||
{
|
||||
static void dump_str_line(uint8_t const* buf, uint16_t count) {
|
||||
tu_printf(" |");
|
||||
|
||||
// each line is 16 bytes
|
||||
for(uint16_t i=0; i<count; i++)
|
||||
{
|
||||
for (uint16_t i = 0; i < count; i++) {
|
||||
const char ch = buf[i];
|
||||
tu_printf("%c", isprint(ch) ? ch : '.');
|
||||
}
|
||||
|
||||
tu_printf("|\r\n");
|
||||
}
|
||||
|
||||
@ -464,39 +409,27 @@ static void dump_str_line(uint8_t const* buf, uint16_t count)
|
||||
* - count : number of item
|
||||
* - indent: prefix spaces on every line
|
||||
*/
|
||||
void tu_print_mem(void const *buf, uint32_t count, uint8_t indent)
|
||||
{
|
||||
void tu_print_mem(void const* buf, uint32_t count, uint8_t indent) {
|
||||
uint8_t const size = 1; // fixed 1 byte for now
|
||||
|
||||
if ( !buf || !count )
|
||||
{
|
||||
if (!buf || !count) {
|
||||
tu_printf("NULL\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t const *buf8 = (uint8_t const *) buf;
|
||||
|
||||
uint8_t const* buf8 = (uint8_t const*) buf;
|
||||
char format[] = "%00X";
|
||||
format[2] += 2*size;
|
||||
|
||||
format[2] += (uint8_t) (2 * size); // 1 byte = 2 hex digits
|
||||
const uint8_t item_per_line = 16 / size;
|
||||
|
||||
for(unsigned int i=0; i<count; i++)
|
||||
{
|
||||
unsigned int value=0;
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
unsigned int value = 0;
|
||||
|
||||
if ( i%item_per_line == 0 )
|
||||
{
|
||||
if (i % item_per_line == 0) {
|
||||
// Print Ascii
|
||||
if ( i != 0 )
|
||||
{
|
||||
dump_str_line(buf8-16, 16);
|
||||
}
|
||||
|
||||
for(uint8_t s=0; s < indent; s++) tu_printf(" ");
|
||||
|
||||
if (i != 0) dump_str_line(buf8 - 16, 16);
|
||||
for (uint8_t s = 0; s < indent; s++) tu_printf(" ");
|
||||
// print offset or absolute address
|
||||
tu_printf("%04X: ", 16*i/item_per_line);
|
||||
tu_printf("%04X: ", 16 * i / item_per_line);
|
||||
}
|
||||
|
||||
tu_memcpy_s(&value, sizeof(value), buf8, size);
|
||||
@ -507,19 +440,16 @@ void tu_print_mem(void const *buf, uint32_t count, uint8_t indent)
|
||||
}
|
||||
|
||||
// fill up last row to 16 for printing ascii
|
||||
const uint32_t remain = count%16;
|
||||
uint8_t nback = (uint8_t)(remain ? remain : 16);
|
||||
|
||||
if ( remain )
|
||||
{
|
||||
for(uint32_t i=0; i< 16-remain; i++)
|
||||
{
|
||||
const uint32_t remain = count % 16;
|
||||
uint8_t nback = (uint8_t) (remain ? remain : 16);
|
||||
if (remain) {
|
||||
for (uint32_t i = 0; i < 16 - remain; i++) {
|
||||
tu_printf(" ");
|
||||
for(int j=0; j<2*size; j++) tu_printf(" ");
|
||||
for (int j = 0; j < 2 * size; j++) tu_printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
dump_str_line(buf8-nback, nback);
|
||||
dump_str_line(buf8 - nback, nback);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -38,8 +38,6 @@
|
||||
#include "osal/osal.h"
|
||||
#include "common/tusb_fifo.h"
|
||||
|
||||
#include "class/hid/hid.h"
|
||||
|
||||
//------------- TypeC -------------//
|
||||
#if CFG_TUC_ENABLED
|
||||
#include "typec/usbc.h"
|
||||
@ -61,9 +59,6 @@
|
||||
#include "class/cdc/cdc_host.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_VENDOR
|
||||
#include "class/vendor/vendor_host.h"
|
||||
#endif
|
||||
#else
|
||||
#ifndef tuh_int_handler
|
||||
#define tuh_int_handler(...)
|
||||
|
@ -29,9 +29,14 @@
|
||||
|
||||
#include "common/tusb_compiler.h"
|
||||
|
||||
// Version is release as major.minor.revision eg 1.0.0. though there could be notable APIs before a new release.
|
||||
// For notable API changes within a release, we increase the build number.
|
||||
#define TUSB_VERSION_MAJOR 0
|
||||
#define TUSB_VERSION_MINOR 16
|
||||
#define TUSB_VERSION_REVISION 0
|
||||
#define TUSB_VERSION_BUILD 3
|
||||
|
||||
#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR << 24 | TUSB_VERSION_MINOR << 16 | TUSB_VERSION_REVISION << 8 | TUSB_VERSION_BUILD)
|
||||
#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -50,7 +55,8 @@
|
||||
#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
|
||||
#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
|
||||
#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
|
||||
#define OPT_MCU_LPC51UXX 9 ///< NXP LPC51U6x
|
||||
#define OPT_MCU_LPC51 9 ///< NXP LPC51
|
||||
#define OPT_MCU_LPC51UXX OPT_MCU_LPC51 ///< NXP LPC51
|
||||
#define OPT_MCU_LPC54 10 ///< NXP LPC54
|
||||
#define OPT_MCU_LPC55 11 ///< NXP LPC55
|
||||
// legacy naming
|
||||
@ -114,6 +120,12 @@
|
||||
// Espressif
|
||||
#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2
|
||||
#define OPT_MCU_ESP32S3 901 ///< Espressif ESP32-S3
|
||||
#define OPT_MCU_ESP32 902 ///< Espressif ESP32 (for host max3421e)
|
||||
#define OPT_MCU_ESP32C3 903 ///< Espressif ESP32-C3
|
||||
#define OPT_MCU_ESP32C6 904 ///< Espressif ESP32-C6
|
||||
#define OPT_MCU_ESP32C2 905 ///< Espressif ESP32-C2
|
||||
#define OPT_MCU_ESP32H2 906 ///< Espressif ESP32-H2
|
||||
#define TUP_MCU_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU
|
||||
|
||||
// Dialog
|
||||
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
|
||||
@ -125,6 +137,7 @@
|
||||
#define OPT_MCU_KINETIS_KL 1200 ///< NXP KL series
|
||||
#define OPT_MCU_KINETIS_K32L 1201 ///< NXP K32L series
|
||||
#define OPT_MCU_KINETIS_K32 1201 ///< Alias to K32L
|
||||
#define OPT_MCU_KINETIS_K 1202 ///< NXP K series
|
||||
|
||||
#define OPT_MCU_MKL25ZXX 1200 ///< Alias to KL (obsolete)
|
||||
#define OPT_MCU_K32L2BXX 1201 ///< Alias to K32 (obsolete)
|
||||
@ -138,7 +151,6 @@
|
||||
#define OPT_MCU_RX72N 1402 ///< Renesas RX72N
|
||||
#define OPT_MCU_RAXXX 1403 ///< Renesas RAxxx families
|
||||
|
||||
|
||||
// Mind Motion
|
||||
#define OPT_MCU_MM32F327X 1500 ///< Mind Motion MM32F327
|
||||
|
||||
@ -171,10 +183,12 @@
|
||||
// WCH
|
||||
#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307
|
||||
#define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x
|
||||
#define OPT_MCU_CH32V20X 2220 ///< WCH CH32V20X
|
||||
|
||||
|
||||
// NXP LPC MCX
|
||||
#define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series
|
||||
#define OPT_MCU_MCXA15 2301 ///< NXP MCX A15 Series
|
||||
|
||||
// Check if configured MCU is one of listed
|
||||
// Apply _TU_CHECK_MCU with || as separator to list of input
|
||||
@ -353,6 +367,11 @@
|
||||
#define CFG_TUD_INTERFACE_MAX 16
|
||||
#endif
|
||||
|
||||
// USB 2.0 compliance test mode support
|
||||
#ifndef CFG_TUD_TEST_MODE
|
||||
#define CFG_TUD_TEST_MODE 0
|
||||
#endif
|
||||
|
||||
//------------- Device Class Driver -------------//
|
||||
#ifndef CFG_TUD_BTH
|
||||
#define CFG_TUD_BTH 0
|
||||
@ -472,6 +491,23 @@
|
||||
{0x10C4, 0xEA60}, {0x10C4, 0xEA70}
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_CDC_CH34X
|
||||
// CH34X is not part of CDC class, only to re-use CDC driver API
|
||||
#define CFG_TUH_CDC_CH34X 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST
|
||||
// List of product IDs that can use the CH34X CDC driver
|
||||
#define CFG_TUH_CDC_CH34X_VID_PID_LIST \
|
||||
{ 0x1a86, 0x5523 }, /* ch341 chip */ \
|
||||
{ 0x1a86, 0x7522 }, /* ch340k chip */ \
|
||||
{ 0x1a86, 0x7523 }, /* ch340 chip */ \
|
||||
{ 0x1a86, 0xe523 }, /* ch330 chip */ \
|
||||
{ 0x4348, 0x5523 }, /* ch340 custom chip */ \
|
||||
{ 0x2184, 0x0057 }, /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ \
|
||||
{ 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_HID
|
||||
#define CFG_TUH_HID 0
|
||||
#endif
|
||||
|
@ -61,14 +61,7 @@ if (NOT PICO_BARE_METAL)
|
||||
pico_add_subdirectory(pico_stdio_usb)
|
||||
pico_add_subdirectory(pico_i2c_slave)
|
||||
|
||||
# networking libraries - note dependency order is important
|
||||
pico_add_subdirectory(pico_async_context)
|
||||
pico_add_subdirectory(pico_btstack)
|
||||
pico_add_subdirectory(pico_cyw43_driver)
|
||||
pico_add_subdirectory(pico_lwip)
|
||||
pico_add_subdirectory(pico_cyw43_arch)
|
||||
pico_add_subdirectory(pico_mbedtls)
|
||||
|
||||
pico_add_subdirectory(pico_stdlib)
|
||||
|
||||
pico_add_subdirectory(pico_cxx_options)
|
||||
|
@ -1,357 +0,0 @@
|
||||
if (DEFINED ENV{PICO_BTSTACK_PATH} AND (NOT PICO_BTSTACK_PATH))
|
||||
set(PICO_BTSTACK_PATH $ENV{PICO_BTSTACK_PATH})
|
||||
message("Using PICO_BTSTACK_PATH from environment ('${PICO_BTSTACK_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(BTSTACK_TEST_PATH "src/bluetooth.h")
|
||||
if (NOT PICO_BTSTACK_PATH)
|
||||
set(PICO_BTSTACK_PATH ${PROJECT_SOURCE_DIR}/lib/btstack)
|
||||
if (PICO_CYW43_SUPPORTED AND NOT EXISTS ${PICO_BTSTACK_PATH}/${BTSTACK_TEST_PATH})
|
||||
message(WARNING "btstack submodule has not been initialized; Pico W BLE support will be unavailable.
|
||||
hint: try 'git submodule update --init' from your SDK directory (${PICO_SDK_PATH}).")
|
||||
endif()
|
||||
elseif (NOT EXISTS ${PICO_BTSTACK_PATH}/${BTSTACK_TEST_PATH})
|
||||
message(WARNING "PICO_BTSTACK_PATH specified but content not present.")
|
||||
endif()
|
||||
|
||||
if (EXISTS ${PICO_BTSTACK_PATH}/${BTSTACK_TEST_PATH})
|
||||
message("BTstack available at ${PICO_BTSTACK_PATH}")
|
||||
|
||||
pico_register_common_scope_var(PICO_BTSTACK_PATH)
|
||||
|
||||
pico_add_library(pico_btstack_base NOFLAG)
|
||||
target_include_directories(pico_btstack_base_headers INTERFACE
|
||||
${PICO_BTSTACK_PATH}/src
|
||||
${PICO_BTSTACK_PATH}/platform/embedded
|
||||
)
|
||||
|
||||
target_sources(pico_btstack_base INTERFACE
|
||||
${PICO_BTSTACK_PATH}/3rd-party/micro-ecc/uECC.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/rijndael/rijndael.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/segger-rtt/SEGGER_RTT.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/segger-rtt/SEGGER_RTT_printf.c
|
||||
${PICO_BTSTACK_PATH}/platform/embedded/btstack_tlv_flash_bank.c
|
||||
${PICO_BTSTACK_PATH}/platform/embedded/hci_dump_embedded_stdout.c
|
||||
${PICO_BTSTACK_PATH}/platform/embedded/hci_dump_segger_rtt_stdout.c
|
||||
${PICO_BTSTACK_PATH}/src/ad_parser.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_audio.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_base64_decoder.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_crypto.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_hid_parser.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_linked_list.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_memory.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_memory_pool.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_resample.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_ring_buffer.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_run_loop.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_run_loop_base.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_slip.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_tlv.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_tlv_none.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_util.c
|
||||
${PICO_BTSTACK_PATH}/src/hci.c
|
||||
${PICO_BTSTACK_PATH}/src/hci_cmd.c
|
||||
${PICO_BTSTACK_PATH}/src/hci_dump.c
|
||||
${PICO_BTSTACK_PATH}/src/hci_event.c
|
||||
${PICO_BTSTACK_PATH}/src/l2cap.c
|
||||
${PICO_BTSTACK_PATH}/src/l2cap_signaling.c
|
||||
${PICO_BTSTACK_PATH}/src/mesh/gatt-service/mesh_provisioning_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/mesh/gatt-service/mesh_proxy_service_server.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/md5/md5.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/yxml/yxml.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/btstack_stdin_pico.c
|
||||
)
|
||||
target_include_directories(pico_btstack_base_headers INTERFACE
|
||||
${PICO_BTSTACK_PATH}/
|
||||
${PICO_BTSTACK_PATH}/3rd-party/md5
|
||||
${PICO_BTSTACK_PATH}/3rd-party/yxml
|
||||
${PICO_BTSTACK_PATH}/3rd-party/rijndael
|
||||
${PICO_BTSTACK_PATH}/3rd-party/micro-ecc
|
||||
${PICO_BTSTACK_PATH}/3rd-party/segger-rtt
|
||||
)
|
||||
|
||||
pico_add_library(pico_btstack_ble)
|
||||
target_sources(pico_btstack_ble INTERFACE
|
||||
${PICO_BTSTACK_PATH}/src/ble/att_db.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/att_db_util.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/att_dispatch.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/att_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/battery_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/battery_service_client.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/cycling_power_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/cycling_speed_and_cadence_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/device_information_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/device_information_service_client.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/heart_rate_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/hids_client.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/hids_device.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/nordic_spp_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/ublox_spp_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/ancs_client.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt_client.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/le_device_db_memory.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/le_device_db_tlv.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/sm.c
|
||||
)
|
||||
pico_mirrored_target_link_libraries(pico_btstack_ble INTERFACE
|
||||
pico_btstack_base
|
||||
)
|
||||
target_compile_definitions(pico_btstack_ble_headers INTERFACE
|
||||
ENABLE_BLE=1
|
||||
)
|
||||
|
||||
pico_add_library(pico_btstack_classic)
|
||||
target_sources(pico_btstack_classic INTERFACE
|
||||
${PICO_BTSTACK_PATH}/src/classic/a2dp.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/a2dp_sink.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/a2dp_source.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp_acceptor.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp_initiator.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp_sink.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp_source.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp_util.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_browsing.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_browsing_controller.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_browsing_target.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_controller.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_cover_art_client.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_media_item_iterator.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_target.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/btstack_cvsd_plc.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/btstack_link_key_db_tlv.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/btstack_sbc_plc.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/device_id_server.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/gatt_sdp.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/goep_client.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/goep_server.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hfp.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hfp_ag.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hfp_gsm_model.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hfp_hf.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hfp_msbc.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hid_device.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hid_host.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hsp_ag.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hsp_hs.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/obex_iterator.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/obex_message_builder.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/obex_parser.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/pan.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/pbap_client.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/rfcomm.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/sdp_client.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/sdp_client_rfcomm.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/sdp_server.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/sdp_util.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/spp_server.c
|
||||
)
|
||||
pico_mirrored_target_link_libraries(pico_btstack_classic INTERFACE
|
||||
pico_btstack_base
|
||||
)
|
||||
target_compile_definitions(pico_btstack_classic_headers INTERFACE
|
||||
ENABLE_CLASSIC=1
|
||||
)
|
||||
|
||||
pico_add_library(pico_btstack_flash_bank)
|
||||
target_sources(pico_btstack_flash_bank INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/btstack_flash_bank.c
|
||||
)
|
||||
target_include_directories(pico_btstack_flash_bank_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
pico_mirrored_target_link_libraries(pico_btstack_flash_bank INTERFACE pico_btstack_base pico_flash)
|
||||
|
||||
pico_add_library(pico_btstack_run_loop_async_context NOFLAG)
|
||||
target_sources(pico_btstack_run_loop_async_context INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/btstack_run_loop_async_context.c
|
||||
)
|
||||
target_include_directories(pico_btstack_run_loop_async_context_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
pico_mirrored_target_link_libraries(pico_btstack_run_loop_async_context INTERFACE pico_btstack_base pico_async_context_base)
|
||||
|
||||
pico_add_library(pico_btstack_sbc_encoder NOFLAG)
|
||||
target_sources(pico_btstack_sbc_encoder INTERFACE
|
||||
# SBC codec for A2DP and HFP demos
|
||||
${PICO_BTSTACK_PATH}/src/classic/btstack_sbc_encoder_bluedroid.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/srce/sbc_analysis.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/srce/sbc_dct.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/srce/sbc_dct_coeffs.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/srce/sbc_enc_bit_alloc_mono.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/srce/sbc_enc_bit_alloc_ste.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/srce/sbc_enc_coeffs.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/srce/sbc_encoder.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/srce/sbc_packing.c
|
||||
)
|
||||
target_include_directories(pico_btstack_sbc_encoder_headers INTERFACE
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/encoder/include
|
||||
)
|
||||
|
||||
pico_add_library(pico_btstack_sbc_decoder NOFLAG)
|
||||
target_sources(pico_btstack_sbc_decoder INTERFACE
|
||||
${PICO_BTSTACK_PATH}/src/classic/btstack_sbc_decoder_bluedroid.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/alloc.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/bitalloc.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/bitalloc-sbc.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/bitstream-decode.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/decoder-oina.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/decoder-private.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/decoder-sbc.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/dequant.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/framing.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/framing-sbc.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/oi_codec_version.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/synthesis-sbc.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/synthesis-dct8.c
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/srce/synthesis-8-generated.c
|
||||
)
|
||||
target_include_directories(pico_btstack_sbc_decoder_headers INTERFACE
|
||||
${PICO_BTSTACK_PATH}/3rd-party/bluedroid/decoder/include
|
||||
)
|
||||
|
||||
pico_add_library(pico_btstack_bnep_lwip NOFLAG)
|
||||
target_sources(pico_btstack_bnep_lwip INTERFACE
|
||||
${PICO_BTSTACK_PATH}/src/classic/bnep.c
|
||||
${PICO_BTSTACK_PATH}/platform/lwip/bnep_lwip.c
|
||||
)
|
||||
target_include_directories(pico_btstack_bnep_lwip_headers INTERFACE
|
||||
${PICO_BTSTACK_PATH}/platform/lwip
|
||||
)
|
||||
|
||||
pico_add_library(pico_btstack_bnep_lwip_sys_freertos NOFLAG)
|
||||
target_include_directories(pico_btstack_bnep_lwip_sys_freertos INTERFACE
|
||||
${PICO_BTSTACK_PATH}/platform/freertos
|
||||
)
|
||||
pico_mirrored_target_link_libraries(pico_btstack_bnep_lwip_sys_freertos INTERFACE
|
||||
pico_btstack_bnep_lwip
|
||||
)
|
||||
target_compile_definitions(pico_btstack_bnep_lwip_sys_freertos INTERFACE
|
||||
LWIP_PROVIDE_ERRNO=1
|
||||
PICO_LWIP_CUSTOM_LOCK_TCPIP_CORE=1
|
||||
)
|
||||
|
||||
pico_promote_common_scope_vars()
|
||||
|
||||
# Make a GATT header file from a BTstack GATT file
|
||||
# Pass the target library name library type and path to the GATT input file
|
||||
function(pico_btstack_make_gatt_header TARGET_LIB TARGET_TYPE GATT_FILE)
|
||||
find_package (Python3 REQUIRED COMPONENTS Interpreter)
|
||||
get_filename_component(GATT_NAME "${GATT_FILE}" NAME_WE)
|
||||
get_filename_component(GATT_PATH "${GATT_FILE}" PATH)
|
||||
set(GATT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
set(GATT_HEADER "${GATT_BINARY_DIR}/${GATT_NAME}.h")
|
||||
set(TARGET_GATT "${TARGET_LIB}_gatt_header")
|
||||
|
||||
add_custom_target(${TARGET_GATT} DEPENDS ${GATT_HEADER})
|
||||
add_custom_command(
|
||||
OUTPUT ${GATT_HEADER}
|
||||
DEPENDS ${GATT_FILE}
|
||||
WORKING_DIRECTORY ${GATT_PATH}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${GATT_BINARY_DIR} &&
|
||||
${Python3_EXECUTABLE} ${PICO_SDK_PATH}/lib/btstack/tool/compile_gatt.py ${GATT_FILE} ${GATT_HEADER}
|
||||
VERBATIM)
|
||||
add_dependencies(${TARGET_LIB}
|
||||
${TARGET_GATT}
|
||||
)
|
||||
target_include_directories(${TARGET_LIB} ${TARGET_TYPE}
|
||||
${GATT_BINARY_DIR}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(suppress_btstack_warnings)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/ble/att_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/device_information_service_server.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/hids_client.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_util.c
|
||||
${PICO_BTSTACK_PATH}/src/btstack_crypto.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/a2dp.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/a2dp_sink.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/a2dp_source.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp_source.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_controller.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/btstack_sbc_decoder_bluedroid.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avrcp_target.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hid_device.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hsp_ag.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/hsp_hs.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/pan.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/pbap_client.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/rfcomm.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/sdp_client_rfcomm.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/sdp_server.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/spp_server.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-cast-qual"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/ble/sm.c
|
||||
${PICO_BTSTACK_PATH}/src/l2cap.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-unused-parameter"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/btstack_hid_parser.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-maybe-uninitialized"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/btstack_tlv_none.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/avdtp_util.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-unused-parameter"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/platform/embedded/hci_dump_embedded_stdout.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-suggest-attribute=format"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/hci.c
|
||||
${PICO_BTSTACK_PATH}/src/classic/rfcomm.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-format"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/platform/embedded/hal_flash_bank_memory.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-sign-compare;-Wno-format"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/platform/embedded/btstack_tlv_flash_bank.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-unused-parameter;-Wno-sign-compare"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/hids_client.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-null-dereference"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/classic/hfp.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-null-dereference;-Wno-unused-parameter"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/classic/goep_server.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-unused-parameter;-Wno-null-dereference"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/battery_service_client.c
|
||||
${PICO_BTSTACK_PATH}/src/ble/gatt-service/device_information_service_client.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-null-dereference"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/classic/hfp_hf.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-type-limits;-Wno-stringop-overflow"
|
||||
)
|
||||
set_source_files_properties(
|
||||
${PICO_BTSTACK_PATH}/src/btstack_crypto.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-sign-compare"
|
||||
)
|
||||
endfunction()
|
||||
endif()
|
@ -1,30 +0,0 @@
|
||||
“BlueKitchen” shall refer to BlueKitchen GmbH.
|
||||
“Raspberry Pi” shall refer to Raspberry Pi Ltd.
|
||||
“Product” shall refer to Raspberry Pi hardware products Raspberry Pi Pico W or Raspberry Pi Pico WH.
|
||||
“Customer” means any purchaser of a Product.
|
||||
“Customer Products” means products manufactured or distributed by Customers which use or are derived from Products.
|
||||
|
||||
Raspberry Pi grants to the Customer a non-exclusive, non-transferable, non-sublicensable, irrevocable, perpetual
|
||||
and worldwide licence to use, copy, store, develop, modify, and transmit BTstack in order to use BTstack with or
|
||||
integrate BTstack into Products or Customer Products, and distribute BTstack as part of these Products or
|
||||
Customer Products or their related documentation or SDKs.
|
||||
|
||||
All use of BTstack by the Customer is limited to Products or Customer Products, and the Customer represents and
|
||||
warrants that all such use shall be in compliance with the terms of this licence and all applicable laws and
|
||||
regulations, including but not limited to, copyright and other intellectual property laws and privacy regulations.
|
||||
|
||||
BlueKitchen retains all rights, title and interest in, to and associated with BTstack and associated websites.
|
||||
Customer shall not take any action inconsistent with BlueKitchen’s ownership of BTstack, any associated services,
|
||||
websites and related content.
|
||||
|
||||
There are no implied licences under the terms set forth in this licence, and any rights not expressly granted
|
||||
hereunder are reserved by BlueKitchen.
|
||||
|
||||
BTSTACK IS PROVIDED BY RASPBERRY PI "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED TO THE FULLEST EXTENT
|
||||
PERMISSIBLE UNDER APPLICABLE LAW. IN NO EVENT SHALL RASPBERRY PI OR BLUEKITCHEN BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF BTSTACK, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico/btstack_flash_bank.h"
|
||||
#include "pico/flash.h"
|
||||
#include "hardware/sync.h"
|
||||
#include <string.h>
|
||||
|
||||
// Check sizes
|
||||
static_assert(PICO_FLASH_BANK_TOTAL_SIZE % (FLASH_SECTOR_SIZE * 2) == 0, "PICO_FLASH_BANK_TOTAL_SIZE invalid");
|
||||
static_assert(PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
|
||||
|
||||
// Size of one bank
|
||||
#define PICO_FLASH_BANK_SIZE (PICO_FLASH_BANK_TOTAL_SIZE / 2)
|
||||
|
||||
#if 0
|
||||
#define DEBUG_PRINT(format,args...) printf(format, ## args)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#endif
|
||||
|
||||
static uint32_t pico_flash_bank_get_size(void * context) {
|
||||
(void)(context);
|
||||
return PICO_FLASH_BANK_SIZE;
|
||||
}
|
||||
|
||||
static uint32_t pico_flash_bank_get_alignment(void * context) {
|
||||
(void)(context);
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool op_is_erase;
|
||||
uintptr_t p0;
|
||||
uintptr_t p1;
|
||||
} mutation_operation_t;
|
||||
|
||||
static void pico_flash_bank_perform_flash_mutation_operation(void *param) {
|
||||
const mutation_operation_t *mop = (const mutation_operation_t *)param;
|
||||
if (mop->op_is_erase) {
|
||||
flash_range_erase(mop->p0, PICO_FLASH_BANK_SIZE);
|
||||
} else {
|
||||
flash_range_program(mop->p0, (const uint8_t *)mop->p1, FLASH_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef pico_flash_bank_get_storage_offset_func
|
||||
static inline uint32_t pico_flash_bank_get_fixed_storage_offset(void) {
|
||||
static_assert(PICO_FLASH_BANK_STORAGE_OFFSET + PICO_FLASH_BANK_TOTAL_SIZE <= PICO_FLASH_SIZE_BYTES, "PICO_FLASH_BANK_TOTAL_SIZE too big");
|
||||
#ifndef NDEBUG
|
||||
// Check we're not overlapping the binary in flash
|
||||
extern char __flash_binary_end;
|
||||
assert(((uintptr_t)&__flash_binary_end - XIP_BASE <= PICO_FLASH_BANK_STORAGE_OFFSET));
|
||||
#endif
|
||||
return PICO_FLASH_BANK_STORAGE_OFFSET;
|
||||
}
|
||||
#define pico_flash_bank_get_storage_offset_func pico_flash_bank_get_fixed_storage_offset
|
||||
#else
|
||||
extern uint32_t pico_flash_bank_get_storage_offset_func(void);
|
||||
#endif
|
||||
|
||||
static void pico_flash_bank_erase(void * context, int bank) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("erase: bank %d\n", bank);
|
||||
mutation_operation_t mop = {
|
||||
.op_is_erase = true,
|
||||
.p0 = pico_flash_bank_get_storage_offset_func() + (PICO_FLASH_BANK_SIZE * bank),
|
||||
};
|
||||
// todo choice of timeout and check return code... currently we have no way to return an error
|
||||
// to the caller anyway. flash_safe_execute asserts by default on problem other than timeout,
|
||||
// so that's fine for now, and UINT32_MAX is a timeout of 49 days which seems long enough
|
||||
flash_safe_execute(pico_flash_bank_perform_flash_mutation_operation, &mop, UINT32_MAX);
|
||||
}
|
||||
|
||||
static void pico_flash_bank_read(void *context, int bank, uint32_t offset, uint8_t *buffer, uint32_t size) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("read: bank %d offset %u size %u\n", bank, offset, size);
|
||||
|
||||
assert(bank <= 1);
|
||||
if (bank > 1) return;
|
||||
|
||||
assert(offset < PICO_FLASH_BANK_SIZE);
|
||||
if (offset >= PICO_FLASH_BANK_SIZE) return;
|
||||
|
||||
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
|
||||
if ((offset + size) > PICO_FLASH_BANK_SIZE) return;
|
||||
|
||||
// Flash is xip
|
||||
memcpy(buffer, (void *)(XIP_BASE + pico_flash_bank_get_storage_offset_func() + (PICO_FLASH_BANK_SIZE * bank) + offset), size);
|
||||
}
|
||||
|
||||
static void pico_flash_bank_write(void * context, int bank, uint32_t offset, const uint8_t *data, uint32_t size) {
|
||||
(void)(context);
|
||||
DEBUG_PRINT("write: bank %d offset %u size %u\n", bank, offset, size);
|
||||
|
||||
assert(bank <= 1);
|
||||
if (bank > 1) return;
|
||||
|
||||
assert(offset < PICO_FLASH_BANK_SIZE);
|
||||
if (offset >= PICO_FLASH_BANK_SIZE) return;
|
||||
|
||||
assert((offset + size) <= PICO_FLASH_BANK_SIZE);
|
||||
if ((offset + size) > PICO_FLASH_BANK_SIZE) return;
|
||||
|
||||
if (size == 0) return;
|
||||
|
||||
// calc bank start position
|
||||
const uint32_t bank_start_pos = pico_flash_bank_get_storage_offset_func() + (PICO_FLASH_BANK_SIZE * bank);
|
||||
|
||||
// Calculate first and last page in the bank
|
||||
const uint32_t first_page = offset / FLASH_PAGE_SIZE;
|
||||
const uint32_t last_page = (offset + size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
|
||||
|
||||
// Now we only care about the offset in the first page
|
||||
offset %= FLASH_PAGE_SIZE;
|
||||
|
||||
// Amount of data we've copied
|
||||
uint32_t data_pos = 0;
|
||||
uint32_t size_left = size;
|
||||
|
||||
// Write all the pages required
|
||||
for(uint32_t page = first_page; page < last_page; page++) {
|
||||
uint8_t page_data[FLASH_PAGE_SIZE];
|
||||
|
||||
assert(data_pos < size && size_left <= size);
|
||||
|
||||
// Copy data we're not going to overwrite in the first page
|
||||
if (page == first_page && offset > 0) {
|
||||
memcpy(page_data,
|
||||
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE)),
|
||||
offset);
|
||||
}
|
||||
|
||||
// Copy the data we're not going to overwrite in the last page
|
||||
if (page == last_page - 1 && (offset + size_left) < FLASH_PAGE_SIZE) {
|
||||
memcpy(page_data + offset + size_left,
|
||||
(void *)(XIP_BASE + bank_start_pos + (page * FLASH_PAGE_SIZE) + offset + size_left),
|
||||
FLASH_PAGE_SIZE - offset - size_left);
|
||||
}
|
||||
|
||||
// Now copy the new data into the page
|
||||
const uint32_t size_to_copy = MIN(size_left, FLASH_PAGE_SIZE - offset);
|
||||
memcpy(page_data + offset, data + data_pos, size_to_copy);
|
||||
|
||||
data_pos += size_to_copy;
|
||||
size_left -= size_to_copy;
|
||||
|
||||
// zero offset for the following pages
|
||||
offset = 0;
|
||||
|
||||
// Now program the entire page
|
||||
mutation_operation_t mop = {
|
||||
.op_is_erase = false,
|
||||
.p0 = bank_start_pos + (page * FLASH_PAGE_SIZE),
|
||||
.p1 = (uintptr_t)page_data
|
||||
};
|
||||
// todo choice of timeout and check return code... currently we have no way to return an error
|
||||
// to the caller anyway. flash_safe_execute asserts by default on problem other than timeout,
|
||||
// so that's fine for now, and UINT32_MAX is a timeout of 49 days which seems long enough
|
||||
flash_safe_execute(pico_flash_bank_perform_flash_mutation_operation, &mop, UINT32_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
static const hal_flash_bank_t pico_flash_bank_instance_obj = {
|
||||
/* uint32_t (*get_size)(..) */ &pico_flash_bank_get_size,
|
||||
/* uint32_t (*get_alignment)(..); */ &pico_flash_bank_get_alignment,
|
||||
/* void (*erase)(..); */ &pico_flash_bank_erase,
|
||||
/* void (*read)(..); */ &pico_flash_bank_read,
|
||||
/* void (*write)(..); */ &pico_flash_bank_write,
|
||||
};
|
||||
|
||||
const hal_flash_bank_t *pico_flash_bank_instance(void) {
|
||||
return &pico_flash_bank_instance_obj;
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico/btstack_run_loop_async_context.h"
|
||||
#include "hardware/sync.h"
|
||||
|
||||
static async_context_t *btstack_async_context;
|
||||
static async_at_time_worker_t btstack_timeout_worker;
|
||||
static async_when_pending_worker_t btstack_processing_worker;
|
||||
static void btstack_timeout_reached(async_context_t *context, async_at_time_worker_t *worker);
|
||||
static void btstack_work_pending(async_context_t *context, async_when_pending_worker_t *worker);
|
||||
static volatile bool run_loop_exit;
|
||||
|
||||
static void btstack_run_loop_async_context_init(void) {
|
||||
btstack_run_loop_base_init();
|
||||
btstack_timeout_worker.do_work = btstack_timeout_reached;
|
||||
btstack_processing_worker.do_work = btstack_work_pending;
|
||||
async_context_add_when_pending_worker(btstack_async_context, &btstack_processing_worker);
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_add_data_source(btstack_data_source_t * data_source) {
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
btstack_run_loop_base_add_data_source(data_source);
|
||||
async_context_release_lock(btstack_async_context);
|
||||
}
|
||||
|
||||
static bool btstack_run_loop_async_context_remove_data_source(btstack_data_source_t * data_source) {
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
bool rc = btstack_run_loop_base_remove_data_source(data_source);
|
||||
async_context_release_lock(btstack_async_context);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_enable_data_source_callbacks(btstack_data_source_t * data_source, uint16_t callbacks) {
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
btstack_run_loop_base_enable_data_source_callbacks(data_source, callbacks);
|
||||
async_context_release_lock(btstack_async_context);
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_disable_data_source_callbacks(btstack_data_source_t * data_source, uint16_t callbacks) {
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
btstack_run_loop_base_disable_data_source_callbacks(data_source, callbacks);
|
||||
async_context_release_lock(btstack_async_context);
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_set_timer(btstack_timer_source_t *ts, uint32_t timeout_in_ms){
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
ts->timeout = to_ms_since_boot(get_absolute_time()) + timeout_in_ms + 1;
|
||||
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
|
||||
async_context_release_lock(btstack_async_context);
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_add_timer(btstack_timer_source_t *timer) {
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
btstack_run_loop_base_add_timer(timer);
|
||||
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
|
||||
async_context_release_lock(btstack_async_context);
|
||||
}
|
||||
|
||||
static bool btstack_run_loop_async_context_remove_timer(btstack_timer_source_t *timer) {
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
bool rc = btstack_run_loop_base_remove_timer(timer);
|
||||
async_context_release_lock(btstack_async_context);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_dump_timer(void){
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
btstack_run_loop_base_dump_timer();
|
||||
async_context_release_lock(btstack_async_context);
|
||||
}
|
||||
|
||||
static uint32_t btstack_run_loop_async_context_get_time_ms(void)
|
||||
{
|
||||
return to_ms_since_boot(get_absolute_time());
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_execute(void)
|
||||
{
|
||||
run_loop_exit = false;
|
||||
while (!run_loop_exit) {
|
||||
async_context_poll(btstack_async_context);
|
||||
async_context_wait_for_work_until(btstack_async_context, at_the_end_of_time);
|
||||
}
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_trigger_exit(void)
|
||||
{
|
||||
run_loop_exit = true;
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_execute_on_main_thread(btstack_context_callback_registration_t *callback_registration)
|
||||
{
|
||||
async_context_acquire_lock_blocking(btstack_async_context);
|
||||
btstack_run_loop_base_add_callback(callback_registration);
|
||||
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
|
||||
async_context_release_lock(btstack_async_context);
|
||||
}
|
||||
|
||||
static void btstack_run_loop_async_context_poll_data_sources_from_irq(void)
|
||||
{
|
||||
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
|
||||
}
|
||||
|
||||
static const btstack_run_loop_t btstack_run_loop_async_context = {
|
||||
&btstack_run_loop_async_context_init,
|
||||
&btstack_run_loop_async_context_add_data_source,
|
||||
&btstack_run_loop_async_context_remove_data_source,
|
||||
&btstack_run_loop_async_context_enable_data_source_callbacks,
|
||||
&btstack_run_loop_async_context_disable_data_source_callbacks,
|
||||
&btstack_run_loop_async_context_set_timer,
|
||||
&btstack_run_loop_async_context_add_timer,
|
||||
&btstack_run_loop_async_context_remove_timer,
|
||||
&btstack_run_loop_async_context_execute,
|
||||
&btstack_run_loop_async_context_dump_timer,
|
||||
&btstack_run_loop_async_context_get_time_ms,
|
||||
&btstack_run_loop_async_context_poll_data_sources_from_irq,
|
||||
&btstack_run_loop_async_context_execute_on_main_thread,
|
||||
&btstack_run_loop_async_context_trigger_exit,
|
||||
};
|
||||
|
||||
const btstack_run_loop_t *btstack_run_loop_async_context_get_instance(async_context_t *async_context)
|
||||
{
|
||||
assert(!btstack_async_context || btstack_async_context == async_context);
|
||||
btstack_async_context = async_context;
|
||||
return &btstack_run_loop_async_context;
|
||||
}
|
||||
|
||||
static void btstack_timeout_reached(__unused async_context_t *context, __unused async_at_time_worker_t *worker) {
|
||||
// simply wakeup worker
|
||||
async_context_set_work_pending(btstack_async_context, &btstack_processing_worker);
|
||||
}
|
||||
|
||||
static void btstack_work_pending(__unused async_context_t *context, __unused async_when_pending_worker_t *worker) {
|
||||
// poll data sources
|
||||
btstack_run_loop_base_poll_data_sources();
|
||||
|
||||
// execute callbacks
|
||||
btstack_run_loop_base_execute_callbacks();
|
||||
|
||||
uint32_t now = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
// process timers
|
||||
btstack_run_loop_base_process_timers(now);
|
||||
now = to_ms_since_boot(get_absolute_time());
|
||||
int ms = btstack_run_loop_base_get_time_until_timeout(now);
|
||||
if (ms == -1) {
|
||||
async_context_remove_at_time_worker(btstack_async_context, &btstack_timeout_worker);
|
||||
} else {
|
||||
async_context_add_at_time_worker_in_ms(btstack_async_context, &btstack_timeout_worker, ms);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "btstack_config.h"
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
|
||||
#include "btstack_stdin.h"
|
||||
#include "btstack_run_loop.h"
|
||||
#include "pico/stdio.h"
|
||||
|
||||
static btstack_data_source_t stdin_data_source;
|
||||
static void (*stdin_handler)(char c);
|
||||
|
||||
// Data source callback, return any character received
|
||||
static void btstack_stdin_process(__unused struct btstack_data_source *ds, __unused btstack_data_source_callback_type_t callback_type){
|
||||
if (stdin_handler) {
|
||||
while(true) {
|
||||
int c = getchar_timeout_us(0);
|
||||
if (c == PICO_ERROR_TIMEOUT) return;
|
||||
(*stdin_handler)(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_chars_available_callback(__unused void *param) {
|
||||
btstack_run_loop_poll_data_sources_from_irq();
|
||||
}
|
||||
|
||||
// Test code calls this if HAVE_BTSTACK_STDIN is defined and it wants key presses
|
||||
void btstack_stdin_setup(void (*handler)(char c)) {
|
||||
if (stdin_handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set handler
|
||||
stdin_handler = handler;
|
||||
|
||||
// set up polling data_source
|
||||
btstack_run_loop_set_data_source_handler(&stdin_data_source, &btstack_stdin_process);
|
||||
btstack_run_loop_enable_data_source_callbacks(&stdin_data_source, DATA_SOURCE_CALLBACK_POLL);
|
||||
btstack_run_loop_add_data_source(&stdin_data_source);
|
||||
|
||||
stdio_set_chars_available_callback(on_chars_available_callback, NULL);
|
||||
}
|
||||
|
||||
// Deinit everything
|
||||
void btstack_stdin_reset(void){
|
||||
if (!stdin_handler) {
|
||||
return;
|
||||
}
|
||||
stdio_set_chars_available_callback(NULL, NULL);
|
||||
stdin_handler = NULL;
|
||||
btstack_run_loop_remove_data_source(&stdin_data_source);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* \defgroup pico_btstack pico_btstack
|
||||
* \brief Integration/wrapper libraries for <a href="https://github.com/bluekitchen/btstack">BTstack</a>
|
||||
* the documentation for which is <a href="https://bluekitchen-gmbh.com/btstack/">here</a>.
|
||||
*
|
||||
* A supplemental license for BTstack (in addition to the stock BTstack licensing terms) is provided <a href="https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_btstack/LICENSE.RP">here</a>.
|
||||
*
|
||||
* The \c \b pico_btstack_ble library adds the support needed for Bluetooth Low Energy (BLE). The \c \b pico_btstack_classic library adds the support needed for Bluetooth Classic.
|
||||
* You can link to either library individually, or to both libraries thus enabling dual-mode support provided by BTstack.
|
||||
*
|
||||
* To use BTstack you need to provide a \c btstack_config.h file in your source tree and add its location to your include path.
|
||||
* The BTstack configuration macros \c ENABLE_CLASSIC and \c ENABLE_BLE are defined for you when you link the \c pico_btstack_classic and \c pico_btstack_ble libraries respectively, so you should not define them yourself.
|
||||
*
|
||||
* For more details, see <a href="https://bluekitchen-gmbh.com/btstack/develop/#how_to/">How to configure BTstack</a> and the relevant <a href="https://github.com/raspberrypi/pico-examples#pico-w-bluetooth">pico-examples</a>.
|
||||
*
|
||||
* The follow libraries are provided for you to link.
|
||||
* * \c \b pico_btstack_ble - Adds Bluetooth Low Energy (LE) support.
|
||||
* * \c \b pico_btstack_classic - Adds Bluetooth Classic support.
|
||||
* * \c \b pico_btstack_sbc_encoder - Adds Bluetooth Sub Band Coding (SBC) encoder support.
|
||||
* * \c \b pico_btstack_sbc_decoder - Adds Bluetooth Sub Band Coding (SBC) decoder support.
|
||||
* * \c \b pico_btstack_bnep_lwip - Adds Bluetooth Network Encapsulation Protocol (BNEP) support using LwIP.
|
||||
* * \c \b pico_btstack_bnep_lwip_sys_freertos - Adds Bluetooth Network Encapsulation Protocol (BNEP) support using LwIP with FreeRTOS for NO_SYS=0.
|
||||
*
|
||||
* \note The CMake function pico_btstack_make_gatt_header can be used to run the BTstack compile_gatt tool to make a GATT header file from a BTstack GATT file.
|
||||
*
|
||||
* \sa pico_btstack_cyw43 in pico_cyw43_driver, which adds the cyw43 driver support needed for BTstack including BTstack run loop support.
|
||||
*/
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_BTSTACK_FLASH_BANK_H
|
||||
#define _PICO_BTSTACK_FLASH_BANK_H
|
||||
|
||||
#include "pico.h"
|
||||
#include "hal_flash_bank.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_FLASH_BANK_TOTAL_SIZE, Total size of the Bluetooth flash storage. Must be an even multiple of FLASH_SECTOR_SIZE, type=int, default=FLASH_SECTOR_SIZE * 2, group=pico_btstack
|
||||
#ifndef PICO_FLASH_BANK_TOTAL_SIZE
|
||||
#define PICO_FLASH_BANK_TOTAL_SIZE (FLASH_SECTOR_SIZE * 2u)
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_FLASH_BANK_STORAGE_OFFSET, Offset in flash of the Bluetooth flash storage, type=int, default=PICO_FLASH_SIZE_BYTES - PICO_FLASH_BANK_TOTAL_SIZE, group=pico_btstack
|
||||
#ifndef PICO_FLASH_BANK_STORAGE_OFFSET
|
||||
#define PICO_FLASH_BANK_STORAGE_OFFSET (PICO_FLASH_SIZE_BYTES - PICO_FLASH_BANK_TOTAL_SIZE)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Return the singleton BTstack HAL flash instance, used for non-volatile storage
|
||||
* \ingroup pico_btstack
|
||||
*
|
||||
* \note By default two sectors at the end of flash are used (see \c PICO_FLASH_BANK_STORAGE_OFFSET and \c PICO_FLASH_BANK_TOTAL_SIZE)
|
||||
*/
|
||||
const hal_flash_bank_t *pico_flash_bank_instance(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_BTSTACK_RUN_LOOP_ASYNC_CONTEXT_H
|
||||
#define _PICO_BTSTACK_RUN_LOOP_ASYNC_CONTEXT_H
|
||||
|
||||
#include "btstack_run_loop.h"
|
||||
#include "pico/async_context.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Initialize and return the singleton BTstack run loop instance that integrates with the async_context API
|
||||
* \ingroup pico_btstack
|
||||
*
|
||||
* \param context the async_context instance that provides the abstraction for handling asynchronous work.
|
||||
* \return the BTstack run loop instance
|
||||
*/
|
||||
const btstack_run_loop_t *btstack_run_loop_async_context_get_instance(async_context_t *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
@ -1,89 +0,0 @@
|
||||
if (PICO_CYW43_SUPPORTED) # set by BOARD=pico-w
|
||||
if (TARGET cyw43_driver_picow)
|
||||
pico_add_library(pico_cyw43_arch)
|
||||
target_sources(pico_cyw43_arch INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/cyw43_arch.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/cyw43_arch_poll.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/cyw43_arch_threadsafe_background.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/cyw43_arch_freertos.c
|
||||
)
|
||||
|
||||
target_include_directories(pico_cyw43_arch_headers INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_arch INTERFACE
|
||||
pico_unique_id
|
||||
cyw43_driver_picow # driver for pico w
|
||||
pico_cyw43_driver # integration with async_context
|
||||
)
|
||||
|
||||
if (NOT TARGET pico_lwip)
|
||||
message(WARNING "lwIP is not available; Full Pico W wireless support will be unavailable")
|
||||
else()
|
||||
message("Pico W Wi-Fi build support available.")
|
||||
pico_add_library(pico_cyw43_arch_poll NOFLAG)
|
||||
target_compile_definitions(pico_cyw43_arch_poll_headers INTERFACE
|
||||
PICO_CYW43_ARCH_POLL=1
|
||||
)
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_arch_poll INTERFACE
|
||||
pico_cyw43_arch
|
||||
pico_async_context_poll)
|
||||
|
||||
pico_add_library(pico_cyw43_arch_lwip_poll NOFLAG)
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_arch_lwip_poll INTERFACE
|
||||
pico_lwip_nosys
|
||||
pico_cyw43_arch_poll)
|
||||
target_compile_definitions(pico_cyw43_arch_lwip_poll_headers INTERFACE
|
||||
CYW43_LWIP=1
|
||||
)
|
||||
|
||||
pico_add_library(pico_cyw43_arch_threadsafe_background NOFLAG)
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_arch_threadsafe_background INTERFACE
|
||||
pico_cyw43_arch
|
||||
pico_async_context_threadsafe_background)
|
||||
target_compile_definitions(pico_cyw43_arch_threadsafe_background_headers INTERFACE
|
||||
PICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1
|
||||
)
|
||||
|
||||
pico_add_library(pico_cyw43_arch_lwip_threadsafe_background NOFLAG)
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_arch_lwip_threadsafe_background INTERFACE
|
||||
pico_lwip_nosys
|
||||
pico_cyw43_arch_threadsafe_background)
|
||||
target_compile_definitions(pico_cyw43_arch_lwip_threadsafe_background_headers INTERFACE
|
||||
CYW43_LWIP=1
|
||||
)
|
||||
|
||||
pico_add_library(pico_cyw43_arch_sys_freertos NOFLAG)
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_arch_sys_freertos INTERFACE
|
||||
pico_cyw43_arch
|
||||
pico_async_context_freertos)
|
||||
target_compile_definitions(pico_cyw43_arch_sys_freertos_headers INTERFACE
|
||||
PICO_CYW43_ARCH_FREERTOS=1
|
||||
)
|
||||
|
||||
pico_add_library(pico_cyw43_arch_lwip_sys_freertos NOFLAG)
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_arch_lwip_sys_freertos INTERFACE
|
||||
pico_lwip_freertos
|
||||
pico_cyw43_arch_sys_freertos)
|
||||
target_compile_definitions(pico_cyw43_arch_lwip_sys_freertos_headers INTERFACE
|
||||
CYW43_LWIP=1
|
||||
LWIP_PROVIDE_ERRNO=1
|
||||
# now the default
|
||||
#PICO_LWIP_CUSTOM_LOCK_TCPIP_CORE=1 # we want to override the lwip locking mechanism to use our mutex
|
||||
)
|
||||
endif()
|
||||
|
||||
pico_add_library(pico_cyw43_arch_none NOFLAG)
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_arch_none INTERFACE
|
||||
pico_cyw43_arch
|
||||
pico_async_context_threadsafe_background)
|
||||
target_compile_definitions(pico_cyw43_arch_none_headers INTERFACE
|
||||
CYW43_LWIP=0
|
||||
PICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 # none still uses threadsafe_background to make gpio use easy
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (PICO_CYW43_DRIVER_PATH AND EXISTS "${PICO_CYW43_DRIVER_PATH}")
|
||||
pico_add_doxygen(${PICO_CYW43_DRIVER_PATH}/src)
|
||||
endif()
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pico/unique_id.h"
|
||||
#include "cyw43.h"
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "cyw43_ll.h"
|
||||
#include "cyw43_stats.h"
|
||||
|
||||
#if PICO_CYW43_ARCH_DEBUG_ENABLED
|
||||
#define CYW43_ARCH_DEBUG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define CYW43_ARCH_DEBUG(...) ((void)0)
|
||||
#endif
|
||||
|
||||
static uint32_t country_code = PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE;
|
||||
|
||||
static async_context_t *async_context;
|
||||
|
||||
void cyw43_arch_set_async_context(async_context_t *context) {
|
||||
async_context = context;
|
||||
}
|
||||
|
||||
void cyw43_arch_enable_sta_mode(void) {
|
||||
assert(cyw43_is_initialized(&cyw43_state));
|
||||
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, cyw43_arch_get_country_code());
|
||||
}
|
||||
|
||||
void cyw43_arch_disable_sta_mode(void) {
|
||||
assert(cyw43_is_initialized(&cyw43_state));
|
||||
if (cyw43_state.itf_state & (1 << CYW43_ITF_STA)) {
|
||||
cyw43_cb_tcpip_deinit(&cyw43_state, CYW43_ITF_STA);
|
||||
cyw43_state.itf_state &= ~(1 << CYW43_ITF_STA);
|
||||
}
|
||||
if (cyw43_state.wifi_join_state) {
|
||||
cyw43_wifi_leave(&cyw43_state, CYW43_ITF_STA);
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_arch_enable_ap_mode(const char *ssid, const char *password, uint32_t auth) {
|
||||
assert(cyw43_is_initialized(&cyw43_state));
|
||||
cyw43_wifi_ap_set_ssid(&cyw43_state, strlen(ssid), (const uint8_t *) ssid);
|
||||
if (password) {
|
||||
cyw43_wifi_ap_set_password(&cyw43_state, strlen(password), (const uint8_t *) password);
|
||||
cyw43_wifi_ap_set_auth(&cyw43_state, auth);
|
||||
} else {
|
||||
cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_OPEN);
|
||||
}
|
||||
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_AP, true, cyw43_arch_get_country_code());
|
||||
}
|
||||
|
||||
void cyw43_arch_disable_ap_mode(void) {
|
||||
assert(cyw43_is_initialized(&cyw43_state));
|
||||
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_AP, false, cyw43_arch_get_country_code());
|
||||
cyw43_state.itf_state &= ~(1 << CYW43_ITF_AP);
|
||||
}
|
||||
|
||||
#if PICO_CYW43_ARCH_DEBUG_ENABLED
|
||||
// Return a string for the wireless state
|
||||
static const char* cyw43_tcpip_link_status_name(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case CYW43_LINK_DOWN:
|
||||
return "link down";
|
||||
case CYW43_LINK_JOIN:
|
||||
return "joining";
|
||||
case CYW43_LINK_NOIP:
|
||||
return "no ip";
|
||||
case CYW43_LINK_UP:
|
||||
return "link up";
|
||||
case CYW43_LINK_FAIL:
|
||||
return "link fail";
|
||||
case CYW43_LINK_NONET:
|
||||
return "network fail";
|
||||
case CYW43_LINK_BADAUTH:
|
||||
return "bad auth";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int cyw43_arch_wifi_connect_bssid_async(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth) {
|
||||
if (!pw) auth = CYW43_AUTH_OPEN;
|
||||
// Connect to wireless
|
||||
return cyw43_wifi_join(&cyw43_state, strlen(ssid), (const uint8_t *)ssid, pw ? strlen(pw) : 0, (const uint8_t *)pw, auth, bssid, CYW43_CHANNEL_NONE);
|
||||
}
|
||||
|
||||
int cyw43_arch_wifi_connect_async(const char *ssid, const char *pw, uint32_t auth) {
|
||||
return cyw43_arch_wifi_connect_bssid_async(ssid, NULL, pw, auth);
|
||||
}
|
||||
|
||||
static int cyw43_arch_wifi_connect_bssid_until(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth, absolute_time_t until) {
|
||||
int err = cyw43_arch_wifi_connect_bssid_async(ssid, bssid, pw, auth);
|
||||
if (err) return err;
|
||||
|
||||
int status = CYW43_LINK_UP + 1;
|
||||
while(status >= 0 && status != CYW43_LINK_UP) {
|
||||
int new_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
|
||||
// If there was no network, keep trying
|
||||
if (new_status == CYW43_LINK_NONET) {
|
||||
new_status = CYW43_LINK_JOIN;
|
||||
err = cyw43_arch_wifi_connect_bssid_async(ssid, bssid, pw, auth);
|
||||
if (err) return err;
|
||||
}
|
||||
if (new_status != status) {
|
||||
status = new_status;
|
||||
CYW43_ARCH_DEBUG("connect status: %s\n", cyw43_tcpip_link_status_name(status));
|
||||
}
|
||||
if (time_reached(until)) {
|
||||
return PICO_ERROR_TIMEOUT;
|
||||
}
|
||||
// Do polling
|
||||
cyw43_arch_poll();
|
||||
cyw43_arch_wait_for_work_until(until);
|
||||
}
|
||||
// Turn status into a pico_error_codes, CYW43_LINK_NONET shouldn't happen as we fail with PICO_ERROR_TIMEOUT instead
|
||||
assert(status == CYW43_LINK_UP || status == CYW43_LINK_BADAUTH || status == CYW43_LINK_FAIL);
|
||||
if (status == CYW43_LINK_UP) {
|
||||
return PICO_OK; // success
|
||||
} else if (status == CYW43_LINK_BADAUTH) {
|
||||
return PICO_ERROR_BADAUTH;
|
||||
} else {
|
||||
return PICO_ERROR_CONNECT_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to wireless, return with success when an IP address has been assigned
|
||||
static int cyw43_arch_wifi_connect_until(const char *ssid, const char *pw, uint32_t auth, absolute_time_t until) {
|
||||
return cyw43_arch_wifi_connect_bssid_until(ssid, NULL, pw, auth, until);
|
||||
}
|
||||
|
||||
int cyw43_arch_wifi_connect_blocking(const char *ssid, const char *pw, uint32_t auth) {
|
||||
return cyw43_arch_wifi_connect_until(ssid, pw, auth, at_the_end_of_time);
|
||||
}
|
||||
|
||||
int cyw43_arch_wifi_connect_bssid_blocking(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth) {
|
||||
return cyw43_arch_wifi_connect_bssid_until(ssid, bssid, pw, auth, at_the_end_of_time);
|
||||
}
|
||||
|
||||
int cyw43_arch_wifi_connect_timeout_ms(const char *ssid, const char *pw, uint32_t auth, uint32_t timeout_ms) {
|
||||
return cyw43_arch_wifi_connect_until(ssid, pw, auth, make_timeout_time_ms(timeout_ms));
|
||||
}
|
||||
|
||||
int cyw43_arch_wifi_connect_bssid_timeout_ms(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth, uint32_t timeout_ms) {
|
||||
return cyw43_arch_wifi_connect_bssid_until(ssid, bssid, pw, auth, make_timeout_time_ms(timeout_ms));
|
||||
}
|
||||
|
||||
uint32_t cyw43_arch_get_country_code(void) {
|
||||
return country_code;
|
||||
}
|
||||
|
||||
int cyw43_arch_init_with_country(uint32_t country) {
|
||||
country_code = country;
|
||||
return cyw43_arch_init();
|
||||
}
|
||||
|
||||
void cyw43_arch_gpio_put(uint wl_gpio, bool value) {
|
||||
invalid_params_if(CYW43_ARCH, wl_gpio >= CYW43_WL_GPIO_COUNT);
|
||||
cyw43_gpio_set(&cyw43_state, (int)wl_gpio, value);
|
||||
}
|
||||
|
||||
bool cyw43_arch_gpio_get(uint wl_gpio) {
|
||||
invalid_params_if(CYW43_ARCH, wl_gpio >= CYW43_WL_GPIO_COUNT);
|
||||
bool value = false;
|
||||
cyw43_gpio_get(&cyw43_state, (int)wl_gpio, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
async_context_t *cyw43_arch_async_context(void) {
|
||||
return async_context;
|
||||
}
|
||||
|
||||
void cyw43_arch_poll(void)
|
||||
{
|
||||
async_context_poll(async_context);
|
||||
}
|
||||
|
||||
void cyw43_arch_wait_for_work_until(absolute_time_t until) {
|
||||
async_context_wait_for_work_until(async_context, until);
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#if PICO_CYW43_ARCH_FREERTOS
|
||||
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/cyw43_driver.h"
|
||||
#include "pico/async_context_freertos.h"
|
||||
|
||||
#if CYW43_LWIP
|
||||
#include "pico/lwip_freertos.h"
|
||||
#include <lwip/tcpip.h>
|
||||
#endif
|
||||
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
#include "pico/btstack_cyw43.h"
|
||||
#endif
|
||||
|
||||
#if NO_SYS
|
||||
#error example_cyw43_arch_freetos_sys requires NO_SYS=0
|
||||
#endif
|
||||
|
||||
static async_context_freertos_t cyw43_async_context_freertos;
|
||||
|
||||
async_context_t *cyw43_arch_init_default_async_context(void) {
|
||||
async_context_freertos_config_t config = async_context_freertos_default_config();
|
||||
#ifdef CYW43_TASK_PRIORITY
|
||||
config.task_priority = CYW43_TASK_PRIORITY;
|
||||
#endif
|
||||
#ifdef CYW43_TASK_STACK_SIZE
|
||||
config.task_stack_size = CYW43_TASK_STACK_SIZE;
|
||||
#endif
|
||||
if (async_context_freertos_init(&cyw43_async_context_freertos, &config))
|
||||
return &cyw43_async_context_freertos.core;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cyw43_arch_init(void) {
|
||||
async_context_t *context = cyw43_arch_async_context();
|
||||
if (!context) {
|
||||
context = cyw43_arch_init_default_async_context();
|
||||
if (!context) return PICO_ERROR_GENERIC;
|
||||
cyw43_arch_set_async_context(context);
|
||||
}
|
||||
bool ok = cyw43_driver_init(context);
|
||||
#if CYW43_LWIP
|
||||
ok &= lwip_freertos_init(context);
|
||||
#endif
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
ok &= btstack_cyw43_init(context);
|
||||
#endif
|
||||
if (!ok) {
|
||||
cyw43_arch_deinit();
|
||||
return PICO_ERROR_GENERIC;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_arch_deinit(void) {
|
||||
async_context_t *context = cyw43_arch_async_context();
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
btstack_cyw43_deinit(context);
|
||||
#endif
|
||||
// there is a bit of a circular dependency here between lwIP and cyw43_driver. We
|
||||
// shut down cyw43_driver first as it has IRQs calling back into lwIP. Also lwIP itself
|
||||
// does not actually get shut down.
|
||||
// todo add a "pause" method to async_context if we need to provide some atomicity (we
|
||||
// don't want to take the lock as these methods may invoke execute_sync()
|
||||
cyw43_driver_deinit(context);
|
||||
#if CYW43_LWIP
|
||||
lwip_freertos_deinit(context);
|
||||
#endif
|
||||
// if it is our context, then we de-init it.
|
||||
if (context == &cyw43_async_context_freertos.core) {
|
||||
async_context_deinit(context);
|
||||
cyw43_arch_set_async_context(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#if PICO_CYW43_ARCH_POLL
|
||||
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/cyw43_driver.h"
|
||||
|
||||
#include "pico/async_context_poll.h"
|
||||
#if CYW43_LWIP
|
||||
#include "pico/lwip_nosys.h"
|
||||
#endif
|
||||
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
#include "pico/btstack_cyw43.h"
|
||||
#endif
|
||||
|
||||
#if CYW43_LWIP && !NO_SYS
|
||||
#error PICO_CYW43_ARCH_POLL requires lwIP NO_SYS=1
|
||||
#endif
|
||||
|
||||
static async_context_poll_t cyw43_async_context_poll;
|
||||
|
||||
async_context_t *cyw43_arch_init_default_async_context(void) {
|
||||
if (async_context_poll_init_with_defaults(&cyw43_async_context_poll))
|
||||
return &cyw43_async_context_poll.core;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cyw43_arch_init(void) {
|
||||
async_context_t *context = cyw43_arch_async_context();
|
||||
if (!context) {
|
||||
context = cyw43_arch_init_default_async_context();
|
||||
if (!context) return PICO_ERROR_GENERIC;
|
||||
cyw43_arch_set_async_context(context);
|
||||
}
|
||||
bool ok = cyw43_driver_init(context);
|
||||
#if CYW43_LWIP
|
||||
ok &= lwip_nosys_init(context);
|
||||
#endif
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
ok &= btstack_cyw43_init(context);
|
||||
#endif
|
||||
if (!ok) {
|
||||
cyw43_arch_deinit();
|
||||
return PICO_ERROR_GENERIC;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_arch_deinit(void) {
|
||||
async_context_t *context = cyw43_arch_async_context();
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
btstack_cyw43_deinit(context);
|
||||
#endif
|
||||
// there is a bit of a circular dependency here between lwIP and cyw43_driver. We
|
||||
// shut down cyw43_driver first as it has IRQs calling back into lwIP. Also lwIP itself
|
||||
// does not actually get shut down.
|
||||
// todo add a "pause" method to async_context if we need to provide some atomicity (we
|
||||
// don't want to take the lock as these methods may invoke execute_sync()
|
||||
cyw43_driver_deinit(context);
|
||||
#if CYW43_LWIP
|
||||
lwip_nosys_deinit(context);
|
||||
#endif
|
||||
// if it is our context, then we de-init it.
|
||||
if (context == &cyw43_async_context_poll.core) {
|
||||
async_context_deinit(context);
|
||||
cyw43_arch_set_async_context(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#if PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/cyw43_driver.h"
|
||||
#include "pico/async_context_threadsafe_background.h"
|
||||
|
||||
#if CYW43_LWIP
|
||||
#include "pico/lwip_nosys.h"
|
||||
#endif
|
||||
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
#include "pico/btstack_cyw43.h"
|
||||
#endif
|
||||
|
||||
#if CYW43_LWIP && !NO_SYS
|
||||
#error PICO_CYW43_ARCH_THREADSAFE_BACKGROUND requires lwIP NO_SYS=1
|
||||
#endif
|
||||
#if CYW43_LWIP && MEM_LIBC_MALLOC
|
||||
// would attempt to use malloc from IRQ context
|
||||
#error MEM_LIBC_MALLOC is incompatible with PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
#endif
|
||||
|
||||
static async_context_threadsafe_background_t cyw43_async_context_threadsafe_background;
|
||||
|
||||
async_context_t *cyw43_arch_init_default_async_context(void) {
|
||||
async_context_threadsafe_background_config_t config = async_context_threadsafe_background_default_config();
|
||||
if (async_context_threadsafe_background_init(&cyw43_async_context_threadsafe_background, &config))
|
||||
return &cyw43_async_context_threadsafe_background.core;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cyw43_arch_init(void) {
|
||||
async_context_t *context = cyw43_arch_async_context();
|
||||
if (!context) {
|
||||
context = cyw43_arch_init_default_async_context();
|
||||
if (!context) return PICO_ERROR_GENERIC;
|
||||
cyw43_arch_set_async_context(context);
|
||||
}
|
||||
bool ok = cyw43_driver_init(context);
|
||||
#if CYW43_LWIP
|
||||
ok &= lwip_nosys_init(context);
|
||||
#endif
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
ok &= btstack_cyw43_init(context);
|
||||
#endif
|
||||
if (!ok) {
|
||||
cyw43_arch_deinit();
|
||||
return PICO_ERROR_GENERIC;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cyw43_arch_deinit(void) {
|
||||
async_context_t *context = cyw43_arch_async_context();
|
||||
#if CYW43_ENABLE_BLUETOOTH
|
||||
btstack_cyw43_deinit(context);
|
||||
#endif
|
||||
// there is a bit of a circular dependency here between lwIP and cyw43_driver. We
|
||||
// shut down cyw43_driver first as it has IRQs calling back into lwIP. Also lwIP itself
|
||||
// does not actually get shut down.
|
||||
// todo add a "pause" method to async_context if we need to provide some atomicity (we
|
||||
// don't want to take the lock as these methods may invoke execute_sync()
|
||||
cyw43_driver_deinit(context);
|
||||
#if CYW43_LWIP
|
||||
lwip_nosys_deinit(context);
|
||||
#endif
|
||||
// if it is our context, then we de-init it.
|
||||
if (context == &cyw43_async_context_threadsafe_background.core) {
|
||||
async_context_deinit(context);
|
||||
cyw43_arch_set_async_context(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,504 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_CYW43_ARCH_H
|
||||
#define _PICO_CYW43_ARCH_H
|
||||
|
||||
#include "pico.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "cyw43.h"
|
||||
#include "cyw43_country.h"
|
||||
#include "pico/async_context.h"
|
||||
|
||||
#ifdef PICO_CYW43_ARCH_HEADER
|
||||
#include __XSTRING(PICO_CYW43_ARCH_HEADER)
|
||||
#else
|
||||
#if PICO_CYW43_ARCH_POLL
|
||||
#include "pico/cyw43_arch/arch_poll.h"
|
||||
#elif PICO_CYW43_ARCH_THREADSAFE_BACKGROUND
|
||||
#include "pico/cyw43_arch/arch_threadsafe_background.h"
|
||||
#elif PICO_CYW43_ARCH_FREERTOS
|
||||
#include "pico/cyw43_arch/arch_freertos.h"
|
||||
#else
|
||||
#error must specify support pico_cyw43_arch architecture type or set PICO_CYW43_ARCH_HEADER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \defgroup cyw43_driver cyw43_driver
|
||||
* \ingroup pico_cyw43_arch
|
||||
* \brief Driver used for Pico W wireless
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup cyw43_ll cyw43_ll
|
||||
* \ingroup cyw43_driver
|
||||
* \brief Low Level CYW43 driver interface
|
||||
*/
|
||||
|
||||
/** \file pico/cyw43_arch.h
|
||||
* \defgroup pico_cyw43_arch pico_cyw43_arch
|
||||
*
|
||||
* Architecture for integrating the CYW43 driver (for the wireless on Pico W) and lwIP (for TCP/IP stack) into the SDK. It is also necessary for accessing the on-board LED on Pico W
|
||||
*
|
||||
* Both the low level \c cyw43_driver and the lwIP stack require periodic servicing, and have limitations
|
||||
* on whether they can be called from multiple cores/threads.
|
||||
*
|
||||
* \c pico_cyw43_arch attempts to abstract these complications into several behavioral groups:
|
||||
*
|
||||
* * \em 'poll' - This not multi-core/IRQ safe, and requires the user to call \ref cyw43_arch_poll periodically from their main loop
|
||||
* * \em 'thread_safe_background' - This is multi-core/thread/task safe, and maintenance of the driver and TCP/IP stack is handled automatically in the background
|
||||
* * \em 'freertos' - This is multi-core/thread/task safe, and uses a separate FreeRTOS task to handle lwIP and and driver work.
|
||||
*
|
||||
* As of right now, lwIP is the only supported TCP/IP stack, however the use of \c pico_cyw43_arch is intended to be independent of
|
||||
* the particular TCP/IP stack used (and possibly Bluetooth stack used) in the future. For this reason, the integration of lwIP
|
||||
* is handled in the base (\c pico_cyw43_arch) library based on the #define \ref CYW43_LWIP used by the \c cyw43_driver.
|
||||
*
|
||||
* \note As of version 1.5.0 of the Raspberry Pi Pico SDK, the \c pico_cyw43_arch library no longer directly implements
|
||||
* the distinct behavioral abstractions. This is now handled by the more general \ref pico_async_context library. The
|
||||
* user facing behavior of pico_cyw43_arch has not changed as a result of this implementation detail, however pico_cyw43_arch
|
||||
* is now just a thin wrapper which creates an appropriate async_context and makes a simple call to add lwIP or cyw43_driver support
|
||||
* as appropriate. You are free to perform this context creation and adding of lwIP, cyw43_driver or indeed any other additional
|
||||
* future protocol/driver support to your async_context, however for now pico_cyw43_arch does still provide a few cyw43_ specific (i.e. Pico W)
|
||||
* APIs for connection management, locking and GPIO interaction.
|
||||
*
|
||||
* \note The connection management APIs at least may be moved
|
||||
* to a more generic library in a future release. The locking methods are now backed by their \ref pico_async_context equivalents, and
|
||||
* those methods may be used interchangeably (see \ref cyw43_arch_lwip_begin, \ref cyw43_arch_lwip_end and \ref cyw43_arch_lwip_check for more details).
|
||||
*
|
||||
* \note For examples of creating of your own async_context and addition of \c cyw43_driver and \c lwIP support, please
|
||||
* refer to the specific source files \c cyw43_arch_poll.c, \c cyw43_arch_threadsafe_background.c and \c cyw43_arch_freertos.c.
|
||||
*
|
||||
* Whilst you can use the \c pico_cyw43_arch library directly and specify \ref CYW43_LWIP (and other defines) yourself, several
|
||||
* other libraries are made available to the build which aggregate the defines and other dependencies for you:
|
||||
*
|
||||
* * \b pico_cyw43_arch_lwip_poll - For using the RAW lwIP API (in `NO_SYS=1` mode) without any background processing or multi-core/thread safety.
|
||||
*
|
||||
* The user must call \ref cyw43_arch_poll periodically from their main loop.
|
||||
*
|
||||
* This wrapper library:
|
||||
* - Sets \c CYW43_LWIP=1 to enable lwIP support in \c pico_cyw43_arch and \c cyw43_driver.
|
||||
* - Sets \c PICO_CYW43_ARCH_POLL=1 to select the polling behavior.
|
||||
* - Adds the \c pico_lwip as a dependency to pull in lwIP.
|
||||
*
|
||||
* * \b pico_cyw43_arch_lwip_threadsafe_background - For using the RAW lwIP API (in `NO_SYS=1` mode) with multi-core/thread safety, and automatic servicing of the \c cyw43_driver and
|
||||
* lwIP in background.
|
||||
*
|
||||
* Calls into the \c cyw43_driver high level API (cyw43.h) may be made from either core or from lwIP callbacks, however calls into lwIP (which
|
||||
* is not thread-safe) other than those made from lwIP callbacks, must be bracketed with \ref cyw43_arch_lwip_begin and \ref cyw43_arch_lwip_end. It is fine to bracket
|
||||
* calls made from within lwIP callbacks too; you just don't have to.
|
||||
*
|
||||
* \note lwIP callbacks happen in a (low priority) IRQ context (similar to an alarm callback), so care should be taken when interacting
|
||||
* with other code.
|
||||
*
|
||||
* This wrapper library:
|
||||
* - Sets \c CYW43_LWIP=1 to enable lwIP support in \c pico_cyw43_arch and \c cyw43_driver
|
||||
* - Sets \c PICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 to select the thread-safe/non-polling behavior.
|
||||
* - Adds the pico_lwip as a dependency to pull in lwIP.
|
||||
*
|
||||
*
|
||||
* This library \em can also be used under the RP2040 port of FreeRTOS with lwIP in `NO_SYS=1` mode (allowing you to call \c cyw43_driver APIs
|
||||
* from any task, and to call lwIP from lwIP callbacks, or from any task if you bracket the calls with \ref cyw43_arch_lwip_begin and \ref cyw43_arch_lwip_end. Again, you should be
|
||||
* careful about what you do in lwIP callbacks, as you cannot call most FreeRTOS APIs from within an IRQ context. Unless you have good reason, you should probably
|
||||
* use the full FreeRTOS integration (with `NO_SYS=0`) provided by \c pico_cyw43_arch_lwip_sys_freertos.
|
||||
*
|
||||
* * \b pico_cyw43_arch_lwip_sys_freertos - For using the full lwIP API including blocking sockets in OS (`NO_SYS=0`) mode, along with with multi-core/task/thread safety, and automatic servicing of the \c cyw43_driver and
|
||||
* the lwIP stack.
|
||||
*
|
||||
* This wrapper library:
|
||||
* - Sets \c CYW43_LWIP=1 to enable lwIP support in \c pico_cyw43_arch and \c cyw43_driver.
|
||||
* - Sets \c PICO_CYW43_ARCH_FREERTOS=1 to select the NO_SYS=0 lwip/FreeRTOS integration
|
||||
* - Sets \c LWIP_PROVIDE_ERRNO=1 to provide error numbers needed for compilation without an OS
|
||||
* - Adds the \c pico_lwip as a dependency to pull in lwIP.
|
||||
* - Adds the lwIP/FreeRTOS code from lwip-contrib (in the contrib directory of lwIP)
|
||||
*
|
||||
* Calls into the \c cyw43_driver high level API (cyw43.h) may be made from any task or from lwIP callbacks, but not from IRQs. Calls into the lwIP RAW API (which is not thread safe)
|
||||
* must be bracketed with \ref cyw43_arch_lwip_begin and \ref cyw43_arch_lwip_end. It is fine to bracket calls made from within lwIP callbacks too; you just don't have to.
|
||||
*
|
||||
* \note this wrapper library requires you to link FreeRTOS functionality with your application yourself.
|
||||
*
|
||||
* * \b pico_cyw43_arch_none - If you do not need the TCP/IP stack but wish to use the on-board LED.
|
||||
*
|
||||
* This wrapper library:
|
||||
* - Sets \c CYW43_LWIP=0 to disable lwIP support in \c pico_cyw43_arch and \c cyw43_driver
|
||||
*/
|
||||
|
||||
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_CYW43_ARCH, Enable/disable assertions in the pico_cyw43_arch module, type=bool, default=0, group=pico_cyw43_arch
|
||||
#ifndef PARAM_ASSERTIONS_ENABLED_CYW43_ARCH
|
||||
#define PARAM_ASSERTIONS_ENABLED_CYW43_ARCH 0
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_CYW43_ARCH_DEBUG_ENABLED, Enable/disable some debugging output in the pico_cyw43_arch module, type=bool, default=1 in debug builds, group=pico_cyw43_arch
|
||||
#ifndef PICO_CYW43_ARCH_DEBUG_ENABLED
|
||||
#ifndef NDEBUG
|
||||
#define PICO_CYW43_ARCH_DEBUG_ENABLED 1
|
||||
#else
|
||||
#define PICO_CYW43_ARCH_DEBUG_ENABLED 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE, Default country code for the cyw43 wireless driver, default=CYW43_COUNTRY_WORLDWIDE, group=pico_cyw43_arch
|
||||
#ifndef PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE
|
||||
#define PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE CYW43_COUNTRY_WORLDWIDE
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Initialize the CYW43 architecture
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This method initializes the `cyw43_driver` code and initializes the lwIP stack (if it
|
||||
* was enabled at build time). This method must be called prior to using any other \c pico_cyw43_arch,
|
||||
* \c cyw43_driver or lwIP functions.
|
||||
*
|
||||
* \note this method initializes wireless with a country code of \c PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE
|
||||
* which defaults to \c CYW43_COUNTRY_WORLDWIDE. Worldwide settings may not give the best performance; consider
|
||||
* setting PICO_CYW43_ARCH_DEFAULT_COUNTRY_CODE to a different value or calling \ref cyw43_arch_init_with_country
|
||||
*
|
||||
* By default this method initializes the cyw43_arch code's own async_context by calling
|
||||
* \ref cyw43_arch_init_default_async_context, however the user can specify use of their own async_context
|
||||
* by calling \ref cyw43_arch_set_async_context() before calling this method
|
||||
*
|
||||
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
|
||||
*/
|
||||
int cyw43_arch_init(void);
|
||||
|
||||
/*!
|
||||
* \brief Initialize the CYW43 architecture for use in a specific country
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This method initializes the `cyw43_driver` code and initializes the lwIP stack (if it
|
||||
* was enabled at build time). This method must be called prior to using any other \c pico_cyw43_arch,
|
||||
* \c cyw43_driver or lwIP functions.
|
||||
*
|
||||
* By default this method initializes the cyw43_arch code's own async_context by calling
|
||||
* \ref cyw43_arch_init_default_async_context, however the user can specify use of their own async_context
|
||||
* by calling \ref cyw43_arch_set_async_context() before calling this method
|
||||
*
|
||||
* \param country the country code to use (see \ref CYW43_COUNTRY_)
|
||||
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
|
||||
*/
|
||||
int cyw43_arch_init_with_country(uint32_t country);
|
||||
|
||||
/*!
|
||||
* \brief De-initialize the CYW43 architecture
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This method de-initializes the `cyw43_driver` code and de-initializes the lwIP stack (if it
|
||||
* was enabled at build time). Note this method should always be called from the same core (or RTOS
|
||||
* task, depending on the environment) as \ref cyw43_arch_init.
|
||||
*
|
||||
* Additionally if the cyw43_arch is using its own async_context instance, then that instance is de-initialized.
|
||||
*/
|
||||
void cyw43_arch_deinit(void);
|
||||
|
||||
/*!
|
||||
* \brief Return the current async_context currently in use by the cyw43_arch code
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* \return the async_context.
|
||||
*/
|
||||
async_context_t *cyw43_arch_async_context(void);
|
||||
|
||||
/*!
|
||||
* \brief Set the async_context to be used by the cyw43_arch_init
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* \note This method must be called before calling cyw43_arch_init or cyw43_arch_init_with_country
|
||||
* if you wish to use a custom async_context instance.
|
||||
*
|
||||
* \param context the async_context to be used
|
||||
*/
|
||||
void cyw43_arch_set_async_context(async_context_t *context);
|
||||
|
||||
/*!
|
||||
* \brief Initialize the default async_context for the current cyw43_arch type
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This method initializes and returns a pointer to the static async_context associated
|
||||
* with cyw43_arch. This method is called by \ref cyw43_arch_init automatically
|
||||
* if a different async_context has not been set by \ref cyw43_arch_set_async_context
|
||||
*
|
||||
* \return the context or NULL if initialization failed.
|
||||
*/
|
||||
async_context_t *cyw43_arch_init_default_async_context(void);
|
||||
|
||||
/*!
|
||||
* \brief Perform any processing required by the \c cyw43_driver or the TCP/IP stack
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This method must be called periodically from the main loop when using a
|
||||
* \em polling style \c pico_cyw43_arch (e.g. \c pico_cyw43_arch_lwip_poll ). It
|
||||
* may be called in other styles, but it is unnecessary to do so.
|
||||
*/
|
||||
void cyw43_arch_poll(void);
|
||||
|
||||
/*!
|
||||
* \brief Sleep until there is cyw43_driver work to be done
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This method may be called by code that is waiting for an event to
|
||||
* come from the cyw43_driver, and has no work to do, but would like
|
||||
* to sleep without blocking any background work associated with the cyw43_driver.
|
||||
*
|
||||
* \param until the time to wait until if there is no work to do.
|
||||
*/
|
||||
void cyw43_arch_wait_for_work_until(absolute_time_t until);
|
||||
|
||||
/*!
|
||||
* \fn cyw43_arch_lwip_begin
|
||||
* \brief Acquire any locks required to call into lwIP
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* The lwIP API is not thread safe. You should surround calls into the lwIP API
|
||||
* with calls to this method and \ref cyw43_arch_lwip_end. Note these calls are not
|
||||
* necessary (but harmless) when you are calling back into the lwIP API from an lwIP callback.
|
||||
* If you are using single-core polling only (pico_cyw43_arch_poll) then these calls are no-ops
|
||||
* anyway it is good practice to call them anyway where they are necessary.
|
||||
*
|
||||
* \note as of SDK release 1.5.0, this is now equivalent to calling \ref async_context_acquire_lock_blocking
|
||||
* on the async_context associated with cyw43_arch and lwIP.
|
||||
*
|
||||
* \sa cyw43_arch_lwip_end
|
||||
* \sa cyw43_arch_lwip_protect
|
||||
* \sa async_context_acquire_lock_blocking
|
||||
* \sa cyw43_arch_async_context
|
||||
*/
|
||||
static inline void cyw43_arch_lwip_begin(void) {
|
||||
cyw43_thread_enter();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \fn void cyw43_arch_lwip_end(void)
|
||||
* \brief Release any locks required for calling into lwIP
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* The lwIP API is not thread safe. You should surround calls into the lwIP API
|
||||
* with calls to \ref cyw43_arch_lwip_begin and this method. Note these calls are not
|
||||
* necessary (but harmless) when you are calling back into the lwIP API from an lwIP callback.
|
||||
* If you are using single-core polling only (pico_cyw43_arch_poll) then these calls are no-ops
|
||||
* anyway it is good practice to call them anyway where they are necessary.
|
||||
*
|
||||
* \note as of SDK release 1.5.0, this is now equivalent to calling \ref async_context_release_lock
|
||||
* on the async_context associated with cyw43_arch and lwIP.
|
||||
*
|
||||
* \sa cyw43_arch_lwip_begin
|
||||
* \sa cyw43_arch_lwip_protect
|
||||
* \sa async_context_release_lock
|
||||
* \sa cyw43_arch_async_context
|
||||
*/
|
||||
static inline void cyw43_arch_lwip_end(void) {
|
||||
cyw43_thread_exit();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \fn int cyw43_arch_lwip_protect(int (*func)(void *param), void *param)
|
||||
* \brief sad Release any locks required for calling into lwIP
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* The lwIP API is not thread safe. You can use this method to wrap a function
|
||||
* with any locking required to call into the lwIP API. If you are using
|
||||
* single-core polling only (pico_cyw43_arch_poll) then there are no
|
||||
* locks to required, but it is still good practice to use this function.
|
||||
*
|
||||
* \param func the function ta call with any required locks held
|
||||
* \param param parameter to pass to \c func
|
||||
* \return the return value from \c func
|
||||
* \sa cyw43_arch_lwip_begin
|
||||
* \sa cyw43_arch_lwip_end
|
||||
*/
|
||||
static inline int cyw43_arch_lwip_protect(int (*func)(void *param), void *param) {
|
||||
cyw43_arch_lwip_begin();
|
||||
int rc = func(param);
|
||||
cyw43_arch_lwip_end();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \fn void cyw43_arch_lwip_check(void)
|
||||
* \brief Checks the caller has any locks required for calling into lwIP
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* The lwIP API is not thread safe. You should surround calls into the lwIP API
|
||||
* with calls to \ref cyw43_arch_lwip_begin and this method. Note these calls are not
|
||||
* necessary (but harmless) when you are calling back into the lwIP API from an lwIP callback.
|
||||
*
|
||||
* This method will assert in debug mode, if the above conditions are not met (i.e. it is not safe to
|
||||
* call into the lwIP API)
|
||||
*
|
||||
* \note as of SDK release 1.5.0, this is now equivalent to calling \ref async_context_lock_check
|
||||
* on the async_context associated with cyw43_arch and lwIP.
|
||||
*
|
||||
* \sa cyw43_arch_lwip_begin
|
||||
* \sa cyw43_arch_lwip_protect
|
||||
* \sa async_context_lock_check
|
||||
* \sa cyw43_arch_async_context
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief Return the country code used to initialize cyw43_arch
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* \return the country code (see \ref CYW43_COUNTRY_)
|
||||
*/
|
||||
uint32_t cyw43_arch_get_country_code(void);
|
||||
|
||||
/*!
|
||||
* \brief Enables Wi-Fi STA (Station) mode.
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This enables the Wi-Fi in \em Station mode such that connections can be made to other Wi-Fi Access Points
|
||||
*/
|
||||
void cyw43_arch_enable_sta_mode(void);
|
||||
|
||||
/*!
|
||||
* \brief Disables Wi-Fi STA (Station) mode.
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This disables the Wi-Fi in \em Station mode, disconnecting any active connection.
|
||||
* You should subsequently check the status by calling \ref cyw43_wifi_link_status.
|
||||
*/
|
||||
void cyw43_arch_disable_sta_mode(void);
|
||||
|
||||
/*!
|
||||
* \brief Enables Wi-Fi AP (Access point) mode.
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This enables the Wi-Fi in \em Access \em Point mode such that connections can be made to the device by other Wi-Fi clients
|
||||
* \param ssid the name for the access point
|
||||
* \param password the password to use or NULL for no password.
|
||||
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
|
||||
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
|
||||
*/
|
||||
void cyw43_arch_enable_ap_mode(const char *ssid, const char *password, uint32_t auth);
|
||||
|
||||
/*!
|
||||
* \brief Disables Wi-Fi AP (Access point) mode.
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This Disbles the Wi-Fi in \em Access \em Point mode.
|
||||
*/
|
||||
void cyw43_arch_disable_ap_mode(void);
|
||||
|
||||
/*!
|
||||
* \brief Attempt to connect to a wireless access point, blocking until the network is joined or a failure is detected.
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* \param ssid the network name to connect to
|
||||
* \param pw the network password or NULL if there is no password required
|
||||
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
|
||||
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
|
||||
*
|
||||
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
|
||||
*/
|
||||
int cyw43_arch_wifi_connect_blocking(const char *ssid, const char *pw, uint32_t auth);
|
||||
|
||||
/*!
|
||||
* \brief Attempt to connect to a wireless access point specified by SSID and BSSID, blocking until the network is joined or a failure is detected.
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* \param ssid the network name to connect to
|
||||
* \param bssid the network BSSID to connect to or NULL if ignored
|
||||
* \param pw the network password or NULL if there is no password required
|
||||
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
|
||||
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
|
||||
*
|
||||
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
|
||||
*/
|
||||
int cyw43_arch_wifi_connect_bssid_blocking(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth);
|
||||
|
||||
/*!
|
||||
* \brief Attempt to connect to a wireless access point, blocking until the network is joined, a failure is detected or a timeout occurs
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* \param ssid the network name to connect to
|
||||
* \param pw the network password or NULL if there is no password required
|
||||
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
|
||||
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
|
||||
* \param timeout how long to wait in milliseconds for a connection to succeed before giving up
|
||||
*
|
||||
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
|
||||
*/
|
||||
int cyw43_arch_wifi_connect_timeout_ms(const char *ssid, const char *pw, uint32_t auth, uint32_t timeout);
|
||||
|
||||
/*!
|
||||
* \brief Attempt to connect to a wireless access point specified by SSID and BSSID, blocking until the network is joined, a failure is detected or a timeout occurs
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* \param ssid the network name to connect to
|
||||
* \param bssid the network BSSID to connect to or NULL if ignored
|
||||
* \param pw the network password or NULL if there is no password required
|
||||
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
|
||||
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
|
||||
* \param timeout how long to wait in milliseconds for a connection to succeed before giving up
|
||||
*
|
||||
* \return 0 if the initialization is successful, an error code otherwise \see pico_error_codes
|
||||
*/
|
||||
int cyw43_arch_wifi_connect_bssid_timeout_ms(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth, uint32_t timeout);
|
||||
|
||||
/*!
|
||||
* \brief Start attempting to connect to a wireless access point
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This method tells the CYW43 driver to start connecting to an access point. You should subsequently check the
|
||||
* status by calling \ref cyw43_wifi_link_status.
|
||||
*
|
||||
* \param ssid the network name to connect to
|
||||
* \param pw the network password or NULL if there is no password required
|
||||
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
|
||||
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
|
||||
*
|
||||
* \return 0 if the scan was started successfully, an error code otherwise \see pico_error_codes
|
||||
*/
|
||||
int cyw43_arch_wifi_connect_async(const char *ssid, const char *pw, uint32_t auth);
|
||||
|
||||
/*!
|
||||
* \brief Start attempting to connect to a wireless access point specified by SSID and BSSID
|
||||
* \ingroup pico_cyw43_arch
|
||||
*
|
||||
* This method tells the CYW43 driver to start connecting to an access point. You should subsequently check the
|
||||
* status by calling \ref cyw43_wifi_link_status.
|
||||
*
|
||||
* \param ssid the network name to connect to
|
||||
* \param bssid the network BSSID to connect to or NULL if ignored
|
||||
* \param pw the network password or NULL if there is no password required
|
||||
* \param auth the authorization type to use when the password is enabled. Values are \ref CYW43_AUTH_WPA_TKIP_PSK,
|
||||
* \ref CYW43_AUTH_WPA2_AES_PSK, or \ref CYW43_AUTH_WPA2_MIXED_PSK (see \ref CYW43_AUTH_)
|
||||
*
|
||||
* \return 0 if the scan was started successfully, an error code otherwise \see pico_error_codes
|
||||
*/
|
||||
int cyw43_arch_wifi_connect_bssid_async(const char *ssid, const uint8_t *bssid, const char *pw, uint32_t auth);
|
||||
|
||||
/*!
|
||||
* \brief Set a GPIO pin on the wireless chip to a given value
|
||||
* \ingroup pico_cyw43_arch
|
||||
* \note this method does not check for errors setting the GPIO. You can use the lower level \ref cyw43_gpio_set instead if you wish
|
||||
* to check for errors.
|
||||
*
|
||||
* \param wl_gpio the GPIO number on the wireless chip
|
||||
* \param value true to set the GPIO, false to clear it.
|
||||
*/
|
||||
void cyw43_arch_gpio_put(uint wl_gpio, bool value);
|
||||
|
||||
/*!
|
||||
* \brief Read the value of a GPIO pin on the wireless chip
|
||||
* \ingroup pico_cyw43_arch
|
||||
* \note this method does not check for errors setting the GPIO. You can use the lower level \ref cyw43_gpio_get instead if you wish
|
||||
* to check for errors.
|
||||
*
|
||||
* \param wl_gpio the GPIO number on the wireless chip
|
||||
* \return true if the GPIO is high, false otherwise
|
||||
*/
|
||||
bool cyw43_arch_gpio_get(uint wl_gpio);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_CYW43_ARCH_ARCH_FREERTOS_H
|
||||
#define _PICO_CYW43_ARCH_ARCH_FREERTOS_H
|
||||
|
||||
// PICO_CONFIG: CYW43_TASK_STACK_SIZE, Stack size for the CYW43 FreeRTOS task in 4-byte words, type=int, default=1024, group=pico_cyw43_arch
|
||||
#ifndef CYW43_TASK_STACK_SIZE
|
||||
#define CYW43_TASK_STACK_SIZE 1024
|
||||
#endif
|
||||
|
||||
// PICO_CONFIG: CYW43_TASK_PRIORITY, Priority for the CYW43 FreeRTOS task, type=int, default=tskIDLE_PRIORITY + 4, group=pico_cyw43_arch
|
||||
#ifndef CYW43_TASK_PRIORITY
|
||||
#define CYW43_TASK_PRIORITY (tskIDLE_PRIORITY + 4)
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_CYW43_ARCH_ARCH_POLL_H
|
||||
#define _PICO_CYW43_ARCH_ARCH_POLL_H
|
||||
|
||||
// now obsolete; kept for backwards compatibility
|
||||
|
||||
#endif
|
@ -1,12 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _PICO_CYW43_ARCH_ARCH_THREADSAFE_BACKGROUND_H
|
||||
#define _PICO_CYW43_ARCH_ARCH_THREADSAFE_BACKGROUND_H
|
||||
|
||||
// now obsolete; kept for backwards compatibility
|
||||
|
||||
#endif
|
@ -1,93 +0,0 @@
|
||||
if (DEFINED ENV{PICO_CYW43_DRIVER_PATH} AND (NOT PICO_CYW43_DRIVER_PATH))
|
||||
set(PICO_CYW43_DRIVER_PATH $ENV{PICO_CYW43_DRIVER_PATH})
|
||||
message("Using PICO_CYW43_DRIVER_PATH from environment ('${PICO_CYW43_DRIVER_PATH}')")
|
||||
endif()
|
||||
|
||||
set(CYW43_DRIVER_TEST_FILE "src/cyw43.h")
|
||||
|
||||
if (NOT PICO_CYW43_DRIVER_PATH)
|
||||
set(PICO_CYW43_DRIVER_PATH ${PICO_SDK_PATH}/lib/cyw43-driver)
|
||||
if (PICO_CYW43_SUPPORTED AND NOT EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
|
||||
message(WARNING "cyw43-driver submodule has not been initialized; Pico W wireless support will be unavailable
|
||||
hint: try 'git submodule update --init' from your SDK directory (${PICO_SDK_PATH}).")
|
||||
endif()
|
||||
elseif (NOT EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
|
||||
message(WARNING "PICO_CYW43_DRIVER_PATH specified but content not present.")
|
||||
endif()
|
||||
|
||||
if (EXISTS ${PICO_CYW43_DRIVER_PATH}/${CYW43_DRIVER_TEST_FILE})
|
||||
message("cyw43-driver available at ${PICO_CYW43_DRIVER_PATH}")
|
||||
|
||||
add_subdirectory(cybt_shared_bus)
|
||||
|
||||
pico_register_common_scope_var(PICO_CYW43_DRIVER_PATH)
|
||||
|
||||
# base driver without our bus
|
||||
pico_add_library(cyw43_driver NOFLAG)
|
||||
target_sources(cyw43_driver INTERFACE
|
||||
${PICO_CYW43_DRIVER_PATH}/src/cyw43_ll.c
|
||||
${PICO_CYW43_DRIVER_PATH}/src/cyw43_stats.c
|
||||
${PICO_CYW43_DRIVER_PATH}/src/cyw43_lwip.c
|
||||
${PICO_CYW43_DRIVER_PATH}/src/cyw43_ctrl.c
|
||||
)
|
||||
target_include_directories(cyw43_driver_headers INTERFACE
|
||||
${PICO_CYW43_DRIVER_PATH}/src
|
||||
${PICO_CYW43_DRIVER_PATH}/firmware
|
||||
)
|
||||
|
||||
# pico_cyw43_driver adds async_context integration to cyw43_driver
|
||||
pico_add_library(pico_cyw43_driver NOFLAG)
|
||||
target_sources(pico_cyw43_driver INTERFACE
|
||||
cyw43_driver.c)
|
||||
target_include_directories(pico_cyw43_driver_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
pico_mirrored_target_link_libraries(pico_cyw43_driver INTERFACE cyw43_driver)
|
||||
|
||||
# cyw43_driver_picow is cyw43_driver plus Pico W specific bus implementation
|
||||
pico_add_library(cyw43_driver_picow NOFLAG)
|
||||
target_sources(cyw43_driver_picow INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/cyw43_bus_pio_spi.c
|
||||
)
|
||||
pico_generate_pio_header(cyw43_driver_picow ${CMAKE_CURRENT_LIST_DIR}/cyw43_bus_pio_spi.pio)
|
||||
pico_mirrored_target_link_libraries(cyw43_driver_picow INTERFACE
|
||||
cyw43_driver
|
||||
cybt_shared_bus
|
||||
hardware_pio
|
||||
hardware_dma
|
||||
hardware_exception
|
||||
)
|
||||
|
||||
# Note: This is used by MP, so check for issues when making changes
|
||||
# e.g. Don't add new depenedences
|
||||
pico_add_library(pico_btstack_hci_transport_cyw43 NOFLAG)
|
||||
target_sources(pico_btstack_hci_transport_cyw43 INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/btstack_hci_transport_cyw43.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/btstack_chipset_cyw43.c
|
||||
)
|
||||
target_include_directories(pico_btstack_hci_transport_cyw43_headers INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/include
|
||||
)
|
||||
target_compile_definitions(pico_btstack_hci_transport_cyw43_headers INTERFACE
|
||||
CYW43_ENABLE_BLUETOOTH=1
|
||||
)
|
||||
|
||||
if (TARGET pico_btstack_base)
|
||||
message("Pico W Bluetooth build support available.")
|
||||
|
||||
pico_add_library(pico_btstack_cyw43)
|
||||
target_sources(pico_btstack_cyw43 INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/btstack_cyw43.c
|
||||
)
|
||||
target_include_directories(pico_btstack_cyw43_headers INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/include
|
||||
)
|
||||
pico_mirrored_target_link_libraries(pico_btstack_cyw43 INTERFACE
|
||||
pico_btstack_base
|
||||
pico_btstack_flash_bank
|
||||
pico_btstack_run_loop_async_context
|
||||
pico_cyw43_arch
|
||||
pico_btstack_hci_transport_cyw43
|
||||
)
|
||||
endif()
|
||||
|
||||
pico_promote_common_scope_vars()
|
||||
endif()
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico/btstack_chipset_cyw43.h"
|
||||
|
||||
static void chipset_set_bd_addr_command(bd_addr_t addr, uint8_t *hci_cmd_buffer) {
|
||||
hci_cmd_buffer[0] = 0x01;
|
||||
hci_cmd_buffer[1] = 0xfc;
|
||||
hci_cmd_buffer[2] = 0x06;
|
||||
reverse_bd_addr(addr, &hci_cmd_buffer[3]);
|
||||
}
|
||||
|
||||
static const btstack_chipset_t btstack_chipset_cyw43 = {
|
||||
.name = "CYW43",
|
||||
.init = NULL,
|
||||
.next_command = NULL,
|
||||
.set_baudrate_command = NULL,
|
||||
.set_bd_addr_command = chipset_set_bd_addr_command,
|
||||
};
|
||||
|
||||
const btstack_chipset_t * btstack_chipset_cyw43_instance(void) {
|
||||
return &btstack_chipset_cyw43;
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "ble/le_device_db_tlv.h"
|
||||
#include "classic/btstack_link_key_db_tlv.h"
|
||||
#include "btstack_tlv.h"
|
||||
#include "btstack_tlv_flash_bank.h"
|
||||
#include "btstack_memory.h"
|
||||
#include "hci.h"
|
||||
|
||||
#if WANT_HCI_DUMP
|
||||
#include "hci_dump.h"
|
||||
#ifdef ENABLE_SEGGER_RTT
|
||||
#include "hci_dump_segger_rtt_stdout.h"
|
||||
#else
|
||||
#include "hci_dump_embedded_stdout.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "pico/btstack_hci_transport_cyw43.h"
|
||||
#include "pico/btstack_run_loop_async_context.h"
|
||||
#include "pico/btstack_flash_bank.h"
|
||||
#include "pico/btstack_cyw43.h"
|
||||
|
||||
static void setup_tlv(void) {
|
||||
static btstack_tlv_flash_bank_t btstack_tlv_flash_bank_context;
|
||||
const hal_flash_bank_t *hal_flash_bank_impl = pico_flash_bank_instance();
|
||||
|
||||
const btstack_tlv_t *btstack_tlv_impl = btstack_tlv_flash_bank_init_instance(
|
||||
&btstack_tlv_flash_bank_context,
|
||||
hal_flash_bank_impl,
|
||||
NULL);
|
||||
|
||||
// setup global TLV
|
||||
btstack_tlv_set_instance(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
|
||||
#ifdef ENABLE_CLASSIC
|
||||
const btstack_link_key_db_t *btstack_link_key_db = btstack_link_key_db_tlv_get_instance(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
|
||||
hci_set_link_key_db(btstack_link_key_db);
|
||||
#endif
|
||||
#ifdef ENABLE_BLE
|
||||
// configure LE Device DB for TLV
|
||||
le_device_db_tlv_configure(btstack_tlv_impl, &btstack_tlv_flash_bank_context);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool btstack_cyw43_init(async_context_t *context) {
|
||||
// Initialise bluetooth
|
||||
btstack_memory_init();
|
||||
btstack_run_loop_init(btstack_run_loop_async_context_get_instance(context));
|
||||
|
||||
#if WANT_HCI_DUMP
|
||||
#ifdef ENABLE_SEGGER_RTT
|
||||
hci_dump_init(hci_dump_segger_rtt_stdout_get_instance());
|
||||
#else
|
||||
hci_dump_init(hci_dump_embedded_stdout_get_instance());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
hci_init(hci_transport_cyw43_instance(), NULL);
|
||||
|
||||
// setup TLV storage
|
||||
setup_tlv();
|
||||
return true;
|
||||
}
|
||||
|
||||
void btstack_cyw43_deinit(__unused async_context_t *context) {
|
||||
hci_power_control(HCI_POWER_OFF);
|
||||
hci_close();
|
||||
btstack_run_loop_deinit();
|
||||
btstack_memory_deinit();
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "pico.h"
|
||||
#include "cyw43.h"
|
||||
#include "hci_transport.h"
|
||||
#include "hci.h"
|
||||
#include "pico/btstack_hci_transport_cyw43.h"
|
||||
#include "pico/btstack_chipset_cyw43.h"
|
||||
|
||||
// assert outgoing pre-buffer for cyw43 header is available
|
||||
#if !defined(HCI_OUTGOING_PRE_BUFFER_SIZE) || (HCI_OUTGOING_PRE_BUFFER_SIZE < 4)
|
||||
#error HCI_OUTGOING_PRE_BUFFER_SIZE not defined or smaller than 4. Please update btstack_config.h
|
||||
#endif
|
||||
|
||||
// assert outgoing packet fragments are word aligned
|
||||
#if !defined(HCI_ACL_CHUNK_SIZE_ALIGNMENT) || ((HCI_ACL_CHUNK_SIZE_ALIGNMENT & 3) != 0)
|
||||
#error HCI_ACL_CHUNK_SIZE_ALIGNMENT not defined or not a multiply of 4. Please update btstack_config.h
|
||||
#endif
|
||||
|
||||
#define BT_DEBUG_ENABLED 0
|
||||
#if BT_DEBUG_ENABLED
|
||||
#define BT_DEBUG(...) CYW43_PRINTF(__VA_ARGS__)
|
||||
#else
|
||||
#define BT_DEBUG(...) (void)0
|
||||
#endif
|
||||
|
||||
// Callback when we have data
|
||||
static void (*hci_transport_cyw43_packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = NULL;
|
||||
|
||||
// Incoming packet buffer - cyw43 packet header (incl packet type) + incoming pre buffer + max(acl header + acl payload, event header + event data)
|
||||
__attribute__((aligned(4)))
|
||||
static uint8_t hci_packet_with_pre_buffer[4 + HCI_INCOMING_PRE_BUFFER_SIZE + HCI_INCOMING_PACKET_BUFFER_SIZE ];
|
||||
|
||||
static btstack_data_source_t transport_data_source;
|
||||
static bool hci_transport_ready;
|
||||
|
||||
// Forward declaration
|
||||
static void hci_transport_cyw43_process(void);
|
||||
|
||||
static void hci_transport_data_source_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) {
|
||||
assert(callback_type == DATA_SOURCE_CALLBACK_POLL);
|
||||
assert(ds == &transport_data_source);
|
||||
(void)callback_type;
|
||||
(void)ds;
|
||||
hci_transport_cyw43_process();
|
||||
}
|
||||
|
||||
static void hci_transport_cyw43_init(const void *transport_config) {
|
||||
UNUSED(transport_config);
|
||||
}
|
||||
|
||||
static int hci_transport_cyw43_open(void) {
|
||||
int err = cyw43_bluetooth_hci_init();
|
||||
if (err != 0) {
|
||||
CYW43_PRINTF("Failed to open cyw43 hci controller: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// OTP should be set in which case BT gets an address of wifi mac + 1
|
||||
// If OTP is not set for some reason BT gets set to 43:43:A2:12:1F:AC.
|
||||
// So for safety, set the bluetooth device address here.
|
||||
bd_addr_t addr;
|
||||
cyw43_hal_get_mac(0, (uint8_t*)&addr);
|
||||
addr[BD_ADDR_LEN - 1]++;
|
||||
hci_set_chipset(btstack_chipset_cyw43_instance());
|
||||
hci_set_bd_addr(addr);
|
||||
|
||||
btstack_run_loop_set_data_source_handler(&transport_data_source, &hci_transport_data_source_process);
|
||||
btstack_run_loop_enable_data_source_callbacks(&transport_data_source, DATA_SOURCE_CALLBACK_POLL);
|
||||
btstack_run_loop_add_data_source(&transport_data_source);
|
||||
hci_transport_ready = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hci_transport_cyw43_close(void) {
|
||||
btstack_run_loop_disable_data_source_callbacks(&transport_data_source, DATA_SOURCE_CALLBACK_POLL);
|
||||
btstack_run_loop_remove_data_source(&transport_data_source);
|
||||
hci_transport_ready = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hci_transport_cyw43_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)) {
|
||||
hci_transport_cyw43_packet_handler = handler;
|
||||
}
|
||||
|
||||
static int hci_transport_cyw43_can_send_now(uint8_t packet_type) {
|
||||
UNUSED(packet_type);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int hci_transport_cyw43_send_packet(uint8_t packet_type, uint8_t *packet, int size) {
|
||||
// store packet type before actual data and increase size
|
||||
// This relies on HCI_OUTGOING_PRE_BUFFER_SIZE being set
|
||||
uint8_t *buffer = &packet[-4];
|
||||
uint32_t buffer_size = size + 4;
|
||||
buffer[3] = packet_type;
|
||||
|
||||
CYW43_THREAD_ENTER
|
||||
int err = cyw43_bluetooth_hci_write(buffer, buffer_size);
|
||||
|
||||
if (err != 0) {
|
||||
CYW43_PRINTF("Failed to send cyw43 hci packet: %d\n", err);
|
||||
assert(false);
|
||||
} else {
|
||||
BT_DEBUG("bt sent %lu\n", buffer_size);
|
||||
static uint8_t packet_sent_event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
|
||||
hci_transport_cyw43_packet_handler(HCI_EVENT_PACKET, &packet_sent_event[0], sizeof(packet_sent_event));
|
||||
}
|
||||
CYW43_THREAD_EXIT
|
||||
return err;
|
||||
}
|
||||
|
||||
// configure and return hci transport singleton
|
||||
static const hci_transport_t hci_transport_cyw43 = {
|
||||
/* const char * name; */ "CYW43",
|
||||
/* void (*init) (const void *transport_config); */ &hci_transport_cyw43_init,
|
||||
/* int (*open)(void); */ &hci_transport_cyw43_open,
|
||||
/* int (*close)(void); */ &hci_transport_cyw43_close,
|
||||
/* void (*register_packet_handler)(void (*handler)(...); */ &hci_transport_cyw43_register_packet_handler,
|
||||
/* int (*can_send_packet_now)(uint8_t packet_type); */ &hci_transport_cyw43_can_send_now,
|
||||
/* int (*send_packet)(...); */ &hci_transport_cyw43_send_packet,
|
||||
/* int (*set_baudrate)(uint32_t baudrate); */ NULL,
|
||||
/* void (*reset_link)(void); */ NULL,
|
||||
/* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL,
|
||||
};
|
||||
|
||||
const hci_transport_t *hci_transport_cyw43_instance(void) {
|
||||
return &hci_transport_cyw43;
|
||||
}
|
||||
|
||||
// Called to perform bt work from a data source
|
||||
static void hci_transport_cyw43_process(void) {
|
||||
CYW43_THREAD_LOCK_CHECK
|
||||
uint32_t len = 0;
|
||||
bool has_work;
|
||||
do {
|
||||
int err = cyw43_bluetooth_hci_read(hci_packet_with_pre_buffer, sizeof(hci_packet_with_pre_buffer), &len);
|
||||
BT_DEBUG("bt in len=%lu err=%d\n", len, err);
|
||||
if (err == 0 && len > 0) {
|
||||
hci_transport_cyw43_packet_handler(hci_packet_with_pre_buffer[3], hci_packet_with_pre_buffer + 4, len - 4);
|
||||
has_work = true;
|
||||
} else {
|
||||
has_work = false;
|
||||
}
|
||||
} while (has_work);
|
||||
}
|
||||
|
||||
// This is called from cyw43_poll_func.
|
||||
void cyw43_bluetooth_hci_process(void) {
|
||||
if (hci_transport_ready) {
|
||||
btstack_run_loop_poll_data_sources_from_irq();
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
# cyw43 shared bus read and write
|
||||
pico_add_library(cybt_shared_bus NOFLAG)
|
||||
|
||||
target_sources(cybt_shared_bus INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/cybt_shared_bus.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/cybt_shared_bus_driver.c
|
||||
)
|
||||
target_include_directories(cybt_shared_bus_headers INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
# The BT firmware is supplied as a source file containing a static array with ascii hex data
|
||||
# Set this to true to use this for testing
|
||||
set(CYW43_USE_HEX_BTFW 0)
|
||||
if (CYW43_USE_HEX_BTFW)
|
||||
message("Warning: CYW43_USE_HEX_BTFW is true")
|
||||
target_sources(cybt_shared_bus INTERFACE
|
||||
${PICO_CYW43_DRIVER_PATH}/firmware/cybt_firmware_43439.c
|
||||
)
|
||||
target_compile_definitions(cybt_shared_bus INTERFACE
|
||||
CYW43_USE_HEX_BTFW=1
|
||||
)
|
||||
endif()
|
@ -1,431 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "cyw43_btbus.h"
|
||||
#include "cyw43_ll.h"
|
||||
#include "cyw43_config.h"
|
||||
#include "cybt_shared_bus_driver.h"
|
||||
|
||||
#include "cyw43_btfw_43439.h"
|
||||
|
||||
#if CYW43_USE_HEX_BTFW
|
||||
extern const char brcm_patch_version[];
|
||||
extern const uint8_t brcm_patchram_buf[];
|
||||
extern const int brcm_patch_ram_length;
|
||||
#endif
|
||||
|
||||
#define BTSDIO_FW_READY_POLLING_INTERVAL_MS (1)
|
||||
#define BTSDIO_BT_AWAKE_POLLING_INTERVAL_MS (1)
|
||||
|
||||
#define BTSDIO_FW_READY_POLLING_RETRY_COUNT (300)
|
||||
#define BTSDIO_FW_AWAKE_POLLING_RETRY_COUNT (300)
|
||||
|
||||
#define BTSDIO_FWBUF_OPER_DELAY_US (250)
|
||||
#define BTFW_WAIT_TIME_MS (150)
|
||||
|
||||
#define CYBT_DEBUG 0
|
||||
#define CYBT_VDEBUG 0
|
||||
|
||||
#if CYBT_DEBUG
|
||||
#define cybt_debug(format,args...) printf("%d.%d: " format, (int)cyw43_hal_ticks_ms() / 1000, (int)cyw43_hal_ticks_ms() % 1000, ## args)
|
||||
#else
|
||||
#define cybt_debug(format, ...) ((void)0)
|
||||
#endif
|
||||
#define cybt_printf(format, args...) printf("%d.%d: " format, (int)cyw43_hal_ticks_ms() / 1000, (int)cyw43_hal_ticks_ms() % 1000, ## args)
|
||||
|
||||
#define ROUNDUP(x, a) ((((x) + ((a) - 1)) / (a)) * (a))
|
||||
#define ROUNDDN(x, a) ((x) & ~((a) - 1))
|
||||
#define ISALIGNED(a, x) (((uint32_t)(a) & ((x) - 1)) == 0)
|
||||
|
||||
#define CIRC_BUF_CNT(in, out) (((in) - (out)) & ((BTSDIO_FWBUF_SIZE)-1))
|
||||
#define CIRC_BUF_SPACE(in, out) CIRC_BUF_CNT((out), ((in) + 4))
|
||||
|
||||
typedef enum {
|
||||
HCI_PACKET_TYPE_IGNORE = 0x00,
|
||||
HCI_PACKET_TYPE_COMMAND = 0x01,
|
||||
HCI_PACKET_TYPE_ACL = 0x02,
|
||||
HCI_PACKET_TYPE_SCO = 0x03,
|
||||
HCI_PACKET_TYPE_EVENT = 0x04,
|
||||
HCI_PACKET_TYPE_DIAG = 0x07,
|
||||
HCI_PACKET_TYPE_LOOPBACK = 0xFF
|
||||
} hci_packet_type_t;
|
||||
|
||||
static cybt_result_t cybt_fw_download_prepare(uint8_t **p_write_buf, uint8_t **p_hex_buf) {
|
||||
*p_write_buf = NULL;
|
||||
*p_hex_buf = NULL;
|
||||
|
||||
*p_write_buf = cyw43_malloc(BTFW_DOWNLOAD_BLK_SIZE + BTFW_SD_ALIGN);
|
||||
if (NULL == *p_write_buf) {
|
||||
return CYBT_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
*p_hex_buf = cyw43_malloc(BTFW_MAX_STR_LEN);
|
||||
if (NULL == *p_hex_buf) {
|
||||
cyw43_free(*p_write_buf);
|
||||
return CYBT_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
static cybt_result_t cybt_fw_download_finish(uint8_t *p_write_buf, uint8_t *p_hex_buf) {
|
||||
if (p_write_buf) {
|
||||
cyw43_free(p_write_buf);
|
||||
}
|
||||
|
||||
if (p_hex_buf) {
|
||||
cyw43_free(p_hex_buf);
|
||||
}
|
||||
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
static cybt_result_t cybt_wait_bt_ready(uint32_t max_polling_times) {
|
||||
cyw43_delay_ms(BTFW_WAIT_TIME_MS);
|
||||
do {
|
||||
if (cybt_ready()) {
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
cyw43_delay_ms(BTSDIO_FW_READY_POLLING_INTERVAL_MS);
|
||||
} while (max_polling_times--);
|
||||
return CYBT_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
static cybt_result_t cybt_wait_bt_awake(uint32_t max_polling_times) {
|
||||
do {
|
||||
if (cybt_awake()) {
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
cyw43_delay_ms(BTSDIO_BT_AWAKE_POLLING_INTERVAL_MS);
|
||||
} while (max_polling_times--);
|
||||
return CYBT_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
int cyw43_btbus_init(cyw43_ll_t *self) {
|
||||
cybt_result_t ret;
|
||||
|
||||
uint8_t *p_write_buf = NULL;
|
||||
uint8_t *p_hex_buf = NULL;
|
||||
|
||||
cybt_sharedbus_driver_init(self);
|
||||
|
||||
ret = cybt_fw_download_prepare(&p_write_buf, &p_hex_buf);
|
||||
if (CYBT_SUCCESS != ret) {
|
||||
cybt_printf("Could not allocate memory\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cybt_debug("cybt_fw_download\n");
|
||||
const uint8_t *fw_data_buf;
|
||||
uint32_t fw_data_len;
|
||||
#if CYW43_USE_HEX_BTFW
|
||||
cybt_printf("CYW43_USE_HEX_BTFW is true\n");
|
||||
#ifndef NDEBUG
|
||||
cybt_printf("BT FW download, version = %s\n", brcm_patch_version);
|
||||
#endif
|
||||
fw_data_len = brcm_patch_ram_length;
|
||||
fw_data_buf = brcm_patchram_buf;
|
||||
#else
|
||||
fw_data_len = cyw43_btfw_43439_len;
|
||||
fw_data_buf = cyw43_btfw_43439;
|
||||
#endif
|
||||
ret = cybt_fw_download(fw_data_buf,
|
||||
fw_data_len,
|
||||
p_write_buf,
|
||||
p_hex_buf
|
||||
);
|
||||
|
||||
cybt_debug("cybt_fw_download_finish\n");
|
||||
cybt_fw_download_finish(p_write_buf, p_hex_buf);
|
||||
|
||||
if (CYBT_SUCCESS != ret) {
|
||||
cybt_printf("hci_open(): FW download failed (0x%x)\n", ret);
|
||||
return CYBT_ERR_HCI_INIT_FAILED;
|
||||
}
|
||||
|
||||
cybt_debug("// cybt_wait_bt_ready\n");
|
||||
ret = cybt_wait_bt_ready(BTSDIO_FW_READY_POLLING_RETRY_COUNT);
|
||||
assert(ret == CYBT_SUCCESS);
|
||||
if (CYBT_SUCCESS == ret) {
|
||||
cybt_debug("hci_open(): FW download successfully\n");
|
||||
} else {
|
||||
cybt_printf("hci_open(): Failed to download FW\n");
|
||||
return CYBT_ERR_HCI_INIT_FAILED;
|
||||
}
|
||||
|
||||
ret = cybt_init_buffer();
|
||||
assert(ret == 0);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = cybt_wait_bt_awake(BTSDIO_FW_AWAKE_POLLING_RETRY_COUNT);
|
||||
assert(ret == 0);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
cybt_set_host_ready();
|
||||
cybt_toggle_bt_intr();
|
||||
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
#if CYBT_VDEBUG
|
||||
static void dump_bytes(const uint8_t *bptr, uint32_t len) {
|
||||
unsigned int i = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((i & 0x07) == 0) {
|
||||
printf("\n ");
|
||||
}
|
||||
printf("0x%02x", bptr[i]);
|
||||
if (i != (len-1)) {
|
||||
printf(", ");
|
||||
} else {
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static cybt_result_t cybt_hci_write_buf(const uint8_t *p_data, uint32_t length) {
|
||||
cybt_result_t ret_result = CYBT_SUCCESS;
|
||||
cybt_fw_membuf_index_t fw_membuf_info = {0};
|
||||
|
||||
assert(ISALIGNED(p_data, 4));
|
||||
if (!ISALIGNED(p_data, 4)) {
|
||||
cybt_printf("cybt_hci_write_hdr: buffer not aligned\n");
|
||||
return CYBT_ERR_BADARG;
|
||||
}
|
||||
|
||||
// total length including header
|
||||
length = ROUNDUP(length, 4);
|
||||
cybt_get_bt_buf_index(&fw_membuf_info);
|
||||
uint32_t buf_space = CIRC_BUF_SPACE(fw_membuf_info.host2bt_in_val, fw_membuf_info.host2bt_out_val);
|
||||
assert(length <= buf_space); // queue full?
|
||||
if (length > buf_space) {
|
||||
return CYBT_ERR_QUEUE_FULL;
|
||||
}
|
||||
|
||||
if (fw_membuf_info.host2bt_in_val + length <= BTSDIO_FWBUF_SIZE) {
|
||||
// Don't need to wrap circular buf
|
||||
cybt_debug("cybt_hci_write_hdr: 1-round write, len = %" PRId32 "\n", length);
|
||||
cybt_mem_write_idx(H2B_BUF_ADDR_IDX, fw_membuf_info.host2bt_in_val, p_data, length);
|
||||
fw_membuf_info.host2bt_in_val += length;
|
||||
} else {
|
||||
// Need to wrap circular buf
|
||||
uint32_t first_write_len = BTSDIO_FWBUF_SIZE - fw_membuf_info.host2bt_in_val;
|
||||
if (first_write_len >= 4) {
|
||||
cybt_mem_write_idx(H2B_BUF_ADDR_IDX, fw_membuf_info.host2bt_in_val, p_data, first_write_len);
|
||||
fw_membuf_info.host2bt_in_val += first_write_len;
|
||||
} else {
|
||||
first_write_len = 0;
|
||||
}
|
||||
uint32_t second_write_len = length - first_write_len;
|
||||
cybt_debug("cybt_hci_write_hdr: 2-round write, 1st_len = %" PRId32 ", 2nd_len = %" PRId32 "\n", first_write_len,
|
||||
second_write_len);
|
||||
if (second_write_len > 0) {
|
||||
cybt_mem_write_idx(H2B_BUF_ADDR_IDX, 0, p_data + first_write_len, second_write_len);
|
||||
fw_membuf_info.host2bt_in_val += second_write_len;
|
||||
}
|
||||
}
|
||||
|
||||
// Update circular buf pointer
|
||||
const uint32_t new_h2b_in_val = fw_membuf_info.host2bt_in_val & (BTSDIO_FWBUF_SIZE - 1);
|
||||
cybt_reg_write_idx(H2B_BUF_IN_ADDR_IDX, new_h2b_in_val);
|
||||
|
||||
cybt_toggle_bt_intr();
|
||||
return ret_result;
|
||||
}
|
||||
|
||||
static cybt_result_t cybt_hci_read(uint8_t *p_data, uint32_t *p_length) {
|
||||
cybt_result_t ret_result = CYBT_SUCCESS;
|
||||
uint32_t fw_b2h_buf_count;
|
||||
uint32_t new_b2h_out_val;
|
||||
cybt_fw_membuf_index_t fw_membuf_info = {0};
|
||||
static uint32_t available = 0;
|
||||
|
||||
assert(ISALIGNED(p_data, 4));
|
||||
if (!ISALIGNED(p_data, 4)) {
|
||||
assert(false);
|
||||
cybt_printf("cybt_hci_read: buffer not aligned\n");
|
||||
return CYBT_ERR_BADARG;
|
||||
}
|
||||
|
||||
uint32_t read_len = ROUNDUP(*p_length, 4);
|
||||
|
||||
cybt_get_bt_buf_index(&fw_membuf_info);
|
||||
fw_b2h_buf_count = CIRC_BUF_CNT(fw_membuf_info.bt2host_in_val,
|
||||
fw_membuf_info.bt2host_out_val);
|
||||
cybt_debug("cybt_hci_read: bt2host_in_val=%lu bt2host_out_val=%lu fw_b2h_buf_count=%ld\n",
|
||||
fw_membuf_info.bt2host_in_val, fw_membuf_info.bt2host_out_val, fw_b2h_buf_count);
|
||||
if (fw_b2h_buf_count < available) {
|
||||
cybt_printf("error: cybt_hci_read buffer overflow fw_b2h_buf_count=%ld available=%lu\n", fw_b2h_buf_count,
|
||||
available);
|
||||
cybt_printf("error: cybt_hci_read bt2host_in_val=%lu bt2host_out_val=%lu\n", fw_membuf_info.bt2host_in_val,
|
||||
fw_membuf_info.bt2host_out_val);
|
||||
panic("cyw43 buffer overflow");
|
||||
}
|
||||
|
||||
// No space in buffer
|
||||
if (fw_b2h_buf_count == 0) {
|
||||
*p_length = 0;
|
||||
} else {
|
||||
if (read_len > fw_b2h_buf_count) {
|
||||
read_len = fw_b2h_buf_count;
|
||||
}
|
||||
|
||||
if (fw_membuf_info.bt2host_out_val + read_len <= BTSDIO_FWBUF_SIZE) {
|
||||
// Don't need to wrap the circular buf
|
||||
cybt_debug("cybt_hci_read: 1-round read, len = %" PRId32 "\n", read_len);
|
||||
cybt_mem_read_idx(B2H_BUF_ADDR_IDX, fw_membuf_info.bt2host_out_val, p_data, read_len);
|
||||
fw_membuf_info.bt2host_out_val += read_len;
|
||||
} else {
|
||||
// Need to wrap the circular buf
|
||||
uint32_t first_read_len = BTSDIO_FWBUF_SIZE - fw_membuf_info.bt2host_out_val;
|
||||
if (first_read_len >= 4) {
|
||||
cybt_mem_read_idx(B2H_BUF_ADDR_IDX, fw_membuf_info.bt2host_out_val, p_data, first_read_len);
|
||||
fw_membuf_info.bt2host_out_val += first_read_len;
|
||||
} else {
|
||||
first_read_len = 0;
|
||||
}
|
||||
uint32_t second_read_len = read_len - first_read_len;
|
||||
cybt_debug("cybt_hci_read: 2-round read, 1st_len = %" PRId32 ", 2nd_len = %" PRId32 "\n", first_read_len,
|
||||
second_read_len);
|
||||
if (second_read_len > 0) {
|
||||
cybt_mem_read_idx(B2H_BUF_ADDR_IDX, 0, p_data + first_read_len, second_read_len);
|
||||
fw_membuf_info.bt2host_out_val += second_read_len;
|
||||
}
|
||||
}
|
||||
available = fw_b2h_buf_count - read_len; // remember amount available to check for buffer overflow
|
||||
|
||||
// Update pointer
|
||||
new_b2h_out_val = fw_membuf_info.bt2host_out_val & (BTSDIO_FWBUF_SIZE - 1);
|
||||
cybt_debug("cybt_hci_read new b2h_out = %" PRId32 "\n", new_b2h_out_val);
|
||||
cybt_reg_write_idx(B2H_BUF_OUT_ADDR_IDX, new_b2h_out_val);
|
||||
|
||||
// in case the real length is less than the requested one
|
||||
*p_length = read_len;
|
||||
}
|
||||
cybt_toggle_bt_intr();
|
||||
return ret_result;
|
||||
}
|
||||
|
||||
static void cybt_bus_request(void) {
|
||||
CYW43_THREAD_ENTER
|
||||
// todo: Handle failure
|
||||
cybt_result_t err = cybt_set_bt_awake(true);
|
||||
assert(err == 0);
|
||||
err = cybt_wait_bt_awake(BTSDIO_FW_AWAKE_POLLING_RETRY_COUNT);
|
||||
assert(err == 0);
|
||||
(void) err;
|
||||
}
|
||||
|
||||
static void cybt_bus_release(void) {
|
||||
// mutex if using wifi
|
||||
CYW43_THREAD_EXIT
|
||||
}
|
||||
|
||||
// Send the buffer which includes space for a 4 byte header at the start
|
||||
// The last byte of the header should already be set to the packet type
|
||||
int cyw43_btbus_write(uint8_t *buf, uint32_t size) {
|
||||
uint16_t cmd_len = 0;
|
||||
|
||||
// The size of the buffer should include a 4 byte header at the start
|
||||
cmd_len = size - 4; //in BTSDIO, cmd_len does not include header length
|
||||
|
||||
// Create payload starting with required headers
|
||||
// Format: Cmd Len B0, Cmd Len B1, Cmd Len B2, HCI pckt type, Data
|
||||
buf[0] = (uint8_t) (cmd_len & 0xFF);
|
||||
buf[1] = (uint8_t) ((cmd_len & 0xFF00) >> 8);
|
||||
buf[2] = 0;
|
||||
|
||||
cybt_bus_request();
|
||||
|
||||
cybt_debug("cyw43_btbus_write: %d\n", cmd_len);
|
||||
#if CYBT_VDEBUG
|
||||
dump_bytes(buf, size); // dump header and data
|
||||
#endif
|
||||
|
||||
cybt_hci_write_buf(buf, size);
|
||||
cybt_bus_release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool cybt_hci_read_packet(uint8_t *buf, uint32_t max_buf_size, uint32_t *size) {
|
||||
uint32_t total_read_len = 0;
|
||||
uint32_t read_len = 0;
|
||||
cybt_result_t bt_result;
|
||||
|
||||
// Read the header into the first 4 bytes of the buffer
|
||||
read_len = 4; //3 bytes BTSDIO packet length + 1 bytes PTI
|
||||
bt_result = cybt_hci_read(buf, &read_len);
|
||||
|
||||
if (bt_result != CYBT_SUCCESS) {
|
||||
*size = 0;
|
||||
cybt_printf("cybt_hci_read_packet: error %d", bt_result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (read_len == 0) {
|
||||
// No data is read from SPI
|
||||
*size = 0;
|
||||
cybt_debug("cybt_hci_read_packet: no data\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t hci_read_len = ((buf[2] << 16) & 0xFFFF00) | ((buf[1] << 8) & 0xFF00) | (buf[0] & 0xFF);
|
||||
if (hci_read_len > max_buf_size - 4) {
|
||||
*size = 0;
|
||||
cybt_printf("cybt_hci_read_packet: too much data len %" PRId32"\n", hci_read_len);
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
total_read_len = hci_read_len;
|
||||
|
||||
// Read the packet data after the header
|
||||
cybt_debug("cybt_hci_read_packet: packet type 0x%" PRIx8 " len %" PRId32 "\n", buf[3], hci_read_len);
|
||||
bt_result = cybt_hci_read(buf + 4, &total_read_len);
|
||||
if (bt_result != CYBT_SUCCESS) {
|
||||
*size = 0;
|
||||
cybt_printf("cybt_hci_read_packet: read failed\n");
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Might read more because of alignment
|
||||
if (total_read_len >= hci_read_len) {
|
||||
assert(total_read_len == ROUNDUP(hci_read_len, 4)); // check if we're losing data?
|
||||
*size = hci_read_len + 4;
|
||||
} else {
|
||||
assert(total_read_len > 0);
|
||||
*size = total_read_len + 4;
|
||||
cybt_printf("cybt_hci_read_packet: failed to read all data %lu < %lu\n", total_read_len, hci_read_len);
|
||||
//assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
cybt_debug("cybt_hci_read_packet: %ld\n", *size);
|
||||
#if CYBT_VDEBUG
|
||||
dump_bytes(buf, *size);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reads the hci packet prepended with 4 byte header. The last header byte is the packet type
|
||||
int cyw43_btbus_read(uint8_t *buf, uint32_t max_buf_size, uint32_t *size) {
|
||||
cybt_bus_request();
|
||||
bool result = cybt_hci_read_packet(buf, max_buf_size, size);
|
||||
cybt_bus_release();
|
||||
return result ? 0 : -1;
|
||||
}
|
@ -1,721 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cyw43_ll.h"
|
||||
#include "cybt_shared_bus_driver.h"
|
||||
|
||||
// Bluetooth register corruption occurs if both wifi and bluetooth are fully utilised.
|
||||
#define CYBT_CORRUPTION_TEST 1
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
static cybt_fw_membuf_index_t last_buf_index;
|
||||
static uint32_t last_host_ctrl_reg;
|
||||
static uint32_t last_bt_ctrl_reg;
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define cybt_printf(format, args ...) printf(format,##args)
|
||||
#else
|
||||
#define cybt_printf(...)
|
||||
#endif
|
||||
|
||||
#ifndef CYBT_DEBUG
|
||||
#define CYBT_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if CYBT_DEBUG
|
||||
#include <stdio.h>
|
||||
#define cybt_debug(format, args ...) printf(format,##args)
|
||||
#else
|
||||
#define cybt_debug(format, ...) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Constants
|
||||
******************************************************************************/
|
||||
#define BTFW_MEM_OFFSET (0x19000000)
|
||||
|
||||
/* BIT0 => WLAN Power UP and BIT1=> WLAN Wake */
|
||||
#define BT2WLAN_PWRUP_WAKE (0x03)
|
||||
#define BT2WLAN_PWRUP_ADDR (0x640894)/* This address is specific to 43012B0 */
|
||||
|
||||
#define BTSDIO_OFFSET_HOST2BT_IN (0x00002000)
|
||||
#define BTSDIO_OFFSET_HOST2BT_OUT (0x00002004)
|
||||
#define BTSDIO_OFFSET_BT2HOST_IN (0x00002008)
|
||||
#define BTSDIO_OFFSET_BT2HOST_OUT (0x0000200C)
|
||||
|
||||
#define H2B_BUF_ADDR (buf_info.host2bt_buf_addr)
|
||||
#define H2B_BUF_IN_ADDR (buf_info.host2bt_in_addr)
|
||||
#define H2B_BUF_OUT_ADDR (buf_info.host2bt_out_addr)
|
||||
#define B2H_BUF_ADDR (buf_info.bt2host_buf_addr)
|
||||
#define B2H_BUF_IN_ADDR (buf_info.bt2host_in_addr)
|
||||
#define B2H_BUF_OUT_ADDR (buf_info.bt2host_out_addr)
|
||||
|
||||
static uint32_t wlan_ram_base_addr;
|
||||
volatile uint32_t host_ctrl_cache_reg = 0;
|
||||
#define WLAN_RAM_BASE_ADDR (wlan_ram_base_addr)
|
||||
|
||||
// In wifi host driver these are all constants
|
||||
#define BT_CTRL_REG_ADDR ((uint32_t)0x18000c7c)
|
||||
#define HOST_CTRL_REG_ADDR ((uint32_t)0x18000d6c)
|
||||
#define WLAN_RAM_BASE_REG_ADDR ((uint32_t)0x18000d68)
|
||||
|
||||
typedef struct {
|
||||
uint32_t host2bt_buf_addr;
|
||||
uint32_t host2bt_in_addr;
|
||||
uint32_t host2bt_out_addr;
|
||||
uint32_t bt2host_buf_addr;
|
||||
uint32_t bt2host_in_addr;
|
||||
uint32_t bt2host_out_addr;
|
||||
} cybt_fw_membuf_info_t;
|
||||
|
||||
cybt_fw_membuf_info_t buf_info;
|
||||
|
||||
#define BTFW_ADDR_MODE_UNKNOWN (0)
|
||||
#define BTFW_ADDR_MODE_EXTENDED (1)
|
||||
#define BTFW_ADDR_MODE_SEGMENT (2)
|
||||
#define BTFW_ADDR_MODE_LINEAR32 (3)
|
||||
|
||||
#define BTFW_HEX_LINE_TYPE_DATA (0)
|
||||
#define BTFW_HEX_LINE_TYPE_END_OF_DATA (1)
|
||||
#define BTFW_HEX_LINE_TYPE_EXTENDED_SEGMENT_ADDRESS (2)
|
||||
#define BTFW_HEX_LINE_TYPE_EXTENDED_ADDRESS (4)
|
||||
#define BTFW_HEX_LINE_TYPE_ABSOLUTE_32BIT_ADDRESS (5)
|
||||
|
||||
#define BTSDIO_REG_DATA_VALID_BITMASK (1 << 1)
|
||||
#define BTSDIO_REG_WAKE_BT_BITMASK (1 << 17)
|
||||
#define BTSDIO_REG_SW_RDY_BITMASK (1 << 24)
|
||||
|
||||
#define BTSDIO_REG_BT_AWAKE_BITMASK (1 << 8)
|
||||
#define BTSDIO_REG_FW_RDY_BITMASK (1 << 24)
|
||||
|
||||
#define BTSDIO_OFFSET_HOST_WRITE_BUF (0)
|
||||
#define BTSDIO_OFFSET_HOST_READ_BUF BTSDIO_FWBUF_SIZE
|
||||
|
||||
#define BTSDIO_FWBUF_OPER_DELAY_US (250)
|
||||
|
||||
#define ROUNDUP(x, a) ((((x) + ((a) - 1)) / (a)) * (a))
|
||||
#define ROUNDDN(x, a) ((x) & ~((a) - 1))
|
||||
#define ISALIGNED(a, x) (((uint32_t)(a) & ((x) - 1)) == 0)
|
||||
|
||||
typedef struct cybt_fw_cb {
|
||||
const uint8_t *p_fw_mem_start;
|
||||
uint32_t fw_len;
|
||||
const uint8_t *p_next_line_start;
|
||||
} cybt_fw_cb_t;
|
||||
|
||||
typedef struct hex_file_data {
|
||||
int addr_mode;
|
||||
uint16_t hi_addr;
|
||||
uint32_t dest_addr;
|
||||
uint8_t *p_ds;
|
||||
} hex_file_data_t;
|
||||
|
||||
static cyw43_ll_t *cyw43_ll = NULL;
|
||||
|
||||
static cybt_result_t cybt_reg_write(uint32_t reg_addr, uint32_t value);
|
||||
static cybt_result_t cybt_reg_read(uint32_t reg_addr, uint32_t *p_value);
|
||||
static cybt_result_t cybt_mem_write(uint32_t mem_addr, const uint8_t *p_data, uint32_t data_len);
|
||||
static cybt_result_t cybt_mem_read(uint32_t mem_addr, uint8_t *p_data, uint32_t data_len);
|
||||
|
||||
#if CYW43_USE_HEX_BTFW
|
||||
const char *strnchr(const char *str, uint32_t len, int character) {
|
||||
const char *end = str + len;
|
||||
char c = (char)character;
|
||||
do {
|
||||
if (*str == c) {
|
||||
return str;
|
||||
}
|
||||
} while (++str <= end);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint32_t cybt_fw_hex_read_line(cybt_fw_cb_t *p_btfw_cb,
|
||||
const char **p_line_start,
|
||||
int len
|
||||
) {
|
||||
uint32_t str_len = 0;
|
||||
const char *p_str_end = NULL;
|
||||
|
||||
if (NULL == p_btfw_cb || NULL == p_line_start) {
|
||||
return str_len;
|
||||
}
|
||||
|
||||
*p_line_start = (const char *)p_btfw_cb->p_next_line_start;
|
||||
p_str_end = strnchr(*p_line_start, len, '\n');
|
||||
if (p_str_end == NULL) {
|
||||
return str_len;
|
||||
}
|
||||
|
||||
str_len = (uint32_t)(p_str_end - *p_line_start);
|
||||
|
||||
/* Advance file pointer past the string length */
|
||||
p_btfw_cb->p_next_line_start += str_len + 1;
|
||||
|
||||
return str_len;
|
||||
}
|
||||
|
||||
static inline uint8_t nibble_for_char(char c) {
|
||||
if ((c >= '0') && (c <= '9')) return c - '0';
|
||||
if ((c >= 'A') && (c <= 'F')) return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline uint8_t read_hex_byte(const char *str) {
|
||||
return nibble_for_char(*str) << 4 | nibble_for_char(*(str + 1));
|
||||
}
|
||||
|
||||
static uint32_t read_hex(const char *str, int nchars) {
|
||||
uint32_t result = 0;
|
||||
assert(nchars > 0 && nchars <= 8 && nchars % 2 == 0);
|
||||
for(int pos = 0; pos < nchars; pos += 2) {
|
||||
result <<= 8;
|
||||
result |= read_hex_byte(str + pos);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t cybt_fw_get_data(cybt_fw_cb_t *p_btfw_cb,
|
||||
hex_file_data_t *hfd
|
||||
) {
|
||||
uint32_t line_len;
|
||||
uint16_t num_bytes, addr, data_pos, type, idx, octet;
|
||||
uint32_t abs_base_addr32 = 0;
|
||||
uint32_t data_len = 0;
|
||||
const char *p_line_start = NULL;
|
||||
|
||||
if (NULL == p_btfw_cb || NULL == hfd->p_ds) {
|
||||
return data_len;
|
||||
}
|
||||
|
||||
while (data_len == 0) {
|
||||
line_len = cybt_fw_hex_read_line(p_btfw_cb, &p_line_start, BTFW_MAX_STR_LEN);
|
||||
if (line_len == 0) {
|
||||
break;
|
||||
} else if (line_len > 9) {
|
||||
|
||||
num_bytes = (uint16_t)read_hex(p_line_start + 1, 2);
|
||||
assert(num_bytes * 2 + 8 + 2 + 1 == line_len);
|
||||
|
||||
int addr32 = read_hex(p_line_start + 3, 4);
|
||||
assert(addr32 <= 0xffff);
|
||||
addr = (uint16_t)addr32;
|
||||
type = (uint16_t)read_hex(p_line_start + 7, 2);
|
||||
assert(type <= 0xff);
|
||||
|
||||
data_pos = 9;
|
||||
|
||||
for (idx = 0; idx < num_bytes; idx++)
|
||||
{
|
||||
octet = (uint16_t)read_hex(p_line_start + data_pos, 2);
|
||||
hfd->p_ds[idx] = (uint8_t)(octet & 0x00FF);
|
||||
data_pos += 2;
|
||||
}
|
||||
|
||||
if (type == BTFW_HEX_LINE_TYPE_EXTENDED_ADDRESS) {
|
||||
hfd->hi_addr = (hfd->p_ds[0] << 8) | hfd->p_ds[1];
|
||||
hfd->addr_mode = BTFW_ADDR_MODE_EXTENDED;
|
||||
} else if (type == BTFW_HEX_LINE_TYPE_EXTENDED_SEGMENT_ADDRESS) {
|
||||
hfd->hi_addr = (hfd->p_ds[0] << 8) | hfd->p_ds[1];
|
||||
hfd->addr_mode = BTFW_ADDR_MODE_SEGMENT;
|
||||
} else if (type == BTFW_HEX_LINE_TYPE_ABSOLUTE_32BIT_ADDRESS) {
|
||||
abs_base_addr32 = (hfd->p_ds[0] << 24) | (hfd->p_ds[1] << 16) |
|
||||
(hfd->p_ds[2] << 8) | hfd->p_ds[3];
|
||||
hfd->addr_mode = BTFW_ADDR_MODE_LINEAR32;
|
||||
} else if (type == BTFW_HEX_LINE_TYPE_DATA) {
|
||||
hfd->dest_addr = addr;
|
||||
|
||||
if (hfd->addr_mode == BTFW_ADDR_MODE_EXTENDED) {
|
||||
hfd->dest_addr += (hfd->hi_addr << 16);
|
||||
} else if (hfd->addr_mode == BTFW_ADDR_MODE_SEGMENT) {
|
||||
hfd->dest_addr += (hfd->hi_addr << 4);
|
||||
} else if (hfd->addr_mode == BTFW_ADDR_MODE_LINEAR32) {
|
||||
hfd->dest_addr += abs_base_addr32;
|
||||
}
|
||||
|
||||
data_len = num_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data_len;
|
||||
}
|
||||
#else
|
||||
|
||||
static uint32_t cybt_fw_get_data(cybt_fw_cb_t *p_btfw_cb, hex_file_data_t *hfd) {
|
||||
uint32_t abs_base_addr32 = 0;
|
||||
while (true) {
|
||||
// 4 byte header
|
||||
uint8_t num_bytes = *(p_btfw_cb->p_next_line_start)++;
|
||||
uint16_t addr = *(p_btfw_cb->p_next_line_start)++ << 8;
|
||||
addr |= *(p_btfw_cb->p_next_line_start)++;
|
||||
uint8_t type = *(p_btfw_cb->p_next_line_start)++;
|
||||
|
||||
// No data?
|
||||
if (num_bytes == 0) break;
|
||||
|
||||
// Copy the data
|
||||
memcpy(hfd->p_ds, p_btfw_cb->p_next_line_start, num_bytes);
|
||||
p_btfw_cb->p_next_line_start += num_bytes;
|
||||
|
||||
// Adjust address based on type
|
||||
if (type == BTFW_HEX_LINE_TYPE_EXTENDED_ADDRESS) {
|
||||
hfd->hi_addr = (hfd->p_ds[0] << 8) | hfd->p_ds[1];
|
||||
hfd->addr_mode = BTFW_ADDR_MODE_EXTENDED;
|
||||
} else if (type == BTFW_HEX_LINE_TYPE_EXTENDED_SEGMENT_ADDRESS) {
|
||||
hfd->hi_addr = (hfd->p_ds[0] << 8) | hfd->p_ds[1];
|
||||
hfd->addr_mode = BTFW_ADDR_MODE_SEGMENT;
|
||||
} else if (type == BTFW_HEX_LINE_TYPE_ABSOLUTE_32BIT_ADDRESS) {
|
||||
abs_base_addr32 = (hfd->p_ds[0] << 24) | (hfd->p_ds[1] << 16) |
|
||||
(hfd->p_ds[2] << 8) | hfd->p_ds[3];
|
||||
hfd->addr_mode = BTFW_ADDR_MODE_LINEAR32;
|
||||
} else if (type == BTFW_HEX_LINE_TYPE_DATA) {
|
||||
hfd->dest_addr = addr;
|
||||
if (hfd->addr_mode == BTFW_ADDR_MODE_EXTENDED) {
|
||||
hfd->dest_addr += (hfd->hi_addr << 16);
|
||||
} else if (hfd->addr_mode == BTFW_ADDR_MODE_SEGMENT) {
|
||||
hfd->dest_addr += (hfd->hi_addr << 4);
|
||||
} else if (hfd->addr_mode == BTFW_ADDR_MODE_LINEAR32) {
|
||||
hfd->dest_addr += abs_base_addr32;
|
||||
}
|
||||
return num_bytes;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
cybt_result_t cybt_fw_download(const uint8_t *p_bt_firmware,
|
||||
uint32_t bt_firmware_len,
|
||||
uint8_t *p_write_buf,
|
||||
uint8_t *p_hex_buf) {
|
||||
cybt_fw_cb_t btfw_cb;
|
||||
hex_file_data_t hfd = {BTFW_ADDR_MODE_EXTENDED, 0, 0, NULL};
|
||||
uint8_t *p_mem_ptr;
|
||||
uint32_t data_len;
|
||||
|
||||
if (cyw43_ll == NULL) {
|
||||
return CYBT_ERR_BADARG;
|
||||
}
|
||||
|
||||
if (NULL == p_bt_firmware || 0 == bt_firmware_len || NULL == p_write_buf || NULL == p_hex_buf) {
|
||||
return CYBT_ERR_BADARG;
|
||||
}
|
||||
|
||||
// BT firmware starts with length of version string including a null terminator
|
||||
#if !CYW43_USE_HEX_BTFW
|
||||
uint8_t version_len = *p_bt_firmware;
|
||||
assert(*(p_bt_firmware + version_len) == 0);
|
||||
#ifndef NDEBUG
|
||||
cybt_printf("BT FW download, version = %s\n", p_bt_firmware + 1);
|
||||
#endif
|
||||
p_bt_firmware += version_len + 1; // skip over version
|
||||
p_bt_firmware += 1; // skip over record count
|
||||
#endif
|
||||
|
||||
p_mem_ptr = p_write_buf;
|
||||
if ((uint32_t) (uintptr_t) p_mem_ptr % BTFW_SD_ALIGN) {
|
||||
p_mem_ptr += (BTFW_SD_ALIGN - ((uint32_t) (uintptr_t) p_mem_ptr % BTFW_SD_ALIGN));
|
||||
}
|
||||
|
||||
hfd.p_ds = p_hex_buf;
|
||||
|
||||
btfw_cb.p_fw_mem_start = p_bt_firmware;
|
||||
btfw_cb.fw_len = bt_firmware_len;
|
||||
btfw_cb.p_next_line_start = p_bt_firmware;
|
||||
|
||||
cybt_reg_write(BTFW_MEM_OFFSET + BT2WLAN_PWRUP_ADDR, BT2WLAN_PWRUP_WAKE);
|
||||
|
||||
while ((data_len = cybt_fw_get_data(&btfw_cb, &hfd)) > 0) {
|
||||
uint32_t fwmem_start_addr, fwmem_start_data, fwmem_end_addr, fwmem_end_data;
|
||||
uint32_t write_data_len, idx, pad;
|
||||
|
||||
fwmem_start_addr = BTFW_MEM_OFFSET + hfd.dest_addr;
|
||||
write_data_len = 0;
|
||||
|
||||
/**
|
||||
* Make sure the start address is 4 byte aligned to avoid alignment issues
|
||||
* with SD host controllers
|
||||
*/
|
||||
if (!ISALIGNED(fwmem_start_addr, 4)) {
|
||||
pad = fwmem_start_addr % 4;
|
||||
fwmem_start_addr = ROUNDDN(fwmem_start_addr, 4);
|
||||
|
||||
cybt_mem_read(fwmem_start_addr, (uint8_t *) &fwmem_start_data, sizeof(uint32_t));
|
||||
|
||||
for (idx = 0; idx < pad; idx++, write_data_len++) {
|
||||
p_mem_ptr[write_data_len] = (uint8_t) ((uint8_t *) &fwmem_start_data)[idx];
|
||||
}
|
||||
}
|
||||
memcpy(&(p_mem_ptr[write_data_len]), hfd.p_ds, data_len);
|
||||
write_data_len += data_len;
|
||||
|
||||
/**
|
||||
* Make sure the length is multiple of 4bytes to avoid alignment issues
|
||||
* with SD host controllers
|
||||
*/
|
||||
fwmem_end_addr = fwmem_start_addr + write_data_len;
|
||||
if (!ISALIGNED(fwmem_end_addr, 4)) {
|
||||
cybt_mem_read(ROUNDDN(fwmem_end_addr, 4), (uint8_t *) &fwmem_end_data, sizeof(uint32_t));
|
||||
for (idx = (fwmem_end_addr % 4); idx < 4; idx++, write_data_len++) {
|
||||
p_mem_ptr[write_data_len] = (uint8_t) ((uint8_t *) &fwmem_end_data)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* write ram
|
||||
*/
|
||||
if (((fwmem_start_addr & 0xFFF) + write_data_len) <= 0x1000) {
|
||||
cybt_mem_write(fwmem_start_addr, p_mem_ptr, write_data_len);
|
||||
} else {
|
||||
uint32_t first_write_len = 0x1000 - (fwmem_start_addr & 0xFFF);
|
||||
cybt_mem_write(fwmem_start_addr, p_mem_ptr, first_write_len);
|
||||
cybt_mem_write(fwmem_start_addr + first_write_len,
|
||||
p_mem_ptr + first_write_len,
|
||||
write_data_len - first_write_len);
|
||||
}
|
||||
}
|
||||
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
cybt_result_t cybt_set_host_ready(void) {
|
||||
uint32_t reg_val;
|
||||
|
||||
cybt_reg_read(HOST_CTRL_REG_ADDR, ®_val);
|
||||
reg_val |= BTSDIO_REG_SW_RDY_BITMASK;
|
||||
cybt_reg_write(HOST_CTRL_REG_ADDR, reg_val);
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
last_host_ctrl_reg = reg_val;
|
||||
#endif
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
cybt_result_t cybt_toggle_bt_intr(void) {
|
||||
uint32_t reg_val, new_val;
|
||||
|
||||
cybt_reg_read(HOST_CTRL_REG_ADDR, ®_val);
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
if ((reg_val & ~(BTSDIO_REG_SW_RDY_BITMASK | BTSDIO_REG_WAKE_BT_BITMASK | BTSDIO_REG_DATA_VALID_BITMASK)) != 0) {
|
||||
cybt_printf("cybt_toggle_bt_intr read HOST_CTRL_REG_ADDR as 0x%08lx\n", reg_val);
|
||||
cybt_debug_dump();
|
||||
panic("cyw43 btsdio register corruption");
|
||||
}
|
||||
assert((reg_val & ~(BTSDIO_REG_SW_RDY_BITMASK | BTSDIO_REG_WAKE_BT_BITMASK | BTSDIO_REG_DATA_VALID_BITMASK)) == 0);
|
||||
#endif
|
||||
new_val = reg_val ^ BTSDIO_REG_DATA_VALID_BITMASK;
|
||||
cybt_reg_write(HOST_CTRL_REG_ADDR, new_val);
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
last_host_ctrl_reg = new_val;
|
||||
#endif
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
cybt_result_t cybt_set_bt_intr(int value) {
|
||||
uint32_t reg_val, new_val;
|
||||
|
||||
cybt_reg_read(HOST_CTRL_REG_ADDR, ®_val);
|
||||
if (value) {
|
||||
new_val = reg_val | BTSDIO_REG_DATA_VALID_BITMASK;
|
||||
} else {
|
||||
new_val = reg_val & ~BTSDIO_REG_DATA_VALID_BITMASK;
|
||||
}
|
||||
cybt_reg_write(HOST_CTRL_REG_ADDR, new_val);
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
last_host_ctrl_reg = new_val;
|
||||
#endif
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
int cybt_ready(void) {
|
||||
uint32_t reg_val;
|
||||
cybt_reg_read(BT_CTRL_REG_ADDR, ®_val);
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
if (reg_val & BTSDIO_REG_FW_RDY_BITMASK) {
|
||||
last_bt_ctrl_reg = reg_val;
|
||||
}
|
||||
#endif
|
||||
return (reg_val & BTSDIO_REG_FW_RDY_BITMASK) ? 1 : 0;
|
||||
}
|
||||
|
||||
int cybt_awake(void) {
|
||||
uint32_t reg_val;
|
||||
cybt_reg_read(BT_CTRL_REG_ADDR, ®_val);
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
if (reg_val & BTSDIO_REG_BT_AWAKE_BITMASK) {
|
||||
last_bt_ctrl_reg = reg_val;
|
||||
}
|
||||
#endif
|
||||
return (reg_val & BTSDIO_REG_BT_AWAKE_BITMASK) ? 1 : 0;
|
||||
}
|
||||
|
||||
cybt_result_t cybt_set_bt_awake(int value) {
|
||||
uint32_t reg_val_before;
|
||||
cybt_reg_read(HOST_CTRL_REG_ADDR, ®_val_before);
|
||||
|
||||
uint32_t reg_val_after = reg_val_before;
|
||||
if (value)
|
||||
reg_val_after |= BTSDIO_REG_WAKE_BT_BITMASK;
|
||||
else
|
||||
reg_val_after &= ~BTSDIO_REG_WAKE_BT_BITMASK;
|
||||
|
||||
if (reg_val_before != reg_val_after) {
|
||||
cybt_reg_write(HOST_CTRL_REG_ADDR, reg_val_after);
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
last_host_ctrl_reg = reg_val_after;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cybt_debug_dump(void) {
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
uint32_t reg_val = 0;
|
||||
cybt_fw_membuf_index_t buf_index;
|
||||
|
||||
cybt_printf("WLAN_RAM_BASE_ADDR: 0x%08lx\n", WLAN_RAM_BASE_ADDR);
|
||||
cybt_printf("H2B_BUF_ADDR: 0x%08lx\n", H2B_BUF_ADDR);
|
||||
cybt_printf("B2H_BUF_ADDR: 0x%08lx\n", B2H_BUF_ADDR);
|
||||
|
||||
cybt_reg_read(H2B_BUF_IN_ADDR, &buf_index.host2bt_in_val);
|
||||
cybt_printf("H2B_BUF_IN_ADDR: 0x%08lx = 0x%08lx (last 0x%08lx)\n", H2B_BUF_IN_ADDR, buf_index.host2bt_in_val,
|
||||
last_buf_index.host2bt_in_val);
|
||||
|
||||
cybt_reg_read(H2B_BUF_OUT_ADDR, &buf_index.host2bt_out_val);
|
||||
cybt_printf("H2B_BUF_OUT_ADDR: 0x%08lx = 0x%08lx (last 0x%08lx)\n", H2B_BUF_OUT_ADDR, buf_index.host2bt_out_val,
|
||||
last_buf_index.host2bt_out_val);
|
||||
|
||||
cybt_reg_read(B2H_BUF_IN_ADDR, &buf_index.bt2host_in_val);
|
||||
cybt_printf("B2H_BUF_IN_ADDR: 0x%08lx = 0x%08lx (last 0x%08lx)\n", B2H_BUF_IN_ADDR, buf_index.bt2host_in_val,
|
||||
last_buf_index.bt2host_in_val);
|
||||
|
||||
cybt_reg_read(B2H_BUF_OUT_ADDR, &buf_index.bt2host_out_val);
|
||||
cybt_printf("B2H_BUF_OUT_ADDR: 0x%08lx = 0x%08lx (last 0x%08lx)\n", B2H_BUF_OUT_ADDR, buf_index.bt2host_out_val,
|
||||
last_buf_index.bt2host_out_val);
|
||||
|
||||
cybt_reg_read(HOST_CTRL_REG_ADDR, ®_val);
|
||||
cybt_printf("HOST_CTRL_REG_ADDR: 0x%08lx = 0x%08lx (last 0x%08lx)\n", HOST_CTRL_REG_ADDR, reg_val,
|
||||
last_host_ctrl_reg);
|
||||
|
||||
cybt_reg_read(BT_CTRL_REG_ADDR, ®_val);
|
||||
cybt_printf("BT_CTRL_REG_ADDR: 0x%08lx = 0x%08lx (last 0x%08lx)\n", BT_CTRL_REG_ADDR, reg_val, last_bt_ctrl_reg);
|
||||
#endif
|
||||
}
|
||||
|
||||
cybt_result_t cybt_get_bt_buf_index(cybt_fw_membuf_index_t *p_buf_index) {
|
||||
uint32_t buf[4];
|
||||
|
||||
cybt_mem_read(H2B_BUF_IN_ADDR, (uint8_t *) buf, sizeof(buf));
|
||||
|
||||
p_buf_index->host2bt_in_val = buf[0];
|
||||
p_buf_index->host2bt_out_val = buf[1];
|
||||
p_buf_index->bt2host_in_val = buf[2];
|
||||
p_buf_index->bt2host_out_val = buf[3];
|
||||
|
||||
cybt_debug("cybt_get_bt_buf_index: h2b_in = 0x%08lx, h2b_out = 0x%08lx, b2h_in = 0x%08lx, b2h_out = 0x%08lx\n",
|
||||
p_buf_index->host2bt_in_val,
|
||||
p_buf_index->host2bt_out_val,
|
||||
p_buf_index->bt2host_in_val,
|
||||
p_buf_index->bt2host_out_val);
|
||||
|
||||
#if CYBT_CORRUPTION_TEST
|
||||
if (p_buf_index->host2bt_in_val >= BTSDIO_FWBUF_SIZE || p_buf_index->host2bt_out_val >= BTSDIO_FWBUF_SIZE ||
|
||||
p_buf_index->bt2host_in_val >= BTSDIO_FWBUF_SIZE || p_buf_index->bt2host_out_val >= BTSDIO_FWBUF_SIZE) {
|
||||
cybt_printf("cybt_get_bt_buf_index invalid buffer value\n");
|
||||
cybt_debug_dump();
|
||||
} else {
|
||||
memcpy((uint8_t *) &last_buf_index, (uint8_t *) p_buf_index, sizeof(cybt_fw_membuf_index_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
assert(p_buf_index->host2bt_in_val < BTSDIO_FWBUF_SIZE);
|
||||
assert(p_buf_index->host2bt_out_val < BTSDIO_FWBUF_SIZE);
|
||||
assert(p_buf_index->bt2host_in_val < BTSDIO_FWBUF_SIZE);
|
||||
assert(p_buf_index->bt2host_out_val < BTSDIO_FWBUF_SIZE);
|
||||
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
static cybt_result_t cybt_reg_write(uint32_t reg_addr, uint32_t value) {
|
||||
cybt_debug("cybt_reg_write 0x%08lx 0x%08lx\n", reg_addr, value);
|
||||
cyw43_ll_write_backplane_reg(cyw43_ll, reg_addr, value);
|
||||
if (reg_addr == HOST_CTRL_REG_ADDR) {
|
||||
host_ctrl_cache_reg = value;
|
||||
}
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
static cybt_result_t cybt_reg_read(uint32_t reg_addr, uint32_t *p_value) {
|
||||
if (reg_addr == HOST_CTRL_REG_ADDR) {
|
||||
*p_value = host_ctrl_cache_reg;
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
*p_value = cyw43_ll_read_backplane_reg(cyw43_ll, reg_addr);
|
||||
cybt_debug("cybt_reg_read 0x%08lx == 0x%08lx\n", reg_addr, *p_value);
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
#if CYBT_DEBUG
|
||||
static void dump_bytes(const uint8_t *bptr, uint32_t len) {
|
||||
unsigned int i = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((i & 0x07) == 0) {
|
||||
cybt_debug("\n ");
|
||||
}
|
||||
cybt_debug("0x%02x", bptr[i]);
|
||||
if (i != (len - 1)) {
|
||||
cybt_debug(", ");
|
||||
}
|
||||
}
|
||||
cybt_debug("\n");
|
||||
}
|
||||
#define DUMP_BYTES dump_bytes
|
||||
#else
|
||||
#define DUMP_BYTES(...)
|
||||
#endif
|
||||
|
||||
static cybt_result_t cybt_mem_write(uint32_t mem_addr, const uint8_t *p_data, uint32_t data_len) {
|
||||
cybt_debug("cybt_mem_write addr 0x%08lx len %ld\n", mem_addr, data_len);
|
||||
do {
|
||||
uint32_t transfer_size = (data_len > CYW43_BUS_MAX_BLOCK_SIZE) ? CYW43_BUS_MAX_BLOCK_SIZE : data_len;
|
||||
if ((mem_addr & 0xFFF) + transfer_size > 0x1000) {
|
||||
transfer_size = 0x1000 - (mem_addr & 0xFFF);
|
||||
}
|
||||
cyw43_ll_write_backplane_mem(cyw43_ll, mem_addr, transfer_size, p_data);
|
||||
cybt_debug(" write_mem addr 0x%08lx len %ld\n", mem_addr, transfer_size);
|
||||
DUMP_BYTES(p_data, transfer_size);
|
||||
data_len -= transfer_size;
|
||||
p_data += transfer_size;
|
||||
mem_addr += transfer_size;
|
||||
} while (data_len > 0);
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
static cybt_result_t cybt_mem_read(uint32_t mem_addr, uint8_t *p_data, uint32_t data_len) {
|
||||
assert(data_len >= 4);
|
||||
cybt_debug("cybt_mem_read addr 0x%08lx len %ld\n", mem_addr, data_len);
|
||||
do {
|
||||
uint32_t transfer_size = (data_len > CYW43_BUS_MAX_BLOCK_SIZE) ? CYW43_BUS_MAX_BLOCK_SIZE : data_len;
|
||||
if ((mem_addr & 0xFFF) + transfer_size > 0x1000) {
|
||||
transfer_size = 0x1000 - (mem_addr & 0xFFF);
|
||||
}
|
||||
cyw43_ll_read_backplane_mem(cyw43_ll, mem_addr, transfer_size, p_data);
|
||||
cybt_debug(" read_mem addr 0x%08lx len %ld\n", transfer_size, mem_addr);
|
||||
DUMP_BYTES(p_data, transfer_size);
|
||||
data_len -= transfer_size;
|
||||
p_data += transfer_size;
|
||||
mem_addr += transfer_size;
|
||||
} while (data_len > 0);
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
static uint32_t cybt_get_addr(cybt_addr_idx_t addr_idx) {
|
||||
uint32_t addr = 0;
|
||||
|
||||
switch (addr_idx) {
|
||||
case H2B_BUF_ADDR_IDX:
|
||||
addr = H2B_BUF_ADDR;
|
||||
break;
|
||||
case H2B_BUF_IN_ADDR_IDX:
|
||||
addr = H2B_BUF_IN_ADDR;
|
||||
break;
|
||||
case H2B_BUF_OUT_ADDR_IDX:
|
||||
addr = H2B_BUF_OUT_ADDR;
|
||||
break;
|
||||
case B2H_BUF_ADDR_IDX:
|
||||
addr = B2H_BUF_ADDR;
|
||||
break;
|
||||
case B2H_BUF_IN_ADDR_IDX:
|
||||
addr = B2H_BUF_IN_ADDR;
|
||||
break;
|
||||
case B2H_BUF_OUT_ADDR_IDX:
|
||||
addr = B2H_BUF_OUT_ADDR;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
cybt_result_t cybt_reg_write_idx(cybt_addr_idx_t reg_idx, uint32_t value) {
|
||||
assert(reg_idx == H2B_BUF_IN_ADDR_IDX || reg_idx == B2H_BUF_OUT_ADDR_IDX);
|
||||
assert(value < BTSDIO_FWBUF_SIZE); // writing out of bounds register value?
|
||||
if ((reg_idx != H2B_BUF_IN_ADDR_IDX && reg_idx != B2H_BUF_OUT_ADDR_IDX) || value >= BTSDIO_FWBUF_SIZE) {
|
||||
assert(0);
|
||||
return CYBT_ERR_BADARG;
|
||||
}
|
||||
uint32_t reg_addr = cybt_get_addr(reg_idx);
|
||||
return cybt_reg_write(reg_addr, value);
|
||||
}
|
||||
|
||||
cybt_result_t cybt_mem_write_idx(cybt_addr_idx_t mem_idx, uint32_t offset, const uint8_t *p_data, uint32_t data_len) {
|
||||
assert(mem_idx == H2B_BUF_ADDR_IDX); // caller should only be writing to here?
|
||||
assert(offset + data_len <= BTSDIO_FWBUF_SIZE); // writing out of bounds?
|
||||
if (mem_idx != H2B_BUF_ADDR_IDX || (offset + data_len) > BTSDIO_FWBUF_SIZE) {
|
||||
assert(0);
|
||||
return CYBT_ERR_BADARG;
|
||||
}
|
||||
if (!ISALIGNED(p_data, 4)) {
|
||||
return CYBT_ERR_BADARG;
|
||||
}
|
||||
uint32_t mem_addr = cybt_get_addr(mem_idx) + offset;
|
||||
return cybt_mem_write(mem_addr, p_data, data_len);
|
||||
}
|
||||
|
||||
cybt_result_t cybt_mem_read_idx(cybt_addr_idx_t mem_idx, uint32_t offset, uint8_t *p_data, uint32_t data_len) {
|
||||
assert(mem_idx == B2H_BUF_ADDR_IDX); // caller should only be reading from here?
|
||||
assert(offset + data_len <= BTSDIO_FWBUF_SIZE); // reading out of bounds?
|
||||
if (mem_idx != B2H_BUF_ADDR_IDX || (offset + data_len) > BTSDIO_FWBUF_SIZE) {
|
||||
assert(0);
|
||||
return CYBT_ERR_BADARG;
|
||||
}
|
||||
uint32_t mem_addr = cybt_get_addr(mem_idx) + offset;
|
||||
return cybt_mem_read(mem_addr, p_data, data_len);
|
||||
}
|
||||
|
||||
cybt_result_t cybt_init_buffer(void) {
|
||||
int result;
|
||||
result = cybt_reg_read(WLAN_RAM_BASE_REG_ADDR, &WLAN_RAM_BASE_ADDR);
|
||||
if (CYBT_SUCCESS != result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
cybt_debug("hci_open(): btfw ram base = 0x%" PRIx32 "\n", WLAN_RAM_BASE_ADDR);
|
||||
|
||||
// Fill in reg info
|
||||
// Data buffers
|
||||
H2B_BUF_ADDR = WLAN_RAM_BASE_ADDR + BTSDIO_OFFSET_HOST_WRITE_BUF;
|
||||
B2H_BUF_ADDR = WLAN_RAM_BASE_ADDR + BTSDIO_OFFSET_HOST_READ_BUF;
|
||||
|
||||
// circular buffer indexes
|
||||
H2B_BUF_IN_ADDR = WLAN_RAM_BASE_ADDR + BTSDIO_OFFSET_HOST2BT_IN;
|
||||
H2B_BUF_OUT_ADDR = WLAN_RAM_BASE_ADDR + BTSDIO_OFFSET_HOST2BT_OUT;
|
||||
B2H_BUF_IN_ADDR = WLAN_RAM_BASE_ADDR + BTSDIO_OFFSET_BT2HOST_IN;
|
||||
B2H_BUF_OUT_ADDR = WLAN_RAM_BASE_ADDR + BTSDIO_OFFSET_BT2HOST_OUT;
|
||||
|
||||
uint32_t reg_val = 0;
|
||||
cybt_reg_write(H2B_BUF_IN_ADDR, reg_val);
|
||||
cybt_reg_write(H2B_BUF_OUT_ADDR, reg_val);
|
||||
cybt_reg_write(B2H_BUF_IN_ADDR, reg_val);
|
||||
cybt_reg_write(B2H_BUF_OUT_ADDR, reg_val);
|
||||
|
||||
return CYBT_SUCCESS;
|
||||
}
|
||||
|
||||
void cybt_sharedbus_driver_init(cyw43_ll_t *driver) {
|
||||
cyw43_ll = driver;
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef CYBT_SHARE_BUS_DRIVER_H
|
||||
#define CYBT_SHARE_BUS_DRIVER_H
|
||||
|
||||
#define BTSDIO_FWBUF_SIZE (0x1000)
|
||||
#define BTFW_MAX_STR_LEN (600)
|
||||
#define BTFW_SD_ALIGN (32)
|
||||
#define BTFW_DOWNLOAD_BLK_SIZE (((BTFW_MAX_STR_LEN) / 2) + 8)
|
||||
|
||||
typedef enum {
|
||||
CYBT_SUCCESS = 0,
|
||||
CYBT_ERR_BADARG = 0xB1,
|
||||
CYBT_ERR_OUT_OF_MEMORY,
|
||||
CYBT_ERR_TIMEOUT,
|
||||
CYBT_ERR_HCI_INIT_FAILED,
|
||||
CYBT_ERR_HCI_UNSUPPORTED_IF,
|
||||
CYBT_ERR_HCI_UNSUPPORTED_BAUDRATE,
|
||||
CYBT_ERR_HCI_NOT_INITIALIZE,
|
||||
CYBT_ERR_HCI_WRITE_FAILED,
|
||||
CYBT_ERR_HCI_READ_FAILED,
|
||||
CYBT_ERR_HCI_GET_TX_MUTEX_FAILED,
|
||||
CYBT_ERR_HCI_GET_RX_MUTEX_FAILED,
|
||||
CYBT_ERR_HCI_SET_BAUDRATE_FAILED,
|
||||
CYBT_ERR_HCI_SET_FLOW_CTRL_FAILED,
|
||||
CYBT_ERR_INIT_MEMPOOL_FAILED,
|
||||
CYBT_ERR_INIT_QUEUE_FAILED,
|
||||
CYBT_ERR_CREATE_TASK_FAILED,
|
||||
CYBT_ERR_SEND_QUEUE_FAILED,
|
||||
CYBT_ERR_MEMPOOL_NOT_INITIALIZE,
|
||||
CYBT_ERR_QUEUE_ALMOST_FULL,
|
||||
CYBT_ERR_QUEUE_FULL,
|
||||
CYBT_ERR_GPIO_POWER_INIT_FAILED,
|
||||
CYBT_ERR_GPIO_DEV_WAKE_INIT_FAILED,
|
||||
CYBT_ERR_GPIO_HOST_WAKE_INIT_FAILED,
|
||||
CYBT_ERR_GENERIC
|
||||
} cybt_result_t;
|
||||
|
||||
typedef enum {
|
||||
H2B_BUF_ADDR_IDX = 0x10,
|
||||
H2B_BUF_IN_ADDR_IDX,
|
||||
H2B_BUF_OUT_ADDR_IDX,
|
||||
B2H_BUF_ADDR_IDX,
|
||||
B2H_BUF_IN_ADDR_IDX,
|
||||
B2H_BUF_OUT_ADDR_IDX,
|
||||
} cybt_addr_idx_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t host2bt_in_val;
|
||||
uint32_t host2bt_out_val;
|
||||
uint32_t bt2host_in_val;
|
||||
uint32_t bt2host_out_val;
|
||||
} cybt_fw_membuf_index_t;
|
||||
|
||||
struct _cyw43_ll_t;
|
||||
void cybt_sharedbus_driver_init(struct _cyw43_ll_t *driver);
|
||||
|
||||
cybt_result_t cybt_init_buffer(void);
|
||||
|
||||
cybt_result_t cybt_reg_write_idx(cybt_addr_idx_t reg_idx, uint32_t value);
|
||||
cybt_result_t cybt_mem_write_idx(cybt_addr_idx_t mem_idx, uint32_t offset, const uint8_t *p_data, uint32_t data_len);
|
||||
cybt_result_t cybt_mem_read_idx(cybt_addr_idx_t mem_idx, uint32_t offset, uint8_t *p_data, uint32_t data_len);
|
||||
|
||||
cybt_result_t cybt_fw_download(const uint8_t *p_bt_firmware, uint32_t bt_firmware_len, uint8_t *p_write_buf, uint8_t *p_hex_buf);
|
||||
|
||||
int cybt_ready(void);
|
||||
int cybt_awake(void);
|
||||
|
||||
cybt_result_t cybt_set_bt_awake(int value);
|
||||
cybt_result_t cybt_set_host_ready(void);
|
||||
cybt_result_t cybt_set_bt_intr(int value);
|
||||
cybt_result_t cybt_toggle_bt_intr(void);
|
||||
cybt_result_t cybt_get_bt_buf_index(cybt_fw_membuf_index_t *p_buf_index);
|
||||
|
||||
void cybt_debug_dump(void);
|
||||
|
||||
#endif
|
@ -1,548 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "cyw43_bus_pio_spi.pio.h"
|
||||
#include "cyw43.h"
|
||||
#include "cyw43_internal.h"
|
||||
#include "cyw43_spi.h"
|
||||
#include "cyw43_debug_pins.h"
|
||||
|
||||
#if CYW43_SPI_PIO
|
||||
#define WL_REG_ON 23
|
||||
#define DATA_OUT_PIN 24u
|
||||
#define DATA_IN_PIN 24u
|
||||
#define IRQ_PIN 24u
|
||||
// #define MONITOR_PIN 3u
|
||||
#define CLOCK_PIN 29u
|
||||
#define CS_PIN 25u
|
||||
#define IRQ_SAMPLE_DELAY_NS 100
|
||||
|
||||
#define SPI_PROGRAM_NAME spi_gap01_sample0
|
||||
#define SPI_PROGRAM_FUNC __CONCAT(SPI_PROGRAM_NAME, _program)
|
||||
#define SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC __CONCAT(SPI_PROGRAM_NAME, _program_get_default_config)
|
||||
#define SPI_OFFSET_END __CONCAT(SPI_PROGRAM_NAME, _offset_end)
|
||||
#define SPI_OFFSET_LP1_END __CONCAT(SPI_PROGRAM_NAME, _offset_lp1_end)
|
||||
|
||||
#define CLOCK_DIV 2
|
||||
#define CLOCK_DIV_MINOR 0
|
||||
#define PADS_DRIVE_STRENGTH PADS_BANK0_GPIO0_DRIVE_VALUE_12MA
|
||||
|
||||
#if !CYW43_USE_SPI
|
||||
#error CYW43_USE_SPI should be true
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
//#define ENABLE_SPI_DUMPING 1
|
||||
#endif
|
||||
|
||||
// Set to 1 to enable
|
||||
#if ENABLE_SPI_DUMPING //NDEBUG
|
||||
#if 0
|
||||
#define DUMP_SPI_TRANSACTIONS(A) A
|
||||
#else
|
||||
static bool enable_spi_packet_dumping; // set to true to dump
|
||||
#define DUMP_SPI_TRANSACTIONS(A) if (enable_spi_packet_dumping) {A}
|
||||
#endif
|
||||
|
||||
static uint32_t counter = 0;
|
||||
#else
|
||||
#define DUMP_SPI_TRANSACTIONS(A)
|
||||
#endif
|
||||
|
||||
//#define SWAP32(A) ((((A) & 0xff000000U) >> 8) | (((A) & 0xff0000U) << 8) | (((A) & 0xff00U) >> 8) | (((A) & 0xffU) << 8))
|
||||
__force_inline static uint32_t __swap16x2(uint32_t a) {
|
||||
pico_default_asm ("rev16 %0, %0" : "+l" (a) : : );
|
||||
return a;
|
||||
}
|
||||
#define SWAP32(a) __swap16x2(a)
|
||||
|
||||
#ifndef CYW43_SPI_PIO_PREFERRED_PIO
|
||||
#define CYW43_SPI_PIO_PREFERRED_PIO 1
|
||||
#endif
|
||||
static_assert(CYW43_SPI_PIO_PREFERRED_PIO >=0 && CYW43_SPI_PIO_PREFERRED_PIO < NUM_PIOS, "");
|
||||
|
||||
typedef struct {
|
||||
pio_hw_t *pio;
|
||||
uint8_t pio_func_sel;
|
||||
int8_t pio_offset;
|
||||
int8_t pio_sm;
|
||||
int8_t dma_out;
|
||||
int8_t dma_in;
|
||||
} bus_data_t;
|
||||
|
||||
static bus_data_t bus_data_instance;
|
||||
|
||||
int cyw43_spi_init(cyw43_int_t *self) {
|
||||
// Only does something if CYW43_LOGIC_DEBUG=1
|
||||
logic_debug_init();
|
||||
|
||||
static_assert(NUM_PIOS == 2, "");
|
||||
|
||||
pio_hw_t *pios[2] = {pio0, pio1};
|
||||
uint pio_index = CYW43_SPI_PIO_PREFERRED_PIO;
|
||||
// Check we can add the program
|
||||
if (!pio_can_add_program(pios[pio_index], &SPI_PROGRAM_FUNC)) {
|
||||
pio_index ^= 1;
|
||||
if (!pio_can_add_program(pios[pio_index], &SPI_PROGRAM_FUNC)) {
|
||||
return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
|
||||
}
|
||||
}
|
||||
assert(!self->bus_data);
|
||||
self->bus_data = &bus_data_instance;
|
||||
bus_data_t *bus_data = (bus_data_t *)self->bus_data;
|
||||
bus_data->pio = pios[pio_index];
|
||||
bus_data->dma_in = -1;
|
||||
bus_data->dma_out = -1;
|
||||
|
||||
static_assert(GPIO_FUNC_PIO1 == GPIO_FUNC_PIO0 + 1, "");
|
||||
bus_data->pio_func_sel = GPIO_FUNC_PIO0 + pio_index;
|
||||
bus_data->pio_sm = (int8_t)pio_claim_unused_sm(bus_data->pio, false);
|
||||
if (bus_data->pio_sm < 0) {
|
||||
cyw43_spi_deinit(self);
|
||||
return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
|
||||
}
|
||||
|
||||
bus_data->pio_offset = pio_add_program(bus_data->pio, &SPI_PROGRAM_FUNC);
|
||||
pio_sm_config config = SPI_PROGRAM_GET_DEFAULT_CONFIG_FUNC(bus_data->pio_offset);
|
||||
|
||||
sm_config_set_clkdiv_int_frac(&config, CLOCK_DIV, CLOCK_DIV_MINOR);
|
||||
hw_write_masked(&padsbank0_hw->io[CLOCK_PIN],
|
||||
(uint)PADS_DRIVE_STRENGTH << PADS_BANK0_GPIO0_DRIVE_LSB,
|
||||
PADS_BANK0_GPIO0_DRIVE_BITS
|
||||
);
|
||||
hw_write_masked(&padsbank0_hw->io[CLOCK_PIN],
|
||||
(uint)1 << PADS_BANK0_GPIO0_SLEWFAST_LSB,
|
||||
PADS_BANK0_GPIO0_SLEWFAST_BITS
|
||||
);
|
||||
|
||||
sm_config_set_out_pins(&config, DATA_OUT_PIN, 1);
|
||||
sm_config_set_in_pins(&config, DATA_IN_PIN);
|
||||
sm_config_set_set_pins(&config, DATA_OUT_PIN, 1);
|
||||
sm_config_set_sideset(&config, 1, false, false);
|
||||
sm_config_set_sideset_pins(&config, CLOCK_PIN);
|
||||
sm_config_set_in_shift(&config, false, true, 32);
|
||||
sm_config_set_out_shift(&config, false, true, 32);
|
||||
hw_set_bits(&bus_data->pio->input_sync_bypass, 1u << DATA_IN_PIN);
|
||||
pio_sm_set_config(bus_data->pio, bus_data->pio_sm, &config);
|
||||
pio_sm_set_consecutive_pindirs(bus_data->pio, bus_data->pio_sm, CLOCK_PIN, 1, true);
|
||||
gpio_set_function(DATA_OUT_PIN, bus_data->pio_func_sel);
|
||||
|
||||
// Set data pin to pull down and schmitt
|
||||
gpio_set_pulls(DATA_IN_PIN, false, true);
|
||||
gpio_set_input_hysteresis_enabled(DATA_IN_PIN, true);
|
||||
|
||||
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_set(pio_pins, 1));
|
||||
|
||||
bus_data->dma_out = (int8_t) dma_claim_unused_channel(false);
|
||||
bus_data->dma_in = (int8_t) dma_claim_unused_channel(false);
|
||||
if (bus_data->dma_out < 0 || bus_data->dma_in < 0) {
|
||||
cyw43_spi_deinit(self);
|
||||
return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cyw43_spi_deinit(cyw43_int_t *self) {
|
||||
if (self->bus_data) {
|
||||
bus_data_t *bus_data = (bus_data_t *)self->bus_data;
|
||||
if (bus_data->pio_sm >= 0) {
|
||||
if (bus_data->pio_offset != -1)
|
||||
pio_remove_program(bus_data->pio, &SPI_PROGRAM_FUNC, bus_data->pio_offset);
|
||||
pio_sm_unclaim(bus_data->pio, bus_data->pio_sm);
|
||||
}
|
||||
if (bus_data->dma_out >= 0) {
|
||||
dma_channel_cleanup(bus_data->dma_out);
|
||||
dma_channel_unclaim(bus_data->dma_out);
|
||||
bus_data->dma_out = -1;
|
||||
}
|
||||
if (bus_data->dma_in >= 0) {
|
||||
dma_channel_cleanup(bus_data->dma_in);
|
||||
dma_channel_unclaim(bus_data->dma_in);
|
||||
bus_data->dma_in = -1;
|
||||
}
|
||||
self->bus_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void cs_set(bool value) {
|
||||
gpio_put(CS_PIN, value);
|
||||
}
|
||||
|
||||
static __noinline void ns_delay(uint32_t ns) {
|
||||
// cycles = ns * clk_sys_hz / 1,000,000,000
|
||||
uint32_t cycles = ns * (clock_get_hz(clk_sys) >> 16u) / (1000000000u >> 16u);
|
||||
busy_wait_at_least_cycles(cycles);
|
||||
}
|
||||
|
||||
static void start_spi_comms(cyw43_int_t *self) {
|
||||
bus_data_t *bus_data = (bus_data_t *)self->bus_data;
|
||||
gpio_set_function(DATA_OUT_PIN, bus_data->pio_func_sel);
|
||||
gpio_set_function(CLOCK_PIN, bus_data->pio_func_sel);
|
||||
gpio_pull_down(CLOCK_PIN);
|
||||
// Pull CS low
|
||||
cs_set(false);
|
||||
}
|
||||
|
||||
// we need to atomically de-assert CS and enable IRQ
|
||||
static void stop_spi_comms(void) {
|
||||
// from this point a positive edge will cause an IRQ to be pending
|
||||
cs_set(true);
|
||||
|
||||
// we need to wait a bit in case the irq line is incorrectly high
|
||||
ns_delay(IRQ_SAMPLE_DELAY_NS);
|
||||
}
|
||||
|
||||
#if ENABLE_SPI_DUMPING
|
||||
static void dump_bytes(const uint8_t *bptr, uint32_t len) {
|
||||
unsigned int i = 0;
|
||||
|
||||
for (i = 0; i < len;) {
|
||||
if ((i & 0x0f) == 0) {
|
||||
printf("\n");
|
||||
} else if ((i & 0x07) == 0) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%02x ", bptr[i++]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
int cyw43_spi_transfer(cyw43_int_t *self, const uint8_t *tx, size_t tx_length, uint8_t *rx,
|
||||
size_t rx_length) {
|
||||
|
||||
if ((tx == NULL) && (rx == NULL)) {
|
||||
return CYW43_FAIL_FAST_CHECK(-CYW43_EINVAL);
|
||||
}
|
||||
|
||||
bus_data_t *bus_data = (bus_data_t *)self->bus_data;
|
||||
start_spi_comms(self);
|
||||
if (rx != NULL) {
|
||||
if (tx == NULL) {
|
||||
tx = rx;
|
||||
assert(tx_length && tx_length < rx_length);
|
||||
}
|
||||
DUMP_SPI_TRANSACTIONS(
|
||||
printf("[%lu] bus TX/RX %u bytes rx %u:", counter++, tx_length, rx_length);
|
||||
dump_bytes(tx, tx_length);
|
||||
)
|
||||
assert(!(tx_length & 3));
|
||||
assert(!(((uintptr_t)tx) & 3));
|
||||
assert(!(((uintptr_t)rx) & 3));
|
||||
assert(!(rx_length & 3));
|
||||
|
||||
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
|
||||
pio_sm_set_wrap(bus_data->pio, bus_data->pio_sm, bus_data->pio_offset, bus_data->pio_offset + SPI_OFFSET_END - 1);
|
||||
pio_sm_clear_fifos(bus_data->pio, bus_data->pio_sm);
|
||||
pio_sm_set_pindirs_with_mask(bus_data->pio, bus_data->pio_sm, 1u << DATA_OUT_PIN, 1u << DATA_OUT_PIN);
|
||||
pio_sm_restart(bus_data->pio, bus_data->pio_sm);
|
||||
pio_sm_clkdiv_restart(bus_data->pio, bus_data->pio_sm);
|
||||
pio_sm_put(bus_data->pio, bus_data->pio_sm, tx_length * 8 - 1);
|
||||
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_x, 32));
|
||||
pio_sm_put(bus_data->pio, bus_data->pio_sm, (rx_length - tx_length) * 8 - 1);
|
||||
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_y, 32));
|
||||
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_jmp(bus_data->pio_offset));
|
||||
dma_channel_abort(bus_data->dma_out);
|
||||
dma_channel_abort(bus_data->dma_in);
|
||||
|
||||
dma_channel_config out_config = dma_channel_get_default_config(bus_data->dma_out);
|
||||
channel_config_set_bswap(&out_config, true);
|
||||
channel_config_set_dreq(&out_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, true));
|
||||
|
||||
dma_channel_configure(bus_data->dma_out, &out_config, &bus_data->pio->txf[bus_data->pio_sm], tx, tx_length / 4, true);
|
||||
|
||||
dma_channel_config in_config = dma_channel_get_default_config(bus_data->dma_in);
|
||||
channel_config_set_bswap(&in_config, true);
|
||||
channel_config_set_dreq(&in_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, false));
|
||||
channel_config_set_write_increment(&in_config, true);
|
||||
channel_config_set_read_increment(&in_config, false);
|
||||
dma_channel_configure(bus_data->dma_in, &in_config, rx + tx_length, &bus_data->pio->rxf[bus_data->pio_sm], rx_length / 4 - tx_length / 4, true);
|
||||
|
||||
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, true);
|
||||
__compiler_memory_barrier();
|
||||
|
||||
dma_channel_wait_for_finish_blocking(bus_data->dma_out);
|
||||
dma_channel_wait_for_finish_blocking(bus_data->dma_in);
|
||||
|
||||
__compiler_memory_barrier();
|
||||
memset(rx, 0, tx_length); // make sure we don't have garbage in what would have been returned data if using real SPI
|
||||
} else if (tx != NULL) {
|
||||
DUMP_SPI_TRANSACTIONS(
|
||||
printf("[%lu] bus TX only %u bytes:", counter++, tx_length);
|
||||
dump_bytes(tx, tx_length);
|
||||
)
|
||||
assert(!(((uintptr_t)tx) & 3));
|
||||
assert(!(tx_length & 3));
|
||||
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
|
||||
pio_sm_set_wrap(bus_data->pio, bus_data->pio_sm, bus_data->pio_offset, bus_data->pio_offset + SPI_OFFSET_LP1_END - 1);
|
||||
pio_sm_clear_fifos(bus_data->pio, bus_data->pio_sm);
|
||||
pio_sm_set_pindirs_with_mask(bus_data->pio, bus_data->pio_sm, 1u << DATA_OUT_PIN, 1u << DATA_OUT_PIN);
|
||||
pio_sm_restart(bus_data->pio, bus_data->pio_sm);
|
||||
pio_sm_clkdiv_restart(bus_data->pio, bus_data->pio_sm);
|
||||
pio_sm_put(bus_data->pio, bus_data->pio_sm, tx_length * 8 - 1);
|
||||
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_x, 32));
|
||||
pio_sm_put(bus_data->pio, bus_data->pio_sm, 0);
|
||||
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_out(pio_y, 32));
|
||||
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_jmp(bus_data->pio_offset));
|
||||
dma_channel_abort(bus_data->dma_out);
|
||||
|
||||
dma_channel_config out_config = dma_channel_get_default_config(bus_data->dma_out);
|
||||
channel_config_set_bswap(&out_config, true);
|
||||
channel_config_set_dreq(&out_config, pio_get_dreq(bus_data->pio, bus_data->pio_sm, true));
|
||||
|
||||
dma_channel_configure(bus_data->dma_out, &out_config, &bus_data->pio->txf[bus_data->pio_sm], tx, tx_length / 4, true);
|
||||
|
||||
uint32_t fdebug_tx_stall = 1u << (PIO_FDEBUG_TXSTALL_LSB + bus_data->pio_sm);
|
||||
bus_data->pio->fdebug = fdebug_tx_stall;
|
||||
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, true);
|
||||
while (!(bus_data->pio->fdebug & fdebug_tx_stall)) {
|
||||
tight_loop_contents(); // todo timeout
|
||||
}
|
||||
__compiler_memory_barrier();
|
||||
pio_sm_set_enabled(bus_data->pio, bus_data->pio_sm, false);
|
||||
pio_sm_set_consecutive_pindirs(bus_data->pio, bus_data->pio_sm, DATA_IN_PIN, 1, false);
|
||||
} else if (rx != NULL) { /* currently do one at a time */
|
||||
DUMP_SPI_TRANSACTIONS(
|
||||
printf("[%lu] bus TX %u bytes:", counter++, rx_length);
|
||||
dump_bytes(rx, rx_length);
|
||||
)
|
||||
panic_unsupported();
|
||||
}
|
||||
pio_sm_exec(bus_data->pio, bus_data->pio_sm, pio_encode_mov(pio_pins, pio_null)); // for next time we turn output on
|
||||
|
||||
stop_spi_comms();
|
||||
DUMP_SPI_TRANSACTIONS(
|
||||
printf("RXed:");
|
||||
dump_bytes(rx, rx_length);
|
||||
printf("\n");
|
||||
)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialise our gpios
|
||||
void cyw43_spi_gpio_setup(void) {
|
||||
// Setup WL_REG_ON (23)
|
||||
gpio_init(WL_REG_ON);
|
||||
gpio_set_dir(WL_REG_ON, GPIO_OUT);
|
||||
gpio_pull_up(WL_REG_ON);
|
||||
|
||||
// Setup DO, DI and IRQ (24)
|
||||
gpio_init(DATA_OUT_PIN);
|
||||
gpio_set_dir(DATA_OUT_PIN, GPIO_OUT);
|
||||
gpio_put(DATA_OUT_PIN, false);
|
||||
|
||||
// Setup CS (25)
|
||||
gpio_init(CS_PIN);
|
||||
gpio_set_dir(CS_PIN, GPIO_OUT);
|
||||
gpio_put(CS_PIN, true);
|
||||
}
|
||||
|
||||
// Reset wifi chip
|
||||
void cyw43_spi_reset(void) {
|
||||
gpio_put(WL_REG_ON, false); // off
|
||||
sleep_ms(20);
|
||||
gpio_put(WL_REG_ON, true); // on
|
||||
sleep_ms(250);
|
||||
|
||||
// Setup IRQ (24) - also used for DO, DI
|
||||
gpio_init(IRQ_PIN);
|
||||
gpio_set_dir(IRQ_PIN, GPIO_IN);
|
||||
}
|
||||
|
||||
static inline uint32_t make_cmd(bool write, bool inc, uint32_t fn, uint32_t addr, uint32_t sz) {
|
||||
return write << 31 | inc << 30 | fn << 28 | (addr & 0x1ffff) << 11 | sz;
|
||||
}
|
||||
|
||||
#if CYW43_VERBOSE_DEBUG
|
||||
static const char *func_name(int fn) {
|
||||
switch (fn)
|
||||
{
|
||||
case BUS_FUNCTION:
|
||||
return "BUS_FUNCTION";
|
||||
case BACKPLANE_FUNCTION:
|
||||
return "BACKPLANE_FUNCTION";
|
||||
case WLAN_FUNCTION:
|
||||
return "WLAN_FUNCTION";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t read_reg_u32_swap(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
|
||||
uint32_t buf[2] = {0};
|
||||
assert(fn != BACKPLANE_FUNCTION);
|
||||
buf[0] = SWAP32(make_cmd(false, true, fn, reg, 4));
|
||||
int ret = cyw43_spi_transfer(self, NULL, 4, (uint8_t *)buf, 8);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
return SWAP32(buf[1]);
|
||||
}
|
||||
|
||||
static inline uint32_t _cyw43_read_reg(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint size) {
|
||||
// Padding plus max read size of 32 bits + another 4?
|
||||
static_assert(CYW43_BACKPLANE_READ_PAD_LEN_BYTES % 4 == 0, "");
|
||||
int index = (CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4) + 1 + 1;
|
||||
uint32_t buf32[index];
|
||||
uint8_t *buf = (uint8_t *)buf32;
|
||||
const uint32_t padding = (fn == BACKPLANE_FUNCTION) ? CYW43_BACKPLANE_READ_PAD_LEN_BYTES : 0; // Add response delay
|
||||
buf32[0] = make_cmd(false, true, fn, reg, size);
|
||||
|
||||
if (fn == BACKPLANE_FUNCTION) {
|
||||
logic_debug_set(pin_BACKPLANE_READ, 1);
|
||||
}
|
||||
int ret = cyw43_spi_transfer(self, NULL, 4, buf, 8 + padding);
|
||||
if (fn == BACKPLANE_FUNCTION) {
|
||||
logic_debug_set(pin_BACKPLANE_READ, 0);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
uint32_t result = buf32[padding > 0 ? index - 1 : 1];
|
||||
CYW43_VDEBUG("cyw43_read_reg_u%d %s 0x%lx=0x%lx\n", size * 8, func_name(fn), reg, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t cyw43_read_reg_u32(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
|
||||
return _cyw43_read_reg(self, fn, reg, 4);
|
||||
}
|
||||
|
||||
int cyw43_read_reg_u16(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
|
||||
return _cyw43_read_reg(self, fn, reg, 2);
|
||||
}
|
||||
|
||||
int cyw43_read_reg_u8(cyw43_int_t *self, uint32_t fn, uint32_t reg) {
|
||||
return _cyw43_read_reg(self, fn, reg, 1);
|
||||
}
|
||||
|
||||
// This is only used to switch the word order on boot
|
||||
int write_reg_u32_swap(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
|
||||
uint32_t buf[2];
|
||||
// Boots up in little endian so command needs swapping too
|
||||
buf[0] = SWAP32(make_cmd(true, true, fn, reg, 4));
|
||||
buf[1] = SWAP32(val);
|
||||
int ret = cyw43_spi_transfer(self, (uint8_t *)buf, 8, NULL, 0);
|
||||
CYW43_VDEBUG("write_reg_u32_swap %s 0x%lx=0x%lx\n", func_name(fn), reg, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int _cyw43_write_reg(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val, uint size) {
|
||||
uint32_t buf[2];
|
||||
buf[0] = make_cmd(true, true, fn, reg, size);
|
||||
buf[1] = val;
|
||||
if (fn == BACKPLANE_FUNCTION) {
|
||||
// In case of f1 overflow
|
||||
self->last_size = 8;
|
||||
self->last_header[0] = buf[0];
|
||||
self->last_header[1] = buf[1];
|
||||
self->last_backplane_window = self->cur_backplane_window;
|
||||
}
|
||||
|
||||
if (fn == BACKPLANE_FUNCTION) {
|
||||
logic_debug_set(pin_BACKPLANE_WRITE, 1);
|
||||
}
|
||||
|
||||
int ret = cyw43_spi_transfer(self, (uint8_t *)buf, 8, NULL, 0);
|
||||
|
||||
if (fn == BACKPLANE_FUNCTION) {
|
||||
logic_debug_set(pin_BACKPLANE_WRITE, 0);
|
||||
}
|
||||
|
||||
CYW43_VDEBUG("cyw43_write_reg_u%d %s 0x%lx=0x%lx\n", size * 8, func_name(fn), reg, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cyw43_write_reg_u32(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
|
||||
return _cyw43_write_reg(self, fn, reg, val, 4);
|
||||
}
|
||||
|
||||
int cyw43_write_reg_u16(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint16_t val) {
|
||||
return _cyw43_write_reg(self, fn, reg, val, 2);
|
||||
}
|
||||
|
||||
int cyw43_write_reg_u8(cyw43_int_t *self, uint32_t fn, uint32_t reg, uint32_t val) {
|
||||
return _cyw43_write_reg(self, fn, reg, val, 1);
|
||||
}
|
||||
|
||||
#if CYW43_BUS_MAX_BLOCK_SIZE > 0x7f8
|
||||
#error Block size is wrong for SPI
|
||||
#endif
|
||||
|
||||
int cyw43_read_bytes(cyw43_int_t *self, uint32_t fn, uint32_t addr, size_t len, uint8_t *buf) {
|
||||
assert(fn != BACKPLANE_FUNCTION || (len <= CYW43_BUS_MAX_BLOCK_SIZE));
|
||||
const uint32_t padding = (fn == BACKPLANE_FUNCTION) ? CYW43_BACKPLANE_READ_PAD_LEN_BYTES : 0; // Add response delay
|
||||
size_t aligned_len = (len + 3) & ~3;
|
||||
assert(aligned_len > 0 && aligned_len <= 0x7f8);
|
||||
assert(buf == self->spid_buf || buf < self->spid_buf || buf >= (self->spid_buf + sizeof(self->spid_buf)));
|
||||
self->spi_header[padding > 0 ? 0 : (CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)] = make_cmd(false, true, fn, addr, len);
|
||||
if (fn == WLAN_FUNCTION) {
|
||||
logic_debug_set(pin_WIFI_RX, 1);
|
||||
}
|
||||
int ret = cyw43_spi_transfer(self, NULL, 4, (uint8_t *)&self->spi_header[padding > 0 ? 0 : (CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)], aligned_len + 4 + padding);
|
||||
if (fn == WLAN_FUNCTION) {
|
||||
logic_debug_set(pin_WIFI_RX, 0);
|
||||
}
|
||||
if (ret != 0) {
|
||||
printf("cyw43_read_bytes error %d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (buf != self->spid_buf) { // avoid a copy in the usual case just to add the header
|
||||
memcpy(buf, self->spid_buf, len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// See whd_bus_spi_transfer_bytes
|
||||
// Note, uses spid_buf if src isn't using it already
|
||||
// Apart from firmware download this appears to only be used for wlan functions?
|
||||
int cyw43_write_bytes(cyw43_int_t *self, uint32_t fn, uint32_t addr, size_t len, const uint8_t *src) {
|
||||
assert(fn != BACKPLANE_FUNCTION || (len <= CYW43_BUS_MAX_BLOCK_SIZE));
|
||||
const size_t aligned_len = (len + 3) & ~3u;
|
||||
assert(aligned_len > 0 && aligned_len <= 0x7f8);
|
||||
if (fn == WLAN_FUNCTION) {
|
||||
// Wait for FIFO to be ready to accept data
|
||||
int f2_ready_attempts = 1000;
|
||||
while (f2_ready_attempts-- > 0) {
|
||||
uint32_t bus_status = cyw43_read_reg_u32(self, BUS_FUNCTION, SPI_STATUS_REGISTER);
|
||||
if (bus_status & STATUS_F2_RX_READY) {
|
||||
logic_debug_set(pin_F2_RX_READY_WAIT, 0);
|
||||
break;
|
||||
} else {
|
||||
logic_debug_set(pin_F2_RX_READY_WAIT, 1);
|
||||
}
|
||||
}
|
||||
if (f2_ready_attempts <= 0) {
|
||||
printf("F2 not ready\n");
|
||||
return CYW43_FAIL_FAST_CHECK(-CYW43_EIO);
|
||||
}
|
||||
}
|
||||
if (src == self->spid_buf) { // avoid a copy in the usual case just to add the header
|
||||
self->spi_header[(CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)] = make_cmd(true, true, fn, addr, len);
|
||||
logic_debug_set(pin_WIFI_TX, 1);
|
||||
int res = cyw43_spi_transfer(self, (uint8_t *)&self->spi_header[(CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)], aligned_len + 4, NULL, 0);
|
||||
logic_debug_set(pin_WIFI_TX, 0);
|
||||
return res;
|
||||
} else {
|
||||
// todo: would be nice to get rid of this. Only used for firmware download?
|
||||
assert(src < self->spid_buf || src >= (self->spid_buf + sizeof(self->spid_buf)));
|
||||
self->spi_header[(CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)] = make_cmd(true, true, fn, addr, len);
|
||||
memcpy(self->spid_buf, src, len);
|
||||
return cyw43_spi_transfer(self, (uint8_t *)&self->spi_header[(CYW43_BACKPLANE_READ_PAD_LEN_BYTES / 4)], aligned_len + 4, NULL, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,61 +0,0 @@
|
||||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
.program spi_gap0_sample1
|
||||
.side_set 1
|
||||
|
||||
; always transmit multiple of 32 bytes
|
||||
lp: out pins, 1 side 0
|
||||
jmp x-- lp side 1
|
||||
public lp1_end:
|
||||
set pindirs, 0 side 0
|
||||
lp2:
|
||||
in pins, 1 side 1
|
||||
jmp y-- lp2 side 0
|
||||
public end:
|
||||
|
||||
.program spi_gap01_sample0
|
||||
.side_set 1
|
||||
|
||||
; always transmit multiple of 32 bytes
|
||||
lp: out pins, 1 side 0
|
||||
jmp x-- lp side 1
|
||||
public lp1_end:
|
||||
set pindirs, 0 side 0
|
||||
nop side 1
|
||||
lp2:
|
||||
in pins, 1 side 0
|
||||
jmp y-- lp2 side 1
|
||||
public end:
|
||||
|
||||
.program spi_gap010_sample1
|
||||
.side_set 1
|
||||
|
||||
; always transmit multiple of 32 bytes
|
||||
lp: out pins, 1 side 0
|
||||
jmp x-- lp side 1
|
||||
public lp1_end:
|
||||
set pindirs, 0 side 0
|
||||
nop side 1
|
||||
nop side 0
|
||||
lp2:
|
||||
in pins, 1 side 1
|
||||
jmp y-- lp2 side 0
|
||||
public end:
|
||||
|
||||
.program spi_gap0_sample1_regular
|
||||
.side_set 1
|
||||
|
||||
; always transmit multiple of 32 bytes
|
||||
lp: out pins, 1 side 0
|
||||
jmp x-- lp side 1
|
||||
public lp1_end:
|
||||
set pindirs, 0 side 0
|
||||
lp2:
|
||||
in pins, 1 side 1
|
||||
jmp y-- lp2 side 0
|
||||
public end:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user