diff --git a/README.md b/README.md index e862f94..ddc4ca2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ PicoGamepadCoverter is a project designed for RP2040 or Raspberry Pi Pico and va - Read input from USB and Bluetooth controllers. - Read inputs from no USB peripherals. - Web interface to choose between modes. -- Different out modes (Dinput, Xinput, Switch, Bluetooth) +- Different out modes (Dinput, Xinput, Switch, Bluetooth, PS1/PS2) - Easy to use, no overcomplicated options. --- @@ -60,7 +60,7 @@ To get started with PicoGamepadConverter, follow the steps below: ![keyboard_connector](./docs/keyboard_connector.jpg) ![keyboard_pc](./docs/keyboard_pc.jpg) -- The conection for PS1 controllers are on **19 GPIO** for **COMMAND PIN**, **20 GPIO** for **CLOCK PIN**, **21 GPIO** for **ATTENTION PIN** and **22 GPIO** for **DATA PIN** +- The connection for PS1 controllers are on **19 GPIO** for **COMMAND PIN**, **20 GPIO** for **CLOCK PIN**, **21 GPIO** for **ATTENTION PIN** and **22 GPIO** for **DATA PIN** ![ps1_schematic](./docs/ps1_pinout.png) ![ps1_connector](./docs/ps1_connector.jpg) @@ -72,6 +72,12 @@ To get started with PicoGamepadConverter, follow the steps below: - On Bluetooth host mode you must put the mac address of your gamepad. You can get this address connecting you gamepad to a PC or a mobile phone. This address _should_ be put just once time, next time you just need choose the mode. +- The connection for PS1/PS2 device mode are on **19 GPIO** for **DATA PIN**, **20 GPIO** for **COMMAND PIN**, **21 GPIO** for **ATTENTION PIN**, **22 GPIO** for **CLOCK PIN** and **26 GPIO** for **ACKNOWLEDGE PIN**. + +- On PS1/PS2 device mode, the host connection is on **native usb female connector on the microcontroller**. + +![ps1_ps2_device_connection](./docs/PS1_PS2_pinout.png) + --- ## Modes Exist two parameter to choose on web interface, **host** and **device**. The first is the input and another one the output. @@ -97,6 +103,9 @@ Exist two parameter to choose on web interface, **host** and **device**. The fir #### Wireless MODES - Bluetooth: Simulation of a generic HID gamepad. +#### SPECIAL MODES +- PS1/PS2: Simulation of a PS1 or PS2 controller. + --- ## Features @@ -128,7 +137,7 @@ Controllers that was tested on different host modes. ### Troubleshooting -- 8bitdo controllers sometimes have problems to connect on USB or Bluetooth mode. On USB if it doesn't connect reboot the microcontroller without disconnect. +- 8bitdo controllers sometimes have problems to connect on USB or Bluetooth mode. On USB if it doesn't connect reboot the microcontroller without disconnect. If it doesn't work either, [reset pico's flash](https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#resetting-flash-memory). - On Bluetooth if it doesn't connect on first, reboot and put your gamepad on pair mode. @@ -144,6 +153,8 @@ Controllers that was tested on different host modes. - [lurk101](https://github.com/lurk101/pico-ps2kbd) for Keyboard PS/2 example. - [dotcypress](https://github.com/dotcypress/ula) for the Logic Analyzer compatible with PulseView. Was very useful for PS1 controller part. - [usedbytes](https://github.com/usedbytes/picow_ds4) for ps4 bluetooth example. +- [dangiu](https://github.com/dangiu/PicoMemcard) for `psxSPI.pio` program from PicoMemcard project. +- [TonyMacDonald1995](https://github.com/TonyMacDonald1995/DS4toPS2) for PS2 controller simulation example. ## License diff --git a/docs/PS1_PS2_pinout.png b/docs/PS1_PS2_pinout.png new file mode 100644 index 0000000..51b426e Binary files /dev/null and b/docs/PS1_PS2_pinout.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4d73873..0025040 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,7 @@ add_subdirectory(host_files/parser-lib) add_subdirectory(host_files/bluehost-lib) add_subdirectory(device_files/rndis-lib) add_subdirectory(device_files/bluehid-lib) +add_subdirectory(device_files/psx-device) add_subdirectory(flash-lib) pico_enable_stdio_uart(${target_name} 1) @@ -65,7 +66,7 @@ add_compile_definitions(PICO_W=1) target_include_directories(${target_name} PRIVATE ${CMAKE_CURRENT_LIST_DIR} device_files/switch device_files/xinput host_files) target_link_libraries(${target_name} PRIVATE pico_stdlib pico_pio_usb pico_cyw43_arch_none tinyusb_device tinyusb_host ps2kbd-lib psx-lib rndis-lib parser-lib - bluehost-lib flash-lib bluehid-lib) + bluehost-lib flash-lib bluehid-lib psx-device) pico_add_extra_outputs(${target_name}) diff --git a/src/convert_data.c b/src/convert_data.c index 613f049..986cc53 100644 --- a/src/convert_data.c +++ b/src/convert_data.c @@ -4,6 +4,7 @@ //Device #include "SwitchDescriptors.h" #include "PS3_Descriptors.h" +#include "controller_simulator.h" //Host #include "xinput_definitions.h" #include "hid_definitions.h" @@ -226,6 +227,29 @@ void new_report_fun(void *report, MODE mode_host, void *new_report, MODE mode_de device_report->r_y_axis = XINPUT_TO_HID_Y(host_report.sThumbRY); } break; + case PSX:{ + PSXInputState *device_report = new_report; + + /*new data*/ + device_report->buttons1 = ~(((host_report.wButtons & XINPUT_GAMEPAD_DPAD_UP ? 1 : 0)<buttons2 = ~(((host_report.wButtons & XINPUT_GAMEPAD_A ? 1 : 0)< 127 ? 1 : 0)< 127 ? 1 : 0)<lx = XINPUT_TO_HID_X(host_report.sThumbLX); + device_report->ly = XINPUT_TO_HID_Y(host_report.sThumbLY); + device_report->rx = XINPUT_TO_HID_X(host_report.sThumbRX); + device_report->ry = XINPUT_TO_HID_Y(host_report.sThumbRY); + + device_report->l2 = host_report.bLeftTrigger; + device_report->r2 = host_report.bRightTrigger; + } + break; default: /*XINPUT*/ //diferent structure host -> device @@ -242,7 +266,8 @@ void new_report_fun(void *report, MODE mode_host, void *new_report, MODE mode_de void set_features_from_flash(unsigned char *data){ //set features - features.set_features = data[0]; + //void incorrect data from flash + features.set_features = (data[0] > 1 ? 0 : data[0]); //num features features.num_enabled_features = data[1]; //enabled features diff --git a/src/device_files/psx-device/CMakeLists.txt b/src/device_files/psx-device/CMakeLists.txt new file mode 100644 index 0000000..fd91807 --- /dev/null +++ b/src/device_files/psx-device/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +add_library(psx-device INTERFACE) +target_include_directories(psx-device INTERFACE ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(psx-device INTERFACE pico_stdlib hardware_pio hardware_gpio) +target_sources(psx-device INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/controller_simulator.c + ${CMAKE_CURRENT_LIST_DIR}/controller_simulator.h) +pico_generate_pio_header(psx-device ${CMAKE_CURRENT_LIST_DIR}/psxSPI.pio) diff --git a/src/device_files/psx-device/controller_simulator.c b/src/device_files/psx-device/controller_simulator.c new file mode 100644 index 0000000..3897511 --- /dev/null +++ b/src/device_files/psx-device/controller_simulator.c @@ -0,0 +1,431 @@ +#include +#include +#include "pico/stdio.h" + +#include "psxSPI.pio.h" +#include "controller_simulator.h" +#include "pico/multicore.h" + + +static PIO psx_device_pio; // pio0 or pio1 +uint smCmdReader; +uint smDatWriter; + +uint offsetCmdReader; +uint offsetDatWriter; + +volatile PSXInputState *inputState; + +static uint8_t mode = MODE_DIGITAL; +static bool config = false; +static bool analogLock = false; +static uint8_t motorBytes[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static uint8_t pollConfig[4] = { 0x00, 0x00, 0x00, 0x00 }; + +void (*core1_function)() = {0}; //callback from the main + +void cancel_ack() { + pio_sm_exec(psx_device_pio, smCmdReader, pio_encode_jmp(offsetCmdReader)); // restart smCmdReader +} + +void SEND(uint8_t byte) { + write_byte_blocking(psx_device_pio, smDatWriter, byte); +} + +uint8_t RECV_CMD() { + return read_byte_blocking(psx_device_pio, smCmdReader); +} + +void initController() { + mode = MODE_DIGITAL; + config = false; + analogLock = false; + memset(motorBytes, 0xFF, sizeof(motorBytes)); + pollConfig[0] = 0xFF; + pollConfig[1] = 0xFF; + pollConfig[2] = 0x03; + pollConfig[3] = 0x00; +} + +void processRumble(uint8_t index, uint8_t value) { + /*if(index == 0 || index > 5) + return; + switch(motorBytes[index - 1]) { + case 0x00: { + outputState.rumbleRight = value ? 0xFF : 0x00; + break; + } + case 0x01: { + outputState.rumbleLeft = value; + break; + } + }*/ +} + + +//0x40 +void processPresConfig() { + if (!config) { + return; + } + uint8_t buf[7] = { 0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A }; + for(uint8_t i = 0; i < 7; i++) + { + SEND(buf[i]); + RECV_CMD(); + } +} +//0x41 +void processPollConfigStatus() { + if (!config) { + return; + } + uint8_t buf[7] = { 0x5A, (mode == MODE_DIGITAL) ? 0x00 : 0xFF, (mode == MODE_DIGITAL) ? 0x00 : 0xFF, (mode == MODE_DIGITAL) ? 0x00 : 0x03, (mode == MODE_DIGITAL) ? 0x00 : 0x00, 0x00, (mode == MODE_DIGITAL) ? 0x00 : 0x5A }; + for(uint8_t i = 0; i < 7; i++) { + SEND(buf[i]); + RECV_CMD(); + } +} +//0x42 +void processPoll() { + config = false; + switch(mode) { + case MODE_DIGITAL: { + uint8_t buf[3] = { 0x5A, inputState->buttons1, inputState->buttons2 }; + for(uint8_t i = 0; i < 3; i++) { + SEND(buf[i]); + processRumble(i, RECV_CMD()); + } + break; + } + case MODE_ANALOG: { + uint8_t buf[7] = { 0x5A,inputState->buttons1, inputState->buttons2, inputState->rx, inputState->ry, inputState->lx, inputState->ly }; + for(uint8_t i = 0; i < 7; i++) { + SEND(buf[i]); + processRumble(i, RECV_CMD()); + } + break; + } + case MODE_ANALOG_PRESSURE: { + uint8_t buf[19] = { 0x5A, inputState->buttons1, inputState->buttons2, inputState->rx, inputState->ry, inputState->lx, inputState->ly, (inputState->buttons1 & RIGHT) ? 0x00 : 0xFF, (inputState->buttons1 & LEFT) ? 0x00 : 0xFF, + (inputState->buttons1 & UP) ? 0x00 : 0xFF, (inputState->buttons1 & DOWN) ? 0x00 : 0xFF , (inputState->buttons2 & TRI) ? 0x00 : 0xFF, (inputState->buttons2 & CIR) ? 0x00 : 0xFF, + (inputState->buttons2 & X) ? 0x00 : 0xFF, (inputState->buttons2 & SQU) ? 0x00 : 0xFF, (inputState->buttons2 & L1) ? 0x00 : 0xFF, (inputState->buttons2 & R1) ? 0x00 : 0xFF, inputState->l2, inputState->r2 }; + for(uint8_t i = 0; i < 19; i++) { + SEND(buf[i]); + processRumble(i, RECV_CMD()); + } + break; + } + } +} +//0x43 +void processConfig() { + switch(config ? MODE_CONFIG : mode) { + case MODE_CONFIG: { + for(uint8_t i = 0; i < 7; i++) { + SEND((i == 0) ? 0x5A : 0x00); + if(i != 1) + RECV_CMD(); + else + config = RECV_CMD(); + } + break; + } + case MODE_DIGITAL: { + uint8_t buf[3] = { 0x5A, inputState->buttons1, inputState->buttons2 }; + for(uint8_t i = 0; i < 3; i++) { + SEND(buf[i]); + if(i != 1) + RECV_CMD(); + else + config = RECV_CMD(); + } + break; + } + case MODE_ANALOG: { + uint8_t buf[7] = { 0x5A, inputState->buttons1, inputState->buttons2, inputState->rx, inputState->ry, inputState->lx, inputState->ly }; + for(uint8_t i = 0; i < 7; i++) { + SEND(buf[i]); + if(i != 1) + RECV_CMD(); + else + config = RECV_CMD(); + } + break; + } + case MODE_ANALOG_PRESSURE: { + uint8_t buf[19] = { 0x5A, inputState->buttons1, inputState->buttons2, inputState->rx, inputState->ry, inputState->lx, inputState->ly, (inputState->buttons1 & RIGHT) ? 0xFF : 0x00, (inputState->buttons1 & LEFT) ? 0xFF : 0x00, + (inputState->buttons1 & UP) ? 0xFF : 0x00, (inputState->buttons1 & DOWN) ? 0xFF : 0x00 , (inputState->buttons2 & TRI) ? 0xFF : 0x00, (inputState->buttons2 & CIR) ? 0xFF : 0x00, + (inputState->buttons2 & X) ? 0xFF : 0x00, (inputState->buttons2 & SQU) ? 0xFF : 0x00, (inputState->buttons2 & L1) ? 0xFF : 0x00, (inputState->buttons2 & R1) ? 0xFF : 0x00, inputState->l2, inputState->r2 }; + for(uint8_t i = 0; i < 19; i++) { + SEND(buf[i]); + if(i != 1) + RECV_CMD(); + else + config = RECV_CMD(); + } + break; + } + } +} + +uint8_t detectAnalog() { + if((pollConfig[0] + pollConfig[1] + pollConfig[2] + pollConfig[3]) > 0) { + return MODE_ANALOG_PRESSURE; + } else { + return MODE_ANALOG; + } +} + +//0x44 +void processAnalogSwitch() { + if (!config) { + return; + } + for(uint8_t i = 0; i < 7; i++) { + switch(i) { + case 0: { + SEND(0x5A); + RECV_CMD(); + break; + } + case 1: { + SEND(0x00); + mode = (RECV_CMD() == 0x01) ? detectAnalog() : MODE_DIGITAL; + break; + } + case 2: { + SEND(0x00); + analogLock = (RECV_CMD() == 0x03) ? 1 : 0; + break; + } + default: { + SEND(0x00); + RECV_CMD(); + } + } + } +} +//0x45 +void processStatus() { + if (!config) { + return; + } + uint8_t buf[7] = { 0x5A, 0x03, 0x02, (mode == MODE_DIGITAL) ? 0x00 : 0x01, 0x02, 0x01, 0x00 }; + for(uint8_t i = 0; i < 7; i++) { + SEND(buf[i]); + RECV_CMD(); + } +} +//0x46 +void processConst46() { + if (!config) { + return; + } + SEND(0x5A); + RECV_CMD(); + SEND(0x00); + uint8_t offset = RECV_CMD(); + uint8_t buf[5] = { 0x00, 0x01, (offset == 0x00) ? 0x02 : 0x01, (offset == 0x00) ? 0x00 : 0x01, 0x0F }; + for(uint8_t i = 0; i < 5; i++) { + SEND(buf[i]); + RECV_CMD(); + } +} +//0x47 +void processConst47() { + if (!config) { + return; + } + uint8_t buf[7] = { 0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00 }; + for(uint8_t i = 0; i < 7; i++) { + SEND(buf[i]); + RECV_CMD(); + } +} +//0x4C +void processConst4c() { + if (!config) { + return; + } + SEND(0x5A); + RECV_CMD(); + SEND(0x00); + uint8_t offset = RECV_CMD(); + uint8_t buf[5] = { 0x00, 0x00, (offset == 0x00) ? 0x04 : 0x07, 0x00, 0x00 }; + for(uint8_t i = 0;i < 5;i++) { + SEND(buf[i]); + RECV_CMD(); + } +} +//0x4D +void processEnableRumble() { + if (!config) { + return; + } + for(uint8_t i = 0; i < 7; i++) { + if(i == 0) { + SEND(0x5A); + RECV_CMD(); + } else { + SEND(motorBytes[i - 1]); + motorBytes[i - 1] = RECV_CMD(); + } + } +} +//0x4F +void processPollConfig() { + if (!config) { + return; + } + for(int i = 0; i < 7; i++) { + if(i >= 1 && i <= 4) { + SEND((0x00)); + pollConfig[i-1] = RECV_CMD(); + } else { + SEND((i == 0 || i == 6) ? 0x5A : 0x00); + RECV_CMD(); + } + } + if((pollConfig[0] + pollConfig[1] + pollConfig[2] + pollConfig[3]) != 0) { + mode = MODE_ANALOG_PRESSURE; + } else { + mode = MODE_ANALOG; + } +} + + +void process_joy_req() { + SEND(config ? MODE_CONFIG : mode); + uint8_t cmd = RECV_CMD(); + switch(cmd) { + case(CMD_POLL): { + processPoll(); + break; + } + case(CMD_CONFIG): { + processConfig(); + break; + } + case(CMD_STATUS): { + processStatus(); + break; + } + case(CMD_CONST_46): { + processConst46(); + break; + } + case(CMD_CONST_47): { + processConst47(); + break; + } + case(CMD_CONST_4C): { + processConst4c(); + break; + } + case(CMD_POLL_CONFIG_STATUS): { + processPollConfigStatus(); + break; + } + case(CMD_ENABLE_RUMBLE): { + processEnableRumble(); + break; + } + case(CMD_POLL_CONFIG): { + processPollConfig(); + break; + } + case(CMD_PRES_CONFIG): { + processPresConfig(); + break; + } + case(CMD_ANALOG_SWITCH): { + processAnalogSwitch(); + break; + } + default: { + //printf("Unknown CMD: 0x%.2x\n", cmd); + break; + } + } +} + + +void psx_device_main() { + while(true) { + if(RECV_CMD() == 0x01) { + process_joy_req(); + } + } +} + +void __time_critical_func(restart_pio_sm)() { + pio_set_sm_mask_enabled(psx_device_pio, 1 << smCmdReader | 1 << smDatWriter, false); + pio_restart_sm_mask(psx_device_pio, 1 << smCmdReader | 1 << smDatWriter); + pio_sm_exec(psx_device_pio, smCmdReader, pio_encode_jmp(offsetCmdReader)); // restart smCmdReader PC + pio_sm_exec(psx_device_pio, smDatWriter, pio_encode_jmp(offsetDatWriter)); // restart smDatWriter PC + pio_sm_clear_fifos(psx_device_pio, smCmdReader); + pio_sm_drain_tx_fifo(psx_device_pio, smDatWriter); // drain instead of clear, so that we empty the OSR + + // resetting and launching core1 here allows to perform the reset of the transaction (e.g. when PSX polls for new MC without completing the read) + multicore_reset_core1(); + multicore_launch_core1(core1_function); + pio_enable_sm_mask_in_sync(psx_device_pio, 1 << smCmdReader | 1 << smDatWriter); +} + +void __time_critical_func(sel_isr_callback()) { + // TODO refractor comment, also is __time_critical_func needed for speed? we should test if everything works without it! + /* begin inlined call of: gpio_acknowledge_irq(PIN_SEL, GPIO_IRQ_EDGE_RISE); kept in RAM for performance reasons */ + check_gpio_param(PIN_SEL); + iobank0_hw->intr[PIN_SEL / 8] = GPIO_IRQ_EDGE_RISE << (4 * (PIN_SEL % 8)); + /* end of inlined call */ + restart_pio_sm(); +} + +void init_pio() { + + gpio_set_dir(PIN_DAT, false); + gpio_set_dir(PIN_CMD, false); + gpio_set_dir(PIN_SEL, false); + gpio_set_dir(PIN_CLK, false); + gpio_set_dir(PIN_ACK, false); + gpio_disable_pulls(PIN_DAT); + gpio_disable_pulls(PIN_CMD); + gpio_disable_pulls(PIN_SEL); + gpio_disable_pulls(PIN_CLK); + gpio_disable_pulls(PIN_ACK); + + smCmdReader = pio_claim_unused_sm(psx_device_pio, true); + smDatWriter = pio_claim_unused_sm(psx_device_pio, true); + + offsetCmdReader = pio_add_program(psx_device_pio, &cmd_reader_program); + offsetDatWriter = pio_add_program(psx_device_pio, &dat_writer_program); + + cmd_reader_program_init(psx_device_pio, smCmdReader, offsetCmdReader); + dat_writer_program_init(psx_device_pio, smDatWriter, offsetDatWriter); +} + + +void psx_device_init(uint pio, PSXInputState *data, void (*reset_pio) ()) { + + psx_device_pio = pio ? pio1 : pio0; + + /*Data from gamepad*/ + inputState = data; + + /*Core 1 function*/ + core1_function = reset_pio; + + init_pio(); + + gpio_set_slew_rate(PIN_DAT, GPIO_SLEW_RATE_FAST); + gpio_set_drive_strength(PIN_DAT, GPIO_DRIVE_STRENGTH_12MA); + + gpio_set_irq_enabled(PIN_SEL, GPIO_IRQ_EDGE_RISE, true); + //irq_set_exclusive_handler(IO_IRQ_BANK0, sel_isr_callback); // instead of normal gpio_set_irq_callback() which has slower handling + irq_add_shared_handler(IO_IRQ_BANK0, sel_isr_callback, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); + irq_set_enabled(IO_IRQ_BANK0, true); + + + printf("Init psx simulator\n"); +} diff --git a/src/device_files/psx-device/controller_simulator.h b/src/device_files/psx-device/controller_simulator.h new file mode 100644 index 0000000..0373a88 --- /dev/null +++ b/src/device_files/psx-device/controller_simulator.h @@ -0,0 +1,48 @@ +#include "pico/stdlib.h" + + +typedef struct __attribute((packed, aligned(1))) +{ + uint8_t buttons1; + uint8_t buttons2; + uint8_t lx; + uint8_t ly; + uint8_t rx; + uint8_t ry; + uint8_t l2; + uint8_t r2; +}PSXInputState; + +#define MODE_DIGITAL 0x41 +#define MODE_ANALOG 0x73 +#define MODE_ANALOG_PRESSURE 0x79 +#define MODE_CONFIG 0xF3 + +#define CMD_PRES_CONFIG 0x40 +#define CMD_POLL_CONFIG_STATUS 0x41 +#define CMD_POLL 0x42 +#define CMD_CONFIG 0x43 +#define CMD_STATUS 0x45 +#define CMD_CONST_46 0x46 +#define CMD_CONST_47 0x47 +#define CMD_CONST_4C 0x4C +#define CMD_ENABLE_RUMBLE 0x4D +#define CMD_POLL_CONFIG 0x4F +#define CMD_ANALOG_SWITCH 0x44 + +#define UP 0b00010000 +#define RIGHT 0b00100000 +#define DOWN 0b01000000 +#define LEFT 0b10000000 + +#define L2 0b00000001 +#define R2 0b00000010 +#define L1 0b00000100 +#define R1 0b00001000 +#define TRI 0b00010000 +#define CIR 0b00100000 +#define X 0b01000000 +#define SQU 0b10000000 + +void psx_device_init(uint pio, PSXInputState *data, void (*reset_pio)()); +void psx_device_main(); diff --git a/src/device_files/psx-device/psxSPI.pio b/src/device_files/psx-device/psxSPI.pio new file mode 100644 index 0000000..d5c27a2 --- /dev/null +++ b/src/device_files/psx-device/psxSPI.pio @@ -0,0 +1,153 @@ +; Author: Daniele Giuliani +; Interfaces with the modified SPI protocol used by +; PSX to communicate with controller/memory cards + +.define PUBLIC PIN_DAT 19 +.define PUBLIC PIN_CMD 20 +.define PUBLIC PIN_SEL 21 +.define PUBLIC PIN_CLK 22 +.define PUBLIC PIN_ACK 26 + +.program cmd_reader +; Input pins mapping: +; 0 - CMD +; Program description: +; Samples CMD line during rising clock edges, +; waits for SEL low signal before starting execution +sel_high: +wait 0 gpio PIN_SEL ; wait for SEL to go low +set x, 7 ; set the bit counter +.wrap_target +wait 0 gpio PIN_CLK ; wait for clock to fall +wait 1 gpio PIN_CLK ; wait for rising clock edge +in pins 1 ; sample 1 bit from CMD line +.wrap + +.program dat_reader +; Input pins mapping: +; 0 - DAT +; Program description: +; Samples DAT line during rising clock edges, +; waits for SEL pin to be low before starting execution. +; Can be used for sniffing DAT line used by other hardware. +sel_high: +wait 0 gpio PIN_SEL ; wait for SEL to go low +.wrap_target +wait 0 gpio PIN_CLK ; wait for clock to fall +wait 1 gpio PIN_CLK ; wait for rising clock edge +in pins 1 ; sample 1 bit form DAT line +.wrap + +.program dat_writer +.side_set 1 pindirs +; Set pins mapping: +; 0 - DAT +; Output pins mapping: +; 0 - DAT +; Sideset pins mapping: +; 0 - ACK +; Program description: +; Asserts ACK (signaling that the memory card must send something) +; and outputs bits to the DAT line on falling clock edges. +; waits for SEL low signal before starting execution. +; Bits are outputted by changing pin direction: +; 0 -> set pin as input (Hi-Z) -> output a one +; 1 -> set pin as output low -> output a zero +set pindirs, 0 side 0 ; release DAT line (set pin as input = Hi-Z) +wait 0 gpio PIN_SEL side 0 ; wait for SEL to go low +.wrap_target +pull side 0 ; manual pull in order to stall SM if TX fifo is empty +nop side 1 [5] ; start ACK +set x, 7 side 0 [5] ; stop ACK delay and set bit counter +sendbit: +wait 1 gpio PIN_CLK side 0 ; stop ACK and check clock is high (sideset completes even if instruction stalls) +wait 0 gpio PIN_CLK side 0 ; wait for falling clock edge +out pindirs 1 side 0 ; output 1 bit +jmp x-- sendbit side 0 ; count and send 8 bits +.wrap + +% c-sdk { +#define SLOW_CLKDIV 50 // 125MHz divided down to 2.5 MHz - we need this so we don't count clocks not meant for us on systems like the PS2 + +static inline void cmd_reader_program_init(PIO pio, uint sm, uint offset) { + pio_sm_config c = cmd_reader_program_get_default_config(offset); + + /* Pin Configuration */ + sm_config_set_in_pins(&c, PIN_CMD); + + pio_sm_set_consecutive_pindirs(pio, sm, PIN_CMD, 1, false); + pio_sm_set_consecutive_pindirs(pio, sm, PIN_SEL, 1, false); + pio_sm_set_consecutive_pindirs(pio, sm, PIN_CLK, 1, false); + + /* Fifo Configuration */ + sm_config_set_in_shift(&c, true, true, 8); // shift ISR to right, autopush every 8 bits + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); // join RX FIFO + + /* Clock configuration */ + sm_config_set_clkdiv_int_frac(&c, SLOW_CLKDIV, 0x00); + + /* Initialize SM */ + pio_sm_init(pio, sm, offset, &c); +} + +static inline void dat_reader_program_init(PIO pio, uint sm, uint offset) { + pio_sm_config c = dat_reader_program_get_default_config(offset); + + /* Pin Configuration */ + sm_config_set_in_pins(&c, PIN_DAT); + + pio_sm_set_consecutive_pindirs(pio, sm, PIN_DAT, 1, false); + pio_sm_set_consecutive_pindirs(pio, sm, PIN_SEL, 1, false); + pio_sm_set_consecutive_pindirs(pio, sm, PIN_CLK, 1, false); + + /* Fifo Configuration */ + sm_config_set_in_shift(&c, true, true, 8); // shift ISR to right, autopush every 8 bits + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); // join RX FIFO + + /* Clock configuration */ + sm_config_set_clkdiv_int_frac(&c, SLOW_CLKDIV, 0x00); + + /* Initialize SM */ + pio_sm_init(pio, sm, offset, &c); +} + +static inline void dat_writer_program_init(PIO pio, uint sm, uint offset) { + pio_sm_config c = dat_writer_program_get_default_config(offset); + + /* Pin Configuration */ + sm_config_set_out_pins(&c, PIN_DAT, 1); // set base OUT pin (DAT) + sm_config_set_set_pins(&c, PIN_DAT, 1); // set base SET pin (DAT) + sm_config_set_sideset_pins(&c, PIN_ACK); // set base SIDESET pin (ACK) + + /* configure DAT pin for open drain (output low but set as input initially) */ + pio_sm_set_pins_with_mask(pio, sm, 0, 1 << PIN_DAT); + pio_sm_set_consecutive_pindirs(pio, sm, PIN_DAT, 1, false); + pio_gpio_init(pio, PIN_DAT); + + /* configure ACK pin for open drain (output low but set as input initially) */ + pio_sm_set_pins_with_mask(pio, sm, 0, 1 << PIN_ACK); + pio_sm_set_consecutive_pindirs(pio, sm, PIN_ACK, 1, false); + pio_gpio_init(pio, PIN_ACK); + + pio_sm_set_consecutive_pindirs(pio, sm, PIN_SEL, 1, false); + pio_sm_set_consecutive_pindirs(pio, sm, PIN_CLK, 1, false); + + /* FIFO Configuration */ + sm_config_set_out_shift(&c, true, true, 8); // shift OSR to right, autopull every 8 bits + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); // join TX FIFO + + /* Clock configuration */ + sm_config_set_clkdiv_int_frac(&c, SLOW_CLKDIV, 0x00); + + /* Initialize SM */ + pio_sm_init(pio, sm, offset, &c); +} + +static inline uint8_t read_byte_blocking(PIO pio, uint sm) { + return (uint8_t) (pio_sm_get_blocking(pio, sm) >> 24); +} + +static inline void write_byte_blocking(PIO pio, uint sm, uint32_t byte) { + pio_sm_put_blocking(pio, sm, ~byte & 0xFF); // invert bits (0 become 1 setting the output to low) +} +%} \ No newline at end of file diff --git a/src/device_files/rndis-lib/packed_fs.c b/src/device_files/rndis-lib/packed_fs.c index 00359f6..a98189c 100644 --- a/src/device_files/rndis-lib/packed_fs.c +++ b/src/device_files/rndis-lib/packed_fs.c @@ -874,6 +874,8 @@ static const unsigned char v2[] = { 68, 105, 110, 112, 117, 116, 60, 47, 111, 112, 116, 105, // Dinput