aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules6
-rw-r--r--CMakeLists.txt15
-rw-r--r--LICENSE9
-rw-r--r--README.md3
m---------deps/tinyusb0
m---------deps/tusb_xinput0
-rw-r--r--pico_sdk_import.cmake73
-rw-r--r--src/CMakeLists.txt29
-rw-r--r--src/btstack_config.h81
-rw-r--r--src/gamepad.c266
-rw-r--r--src/gamepad.h20
-rw-r--r--src/main.c30
-rw-r--r--src/tusb_config.h127
-rw-r--r--src/xinput.c96
15 files changed, 757 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9ef9604
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build
+
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..3a2b455
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "deps/tusb_xinput"]
+ path = deps/tusb_xinput
+ url = https://github.com/Ryzee119/tusb_xinput.git
+[submodule "deps/tinyusb"]
+ path = deps/tinyusb
+ url = https://github.com/hathach/tinyusb
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..1622134
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(PICO_BOARD "pico_w")
+set(PICO_TINYUSB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/deps/tinyusb)
+
+include(pico_sdk_import.cmake)
+
+project(btinput)
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
+
+pico_sdk_init()
+
+add_subdirectory(src)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3f8b43c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright Jan Drögehoff
+
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b6ab9cd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# Wirecutter
+
+RP2040 Project for making wired xinput controllers wireless \ No newline at end of file
diff --git a/deps/tinyusb b/deps/tinyusb
new file mode 160000
+Subproject 1eb6ce784ca9b8acbbe43dba9f1d9c26c2e80eb
diff --git a/deps/tusb_xinput b/deps/tusb_xinput
new file mode 160000
+Subproject 073d73c47a275083abbfb7b07ab9562b6dfe82b
diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake
new file mode 100644
index 0000000..65f8a6f
--- /dev/null
+++ b/pico_sdk_import.cmake
@@ -0,0 +1,73 @@
+# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
+
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+ message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+ message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+ message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+
+if (NOT PICO_SDK_PATH)
+ if (PICO_SDK_FETCH_FROM_GIT)
+ include(FetchContent)
+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+ if (PICO_SDK_FETCH_FROM_GIT_PATH)
+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+ endif ()
+ # GIT_SUBMODULES_RECURSE was added in 3.17
+ if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG master
+ GIT_SUBMODULES_RECURSE FALSE
+ )
+ else ()
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG master
+ )
+ endif ()
+
+ if (NOT pico_sdk)
+ message("Downloading Raspberry Pi Pico SDK")
+ FetchContent_Populate(pico_sdk)
+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+ endif ()
+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+ else ()
+ message(FATAL_ERROR
+ "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+ )
+ endif ()
+endif ()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_SDK_PATH})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif ()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
+endif ()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..4782941
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,29 @@
+add_subdirectory(${PROJECT_SOURCE_DIR}/deps/tusb_xinput xinput_host)
+
+add_executable(btinput
+ main.c
+ xinput.c
+ gamepad.c
+ tusb_config.h
+ btstack_config.h
+)
+
+target_include_directories(btinput PUBLIC
+ ${CMAKE_CURRENT_LIST_DIR}
+)
+
+pico_enable_stdio_usb(btinput 0)
+pico_enable_stdio_uart(btinput 1)
+
+pico_add_extra_outputs(btinput)
+
+target_link_libraries(btinput PUBLIC
+ pico_stdlib
+ pico_cyw43_arch_none
+ pico_btstack_cyw43
+ pico_btstack_classic
+ tinyusb_host
+ tinyusb_board
+ xinput_host
+)
+
diff --git a/src/btstack_config.h b/src/btstack_config.h
new file mode 100644
index 0000000..e38468b
--- /dev/null
+++ b/src/btstack_config.h
@@ -0,0 +1,81 @@
+#ifndef _PICO_BTSTACK_BTSTACK_CONFIG_H
+#define _PICO_BTSTACK_BTSTACK_CONFIG_H
+
+// BTstack features that can be enabled
+#ifdef ENABLE_BLE
+#warning no BLE
+#undef ENABLE_BLE
+#endif
+#define ENABLE_LOG_DEBUG
+#define ENABLE_LOG_INFO
+#define ENABLE_LOG_ERROR
+#define ENABLE_PRINTF_HEXDUMP
+#define ENABLE_SCO_OVER_HCI
+#define ENABLE_CLASSIC_OOB_PAIRING
+#define ENABLE_LOG_BTSTACK_EVENTS
+
+// BTstack configuration. buffers, sizes, ...
+#define HCI_OUTGOING_PRE_BUFFER_SIZE 16
+#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
+#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4
+#define MAX_NR_AVDTP_CONNECTIONS 1
+#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1
+#define MAX_NR_AVRCP_CONNECTIONS 2
+#define MAX_NR_BNEP_CHANNELS 1
+#define MAX_NR_BNEP_SERVICES 1
+#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2
+#define MAX_NR_GATT_CLIENTS 1
+#define MAX_NR_HCI_CONNECTIONS 1
+#define MAX_NR_HID_HOST_CONNECTIONS 1
+#define MAX_NR_HIDS_CLIENTS 1
+#define MAX_NR_HFP_CONNECTIONS 1
+#define MAX_NR_L2CAP_CHANNELS 4
+#define MAX_NR_L2CAP_SERVICES 3
+#define MAX_NR_RFCOMM_CHANNELS 1
+#define MAX_NR_RFCOMM_MULTIPLEXERS 1
+#define MAX_NR_RFCOMM_SERVICES 1
+#define MAX_NR_SERVICE_RECORD_ITEMS 4
+#define MAX_NR_SM_LOOKUP_ENTRIES 3
+#define MAX_NR_WHITELIST_ENTRIES 16
+#define MAX_NR_LE_DEVICE_DB_ENTRIES 16
+
+// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun
+#define MAX_NR_CONTROLLER_ACL_BUFFERS 3
+#define MAX_NR_CONTROLLER_SCO_PACKETS 3
+
+// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun
+#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL
+#define HCI_HOST_ACL_PACKET_LEN 1024
+#define HCI_HOST_ACL_PACKET_NUM 3
+#define HCI_HOST_SCO_PACKET_LEN 120
+#define HCI_HOST_SCO_PACKET_NUM 3
+
+// Link Key DB and LE Device DB using TLV on top of Flash Sector interface
+#define NVM_NUM_DEVICE_DB_ENTRIES 16
+#define NVM_NUM_LINK_KEYS 16
+
+// We don't give btstack a malloc, so use a fixed-size ATT DB.
+#define MAX_ATT_DB_SIZE 512
+
+// BTstack HAL configuration
+#define HAVE_EMBEDDED_TIME_MS
+
+// map btstack_assert onto Pico SDK assert()
+#define HAVE_ASSERT
+
+// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A).
+#define HCI_RESET_RESEND_TIMEOUT_MS 1000
+
+#define ENABLE_SOFTWARE_AES128
+#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
+
+#define HAVE_BTSTACK_STDIN
+
+// To get the audio demos working even with HCI dump at 115200, this truncates long ACL packets
+//#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100
+
+#ifdef ENABLE_CLASSIC
+#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
+#endif
+
+#endif // _PICO_BTSTACK_BTSTACK_CONFIG_H
diff --git a/src/gamepad.c b/src/gamepad.c
new file mode 100644
index 0000000..93af18f
--- /dev/null
+++ b/src/gamepad.c
@@ -0,0 +1,266 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "btstack.h"
+#include "gamepad.h"
+#include "hardware/gpio.h"
+#include "classic/rfcomm.h"
+
+#define BUTTON_PIN 13
+
+static const char hid_device_name[] = "Wirecutter Adapter";
+static const char service_name[] = "Wireless Gamepad";
+static uint8_t hid_service_buffer[4090];
+static uint8_t device_id_sdp_service_buffer[100];
+static btstack_packet_callback_registration_t hci_event_callback_registration;
+uint16_t hid_cid;
+
+const uint16_t host_max_latency = 1600;
+const uint16_t host_min_timeout = 3200;
+
+const uint8_t hid_descriptor_gamepad[] = {
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x05, // USAGE (Joystick - 0x04; Gamepad - 0x05; Multi-axis Controller - 0x08)
+ 0xa1, 0x01, // COLLECTION (Application)
+ // Buttons
+ 0x85, 0x03, // REPORT_ID (Default: 3)
+ 0x05, 0x09, // USAGE_PAGE (Button)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x25, 0x01, // LOGICAL_MAXIMUM (1)
+ 0x75, 0x01, // REPORT_SIZE (1)
+ 0x19, 0x01, // USAGE_MINIMUM (Button 1)
+ 0x29, 0x10, // USAGE_MAXIMUM (Up to 128 buttons possible)
+ 0x95, 0x10, // REPORT_COUNT (# of buttons)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+
+ // Axis
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x01, // USAGE (Pointer)
+ 0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767)
+ 0x26, 0xFF, 0x7F, // LOGICAL_MAXIMUM (+32767)
+ 0x75, 0x10, // REPORT_SIZE (16)
+ 0x95, 0x04, // REPORT_COUNT (configuration.getAxisCount())
+ 0xA1, 0x00, // COLLECTION (Physical)
+ 0x09, 0x30, // USAGE (X)
+ 0x09, 0x31, // USAGE (Y)
+ 0x09, 0x32, // USAGE (Z)
+ 0x09, 0x35, // USAGE (Rz)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0xc0, // END_COLLECTION (Physical)
+
+ // Simulation
+ 0x05, 0x02, // USAGE_PAGE (Simulation Controls)
+ 0x16, 0x00, 0x00, // LOGICAL_MINIMUM (0)
+ 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (255)
+ 0x75, 0x08, // REPORT_SIZE (8)
+ 0x95, 0x02, // REPORT_COUNT (2)
+ 0xA1, 0x00, // COLLECTION (Physical)
+ 0x09, 0xC4, // USAGE (Accelerator)
+ 0x09, 0xC5, // USAGE (Brake)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0xc0, // END_COLLECTION (Physical)
+
+ // HAT
+ 0xA1, 0x00, // COLLECTION (Physical)
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x39, // USAGE (Hat Switch)
+ 0x15, 0x01, // Logical Min (1)
+ 0x25, 0x08, // Logical Max (8)
+ 0x35, 0x00, // Physical Min (0)
+ 0x46, 0x3B, 0x01, // Physical Max (315)
+ 0x65, 0x12, // Unit (SI Rot : Ang Pos)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 1, // Report Count (4)
+ 0x81, 0x42, // Input (Data, Variable, Absolute)
+ 0xc0, // END_COLLECTION (Physical)
+ 0xc0, // END_COLLECTION (Physical)
+};
+
+gamepad_t gamepad;
+
+void request_can_send_now_event(void)
+{
+ hid_device_request_can_send_now_event(hid_cid);
+}
+
+static void send_report_joystick(){
+ uint8_t report[] = {
+ 0xa1, 0x03,
+ (gamepad.buttons), (gamepad.buttons >> 8),
+ (gamepad.x), (gamepad.x >> 8),
+ (gamepad.y), (gamepad.y >> 8),
+ (gamepad.z), (gamepad.z >> 8),
+ (gamepad.rz), (gamepad.rz >> 8),
+ gamepad.gas, gamepad.brake,
+ gamepad.hat,
+ };
+ hid_device_send_interrupt_message(hid_cid, &report[0], sizeof(report));
+}
+
+static bd_addr_t local_addr;
+static bd_addr_t event_addr;
+static uint16_t rfcomm_channel_id;
+static uint8_t rfcomm_channel_nr;
+static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
+ UNUSED(channel);
+ UNUSED(packet_size);
+ uint8_t status;
+
+
+ switch(packet_type)
+ {
+ case HCI_EVENT_PACKET:
+ switch (hci_event_packet_get_type(packet))
+ {
+ case BTSTACK_EVENT_STATE:
+ if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return;
+ gap_local_bd_addr(local_addr);
+ printf("BTstack up and running on %s.\n", bd_addr_to_str(local_addr));
+ break;
+ case GAP_EVENT_DEDICATED_BONDING_COMPLETED:
+ printf("GAP Dedicated Bonding Complete, status 0x%02x\n", packet[2]);
+ break;
+ case HCI_EVENT_PIN_CODE_REQUEST:
+ printf("Pin code request - using '0000'\n");
+ hci_event_pin_code_request_get_bd_addr(packet, event_addr);
+ gap_pin_code_response(event_addr, "0000");
+ break;
+ case HCI_EVENT_USER_CONFIRMATION_REQUEST:
+ // ssp: inform about user confirmation request
+ printf("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", hci_event_user_confirmation_request_get_numeric_value(packet));
+ printf("SSP User Confirmation Auto accept\n");
+ break;
+ case RFCOMM_EVENT_INCOMING_CONNECTION:
+ rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr);
+ rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet);
+ rfcomm_channel_id = rfcomm_event_incoming_connection_get_rfcomm_cid(packet);
+ printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr));
+ rfcomm_decline_connection(rfcomm_channel_id);
+ break;
+ case HCI_EVENT_HID_META:
+ switch (hci_event_hid_meta_get_subevent_code(packet)){
+ case HID_SUBEVENT_CONNECTION_OPENED:
+ status = hid_subevent_connection_opened_get_status(packet);
+ if (status != ERROR_CODE_SUCCESS) {
+ // outgoing connection failed
+ printf("Connection failed, status 0x%x\n", status);
+ hid_cid = 0;
+ return;
+ }
+ hid_cid = hid_subevent_connection_opened_get_hid_cid(packet);
+ printf("HID Connected\n");
+ hid_device_request_can_send_now_event(hid_cid);
+ gap_discoverable_control(0);
+ break;
+ case HID_SUBEVENT_CONNECTION_CLOSED:
+ printf("HID Disconnected\n");
+ hid_cid = 0;
+ break;
+ case HID_SUBEVENT_CAN_SEND_NOW:
+ if(hid_cid!=0){ //Solves crash when disconnecting gamepad on android
+ send_report_joystick();
+ hid_device_request_can_send_now_event(hid_cid);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void sync_irq_handler(void)
+{
+ if (gpio_get_irq_event_mask(BUTTON_PIN) & GPIO_IRQ_EDGE_FALL)
+ {
+ gpio_acknowledge_irq(BUTTON_PIN, GPIO_IRQ_EDGE_FALL);
+ hid_device_disconnect(hid_cid);
+ gap_discoverable_control(1);
+ printf("Set discoverable\n");
+ }
+}
+
+static void setup_sync_button(void)
+{
+ gpio_init(BUTTON_PIN);
+ gpio_set_dir(BUTTON_PIN, GPIO_IN);
+ gpio_pull_up(BUTTON_PIN);
+
+ gpio_set_irq_enabled(BUTTON_PIN, GPIO_IRQ_EDGE_FALL, true);
+ gpio_add_raw_irq_handler(BUTTON_PIN, &sync_irq_handler);
+ irq_set_enabled(IO_IRQ_BANK0, true);
+}
+
+int btstack_main(int argc, const char * argv[]);
+int btstack_main(int argc, const char * argv[]){
+ (void)argc;
+ (void)argv;
+
+ gap_discoverable_control(0);
+ gap_set_class_of_device(0x2508);
+ gap_set_local_name(hid_device_name);
+#ifdef ENABLE_BLE
+ gap_set_connection_parameters(0x0060, 0x0030, 0x06, 0x06, 0, 0x0048, 2, 0x0030);
+#endif
+
+ // L2CAP
+ l2cap_init();
+ // SDP Server
+ sdp_init();
+ memset(hid_service_buffer, 0, sizeof(hid_service_buffer));
+
+ uint8_t hid_virtual_cable = 0;
+ uint8_t hid_remote_wake = 1;
+ uint8_t hid_reconnect_initiate = 1;
+ uint8_t hid_normally_connectable = 1;
+
+ hid_sdp_record_t hid_params = {
+ 0x2508,
+ 0,
+ 0,
+ 1, // remote wake
+ 0,
+ 1, // normally connectable
+ 0, // boot device
+ host_max_latency, host_min_timeout,
+ 3200,
+ hid_descriptor_gamepad,
+ sizeof(hid_descriptor_gamepad),
+ hid_device_name
+ };
+
+ hid_create_sdp_record(
+ hid_service_buffer,
+ 0x10000,
+ &hid_params
+ );
+
+ hid_create_sdp_record(hid_service_buffer, sdp_create_service_record_handle(), &hid_params);
+ btstack_assert(de_get_len( hid_service_buffer) <= sizeof(hid_service_buffer));
+ sdp_register_service(hid_service_buffer);
+
+ // HID Device
+ hid_device_init(0, sizeof(hid_descriptor_gamepad), hid_descriptor_gamepad);
+
+ hci_event_callback_registration.callback = &packet_handler;
+ hci_add_event_handler(&hci_event_callback_registration);
+
+ hid_device_register_packet_handler(&packet_handler);
+
+ // turn on!
+ hci_power_control(HCI_POWER_ON);
+
+ setup_sync_button();
+
+ return 0;
+}
+/* LISTING_END */
+/* EXAMPLE_END */ \ No newline at end of file
diff --git a/src/gamepad.h b/src/gamepad.h
new file mode 100644
index 0000000..295619c
--- /dev/null
+++ b/src/gamepad.h
@@ -0,0 +1,20 @@
+#ifndef GAMEPAD_H
+#define GAMEPAD_H
+
+typedef struct
+{
+ int16_t x;
+ int16_t y;
+ int16_t z;
+ int16_t rz;
+ uint8_t gas;
+ uint8_t brake;
+ uint8_t hat;
+ uint16_t buttons;
+} gamepad_t;
+
+extern gamepad_t gamepad;
+
+void request_can_send_now_event(void);
+
+#endif \ No newline at end of file
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..be8e598
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include "pico/stdlib.h"
+#include "pico/cyw43_arch.h"
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+const uint LED_PIN = 25;
+
+int btstack_main(int argc, const char * argv[]);
+
+int main()
+{
+ if (cyw43_arch_init()) {
+ printf("failed to initialise cyw43_arch\n");
+ return -1;
+ }
+ cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
+
+ board_init();
+ tuh_init(BOARD_TUH_RHPORT);
+ btstack_main(0, NULL);
+
+ printf("Init done\n");
+
+ while(1)
+ {
+ tuh_task();
+ }
+}
diff --git a/src/tusb_config.h b/src/tusb_config.h
new file mode 100644
index 0000000..9363194
--- /dev/null
+++ b/src/tusb_config.h
@@ -0,0 +1,127 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+// change to 1 if using pico-pio-usb as host controller for raspberry rp2040
+#define CFG_TUH_RPI_PIO_USB 0
+#define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB
+#endif
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 3
+#endif
+
+// Enable Host stack
+#define CFG_TUH_ENABLED 1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// CONFIGURATION
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 512
+
+#define CFG_TUH_HUB 0 // number of supported hubs
+#define CFG_TUH_CDC 0
+#define CFG_TUH_HID 0 // typical keyboard + mouse device can have 3-4 HID interfaces
+#define CFG_TUH_MSC 0
+#define CFG_TUH_VENDOR 0
+#define CFG_TUH_XINPUT 1
+
+// max device support (excluding hub device)
+#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
+
+//------------- HID -------------//
+#define CFG_TUH_HID_EPIN_BUFSIZE 64
+#define CFG_TUH_HID_EPOUT_BUFSIZE 64
+
+//------------- CDC -------------//
+
+// Set Line Control state on enumeration/mounted:
+// DTR ( bit 0), RTS (bit 1)
+#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0x03
+
+// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
+// bit rate = 115200, 1 stop bit, no parity, 8 bit data width
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/src/xinput.c b/src/xinput.c
new file mode 100644
index 0000000..07eb01a
--- /dev/null
+++ b/src/xinput.c
@@ -0,0 +1,96 @@
+#include "tusb.h"
+#include "xinput_host.h"
+#include "gamepad.h"
+
+#define XINPUT_GAMEPAD_SHOULDER (XINPUT_GAMEPAD_LEFT_SHOULDER | XINPUT_GAMEPAD_RIGHT_SHOULDER)
+#define XINPUT_GAMEPAD_DPAD (XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_RIGHT)
+#define XINPUT_GAMEPAD_THUMB (XINPUT_GAMEPAD_LEFT_THUMB | XINPUT_GAMEPAD_RIGHT_THUMB)
+
+
+//Since https://github.com/hathach/tinyusb/pull/2222, we can add in custom vendor drivers easily
+usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count){
+ *driver_count = 1;
+ return &usbh_xinput_driver;
+}
+
+void tuh_xinput_report_received_cb(uint8_t dev_addr, uint8_t instance, xinputh_interface_t const* xid_itf, uint16_t len)
+{
+ const xinput_gamepad_t *p = &xid_itf->pad;
+
+ if (xid_itf->last_xfer_result == XFER_RESULT_SUCCESS)
+ {
+ if (xid_itf->connected && xid_itf->new_pad_data)
+ {
+ gamepad.x = p->sThumbLX;
+ gamepad.y = p->sThumbLY;
+ gamepad.z = p->sThumbRX;
+ gamepad.rz = p->sThumbRY;
+ //gamepad.rx = ((float)p->bLeftTrigger * 256) - 32767;
+ //gamepad.ry = ((float)p->bRightTrigger * 256) - 32767;
+ gamepad.gas = p->bRightTrigger;
+ gamepad.brake = p->bLeftTrigger;
+
+ uint8_t dpad = (p->wButtons & XINPUT_GAMEPAD_DPAD);
+
+ if (dpad & XINPUT_GAMEPAD_DPAD_UP)
+ {
+ if (dpad & XINPUT_GAMEPAD_DPAD_LEFT)
+ gamepad.hat = 8;
+ else if (dpad & XINPUT_GAMEPAD_DPAD_RIGHT)
+ gamepad.hat = 2;
+ else
+ gamepad.hat = 1;
+ }
+ else if (dpad & XINPUT_GAMEPAD_DPAD_DOWN)
+ {
+ if (dpad & XINPUT_GAMEPAD_DPAD_LEFT)
+ gamepad.hat = 6;
+ else if (dpad & XINPUT_GAMEPAD_DPAD_RIGHT)
+ gamepad.hat = 4;
+ else
+ gamepad.hat = 5;
+ }
+ else if (dpad & XINPUT_GAMEPAD_DPAD_LEFT)
+ gamepad.hat = 7;
+ else if (dpad & XINPUT_GAMEPAD_DPAD_RIGHT)
+ gamepad.hat = 3;
+ else
+ gamepad.hat = 0;
+
+ gamepad.buttons = \
+ (p->wButtons & (XINPUT_GAMEPAD_A | XINPUT_GAMEPAD_B)) >> 12 |
+ (p->wButtons & (XINPUT_GAMEPAD_X | XINPUT_GAMEPAD_Y)) >> 11 |
+ (p->wButtons & XINPUT_GAMEPAD_SHOULDER) >> 2 |
+ (p->wButtons & XINPUT_GAMEPAD_BACK) << 5 |
+ (p->wButtons & XINPUT_GAMEPAD_START) << 7 |
+ (p->wButtons & XINPUT_GAMEPAD_THUMB) << 7 |
+ (p->wButtons & XINPUT_GAMEPAD_GUIDE) << 2 |
+ 0;
+
+ request_can_send_now_event();
+ }
+ }
+ tuh_xinput_receive_report(dev_addr, instance);
+}
+
+void tuh_xinput_mount_cb(uint8_t dev_addr, uint8_t instance, const xinputh_interface_t *xinput_itf)
+{
+ TU_LOG1("Controller attached\n");
+ // If this is a Xbox 360 Wireless controller we need to wait for a connection packet
+ // on the in pipe before setting LEDs etc. So just start getting data until a controller is connected.
+ if (xinput_itf->type == XBOX360_WIRELESS && xinput_itf->connected == false)
+ {
+ tuh_xinput_receive_report(dev_addr, instance);
+ return;
+ }
+ tuh_xinput_set_led(dev_addr, instance, 0, true);
+ tuh_xinput_set_led(dev_addr, instance, 1, true);
+ tuh_xinput_set_rumble(dev_addr, instance, 0, 0, true);
+ tuh_xinput_receive_report(dev_addr, instance);
+}
+
+void tuh_xinput_umount_cb(uint8_t dev_addr, uint8_t instance)
+{
+ TU_LOG1("Controller removed, clearing controls\n");
+ memset(&gamepad, sizeof(gamepad), 0);
+} \ No newline at end of file