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:
Hrvoje Cavrak 2024-07-28 22:24:28 +02:00
parent be645f7596
commit 1415c1d31a
153 changed files with 8358 additions and 12670 deletions

View File

@ -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_enable_stdio_uart(${binary} 0)
pico_enable_stdio_usb(${binary} 0)
pico_add_extra_outputs(${binary})
pico_add_extra_outputs(${binary})
target_link_options(${binary} PRIVATE
-Xlinker
--print-memory-usage
)
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"
)
pico_set_linker_script(${binary} ${CMAKE_SOURCE_DIR}/memory_map.ld)
target_link_options(${binary} PRIVATE
-Xlinker
--print-memory-usage
)
endforeach()
pico_set_linker_script(${binary} ${CMAKE_SOURCE_DIR}/memory_map.ld)

View File

@ -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

View File

@ -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
View File

@ -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
View 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
View 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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

View File

@ -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) */
.section_config : {
"ADDR_CONFIG" = .;
/* Store web configuration utility HTML */
.section_disk : {
ADDR_DISK_IMAGE = .;
KEEP(*(.section_disk))
} > DISK_IMAGE
/* 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);

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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
#----------------------------------

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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

View File

@ -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_ */

View File

@ -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_ */
/** @} */
/** @} */

View File

@ -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

View File

@ -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_ */
/** @} */

View File

@ -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
{
@ -353,177 +366,224 @@ typedef enum
//--------------------------------------------------------------------+
// HID KEYCODE
//--------------------------------------------------------------------+
#define HID_KEY_NONE 0x00
#define HID_KEY_A 0x04
#define HID_KEY_B 0x05
#define HID_KEY_C 0x06
#define HID_KEY_D 0x07
#define HID_KEY_E 0x08
#define HID_KEY_F 0x09
#define HID_KEY_G 0x0A
#define HID_KEY_H 0x0B
#define HID_KEY_I 0x0C
#define HID_KEY_J 0x0D
#define HID_KEY_K 0x0E
#define HID_KEY_L 0x0F
#define HID_KEY_M 0x10
#define HID_KEY_N 0x11
#define HID_KEY_O 0x12
#define HID_KEY_P 0x13
#define HID_KEY_Q 0x14
#define HID_KEY_R 0x15
#define HID_KEY_S 0x16
#define HID_KEY_T 0x17
#define HID_KEY_U 0x18
#define HID_KEY_V 0x19
#define HID_KEY_W 0x1A
#define HID_KEY_X 0x1B
#define HID_KEY_Y 0x1C
#define HID_KEY_Z 0x1D
#define HID_KEY_1 0x1E
#define HID_KEY_2 0x1F
#define HID_KEY_3 0x20
#define HID_KEY_4 0x21
#define HID_KEY_5 0x22
#define HID_KEY_6 0x23
#define HID_KEY_7 0x24
#define HID_KEY_8 0x25
#define HID_KEY_9 0x26
#define HID_KEY_0 0x27
#define HID_KEY_ENTER 0x28
#define HID_KEY_ESCAPE 0x29
#define HID_KEY_BACKSPACE 0x2A
#define HID_KEY_TAB 0x2B
#define HID_KEY_SPACE 0x2C
#define HID_KEY_MINUS 0x2D
#define HID_KEY_EQUAL 0x2E
#define HID_KEY_BRACKET_LEFT 0x2F
#define HID_KEY_BRACKET_RIGHT 0x30
#define HID_KEY_BACKSLASH 0x31
#define HID_KEY_EUROPE_1 0x32
#define HID_KEY_SEMICOLON 0x33
#define HID_KEY_APOSTROPHE 0x34
#define HID_KEY_GRAVE 0x35
#define HID_KEY_COMMA 0x36
#define HID_KEY_PERIOD 0x37
#define HID_KEY_SLASH 0x38
#define HID_KEY_CAPS_LOCK 0x39
#define HID_KEY_F1 0x3A
#define HID_KEY_F2 0x3B
#define HID_KEY_F3 0x3C
#define HID_KEY_F4 0x3D
#define HID_KEY_F5 0x3E
#define HID_KEY_F6 0x3F
#define HID_KEY_F7 0x40
#define HID_KEY_F8 0x41
#define HID_KEY_F9 0x42
#define HID_KEY_F10 0x43
#define HID_KEY_F11 0x44
#define HID_KEY_F12 0x45
#define HID_KEY_PRINT_SCREEN 0x46
#define HID_KEY_SCROLL_LOCK 0x47
#define HID_KEY_PAUSE 0x48
#define HID_KEY_INSERT 0x49
#define HID_KEY_HOME 0x4A
#define HID_KEY_PAGE_UP 0x4B
#define HID_KEY_DELETE 0x4C
#define HID_KEY_END 0x4D
#define HID_KEY_PAGE_DOWN 0x4E
#define HID_KEY_ARROW_RIGHT 0x4F
#define HID_KEY_ARROW_LEFT 0x50
#define HID_KEY_ARROW_DOWN 0x51
#define HID_KEY_ARROW_UP 0x52
#define HID_KEY_NUM_LOCK 0x53
#define HID_KEY_KEYPAD_DIVIDE 0x54
#define HID_KEY_KEYPAD_MULTIPLY 0x55
#define HID_KEY_KEYPAD_SUBTRACT 0x56
#define HID_KEY_KEYPAD_ADD 0x57
#define HID_KEY_KEYPAD_ENTER 0x58
#define HID_KEY_KEYPAD_1 0x59
#define HID_KEY_KEYPAD_2 0x5A
#define HID_KEY_KEYPAD_3 0x5B
#define HID_KEY_KEYPAD_4 0x5C
#define HID_KEY_KEYPAD_5 0x5D
#define HID_KEY_KEYPAD_6 0x5E
#define HID_KEY_KEYPAD_7 0x5F
#define HID_KEY_KEYPAD_8 0x60
#define HID_KEY_KEYPAD_9 0x61
#define HID_KEY_KEYPAD_0 0x62
#define HID_KEY_KEYPAD_DECIMAL 0x63
#define HID_KEY_EUROPE_2 0x64
#define HID_KEY_APPLICATION 0x65
#define HID_KEY_POWER 0x66
#define HID_KEY_KEYPAD_EQUAL 0x67
#define HID_KEY_F13 0x68
#define HID_KEY_F14 0x69
#define HID_KEY_F15 0x6A
#define HID_KEY_F16 0x6B
#define HID_KEY_F17 0x6C
#define HID_KEY_F18 0x6D
#define HID_KEY_F19 0x6E
#define HID_KEY_F20 0x6F
#define HID_KEY_F21 0x70
#define HID_KEY_F22 0x71
#define HID_KEY_F23 0x72
#define HID_KEY_F24 0x73
#define HID_KEY_EXECUTE 0x74
#define HID_KEY_HELP 0x75
#define HID_KEY_MENU 0x76
#define HID_KEY_SELECT 0x77
#define HID_KEY_STOP 0x78
#define HID_KEY_AGAIN 0x79
#define HID_KEY_UNDO 0x7A
#define HID_KEY_CUT 0x7B
#define HID_KEY_COPY 0x7C
#define HID_KEY_PASTE 0x7D
#define HID_KEY_FIND 0x7E
#define HID_KEY_MUTE 0x7F
#define HID_KEY_VOLUME_UP 0x80
#define HID_KEY_VOLUME_DOWN 0x81
#define HID_KEY_LOCKING_CAPS_LOCK 0x82
#define HID_KEY_LOCKING_NUM_LOCK 0x83
#define HID_KEY_LOCKING_SCROLL_LOCK 0x84
#define HID_KEY_KEYPAD_COMMA 0x85
#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
#define HID_KEY_KANJI1 0x87
#define HID_KEY_KANJI2 0x88
#define HID_KEY_KANJI3 0x89
#define HID_KEY_KANJI4 0x8A
#define HID_KEY_KANJI5 0x8B
#define HID_KEY_KANJI6 0x8C
#define HID_KEY_KANJI7 0x8D
#define HID_KEY_KANJI8 0x8E
#define HID_KEY_KANJI9 0x8F
#define HID_KEY_LANG1 0x90
#define HID_KEY_LANG2 0x91
#define HID_KEY_LANG3 0x92
#define HID_KEY_LANG4 0x93
#define HID_KEY_LANG5 0x94
#define HID_KEY_LANG6 0x95
#define HID_KEY_LANG7 0x96
#define HID_KEY_LANG8 0x97
#define HID_KEY_LANG9 0x98
#define HID_KEY_ALTERNATE_ERASE 0x99
#define HID_KEY_SYSREQ_ATTENTION 0x9A
#define HID_KEY_CANCEL 0x9B
#define HID_KEY_CLEAR 0x9C
#define HID_KEY_PRIOR 0x9D
#define HID_KEY_RETURN 0x9E
#define HID_KEY_SEPARATOR 0x9F
#define HID_KEY_OUT 0xA0
#define HID_KEY_OPER 0xA1
#define HID_KEY_CLEAR_AGAIN 0xA2
#define HID_KEY_CRSEL_PROPS 0xA3
#define HID_KEY_EXSEL 0xA4
// RESERVED 0xA5-DF
#define HID_KEY_CONTROL_LEFT 0xE0
#define HID_KEY_SHIFT_LEFT 0xE1
#define HID_KEY_ALT_LEFT 0xE2
#define HID_KEY_GUI_LEFT 0xE3
#define HID_KEY_CONTROL_RIGHT 0xE4
#define HID_KEY_SHIFT_RIGHT 0xE5
#define HID_KEY_ALT_RIGHT 0xE6
#define HID_KEY_GUI_RIGHT 0xE7
#define HID_KEY_NONE 0x00
#define HID_KEY_A 0x04
#define HID_KEY_B 0x05
#define HID_KEY_C 0x06
#define HID_KEY_D 0x07
#define HID_KEY_E 0x08
#define HID_KEY_F 0x09
#define HID_KEY_G 0x0A
#define HID_KEY_H 0x0B
#define HID_KEY_I 0x0C
#define HID_KEY_J 0x0D
#define HID_KEY_K 0x0E
#define HID_KEY_L 0x0F
#define HID_KEY_M 0x10
#define HID_KEY_N 0x11
#define HID_KEY_O 0x12
#define HID_KEY_P 0x13
#define HID_KEY_Q 0x14
#define HID_KEY_R 0x15
#define HID_KEY_S 0x16
#define HID_KEY_T 0x17
#define HID_KEY_U 0x18
#define HID_KEY_V 0x19
#define HID_KEY_W 0x1A
#define HID_KEY_X 0x1B
#define HID_KEY_Y 0x1C
#define HID_KEY_Z 0x1D
#define HID_KEY_1 0x1E
#define HID_KEY_2 0x1F
#define HID_KEY_3 0x20
#define HID_KEY_4 0x21
#define HID_KEY_5 0x22
#define HID_KEY_6 0x23
#define HID_KEY_7 0x24
#define HID_KEY_8 0x25
#define HID_KEY_9 0x26
#define HID_KEY_0 0x27
#define HID_KEY_ENTER 0x28
#define HID_KEY_ESCAPE 0x29
#define HID_KEY_BACKSPACE 0x2A
#define HID_KEY_TAB 0x2B
#define HID_KEY_SPACE 0x2C
#define HID_KEY_MINUS 0x2D
#define HID_KEY_EQUAL 0x2E
#define HID_KEY_BRACKET_LEFT 0x2F
#define HID_KEY_BRACKET_RIGHT 0x30
#define HID_KEY_BACKSLASH 0x31
#define HID_KEY_EUROPE_1 0x32
#define HID_KEY_SEMICOLON 0x33
#define HID_KEY_APOSTROPHE 0x34
#define HID_KEY_GRAVE 0x35
#define HID_KEY_COMMA 0x36
#define HID_KEY_PERIOD 0x37
#define HID_KEY_SLASH 0x38
#define HID_KEY_CAPS_LOCK 0x39
#define HID_KEY_F1 0x3A
#define HID_KEY_F2 0x3B
#define HID_KEY_F3 0x3C
#define HID_KEY_F4 0x3D
#define HID_KEY_F5 0x3E
#define HID_KEY_F6 0x3F
#define HID_KEY_F7 0x40
#define HID_KEY_F8 0x41
#define HID_KEY_F9 0x42
#define HID_KEY_F10 0x43
#define HID_KEY_F11 0x44
#define HID_KEY_F12 0x45
#define HID_KEY_PRINT_SCREEN 0x46
#define HID_KEY_SCROLL_LOCK 0x47
#define HID_KEY_PAUSE 0x48
#define HID_KEY_INSERT 0x49
#define HID_KEY_HOME 0x4A
#define HID_KEY_PAGE_UP 0x4B
#define HID_KEY_DELETE 0x4C
#define HID_KEY_END 0x4D
#define HID_KEY_PAGE_DOWN 0x4E
#define HID_KEY_ARROW_RIGHT 0x4F
#define HID_KEY_ARROW_LEFT 0x50
#define HID_KEY_ARROW_DOWN 0x51
#define HID_KEY_ARROW_UP 0x52
#define HID_KEY_NUM_LOCK 0x53
#define HID_KEY_KEYPAD_DIVIDE 0x54
#define HID_KEY_KEYPAD_MULTIPLY 0x55
#define HID_KEY_KEYPAD_SUBTRACT 0x56
#define HID_KEY_KEYPAD_ADD 0x57
#define HID_KEY_KEYPAD_ENTER 0x58
#define HID_KEY_KEYPAD_1 0x59
#define HID_KEY_KEYPAD_2 0x5A
#define HID_KEY_KEYPAD_3 0x5B
#define HID_KEY_KEYPAD_4 0x5C
#define HID_KEY_KEYPAD_5 0x5D
#define HID_KEY_KEYPAD_6 0x5E
#define HID_KEY_KEYPAD_7 0x5F
#define HID_KEY_KEYPAD_8 0x60
#define HID_KEY_KEYPAD_9 0x61
#define HID_KEY_KEYPAD_0 0x62
#define HID_KEY_KEYPAD_DECIMAL 0x63
#define HID_KEY_EUROPE_2 0x64
#define HID_KEY_APPLICATION 0x65
#define HID_KEY_POWER 0x66
#define HID_KEY_KEYPAD_EQUAL 0x67
#define HID_KEY_F13 0x68
#define HID_KEY_F14 0x69
#define HID_KEY_F15 0x6A
#define HID_KEY_F16 0x6B
#define HID_KEY_F17 0x6C
#define HID_KEY_F18 0x6D
#define HID_KEY_F19 0x6E
#define HID_KEY_F20 0x6F
#define HID_KEY_F21 0x70
#define HID_KEY_F22 0x71
#define HID_KEY_F23 0x72
#define HID_KEY_F24 0x73
#define HID_KEY_EXECUTE 0x74
#define HID_KEY_HELP 0x75
#define HID_KEY_MENU 0x76
#define HID_KEY_SELECT 0x77
#define HID_KEY_STOP 0x78
#define HID_KEY_AGAIN 0x79
#define HID_KEY_UNDO 0x7A
#define HID_KEY_CUT 0x7B
#define HID_KEY_COPY 0x7C
#define HID_KEY_PASTE 0x7D
#define HID_KEY_FIND 0x7E
#define HID_KEY_MUTE 0x7F
#define HID_KEY_VOLUME_UP 0x80
#define HID_KEY_VOLUME_DOWN 0x81
#define HID_KEY_LOCKING_CAPS_LOCK 0x82
#define HID_KEY_LOCKING_NUM_LOCK 0x83
#define HID_KEY_LOCKING_SCROLL_LOCK 0x84
#define HID_KEY_KEYPAD_COMMA 0x85
#define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
#define HID_KEY_KANJI1 0x87
#define HID_KEY_KANJI2 0x88
#define HID_KEY_KANJI3 0x89
#define HID_KEY_KANJI4 0x8A
#define HID_KEY_KANJI5 0x8B
#define HID_KEY_KANJI6 0x8C
#define HID_KEY_KANJI7 0x8D
#define HID_KEY_KANJI8 0x8E
#define HID_KEY_KANJI9 0x8F
#define HID_KEY_LANG1 0x90
#define HID_KEY_LANG2 0x91
#define HID_KEY_LANG3 0x92
#define HID_KEY_LANG4 0x93
#define HID_KEY_LANG5 0x94
#define HID_KEY_LANG6 0x95
#define HID_KEY_LANG7 0x96
#define HID_KEY_LANG8 0x97
#define HID_KEY_LANG9 0x98
#define HID_KEY_ALTERNATE_ERASE 0x99
#define HID_KEY_SYSREQ_ATTENTION 0x9A
#define HID_KEY_CANCEL 0x9B
#define HID_KEY_CLEAR 0x9C
#define HID_KEY_PRIOR 0x9D
#define HID_KEY_RETURN 0x9E
#define HID_KEY_SEPARATOR 0x9F
#define HID_KEY_OUT 0xA0
#define HID_KEY_OPER 0xA1
#define HID_KEY_CLEAR_AGAIN 0xA2
#define HID_KEY_CRSEL_PROPS 0xA3
#define HID_KEY_EXSEL 0xA4
// 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
#define HID_KEY_GUI_LEFT 0xE3
#define HID_KEY_CONTROL_RIGHT 0xE4
#define HID_KEY_SHIFT_RIGHT 0xE5
#define HID_KEY_ALT_RIGHT 0xE6
#define HID_KEY_GUI_RIGHT 0xE7
//--------------------------------------------------------------------+
@ -684,32 +744,33 @@ enum {
/// HID Usage Table - Table 1: Usage Page Summary
enum {
HID_USAGE_PAGE_DESKTOP = 0x01,
HID_USAGE_PAGE_SIMULATE = 0x02,
HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
HID_USAGE_PAGE_SPORT = 0x04,
HID_USAGE_PAGE_GAME = 0x05,
HID_USAGE_PAGE_GENERIC_DEVICE = 0x06,
HID_USAGE_PAGE_KEYBOARD = 0x07,
HID_USAGE_PAGE_LED = 0x08,
HID_USAGE_PAGE_BUTTON = 0x09,
HID_USAGE_PAGE_ORDINAL = 0x0a,
HID_USAGE_PAGE_TELEPHONY = 0x0b,
HID_USAGE_PAGE_CONSUMER = 0x0c,
HID_USAGE_PAGE_DIGITIZER = 0x0d,
HID_USAGE_PAGE_PID = 0x0f,
HID_USAGE_PAGE_UNICODE = 0x10,
HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14,
HID_USAGE_PAGE_MEDICAL = 0x40,
HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83
HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87
HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
HID_USAGE_PAGE_SCALE = 0x8d,
HID_USAGE_PAGE_MSR = 0x8e,
HID_USAGE_PAGE_CAMERA = 0x90,
HID_USAGE_PAGE_ARCADE = 0x91,
HID_USAGE_PAGE_FIDO = 0xF1D0, // FIDO alliance HID usage page
HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF
HID_USAGE_PAGE_DESKTOP = 0x01,
HID_USAGE_PAGE_SIMULATE = 0x02,
HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
HID_USAGE_PAGE_SPORT = 0x04,
HID_USAGE_PAGE_GAME = 0x05,
HID_USAGE_PAGE_GENERIC_DEVICE = 0x06,
HID_USAGE_PAGE_KEYBOARD = 0x07,
HID_USAGE_PAGE_LED = 0x08,
HID_USAGE_PAGE_BUTTON = 0x09,
HID_USAGE_PAGE_ORDINAL = 0x0a,
HID_USAGE_PAGE_TELEPHONY = 0x0b,
HID_USAGE_PAGE_CONSUMER = 0x0c,
HID_USAGE_PAGE_DIGITIZER = 0x0d,
HID_USAGE_PAGE_PID = 0x0f,
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,
HID_USAGE_PAGE_SCALE = 0x8d,
HID_USAGE_PAGE_MSR = 0x8e,
HID_USAGE_PAGE_CAMERA = 0x90,
HID_USAGE_PAGE_ARCADE = 0x91,
HID_USAGE_PAGE_FIDO = 0xF1D0, // FIDO alliance HID usage page
HID_USAGE_PAGE_VENDOR = 0xFF00 // 0xFF00 - 0xFFFF
};
/// HID Usage Table - Table 6: Generic Desktop Page
@ -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

View File

@ -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 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,12 +63,12 @@ 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;
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,44 +112,51 @@ 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,
.wheel = vertical,
.pan = horizontal
.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,
.rz = rz,
.rx = rx,
.ry = ry,
.hat = hat,
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,
.rz = rz,
.rx = rx,
.ry = ry,
.hat = hat,
.buttons = buttons,
};
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
@ -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;
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,144 +234,120 @@ 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 )
{
uint8_t const desc_type = tu_u16_high(request->wValue);
//uint8_t const desc_index = tu_u16_low (request->wValue);
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);
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 )
{
case HID_REQ_CONTROL_GET_REPORT:
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);
switch (request->bRequest) {
case HID_REQ_CONTROL_GET_REPORT:
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;
uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
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;
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) )
{
*report_buf++ = report_id;
req_len--;
// 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)) {
*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 );
tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
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);
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);
case HID_REQ_CONTROL_SET_REPORT:
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->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])) {
report_buf++;
report_len--;
}
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;
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]) )
{
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 )
{
p_hid->idle_rate = tu_u16_high(request->wValue);
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) );
}
tud_control_status(rhport, request);
case HID_REQ_CONTROL_SET_IDLE:
if (stage == CONTROL_STAGE_SETUP) {
p_hid->idle_rate = tu_u16_high(request->wValue);
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));
}
tud_control_status(rhport, request);
}
break;
case HID_REQ_CONTROL_GET_IDLE:
if ( stage == CONTROL_STAGE_SETUP )
{
// TODO idle rate of report
tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
}
case HID_REQ_CONTROL_GET_IDLE:
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 )
{
tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1);
}
case HID_REQ_CONTROL_GET_PROTOCOL:
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 )
{
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)
{
tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
}
case HID_REQ_CONTROL_SET_PROTOCOL:
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) {
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)));
}

View File

@ -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

View File

@ -39,22 +39,22 @@
#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)
uint8_t report_desc_type;
uint8_t report_desc_type;
uint16_t report_desc_len;
uint16_t epin_size;
@ -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);
@ -151,34 +130,30 @@ bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info)
// re-construct descriptor
tusb_desc_interface_t* desc = &info->desc;
desc->bLength = sizeof(tusb_desc_interface_t);
desc->bDescriptorType = TUSB_DESC_INTERFACE;
desc->bLength = sizeof(tusb_desc_interface_t);
desc->bDescriptorType = TUSB_DESC_INTERFACE;
desc->bInterfaceNumber = p_hid->itf_num;
desc->bAlternateSetting = 0;
desc->bNumEndpoints = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u));
desc->bInterfaceClass = TUSB_CLASS_HID;
desc->bInterfaceNumber = p_hid->itf_num;
desc->bAlternateSetting = 0;
desc->bNumEndpoints = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u));
desc->bInterfaceClass = TUSB_CLASS_HID;
desc->bInterfaceSubClass = (p_hid->itf_protocol ? HID_SUBCLASS_BOOT : HID_SUBCLASS_NONE);
desc->bInterfaceProtocol = p_hid->itf_protocol;
desc->iInterface = 0; // not used yet
desc->iInterface = 0; // not used yet
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);
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,123 +187,153 @@ 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 =
{
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
},
.bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
.wValue = protocol,
.wIndex = itf_num,
.wLength = 0
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_PROTOCOL,
.wValue = protocol,
.wIndex = itf_num,
.wLength = 0
};
tuh_xfer_t xfer =
{
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
.complete_cb = complete_cb,
.user_data = user_data
tuh_xfer_t xfer = {
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
.complete_cb = complete_cb,
.user_data = user_data
};
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)
{
TU_LOG_DRV("HID Set Report complete\r\n");
static void get_report_complete(tuh_xfer_t* xfer) {
TU_LOG_DRV("HID Get Report complete\r\n");
if (tuh_hid_set_report_complete_cb)
{
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 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);
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) {
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_set_report_complete_cb(xfer->daddr, idx, report_id, report_type,
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
}
}
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 =
{
.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),
.wLength = len
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),
.wLength = len
};
tuh_xfer_t xfer =
{
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = report,
.complete_cb = set_report_complete,
.user_data = 0
tuh_xfer_t xfer = {
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = report,
.complete_cb = set_report_complete,
.user_data = 0
};
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 =
{
.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),
.wLength = 0
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),
.wLength = 0
};
tuh_xfer_t xfer =
{
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
.complete_cb = complete_cb,
.user_data = user_data
tuh_xfer_t xfer = {
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
.complete_cb = complete_cb,
.user_data = user_data
};
return tuh_control_xfer(&xfer);
@ -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,38 +487,33 @@ 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)
{
p_hid->ep_in = desc_ep->bEndpointAddress;
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
{
p_hid->ep_out = desc_ep->bEndpointAddress;
} 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;
p_hid->itf_num = desc_itf->bInterfaceNumber;
// Assume bNumDescriptors = 1
p_hid->report_desc_type = desc_hid->bReportType;
p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
// 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,15 +534,14 @@ 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);
tuh_xfer_t xfer;
xfer.daddr = daddr;
xfer.result = XFER_RESULT_SUCCESS;
xfer.setup = &request;
xfer.daddr = daddr;
xfer.result = XFER_RESULT_SUCCESS;
xfer.setup = &request;
xfer.user_data = CONFG_SET_IDLE;
// fake request to kick-off the set config process
@ -567,71 +550,68 @@ 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;
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
uint8_t const daddr = xfer->daddr;
uint8_t const daddr = xfer->daddr;
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
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;
}
break;
case CONFIG_SET_PROTOCOL:
_hidh_set_protocol(daddr, p_hid->itf_num, _hidh_default_protocol, process_set_config, CONFIG_GET_REPORT_DESC);
break;
break;
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);
uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
config_driver_mount_complete(daddr, idx, desc_report, desc_len);
break;
}
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
{
uint8_t size : 2;
uint8_t type : 2;
uint8_t tag : 4;
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,114 +646,105 @@ 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--;
uint8_t const tag = header.tag;
uint8_t const tag = header.tag;
uint8_t const type = header.type;
uint8_t const size = header.size;
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;
break;
case RI_MAIN_COLLECTION_END:
ri_collection_depth--;
if (ri_collection_depth == 0)
{
if (ri_collection_depth == 0) {
info++;
report_num++;
}
break;
break;
default: break;
default:break;
}
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);
break;
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;
break;
break;
case RI_GLOBAL_REPORT_SIZE:
// ri_report_size = data8;
break;
break;
case RI_GLOBAL_REPORT_COUNT:
// ri_report_count = data8;
break;
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;
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;
break;
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;
break;
// error
// error
default: break;
}
desc_report += size;
desc_len -= size;
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);
}

View File

@ -30,7 +30,7 @@
#include "hid.h"
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
//--------------------------------------------------------------------+
@ -47,10 +47,9 @@
#endif
typedef struct
{
uint8_t report_id;
uint8_t usage;
typedef struct {
uint8_t report_id;
uint8_t usage;
uint16_t usage_page;
// TODO still use the endpoint size for now
@ -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
}

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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_ */

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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
/******************************************************************************/
/*!

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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_

View File

@ -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)

View File

@ -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

View File

@ -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) */\

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -35,7 +35,11 @@
extern "C" {
#endif
#define TU_LOG_USBH(...) TU_LOG(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
#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);

View File

@ -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);

View File

@ -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

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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,75 +102,53 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
//--------------------------------------------------------------------+
#include "common/tusb_fifo.h"
typedef struct
{
tu_fifo_t ff;
struct critical_section critsec; // osal_queue may be used in IRQs, so need critical section
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;
typedef osal_queue_def_t* osal_queue_t;
// role device/host is used by OS NONE for mutex (disable usb isr) only
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
uint8_t _name##_buf[_depth*sizeof(_type)]; \
osal_queue_def_t _name = { \
.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

View File

@ -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_ */

View File

@ -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

View File

@ -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,79 +56,70 @@ 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)
{
// Clear hardware registers and then zero the struct
// Clears endpoint enable
*ep->endpoint_control = 0;
// Clears buffer available, etc
*ep->buffer_control = 0;
// Clear any endpoint state
memset(ep, 0, sizeof(struct hw_endpoint));
static void _hw_endpoint_close(struct hw_endpoint* ep) {
// Clear hardware registers and then zero the struct
// Clears endpoint enable
*ep->endpoint_control = 0;
// Clears buffer available, etc
*ep->buffer_control = 0;
// Clear any endpoint state
memset(ep, 0, sizeof(struct hw_endpoint));
// 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)
{
reclaim_buffers = false;
break;
}
}
if (reclaim_buffers)
{
next_buffer_ptr = &usb_dpram->epx_data[0];
// 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) {
reclaim_buffers = false;
break;
}
}
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);
_hw_endpoint_close(ep);
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,76 +162,82 @@ 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);
hw_endpoint_xfer_start(ep, buffer, total_bytes);
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)
{
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)
{
// clear this in advance
usb_hw_clear->buf_status = bit;
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) {
// 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);
// 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);
// Continue xfer
bool done = hw_endpoint_xfer_continue(ep);
if (done)
{
// Notify
dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
hw_endpoint_reset_transfer(ep);
}
remaining_buffers &= ~bit;
}
bit <<= 1u;
// Continue xfer
bool done = hw_endpoint_xfer_continue(ep);
if (done) {
// Notify
dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
hw_endpoint_reset_transfer(ep);
}
remaining_buffers &= ~bit;
}
bit <<= 1u;
}
}
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0_pid(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]);
ep->next_pid = 1u;
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
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);
@ -420,27 +400,37 @@ void dcd_init (uint8_t rhport)
// for the global interrupt enable...
// Note: Force VBUS detect cause disconnection not detectable
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS |
(FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS);
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS |
(FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS);
dcd_connect(rhport);
}
void dcd_int_enable(__unused uint8_t rhport)
{
assert(rhport == 0);
irq_set_enabled(USBCTRL_IRQ, true);
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_disable(__unused uint8_t rhport)
{
assert(rhport == 0);
irq_set_enabled(USBCTRL_IRQ, false);
void dcd_int_enable(__unused uint8_t rhport) {
assert(rhport == 0);
irq_set_enabled(USBCTRL_IRQ, true);
}
void dcd_set_address (__unused uint8_t rhport, __unused uint8_t dev_addr)
{
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) {
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 &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
request->bRequest == TUSB_REQ_SET_ADDRESS )
{
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
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)
{
assert(rhport == 0);
hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer);
return true;
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)
{
assert(rhport == 0);
hw_endpoint_xfer(ep_addr, buffer, total_bytes);
return true;
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) rhport;
pico_trace("dcd_edpt_close %02x\r\n", ep_addr);
hw_endpoint_close(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();
}

View File

@ -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;

View File

@ -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,35 +184,28 @@ 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);
}
// Fill in info now that we're kicking off the hw
ep->remaining_len = total_len;
ep->xferred_len = 0;
ep->active = true;
ep->user_buf = buffer;
ep->xferred_len = 0;
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;
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

View File

@ -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 \

View File

@ -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;
}
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;
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;
}
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" ,
"Synch Frame"
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;
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

View File

@ -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(...)

View File

@ -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

View File

@ -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)
@ -85,4 +78,4 @@ set(CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}" PARENT_SCOPE)
pico_add_doxygen(${CMAKE_CURRENT_LIST_DIR})
pico_add_doxygen_exclude(${CMAKE_CURRENT_LIST_DIR}/cmsis)
pico_promote_common_scope_vars()
pico_promote_common_scope_vars()

View File

@ -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()

View File

@ -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 BlueKitchens 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.

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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.
*/

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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()

View File

@ -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;
}

View File

@ -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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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;
}

View File

@ -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

View File

@ -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

View File

@ -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