diff --git a/Makefile b/Makefile index 8697db996..ce261d821 100644 --- a/Makefile +++ b/Makefile @@ -133,7 +133,8 @@ else endif ifeq ($(PADEMU),1) - IOP_OBJS += bt_pademu.o usb_pademu.o ds34usb.o ds34bt.o libds34usb.a libds34bt.a + IOP_OBJS += pademu.o btstack.o ds3usb.o ds4usb.o xbox360usb.o xboxoneusb.o \ + ds3bt.o ds4bt.o hidusb.o ds34usb.o ds34bt.o libds34usb.a libds34bt.a EE_CFLAGS += -DPADEMU EE_INCS += -Imodules/ds34bt/ee -Imodules/ds34usb/ee PADEMU_FLAGS = PADEMU=1 @@ -291,8 +292,15 @@ clean: echo " -ds34bt" $(MAKE) -C modules/ds34bt clean echo " -pademu" - $(MAKE) -C modules/pademu USE_BT=1 clean - $(MAKE) -C modules/pademu USE_USB=1 clean + $(MAKE) -C modules/pademu clean + $(MAKE) -C modules/pademu/btstack clean + $(MAKE) -C modules/pademu/ds3usb clean + $(MAKE) -C modules/pademu/ds4usb clean + $(MAKE) -C modules/pademu/xbox360usb clean + $(MAKE) -C modules/pademu/xboxoneusb clean + $(MAKE) -C modules/pademu/ds3bt clean + $(MAKE) -C modules/pademu/ds4bt clean + $(MAKE) -C modules/pademu/hidusb clean echo "-pc tools" $(MAKE) -C pc clean @@ -493,17 +501,59 @@ modules/ds34usb/iop/ds34usb.irx: modules/ds34usb/iop $(EE_ASM_DIR)ds34usb.s: modules/ds34usb/iop/ds34usb.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ ds34usb_irx -modules/pademu/bt_pademu.irx: modules/pademu - $(MAKE) -C $< USE_BT=1 +modules/pademu/pademu.irx: modules/pademu + $(MAKE) -C $< + +$(EE_ASM_DIR)pademu.s: modules/pademu/pademu.irx + $(BIN2S) $< $@ pademu_irx + +modules/pademu/btstack/btstack.irx: modules/pademu/btstack + $(MAKE) -C $< + +$(EE_ASM_DIR)btstack.s: modules/pademu/btstack/btstack.irx + $(BIN2S) $< $@ btstack_irx + +modules/pademu/ds3usb/ds3usb.irx: modules/pademu/ds3usb + $(MAKE) -C $< + +$(EE_ASM_DIR)ds3usb.s: modules/pademu/ds3usb/ds3usb.irx + $(BIN2S) $< $@ ds3usb_irx + +modules/pademu/ds4usb/ds4usb.irx: modules/pademu/ds4usb + $(MAKE) -C $< + +$(EE_ASM_DIR)ds4usb.s: modules/pademu/ds4usb/ds4usb.irx + $(BIN2S) $< $@ ds4usb_irx -$(EE_ASM_DIR)bt_pademu.s: modules/pademu/bt_pademu.irx - $(BIN2S) $< $@ bt_pademu_irx +modules/pademu/xbox360usb/xbox360usb.irx: modules/pademu/xbox360usb + $(MAKE) -C $< -modules/pademu/usb_pademu.irx: modules/pademu - $(MAKE) -C $< USE_USB=1 +$(EE_ASM_DIR)xbox360usb.s: modules/pademu/xbox360usb/xbox360usb.irx + $(BIN2S) $< $@ xbox360usb_irx + +modules/pademu/xboxoneusb/xboxoneusb.irx: modules/pademu/xboxoneusb + $(MAKE) -C $< + +$(EE_ASM_DIR)xboxoneusb.s: modules/pademu/xboxoneusb/xboxoneusb.irx + $(BIN2S) $< $@ xboxoneusb_irx + +modules/pademu/ds3bt/ds3bt.irx: modules/pademu/ds3bt + $(MAKE) -C $< + +$(EE_ASM_DIR)ds3bt.s: modules/pademu/ds3bt/ds3bt.irx + $(BIN2S) $< $@ ds3bt_irx + +modules/pademu/ds4bt/ds4bt.irx: modules/pademu/ds4bt + $(MAKE) -C $< + +$(EE_ASM_DIR)ds4bt.s: modules/pademu/ds4bt/ds4bt.irx + $(BIN2S) $< $@ ds4bt_irx + +modules/pademu/hidusb/hidusb.irx: modules/pademu/hidusb + $(MAKE) -C $< -$(EE_ASM_DIR)usb_pademu.s: modules/pademu/usb_pademu.irx - $(BIN2S) $< $@ usb_pademu_irx +$(EE_ASM_DIR)hidusb.s: modules/pademu/hidusb/hidusb.irx + $(BIN2S) $< $@ hidusb_irx $(EE_ASM_DIR)usbhdfsd.s: $(PS2SDK)/iop/irx/usbhdfsd.irx | $(EE_ASM_DIR) $(BIN2S) $< $@ usbhdfsd_irx diff --git a/ee_core/include/ee_core.h b/ee_core/include/ee_core.h index 9e86dd1a6..85e42feef 100644 --- a/ee_core/include/ee_core.h +++ b/ee_core/include/ee_core.h @@ -81,6 +81,7 @@ int EnableCheatOp; #ifdef PADEMU int EnablePadEmuOp; int PadEmuSettings; +int PadEmuModules; #endif int DisableDebug; diff --git a/ee_core/include/modules.h b/ee_core/include/modules.h index 2addacfd3..6156d5ca3 100644 --- a/ee_core/include/modules.h +++ b/ee_core/include/modules.h @@ -17,6 +17,14 @@ enum OPL_MODULE_ID { OPL_MODULE_ID_MCEMU, OPL_MODULE_ID_PADEMU, + OPL_MODULE_ID_BTSTACK, + OPL_MODULE_ID_DS3USB, + OPL_MODULE_ID_DS4USB, + OPL_MODULE_ID_XBOX360USB, + OPL_MODULE_ID_XBOXONEUSB, + OPL_MODULE_ID_DS3BT, + OPL_MODULE_ID_DS4BT, + OPL_MODULE_ID_HIDUSB, //Debugging modules OPL_MODULE_ID_UDPTTY, diff --git a/ee_core/src/iopmgr.c b/ee_core/src/iopmgr.c index 2816aa619..5e8536add 100644 --- a/ee_core/src/iopmgr.c +++ b/ee_core/src/iopmgr.c @@ -154,8 +154,38 @@ int New_Reset_Iop(const char *arg, int arglen) } #ifdef PADEMU + int btstack_loaded = 0; if (iop_reboot_count >= 2 && EnablePadEmuOp) { LoadOPLModule(OPL_MODULE_ID_PADEMU, 0, 4, (char *)&PadEmuSettings); + if (PadEmuModules & (1 << 0)) { + LoadOPLModule(OPL_MODULE_ID_DS3USB, 0, 0, NULL); + } + if (PadEmuModules & (1 << 1)) { + if (!btstack_loaded) { + LoadOPLModule(OPL_MODULE_ID_BTSTACK, 0, 0, NULL); + btstack_loaded = 1; + } + LoadOPLModule(OPL_MODULE_ID_DS3BT, 0, 0, NULL); + } + if (PadEmuModules & (1 << 2)) { + LoadOPLModule(OPL_MODULE_ID_DS4USB, 0, 0, NULL); + } + if (PadEmuModules & (1 << 3)) { + if (!btstack_loaded) { + LoadOPLModule(OPL_MODULE_ID_BTSTACK, 0, 0, NULL); + btstack_loaded = 1; + } + LoadOPLModule(OPL_MODULE_ID_DS4BT, 0, 0, NULL); + } + if (PadEmuModules & (1 << 4)) { + LoadOPLModule(OPL_MODULE_ID_XBOX360USB, 0, 0, NULL); + } + if (PadEmuModules & (1 << 5)) { + LoadOPLModule(OPL_MODULE_ID_XBOXONEUSB, 0, 0, NULL); + } + if (PadEmuModules & (1 << 6)) { + LoadOPLModule(OPL_MODULE_ID_HIDUSB, 0, 0, NULL); + } } #endif diff --git a/ee_core/src/main.c b/ee_core/src/main.c index bdf4f8fc3..b99154452 100644 --- a/ee_core/src/main.c +++ b/ee_core/src/main.c @@ -73,6 +73,7 @@ static int eecoreInit(int argc, char **argv) DPRINTF("PADEMU = %s\n", EnablePadEmuOp == 0 ? "Disabled" : "Enabled"); PadEmuSettings = _strtoi(_strtok(NULL, " ")); + PadEmuModules = _strtoi(_strtok(NULL, " ")); #endif i++; diff --git a/include/config.h b/include/config.h index 2a8c0f323..6a8f3e793 100644 --- a/include/config.h +++ b/include/config.h @@ -54,6 +54,7 @@ enum CONFIG_INDEX { #define CONFIG_ITEM_PADEMUSOURCE "$PADEMUSource" #define CONFIG_ITEM_ENABLEPADEMU "$EnablePadEmu" #define CONFIG_ITEM_PADEMUSETTINGS "$PadEmuSettings" +#define CONFIG_ITEM_PADEMUMODULES "$PadEmuModules" //OPL config keys #define CONFIG_OPL_THEME "theme" diff --git a/include/dialogs.h b/include/dialogs.h index 334cb5d96..93b1a8476 100644 --- a/include/dialogs.h +++ b/include/dialogs.h @@ -133,7 +133,7 @@ enum UI_ITEMS { PADCFG_PADEMU_SOURCE, PADCFG_PADEMU_CONFIG, PADCFG_PADEMU_ENABLE, - PADCFG_PADEMU_MODE, + PADCFG_PADEMU_MODULES_LIST, PADCFG_PADEMU_PORT, PADCFG_PADEMU_VIB, PADCFG_PADPORT, @@ -157,8 +157,9 @@ enum UI_ITEMS { PADCFG_PADEMU_MTAP_PORT, PADCFG_PADEMU_WORKAROUND, PADCFG_PADEMU_WORKAROUND_STR, + PADCFG_PADEMU_MODULES_SET, - COMPAT_MODE_BASE = 250, + COMPAT_MODE_BASE = 251, #else COMPAT_MODE_BASE = 200, #endif diff --git a/include/extern_irx.h b/include/extern_irx.h index bfef97b26..3d49565c1 100644 --- a/include/extern_irx.h +++ b/include/extern_irx.h @@ -10,8 +10,8 @@ extern int size_apemodpatch_irx; extern void *audsrv_irx; extern int size_audsrv_irx; -extern void *bt_pademu_irx; -extern int size_bt_pademu_irx; +extern void *btstack_irx; +extern int size_btstack_irx; extern void *cdvdfsv_irx; extern int size_cdvdfsv_irx; @@ -34,6 +34,18 @@ extern int size_ds34bt_irx; extern void *ds34usb_irx; extern int size_ds34usb_irx; +extern void *ds3bt_irx; +extern int size_ds3bt_irx; + +extern void *ds3usb_irx; +extern int size_ds3usb_irx; + +extern void *ds4bt_irx; +extern int size_ds4bt_irx; + +extern void *ds4usb_irx; +extern int size_ds4usb_irx; + extern void *filexio_irx; extern int size_filexio_irx; @@ -55,6 +67,9 @@ extern int size_hdd_mcemu_irx; extern void *hdpro_atad_irx; extern int size_hdpro_atad_irx; +extern void *hidusb_irx; +extern int size_hidusb_irx; + extern void *httpclient_irx; extern int size_httpclient_irx; @@ -94,6 +109,9 @@ extern int size_netman_irx; extern void *f2techioppatch_irx; extern int size_f2techioppatch_irx; +extern void *pademu_irx; +extern int size_pademu_irx; + extern void *padman_irx; extern int size_padman_irx; @@ -181,8 +199,11 @@ extern int size_usbhdfsdfsv_irx; extern void *usb_mcemu_irx; extern int size_usb_mcemu_irx; -extern void *usb_pademu_irx; -extern int size_usb_pademu_irx; +extern void *xbox360usb_irx; +extern int size_xbox360usb_irx; + +extern void *xboxoneusb_irx; +extern int size_xboxoneusb_irx; extern void *xhdd_irx; extern int size_xhdd_irx; diff --git a/include/lang.h b/include/lang.h index 9e137c585..e4a2654e5 100644 --- a/include/lang.h +++ b/include/lang.h @@ -217,10 +217,9 @@ enum _STR_IDS { _STR_PADEMU_SETTINGS, _STR_PADEMU_ENABLE, _STR_HINT_PADEMU_ENABLE, - _STR_PADEMU_MODE, - _STR_HINT_PADEMU_MODE, - _STR_DS34USB_MODE, - _STR_DS34BT_MODE, + _STR_PADEMU_MODULES, + _STR_HINT_PADEMU_MODULES, + _STR_HINT_PADEMU_MODULES_SET, _STR_PADPORT, _STR_HINT_PAD_PORT, _STR_PADEMU_PORT, diff --git a/include/opl.h b/include/opl.h index fcecdef78..9f4117a47 100644 --- a/include/opl.h +++ b/include/opl.h @@ -154,6 +154,7 @@ int showCfgPopup; #ifdef PADEMU int gEnablePadEmu; int gPadEmuSettings; +int gPadEmuModules; #endif // ------------------------------------------------------------------------------------------------------------------------ diff --git a/modules/pademu/Makefile b/modules/pademu/Makefile index fe080dcef..de5d8a9ef 100644 --- a/modules/pademu/Makefile +++ b/modules/pademu/Makefile @@ -1,19 +1,7 @@ +IOP_BIN = pademu.irx IOP_OBJS = pademu.o sys_utils.o imports.o exports.o - -ifeq ($(USE_USB),1) -IOP_BIN = usb_pademu.irx -IOP_CFLAGS += -DUSB -IOP_OBJS_DIR = obj.usb/ -IOP_OBJS += ds34usb.o -endif - -ifeq ($(USE_BT),1) -IOP_BIN = bt_pademu.irx -IOP_CFLAGS += -DBT -IOP_OBJS_DIR = obj.bt/ -IOP_OBJS += ds34bt.o -endif +IOP_OBJS_DIR = obj.pademu/ ifeq ($(VMC),1) IOP_CFLAGS += -DVMC diff --git a/modules/pademu/btstack/Makefile b/modules/pademu/btstack/Makefile new file mode 100644 index 000000000..751a40e93 --- /dev/null +++ b/modules/pademu/btstack/Makefile @@ -0,0 +1,22 @@ + +IOP_BIN = btstack.irx +IOP_OBJS = btstack.o imports.o exports.o +IOP_OBJS_DIR = obj.btstack/ + +IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%) + +IOP_CFLAGS += -Wall -fno-builtin -DUSE_SMSUTILS +IOP_LDFLAGS += -s + +all: OBJ_DIR $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +rebuild: clean all + +OBJ_DIR: + mkdir -p $(IOP_OBJS_DIR) + +include $(PS2SDK)/Defs.make +include ../Rules.make diff --git a/modules/pademu/btstack/btstack.c b/modules/pademu/btstack/btstack.c new file mode 100644 index 000000000..0eef308df --- /dev/null +++ b/modules/pademu/btstack/btstack.c @@ -0,0 +1,1125 @@ + +/* based on https://github.com/IonAgorria/Arduino-PSRemote */ +/* and https://github.com/felis/USB_Host_Shield_2.0 */ + +#include "types.h" +#include "loadcore.h" +#include "xloadcore.h" +#include "stdio.h" +#include "sifrpc.h" +#include "sysclib.h" +#include "usbd.h" +#include "usbd_macro.h" +#include "thbase.h" +#include "thsemap.h" +#include "../pademu.h" +#include "../sys_utils.h" +#include "btstack.h" + +//#define DPRINTF(x...) printf(x) +#define DPRINTF(x...) + +static int bt_probe(int devId); +static int bt_connect(int devId); +static int bt_disconnect(int devId); +static void bt_config_set(int result, int count, void *arg); + +static UsbDriver bt_driver = {NULL, NULL, "btstack", bt_probe, bt_connect, bt_disconnect}; +static bt_adapter_t bt_adp = {-1, -1, -1, -1, -1, -1}; + +static void dev_clear(int pad); +static void dev_init(); +static void disconnect_all(); + +static int bt_probe(int devId) +{ + UsbDeviceDescriptor *device = NULL; + UsbConfigDescriptor *config = NULL; + UsbInterfaceDescriptor *intf = NULL; + + DPRINTF("BTSTACK: probe: devId=%i\n", devId); + + if (bt_adp.devId != -1) { + DPRINTF("BTSTACK: Error - only one device allowed !\n"); + return 0; + } + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + if (device == NULL) { + DPRINTF("BTSTACK: Error - Couldn't get device descriptor\n"); + return 0; + } + + if (device->bNumConfigurations < 1) + return 0; + + config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG); + if (config == NULL) { + DPRINTF("BTSTACK: Error - Couldn't get configuration descriptor\n"); + return 0; + } + + if ((config->bNumInterfaces < 1) || (config->wTotalLength < (sizeof(UsbConfigDescriptor) + sizeof(UsbInterfaceDescriptor)))) { + DPRINTF("BTSTACK: Error - No interfaces available\n"); + return 0; + } + + intf = (UsbInterfaceDescriptor *)((char *)config + config->bLength); + + DPRINTF("BTSTACK: bInterfaceClass %X bInterfaceSubClass %X bInterfaceProtocol %X\n", intf->bInterfaceClass, intf->bInterfaceSubClass, intf->bInterfaceProtocol); + + if ((intf->bInterfaceClass != USB_CLASS_WIRELESS_CONTROLLER) || + (intf->bInterfaceSubClass != USB_SUBCLASS_RF_CONTROLLER) || + (intf->bInterfaceProtocol != USB_PROTOCOL_BLUETOOTH_PROG) || + (intf->bNumEndpoints < 3)) { + return 0; + } + + return 1; +} + +static int bt_connect(int devId) +{ + int epCount; + UsbDeviceDescriptor *device; + UsbConfigDescriptor *config; + UsbInterfaceDescriptor *interface; + UsbEndpointDescriptor *endpoint; + + DPRINTF("BTSTACK: connect: devId=%i\n", devId); + + if (bt_adp.devId != -1) { + DPRINTF("BTSTACK: Error - only one device allowed !\n"); + return 1; + } + + bt_adp.interruptEndp = -1; + bt_adp.inEndp = -1; + bt_adp.outEndp = -1; + + bt_adp.controlEndp = UsbOpenEndpoint(devId, NULL); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG); + interface = (UsbInterfaceDescriptor *)((char *)config + config->bLength); + + epCount = interface->bNumEndpoints - 1; + + DPRINTF("BTSTACK: Endpoint Count %d \n", epCount + 1); + + endpoint = (UsbEndpointDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_ENDPOINT); + + do { + + if (endpoint->bmAttributes == USB_ENDPOINT_XFER_BULK) { + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT && bt_adp.outEndp < 0) { + bt_adp.outEndp = UsbOpenEndpointAligned(devId, endpoint); + DPRINTF("BTSTACK: register Output endpoint id =%i addr=%02X packetSize=%i\n", bt_adp.outEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } else if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && bt_adp.inEndp < 0) { + bt_adp.inEndp = UsbOpenEndpointAligned(devId, endpoint); + DPRINTF("BTSTACK: register Input endpoint id =%i addr=%02X packetSize=%i\n", bt_adp.inEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + } else if (endpoint->bmAttributes == USB_ENDPOINT_XFER_INT) { + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && bt_adp.interruptEndp < 0) { + bt_adp.interruptEndp = UsbOpenEndpoint(devId, endpoint); + DPRINTF("BTSTACK: register Interrupt endpoint id =%i addr=%02X packetSize=%i\n", bt_adp.interruptEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + } + + endpoint = (UsbEndpointDescriptor *)((char *)endpoint + endpoint->bLength); + + } while (epCount--); + + if (bt_adp.interruptEndp < 0 || bt_adp.inEndp < 0 || bt_adp.outEndp < 0) { + DPRINTF("BTSTACK: Error - connect failed: not enough endpoints! \n"); + return -1; + } + + bt_adp.devId = devId; + + UsbSetDeviceConfiguration(bt_adp.controlEndp, config->bConfigurationValue, bt_config_set, NULL); + + return 0; +} + +static int bt_disconnect(int devId) +{ + DPRINTF("BTSTACK: disconnect: devId=%i\n", devId); + + if (bt_adp.devId != -1) { + + disconnect_all(); + + if (bt_adp.interruptEndp >= 0) + UsbCloseEndpoint(bt_adp.interruptEndp); + + if (bt_adp.inEndp >= 0) + UsbCloseEndpoint(bt_adp.inEndp); + + if (bt_adp.outEndp >= 0) + UsbCloseEndpoint(bt_adp.outEndp); + + bt_adp.devId = -1; + bt_adp.interruptEndp = -1; + bt_adp.inEndp = -1; + bt_adp.outEndp = -1; + bt_adp.controlEndp = -1; + + dev_init(); + SignalSema(bt_adp.hid_sema); + } + + return 0; +} + +// Taken from nefarius' SCPToolkit +// https://github.com/nefarius/ScpToolkit/blob/master/ScpControl/ScpControl.ini +// Valid MAC addresses used by Sony +static u8 GenuineMacAddress[][3] = + { + // Bluetooth chips by ALPS ELECTRIC CO., LTD + {0x00, 0x02, 0xC7}, + {0x00, 0x06, 0xF5}, + {0x00, 0x06, 0xF7}, + {0x00, 0x07, 0x04}, + {0x00, 0x16, 0xFE}, + {0x00, 0x19, 0xC1}, + {0x00, 0x1B, 0xFB}, + {0x00, 0x1E, 0x3D}, + {0x00, 0x21, 0x4F}, + {0x00, 0x23, 0x06}, + {0x00, 0x24, 0x33}, + {0x00, 0x26, 0x43}, + {0x00, 0xA0, 0x79}, + {0x04, 0x76, 0x6E}, + {0x04, 0x98, 0xF3}, + {0x28, 0xA1, 0x83}, + {0x34, 0xC7, 0x31}, + {0x38, 0xC0, 0x96}, + {0x60, 0x38, 0x0E}, + {0x64, 0xD4, 0xBD}, + {0xAC, 0x7A, 0x4D}, + {0xE0, 0x75, 0x0A}, + {0xE0, 0xAE, 0x5E}, + {0xFC, 0x62, 0xB9}, + // Bluetooth chips by AzureWave Technology Inc. + {0xE0, 0xB9, 0xA5}, + {0xDC, 0x85, 0xDE}, + {0xD0, 0xE7, 0x82}, + {0xB0, 0xEE, 0x45}, + {0xAC, 0x89, 0x95}, + {0xA8, 0x1D, 0x16}, + {0x94, 0xDB, 0xC9}, + {0x80, 0xD2, 0x1D}, + {0x80, 0xA5, 0x89}, + {0x78, 0x18, 0x81}, + {0x74, 0xF0, 0x6D}, + {0x74, 0xC6, 0x3B}, + {0x74, 0x2F, 0x68}, + {0x6C, 0xAD, 0xF8}, + {0x6C, 0x71, 0xD9}, + {0x60, 0x5B, 0xB4}, + {0x5C, 0x96, 0x56}, + {0x54, 0x27, 0x1E}, + {0x4C, 0xAA, 0x16}, + {0x48, 0x5D, 0x60}, + {0x44, 0xD8, 0x32}, + {0x40, 0xE2, 0x30}, + {0x38, 0x4F, 0xF0}, + {0x28, 0xC2, 0xDD}, + {0x24, 0x0A, 0x64}, + {0x1C, 0x4B, 0xD6}, + {0x08, 0xA9, 0x5A}, + {0x00, 0x25, 0xD3}, + {0x00, 0x24, 0x23}, + {0x00, 0x22, 0x43}, + {0x00, 0x15, 0xAF}, + //fake with AirohaTechnologyCorp's Chip + {0x0C, 0xFC, 0x83}}; + +static u8 link_key[] = //for ds4 authorisation + { + 0x56, 0xE8, 0x81, 0x38, 0x08, 0x06, 0x51, 0x41, + 0xC0, 0x7F, 0x12, 0xAA, 0xD9, 0x66, 0x3C, 0xCE}; + +#define REQ_HCI_OUT (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) +#define HCI_COMMAND_REQ 0 + +#define MAX_PADS 4 +#define MAX_DELAY 10 + +static u8 hci_buf[MAX_BUFFER_SIZE] __attribute((aligned(4))) = {0}; +static u8 l2cap_buf[MAX_BUFFER_SIZE + 32] __attribute((aligned(4))) = {0}; +static u8 hci_cmd_buf[MAX_BUFFER_SIZE] __attribute((aligned(4))) = {0}; +static u8 l2cap_cmd_buf[MAX_BUFFER_SIZE + 32] __attribute((aligned(4))) = {0}; + +static u8 identifier = 0; + +static bt_dev_t dev[MAX_PADS]; +static bt_paddrv_t *btpad[MAX_PADS]; + +static void hci_event_cb(int resultCode, int bytes, void *arg); +static void l2cap_event_cb(int resultCode, int bytes, void *arg); + +static int l2cap_connection_request(u16 handle, u8 rxid, u16 scid, u16 psm); +static int hci_reset(); + +static void bt_config_set(int result, int count, void *arg) +{ + PollSema(bt_adp.hid_sema); + + UsbInterruptTransfer(bt_adp.interruptEndp, hci_buf, MAX_BUFFER_SIZE, hci_event_cb, NULL); + UsbBulkTransfer(bt_adp.inEndp, l2cap_buf, MAX_BUFFER_SIZE, l2cap_event_cb, NULL); + + dev_init(); + hci_reset(); + + SignalSema(bt_adp.hid_sema); +} + +/************************************************************/ +/* HCI Commands */ +/************************************************************/ + +static int HCI_Command(int nbytes, u8 *dataptr) +{ + return UsbControlTransfer(bt_adp.controlEndp, REQ_HCI_OUT, HCI_COMMAND_REQ, 0, 0, nbytes, dataptr, NULL, NULL); +} + +static int hci_reset() +{ + hci_cmd_buf[0] = HCI_OCF_RESET; + hci_cmd_buf[1] = HCI_OGF_CTRL_BBAND; + hci_cmd_buf[2] = 0x00; // Parameter Total Length = 0 + + return HCI_Command(3, hci_cmd_buf); +} + +static int hci_write_scan_enable(u8 conf) +{ + hci_cmd_buf[0] = HCI_OCF_WRITE_SCAN_ENABLE; + hci_cmd_buf[1] = HCI_OGF_CTRL_BBAND; + hci_cmd_buf[2] = 0x01; + hci_cmd_buf[3] = conf; + + return HCI_Command(4, hci_cmd_buf); +} + +static int hci_accept_connection(u8 *bdaddr) +{ + hci_cmd_buf[0] = HCI_OCF_ACCEPT_CONNECTION; // HCI OCF = 9 + hci_cmd_buf[1] = HCI_OGF_LINK_CNTRL; // HCI OGF = 1 + hci_cmd_buf[2] = 0x07; // parameter length 7 + hci_cmd_buf[3] = *bdaddr; // 6 octet bluetooth address + hci_cmd_buf[4] = *(bdaddr + 1); + hci_cmd_buf[5] = *(bdaddr + 2); + hci_cmd_buf[6] = *(bdaddr + 3); + hci_cmd_buf[7] = *(bdaddr + 4); + hci_cmd_buf[8] = *(bdaddr + 5); + hci_cmd_buf[9] = 0x01; //switch role to (slave = 1 / master = 0) + + return HCI_Command(10, hci_cmd_buf); +} + +static int hci_remote_name(u8 *bdaddr) +{ + hci_cmd_buf[0] = HCI_OCF_REMOTE_NAME; // HCI OCF = 19 + hci_cmd_buf[1] = HCI_OGF_LINK_CNTRL; // HCI OGF = 1 + hci_cmd_buf[2] = 0x0A; // parameter length = 10 + hci_cmd_buf[3] = *bdaddr; // 6 octet bluetooth address + hci_cmd_buf[4] = *(bdaddr + 1); + hci_cmd_buf[5] = *(bdaddr + 2); + hci_cmd_buf[6] = *(bdaddr + 3); + hci_cmd_buf[7] = *(bdaddr + 4); + hci_cmd_buf[8] = *(bdaddr + 5); + hci_cmd_buf[9] = 0x01; // Page Scan Repetition Mode + hci_cmd_buf[10] = 0x00; // Reserved + hci_cmd_buf[11] = 0x00; // Clock offset - low byte + hci_cmd_buf[12] = 0x00; // Clock offset - high byte + + return HCI_Command(13, hci_cmd_buf); +} + +static int hci_reject_connection(u8 *bdaddr) +{ + hci_cmd_buf[0] = HCI_OCF_REJECT_CONNECTION; // HCI OCF = A + hci_cmd_buf[1] = HCI_OGF_LINK_CNTRL; // HCI OGF = 1 + hci_cmd_buf[2] = 0x07; // parameter length 7 + hci_cmd_buf[3] = *bdaddr; // 6 octet bluetooth address + hci_cmd_buf[4] = *(bdaddr + 1); + hci_cmd_buf[5] = *(bdaddr + 2); + hci_cmd_buf[6] = *(bdaddr + 3); + hci_cmd_buf[7] = *(bdaddr + 4); + hci_cmd_buf[8] = *(bdaddr + 5); + hci_cmd_buf[9] = 0x09; //reason max connection + + return HCI_Command(10, hci_cmd_buf); +} + +static int hci_disconnect(u16 handle) +{ + hci_cmd_buf[0] = HCI_OCF_DISCONNECT; // HCI OCF = 6 + hci_cmd_buf[1] = HCI_OGF_LINK_CNTRL; // HCI OGF = 1 + hci_cmd_buf[2] = 0x03; // parameter length = 3 + hci_cmd_buf[3] = (u8)(handle & 0xFF); // connection handle - low byte + hci_cmd_buf[4] = (u8)((handle >> 8) & 0x0F); // connection handle - high byte + hci_cmd_buf[5] = 0x13; // reason + + return HCI_Command(6, hci_cmd_buf); +} + +static int hci_change_connection_type(u16 handle) +{ + hci_cmd_buf[0] = HCI_OCF_CHANGE_CONNECTION_TYPE; + hci_cmd_buf[1] = HCI_OGF_LINK_CNTRL; + hci_cmd_buf[2] = 0x04; // parameter length 4 + hci_cmd_buf[3] = (u8)(handle & 0xFF); // connection handle - low byte + hci_cmd_buf[4] = (u8)((handle >> 8) & 0x0F); // connection handle - high byte + hci_cmd_buf[5] = 0x18; // Packet Type: 0xcc18 + hci_cmd_buf[6] = 0xcc; + + return HCI_Command(7, hci_cmd_buf); +} + +static int hci_link_key_request_reply(u8 *bdaddr) +{ + hci_cmd_buf[0] = HCI_OCF_LINK_KEY_REQUEST_REPLY; // HCI OCF = 0E + hci_cmd_buf[1] = HCI_OGF_LINK_CNTRL; // HCI OGF = 1 + hci_cmd_buf[2] = 0x06 + sizeof(link_key); // parameter length 6 + hci_cmd_buf[3] = *bdaddr; // 6 octet bluetooth address + hci_cmd_buf[4] = *(bdaddr + 1); + hci_cmd_buf[5] = *(bdaddr + 2); + hci_cmd_buf[6] = *(bdaddr + 3); + hci_cmd_buf[7] = *(bdaddr + 4); + hci_cmd_buf[8] = *(bdaddr + 5); + + mips_memcpy(&hci_cmd_buf[9], link_key, sizeof(link_key)); + + return HCI_Command(9 + sizeof(link_key), hci_cmd_buf); +} + +static void HCI_event_task(int result) +{ + int i, pad; + + if (!result) { + /* buf[0] = Event Code */ + /* buf[1] = Parameter Total Length */ + /* buf[n] = Event Parameters based on each event */ + DPRINTF("HCI event = 0x%02X \n", hci_buf[0]); + switch (hci_buf[0]) { // switch on event type + case HCI_EVENT_COMMAND_COMPLETE: + DPRINTF("HCI Command Complete = 0x%02X \n", hci_buf[3]); + DPRINTF("\tReturned = 0x%02X \n", hci_buf[5]); + if ((hci_buf[3] == HCI_OCF_RESET) && (hci_buf[4] == HCI_OGF_CTRL_BBAND)) { + if (hci_buf[5] == 0) { + hci_write_scan_enable(SCAN_ENABLE_NOINQ_ENPAG); + } else { + DelayThread(500); + hci_reset(); + } + } else if ((hci_buf[3] == HCI_OCF_WRITE_SCAN_ENABLE) && (hci_buf[4] == HCI_OGF_CTRL_BBAND)) { + if (hci_buf[5] != 0) { + DelayThread(500); + hci_reset(); + } + } + break; + + case HCI_EVENT_COMMAND_STATUS: + if (hci_buf[2]) { // show status on serial if not OK + DPRINTF("HCI Command Failed: \n"); + DPRINTF("\t Status = 0x%02X \n", hci_buf[2]); + DPRINTF("\t Command_OpCode(OGF) = 0x%02X \n", ((hci_buf[5] & 0xFC) >> 2)); + DPRINTF("\t Command_OpCode(OCF) = 0x%02X 0x%02X \n", (hci_buf[5] & 0x03), hci_buf[4]); + } + break; + + case HCI_EVENT_CONNECT_COMPLETE: + DPRINTF("HCI event Connect Complete: \n"); + if (!hci_buf[2]) { // check if connected OK + DPRINTF("\t Connection_Handle 0x%02X \n", hci_buf[3] | ((hci_buf[4] & 0x0F) << 8)); + DPRINTF("\t Requested by BD_ADDR: \n\t"); + for (i = 0; i < 6; i++) { + DPRINTF("0x%02X", hci_buf[5 + i]); + if (i < 5) + DPRINTF(":"); + } + DPRINTF("\n"); + for (i = 0; i < MAX_PADS; i++) { + if (strncmp(dev[i].bdaddr, hci_buf + 5, 6) == 0) { + // store the handle for the ACL connection + dev[i].hci_handle = hci_buf[3] | ((hci_buf[4] & 0x0F) << 8); + break; + } + } + if (i == MAX_PADS) { + break; + } + btdev_status_set(BTDEV_STATE_CONNECTED, i); + hci_remote_name(dev[i].bdaddr); + } else { + DPRINTF("\t Error 0x%02X \n", hci_buf[2]); + } + break; + + case HCI_EVENT_NUM_COMPLETED_PKT: + DPRINTF("HCI Number Of Completed Packets Event: \n"); + DPRINTF("\t Number_of_Handles = 0x%02X \n", hci_buf[2]); + for (i = 0; i < hci_buf[2]; i++) { + DPRINTF("\t Connection_Handle = 0x%04X \n", (hci_buf[3 + i] | ((hci_buf[4 + i] & 0x0F) << 8))); + } + break; + + case HCI_EVENT_QOS_SETUP_COMPLETE: + break; + + case HCI_EVENT_DISCONN_COMPLETE: + DPRINTF("HCI Disconnection Complete Event: \n"); + DPRINTF("\t Status = 0x%02X \n", hci_buf[2]); + DPRINTF("\t Connection_Handle = 0x%04X \n", (hci_buf[3] | ((hci_buf[4] & 0x0F) << 8))); + DPRINTF("\t Reason = 0x%02X \n", hci_buf[5]); + for (i = 0; i < MAX_PADS; i++) { //detect pad + if (dev[i].hci_handle == (hci_buf[3] | ((hci_buf[4] & 0x0F) << 8))) { + break; + } + } + if (i == MAX_PADS) { + break; + } + pademu_disconnect(&dev[i].dev); + dev_clear(i); + break; + + case HCI_EVENT_AUTHENTICATION_COMPLETE: + DPRINTF("HCI Authentication Complete Event: \n"); + DPRINTF("\t Status = 0x%02X \n", hci_buf[2]); + DPRINTF("\t Connection_Handle = 0x%04X \n", (hci_buf[3] | ((hci_buf[4] & 0x0F) << 8))); + if (!hci_buf[2]) { + DPRINTF("\t Success \n"); + } else { + DPRINTF("\t Failed \n"); + } + break; + + case HCI_EVENT_REMOTE_NAME_COMPLETE: + DPRINTF("HCI Remote Name Requested Complete Event: \n"); + DPRINTF("\t Status = 0x%02X \n", hci_buf[2]); + if (!hci_buf[2]) { + for (i = 0; i < MAX_PADS; i++) { + if (strncmp(dev[i].bdaddr, hci_buf + 3, 6) == 0) { + break; + } + } + if (i == MAX_PADS) { + break; + } + for (pad = 0; pad < MAX_PADS; pad++) { + if (btpad[pad] != NULL && btpad[pad]->pad_connect(&dev[i].data, hci_buf + 9, 32 - 9)) { + break; + } + } + if (pad == MAX_PADS) { + break; + } + dev[i].pad = btpad[pad]; + hci_change_connection_type(dev[i].hci_handle); + if (dev[i].pad->control_dcid == 0x0070) { + identifier++; + l2cap_connection_request(dev[i].hci_handle, identifier, dev[i].pad->control_dcid, L2CAP_PSM_CTRL); + } + } + break; + + case HCI_EVENT_ENCRYPTION_CHANGE: + DPRINTF("HCI Encryption Change Event: \n"); + DPRINTF("\t Status = 0x%02X \n", hci_buf[2]); + DPRINTF("\t Connection_Handle = 0x%04X \n", (hci_buf[3] | ((hci_buf[4] & 0x0F) << 8))); + DPRINTF("\t Encryption_Enabled = 0x%02X \n", hci_buf[5]); + break; + + case HCI_EVENT_CONNECT_REQUEST: + DPRINTF("HCI Connection Requested by BD_ADDR: \n\t"); + for (i = 0; i < 6; i++) { + DPRINTF("0x%02X", hci_buf[2 + i]); + if (i < 5) + DPRINTF(":"); + } + DPRINTF("\n\t Link = 0x%02X \n", hci_buf[11]); + DPRINTF("\t Class = 0x%02X 0x%02X 0x%02X \n", hci_buf[8], hci_buf[9], hci_buf[10]); + for (i = 0; i < MAX_PADS; i++) { //find free slot + if (dev[i].pad == NULL && !btdev_status_check(BTDEV_STATE_RUNNING, i)) { + if (btdev_status_check(BTDEV_STATE_CONNECTED, i)) { + if (btdev_status_check(BTDEV_STATE_DISCONNECTING, i)) //if we're waiting for hci disconnect event + continue; + else + hci_disconnect(dev[i].hci_handle); //try to disconnect + } + break; + } + } + if (i == MAX_PADS) { //no free slot + DPRINTF("\t No free slot! \n"); + hci_reject_connection(hci_buf + 2); + break; + } + pad = i; + mips_memcpy(dev[pad].bdaddr, hci_buf + 2, 6); + dev[pad].data.fix = 1; + for (i = 0; i < sizeof(GenuineMacAddress) / 3; i++) { //check if ds3 is genuine + if (dev[pad].bdaddr[5] == GenuineMacAddress[i][0] && + dev[pad].bdaddr[4] == GenuineMacAddress[i][1] && + dev[pad].bdaddr[3] == GenuineMacAddress[i][2]) { + dev[pad].data.fix = 0; + break; + } + } + btdev_status_clear(BTDEV_STATE_CONNECTED, pad); + btdev_status_clear(BTDEV_STATE_RUNNING, pad); + btdev_status_clear(BTDEV_STATE_DISCONNECTING, pad); + hci_accept_connection(dev[pad].bdaddr); + break; + + case HCI_EVENT_ROLE_CHANGED: + DPRINTF("HCI Role Change Event: \n"); + DPRINTF("\t Status = 0x%02X \n", hci_buf[2]); + DPRINTF("\t BD_ADDR: "); + for (i = 0; i < 6; i++) { + DPRINTF("0x%02X", hci_buf[3 + i]); + if (i < 5) + DPRINTF(":"); + } + DPRINTF("\n\t Role 0x%02X \n", hci_buf[9]); + break; + + case HCI_EVENT_MAX_SLOT_CHANGE: + DPRINTF("HCI Max Slot Change Event: \n"); + DPRINTF("\t Connection_Handle = 0x%x \n", (hci_buf[2] | ((hci_buf[3] & 0x0F) << 8))); + DPRINTF("\t LMP Max Slots = 0x%x \n", hci_buf[5]); + break; + + case HCI_EVENT_PIN_CODE_REQUEST: + DPRINTF("HCI Pin Code Request Event \n"); + break; + + case HCI_EVENT_LINK_KEY_REQUEST: + DPRINTF("HCI Link Key Request Event by BD_ADDR: \n\t"); + for (i = 0; i < 6; i++) { + DPRINTF("0x%02X", hci_buf[2 + i]); + if (i < 5) + DPRINTF(":"); + } + DPRINTF("\n"); + hci_link_key_request_reply(hci_buf + 2); + break; + + case HCI_EVENT_CHANGED_CONNECTION_TYPE: + DPRINTF("Packet Type Changed STATUS: 0x%x \n", hci_buf[2]); + DPRINTF("\t Type = %x \n", (hci_buf[5] | (hci_buf[6] << 8))); + break; + + case HCI_EVENT_PAGE_SR_CHANGED: + break; + + default: + DPRINTF("Unmanaged Event: 0x%02X \n", hci_buf[0]); + break; + } // switch (buf[0]) + } +} + +static void dev_init() +{ + int i; + + for (i = 0; i < MAX_PADS; i++) { + dev_clear(i); + } + identifier = 0; +} + +static void disconnect_all() +{ + int i; + for (i = 0; i < MAX_PADS; i++) { + pademu_disconnect(&dev[i].dev); + } +} + +static void hci_event_cb(int resultCode, int bytes, void *arg) +{ + PollSema(bt_adp.hid_sema); + HCI_event_task(resultCode); + UsbInterruptTransfer(bt_adp.interruptEndp, hci_buf, MAX_BUFFER_SIZE, hci_event_cb, NULL); + SignalSema(bt_adp.hid_sema); +} + +/************************************************************/ +/* L2CAP Commands */ +/************************************************************/ + +static int L2CAP_Command(u16 handle, u16 scid, u8 *data, u8 length) +{ + l2cap_cmd_buf[0] = (u8)(handle & 0xff); // HCI handle with PB,BC flag + l2cap_cmd_buf[1] = (u8)(((handle >> 8) & 0x0f) | 0x20); + l2cap_cmd_buf[2] = (u8)((4 + length) & 0xff); // HCI ACL total data length + l2cap_cmd_buf[3] = (u8)((4 + length) >> 8); + l2cap_cmd_buf[4] = (u8)(length & 0xff); // L2CAP header: Length + l2cap_cmd_buf[5] = (u8)(length >> 8); + l2cap_cmd_buf[6] = (u8)(scid & 0xff); // L2CAP header: Channel ID + l2cap_cmd_buf[7] = (u8)(scid >> 8); + + mips_memcpy(&l2cap_cmd_buf[8], data, length); + + // output on endpoint 2 + return UsbBulkTransfer(bt_adp.outEndp, l2cap_cmd_buf, (8 + length), NULL, NULL); +} + +static int l2cap_connection_request(u16 handle, u8 rxid, u16 scid, u16 psm) +{ + u8 cmd_buf[8]; + + cmd_buf[0] = L2CAP_CMD_CONNECTION_REQUEST; // Code + cmd_buf[1] = rxid; // Identifier + cmd_buf[2] = 0x04; // Length + cmd_buf[3] = 0x00; + cmd_buf[4] = (u8)(psm & 0xff); // PSM + cmd_buf[5] = (u8)(psm >> 8); + cmd_buf[6] = (u8)(scid & 0xff); // Source CID (PS Remote) + cmd_buf[7] = (u8)(scid >> 8); + + return L2CAP_Command(handle, 0x0001, cmd_buf, 8); +} + +static int l2cap_connection_response(u16 handle, u8 rxid, u16 dcid, u16 scid, u8 result) +{ + u8 cmd_buf[12]; + + cmd_buf[0] = L2CAP_CMD_CONNECTION_RESPONSE; // Code + cmd_buf[1] = rxid; // Identifier + cmd_buf[2] = 0x08; // Length + cmd_buf[3] = 0x00; + cmd_buf[4] = (u8)(dcid & 0xff); // Destination CID (Our) + cmd_buf[5] = (u8)(dcid >> 8); + cmd_buf[6] = (u8)(scid & 0xff); // Source CID (PS Remote) + cmd_buf[7] = (u8)(scid >> 8); + cmd_buf[8] = result; // Result + cmd_buf[9] = 0x00; + cmd_buf[10] = 0x00; // Status + cmd_buf[11] = 0x00; + + if (result != 0) + cmd_buf[10] = 0x01; // Authentication pending + + return L2CAP_Command(handle, 0x0001, cmd_buf, 12); +} + +static int l2cap_config_request(u16 handle, u8 rxid, u16 dcid) +{ + u8 cmd_buf[12]; + + cmd_buf[0] = L2CAP_CMD_CONFIG_REQUEST; // Code + cmd_buf[1] = rxid; // Identifier + cmd_buf[2] = 0x08; // Length + cmd_buf[3] = 0x00; + cmd_buf[4] = (u8)(dcid & 0xff); // Destination CID + cmd_buf[5] = (u8)(dcid >> 8); + cmd_buf[6] = 0x00; // Flags + cmd_buf[7] = 0x00; + cmd_buf[8] = 0x01; // Config Opt: type = MTU (Maximum Transmission Unit) + cmd_buf[9] = 0x02; // Config Opt: length + //cmd_buf[10] = 0x96; // Config Opt: data + //cmd_buf[11] = 0x00; + + //this setting disable hid cmd reports from ds3 + cmd_buf[10] = 0xFF; // Config Opt: data + cmd_buf[11] = 0xFF; + + return L2CAP_Command(handle, 0x0001, cmd_buf, 12); +} + +static int l2cap_config_response(u16 handle, u8 rxid, u16 scid) +{ + u8 cmd_buf[14]; + + cmd_buf[0] = L2CAP_CMD_CONFIG_RESPONSE; // Code + cmd_buf[1] = rxid; // Identifier + cmd_buf[2] = 0x0A; // Length + cmd_buf[3] = 0x00; + cmd_buf[4] = (u8)(scid & 0xff); // Source CID + cmd_buf[5] = (u8)(scid >> 8); + cmd_buf[6] = 0x00; // Result + cmd_buf[7] = 0x00; + cmd_buf[8] = 0x00; // Config + cmd_buf[9] = 0x00; + cmd_buf[10] = 0x01; // Config + cmd_buf[11] = 0x02; + cmd_buf[12] = 0xA0; + cmd_buf[13] = 0x02; + + return L2CAP_Command(handle, 0x0001, cmd_buf, 14); +} + +static int l2cap_disconnection_request(u16 handle, u8 rxid, u16 dcid, u16 scid) +{ + u8 cmd_buf[8]; + + cmd_buf[0] = L2CAP_CMD_DISCONNECT_REQUEST; // Code + cmd_buf[1] = rxid; // Identifier + cmd_buf[2] = 0x04; // Length + cmd_buf[3] = 0x00; + cmd_buf[4] = (u8)(dcid & 0xff); // Destination CID + cmd_buf[5] = (u8)(dcid >> 8); + cmd_buf[6] = (u8)(scid & 0xff); // Source CID + cmd_buf[7] = (u8)(scid >> 8); + + return L2CAP_Command(handle, 0x0001, cmd_buf, 8); +} + +static int l2cap_disconnection_response(u16 handle, u8 rxid, u16 scid, u16 dcid) +{ + u8 cmd_buf[8]; + + cmd_buf[0] = L2CAP_CMD_DISCONNECT_RESPONSE; // Code + cmd_buf[1] = rxid; // Identifier + cmd_buf[2] = 0x04; // Length + cmd_buf[3] = 0x00; + cmd_buf[4] = (u8)(dcid & 0xff); // Destination CID + cmd_buf[5] = (u8)(dcid >> 8); + cmd_buf[6] = (u8)(scid & 0xff); // Source CID + cmd_buf[7] = (u8)(scid >> 8); + + return L2CAP_Command(handle, 0x0001, cmd_buf, 8); +} + +#define CMD_DELAY 2 + +static int L2CAP_event_task(int result, int bytes) +{ + int pad = -1; + + if (!result) { + for (pad = 0; pad < MAX_PADS; pad++) { + if (l2cap_handle_ok(dev[pad].hci_handle)) + break; + } + + if (pad == MAX_PADS) { + DPRINTF("L2CAP Wrong Handle = 0x%04X\n", ((l2cap_buf[0] | (l2cap_buf[1] << 8)))); + return pad; + } + + if (l2cap_handle_ok(dev[pad].hci_handle)) { + if (l2cap_control_channel) { + DPRINTF("L2CAP Signaling Command = 0x%02X, pad %d \n", l2cap_buf[8], pad); + + switch (l2cap_buf[8]) { + case L2CAP_CMD_COMMAND_REJECT: + DPRINTF("Command Reject ID = 0x%02X \n", l2cap_buf[9]); + DPRINTF("\t Reason = 0x%04X \n", (l2cap_buf[12] | (l2cap_buf[13] << 8))); + DPRINTF("\t DATA = 0x%04X \n", (l2cap_buf[14] | (l2cap_buf[15] << 8))); + break; + + case L2CAP_CMD_CONNECTION_REQUEST: + DPRINTF("Connection Request ID = 0x%02X \n", l2cap_buf[9]); + DPRINTF("\t PSM = 0x%04X \n", (l2cap_buf[12] | (l2cap_buf[13] << 8))); + DPRINTF("\t SCID = 0x%04X \n", (l2cap_buf[14] | (l2cap_buf[15] << 8))); + + if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == L2CAP_PSM_CTRL) { + dev[pad].control_scid = l2cap_buf[14] | (l2cap_buf[15] << 8); + l2cap_connection_response(dev[pad].hci_handle, l2cap_buf[9], dev[pad].pad->control_dcid, dev[pad].control_scid, PENDING); + DelayThread(CMD_DELAY); + l2cap_connection_response(dev[pad].hci_handle, l2cap_buf[9], dev[pad].pad->control_dcid, dev[pad].control_scid, SUCCESSFUL); + DelayThread(CMD_DELAY); + identifier++; + l2cap_config_request(dev[pad].hci_handle, identifier, dev[pad].control_scid); + } else if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == L2CAP_PSM_INTR) { + dev[pad].interrupt_scid = l2cap_buf[14] | (l2cap_buf[15] << 8); + l2cap_connection_response(dev[pad].hci_handle, l2cap_buf[9], dev[pad].pad->interrupt_dcid, dev[pad].interrupt_scid, PENDING); + DelayThread(CMD_DELAY); + l2cap_connection_response(dev[pad].hci_handle, l2cap_buf[9], dev[pad].pad->interrupt_dcid, dev[pad].interrupt_scid, SUCCESSFUL); + DelayThread(CMD_DELAY); + identifier++; + l2cap_config_request(dev[pad].hci_handle, identifier, dev[pad].interrupt_scid); + } + break; + + case L2CAP_CMD_CONNECTION_RESPONSE: + DPRINTF("Connection Response ID = 0x%02X \n", l2cap_buf[9]); + DPRINTF("\t PSM = 0x%04X \n", (l2cap_buf[12] | (l2cap_buf[13] << 8))); + DPRINTF("\t SCID = 0x%04X \n", (l2cap_buf[14] | (l2cap_buf[15] << 8))); + DPRINTF("\t RESULT = 0x%04X \n", (l2cap_buf[16] | (l2cap_buf[17] << 8))); + + if (((l2cap_buf[16] | (l2cap_buf[17] << 8)) == 0x0000) && ((l2cap_buf[18] | (l2cap_buf[19] << 8)) == 0x0000)) { + if ((l2cap_buf[14] | (l2cap_buf[15] << 8)) == dev[pad].pad->control_dcid) { + dev[pad].control_scid = l2cap_buf[12] | (l2cap_buf[13] << 8); + identifier++; + l2cap_config_request(dev[pad].hci_handle, identifier, dev[pad].control_scid); + } else if ((l2cap_buf[14] | (l2cap_buf[15] << 8)) == dev[pad].pad->interrupt_dcid) { + dev[pad].interrupt_scid = l2cap_buf[12] | (l2cap_buf[13] << 8); + identifier++; + l2cap_config_request(dev[pad].hci_handle, identifier, dev[pad].interrupt_scid); + } + } + break; + + case L2CAP_CMD_CONFIG_REQUEST: + DPRINTF("Configuration Request ID = 0x%02X \n", l2cap_buf[9]); + DPRINTF("\t LEN = 0x%04X \n", (l2cap_buf[10] | (l2cap_buf[11] << 8))); + DPRINTF("\t SCID = 0x%04X \n", (l2cap_buf[12] | (l2cap_buf[13] << 8))); + DPRINTF("\t FLAG = 0x%04X \n", (l2cap_buf[14] | (l2cap_buf[15] << 8))); + + if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == dev[pad].pad->control_dcid) { + l2cap_config_response(dev[pad].hci_handle, l2cap_buf[9], dev[pad].control_scid); + } else if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == dev[pad].pad->interrupt_dcid) { + l2cap_config_response(dev[pad].hci_handle, l2cap_buf[9], dev[pad].interrupt_scid); + } + break; + + case L2CAP_CMD_CONFIG_RESPONSE: + DPRINTF("Configuration Response ID = 0x%02X \n", l2cap_buf[9]); + DPRINTF("\t LEN = 0x%04X \n", (l2cap_buf[10] | (l2cap_buf[11] << 8))); + DPRINTF("\t SCID = 0x%04X \n", (l2cap_buf[12] | (l2cap_buf[13] << 8))); + DPRINTF("\t FLAG = 0x%04X \n", (l2cap_buf[14] | (l2cap_buf[15] << 8))); + DPRINTF("\t RESULT = 0x%04X \n", (l2cap_buf[16] | (l2cap_buf[17] << 8))); + + if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == dev[pad].pad->control_dcid) { + if (dev[pad].pad->interrupt_dcid == 0x0071) { + identifier++; + l2cap_connection_request(dev[pad].hci_handle, identifier, dev[pad].pad->interrupt_dcid, L2CAP_PSM_INTR); + } + } else if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == dev[pad].pad->interrupt_dcid) { + dev[pad].pad->pad_init(&dev[pad].data, dev[pad].dev.id); + pademu_connect(&dev[pad].dev); + btdev_status_set(BTDEV_STATE_RUNNING, pad); + } + break; + + case L2CAP_CMD_DISCONNECT_REQUEST: + DPRINTF("Disconnect Request SCID = 0x%04X \n", (l2cap_buf[12] | (l2cap_buf[13] << 8))); + + if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == dev[pad].pad->control_dcid) { + btdev_status_set(BTDEV_STATE_DISCONNECTING, pad); + l2cap_disconnection_response(dev[pad].hci_handle, l2cap_buf[9], dev[pad].pad->control_dcid, dev[pad].control_scid); + } else if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == dev[pad].pad->interrupt_dcid) { + btdev_status_set(BTDEV_STATE_DISCONNECTING, pad); + l2cap_disconnection_response(dev[pad].hci_handle, l2cap_buf[9], dev[pad].pad->interrupt_dcid, dev[pad].interrupt_scid); + } + break; + + case L2CAP_CMD_DISCONNECT_RESPONSE: + DPRINTF("Disconnect Response SCID = 0x%04X \n", (l2cap_buf[12] | (l2cap_buf[13] << 8))); + + if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == dev[pad].control_scid) { + btdev_status_set(BTDEV_STATE_DISCONNECTING, pad); + hci_disconnect(dev[pad].hci_handle); + } else if ((l2cap_buf[12] | (l2cap_buf[13] << 8)) == dev[pad].interrupt_scid) { + btdev_status_set(BTDEV_STATE_DISCONNECTING, pad); + identifier++; + l2cap_disconnection_request(dev[pad].hci_handle, identifier, dev[pad].control_scid, dev[pad].pad->control_dcid); + } + break; + + default: + break; + } + } else if (l2cap_interrupt_channel) { + dev[pad].pad->pad_read_report(&dev[pad].data, l2cap_buf, bytes); + } else if (l2cap_command_channel) { + DPRINTF("HID command status 0x%02X \n", l2cap_buf[8]); + pad = MAX_PADS; + } + } // acl_handle_ok + } // !rcode + + return pad; +} + +static void l2cap_event_cb(int resultCode, int bytes, void *arg) +{ + int ret; + + PollSema(bt_adp.hid_sema); + + ret = L2CAP_event_task(resultCode, bytes); + + if (ret < MAX_PADS) { + if (btdev_status_check(BTDEV_STATE_RUNNING, ret)) { + if (btdev_status_check(BTDEV_STATE_DISCONNECT_REQUEST, ret)) { + if (!dev[ret].data.fix) { + identifier++; + l2cap_disconnection_request(dev[ret].hci_handle, identifier, dev[ret].interrupt_scid, dev[ret].pad->interrupt_dcid); + } else { + hci_disconnect(dev[ret].hci_handle); + } + btdev_status_clear(BTDEV_STATE_DISCONNECT_REQUEST, ret); + } else if (dev[ret].data.update_rum) { + dev[ret].pad->pad_rumble(&dev[ret].data); + dev[ret].data.update_rum = 0; + } + } else { + if (!dev[ret].data.fix) { + DelayThread(42000); //fix for some bt adapters + } + } + } + + UsbBulkTransfer(bt_adp.inEndp, l2cap_buf, MAX_BUFFER_SIZE + 23, l2cap_event_cb, arg); + SignalSema(bt_adp.hid_sema); +} + +void btstack_register(bt_paddrv_t *pad) +{ + int i; + for (i = 0; i < MAX_PADS; i++) { + if (btpad[i] == NULL) { + btpad[i] = pad; + break; + } + } +} + +void btstack_unregister(bt_paddrv_t *pad) +{ + int i; + for (i = 0; i < MAX_PADS; i++) { + if (btpad[i] == pad) { + btpad[i] = NULL; + break; + } + } +} + +int btstack_hid_command(bt_paddrv_t *pad, u8 *data, u8 length, int id) +{ + int i; + for (i = 0; i < MAX_PADS; i++) { + if (dev[i].pad == pad && dev[i].data.id == id) { + break; + } + } + + if (i == MAX_PADS) { + return 0; + } + + return L2CAP_Command(dev[i].hci_handle, dev[i].control_scid, data, length); +} + +void btstack_exit(int mode) +{ + int pad; + + if (bt_adp.devId != -1) { + for (pad = 0; pad < MAX_PADS; pad++) { + WaitSema(bt_adp.hid_sema); + btdev_status_set(BTDEV_STATE_DISCONNECT_REQUEST, pad); + SignalSema(bt_adp.hid_sema); + while (1) { + DelayThread(500); + WaitSema(bt_adp.hid_sema); + if (!btdev_status_check(BTDEV_STATE_RUNNING, pad)) { + SignalSema(bt_adp.hid_sema); + break; + } + SignalSema(bt_adp.hid_sema); + } + } + DelayThread(1000000); + bt_disconnect(bt_adp.devId); + } +} + +int btpad_get_data(u8 *dst, int size, int port) +{ + int ret; + + WaitSema(bt_adp.hid_sema); + + //DPRINTF("BTSTACK: Get data\n"); + mips_memcpy(dst, dev[port].data.data, size); + ret = dev[port].data.analog_btn & 1; + + SignalSema(bt_adp.hid_sema); + + return ret; +} + +void btpad_set_rumble(u8 lrum, u8 rrum, int port) +{ + WaitSema(bt_adp.hid_sema); + + //DPRINTF("BTSTACK: Rumble\n"); + dev[port].data.update_rum = 1; + dev[port].data.lrum = lrum; + dev[port].data.rrum = rrum; + + SignalSema(bt_adp.hid_sema); +} + +void btpad_set_mode(int mode, int lock, int port) +{ + WaitSema(bt_adp.hid_sema); + + //DPRINTF("BTSTACK: Set mode\n"); + if (lock == 3) + dev[port].data.analog_btn = 3; + else + dev[port].data.analog_btn = mode; + + SignalSema(bt_adp.hid_sema); +} + +static void dev_clear(int pad) +{ + dev[pad].pad = NULL; + dev[pad].hci_handle = 0x0FFF; + dev[pad].control_scid = 0; + dev[pad].interrupt_scid = 0; + dev[pad].data.lrum = 0; + dev[pad].data.rrum = 0; + dev[pad].data.update_rum = 1; + dev[pad].data.data[0] = 0xFF; + dev[pad].data.data[1] = 0xFF; + dev[pad].data.analog_btn = 0; + dev[pad].status = BTDEV_STATE_USB_CONFIGURED; + dev[pad].dev.id = pad; + dev[pad].dev.pad_get_data = btpad_get_data; + dev[pad].dev.pad_set_rumble = btpad_set_rumble; + dev[pad].dev.pad_set_mode = btpad_set_mode; + + mips_memset(&dev[pad].data.data[2], 0x7F, 4); + mips_memset(&dev[pad].data.data[6], 0x00, 12); + mips_memset(dev[pad].bdaddr, 0, 6); +} + +extern struct irx_export_table _exp_btstack; + +int _start(int argc, char *argv[]) +{ + int ret; + + dev_init(); + + if (RegisterLibraryEntries(&_exp_btstack) != 0) { + return MODULE_NO_RESIDENT_END; + } + + SetRebootTimeLibraryHandlingMode(&_exp_btstack, 2); + + bt_adp.hid_sema = CreateMutex(IOP_MUTEX_UNLOCKED); + + if (bt_adp.hid_sema < 0) { + DPRINTF("BTSTACK: Failed to allocate semaphore.\n"); + return MODULE_NO_RESIDENT_END; + } + + ret = UsbRegisterDriver(&bt_driver); + + if (ret != USB_RC_OK) { + DPRINTF("BTSTACK: Error registering USB devices: %02X\n", ret); + return MODULE_NO_RESIDENT_END; + } + + return MODULE_RESIDENT_END; +} diff --git a/modules/pademu/btstack/btstack.h b/modules/pademu/btstack/btstack.h new file mode 100644 index 000000000..047ff5978 --- /dev/null +++ b/modules/pademu/btstack/btstack.h @@ -0,0 +1,222 @@ +#ifndef _BTSTACK_H_ +#define _BTSTACK_H_ + +#define USB_CLASS_WIRELESS_CONTROLLER 0xE0 +#define USB_SUBCLASS_RF_CONTROLLER 0x01 +#define USB_PROTOCOL_BLUETOOTH_PROG 0x01 + +#define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer + +#define PENDING 1 +#define SUCCESSFUL 0 + +typedef struct +{ + int devId; + int hid_sema; + int controlEndp; + int interruptEndp; + int inEndp; + int outEndp; +} bt_adapter_t; + +typedef struct +{ + int id; + u8 fix; + u8 lrum; + u8 rrum; + u8 update_rum; + u8 analog_btn; + u8 data[18]; + u8 led[4]; + u8 btn_delay; +} bt_paddata_t; + +typedef struct +{ + u16 control_dcid; + u16 interrupt_dcid; + int (*pad_connect)(bt_paddata_t *data, u8 *str, int size); + void (*pad_init)(bt_paddata_t *data, int id); + void (*pad_read_report)(bt_paddata_t *data, u8 *in, int bytes); + void (*pad_rumble)(bt_paddata_t *data); +} bt_paddrv_t; + +typedef struct +{ + u16 hci_handle; // hci connection handle + u16 control_scid; // Channel endpoint on command destination + u16 interrupt_scid; // Channel endpoint on interrupt destination + u8 bdaddr[6]; + bt_paddata_t data; + bt_paddrv_t *pad; + pad_device_t dev; + u8 status; +} bt_dev_t; + +enum eBTDEVStatus { + BTDEV_STATE_USB_DISCONNECTED = 0x00, + BTDEV_STATE_USB_AUTHORIZED = 0x01, + BTDEV_STATE_USB_CONFIGURED = 0x02, + BTDEV_STATE_CONNECTED = 0x04, + BTDEV_STATE_RUNNING = 0x08, + BTDEV_STATE_DISCONNECTING = 0x10, + BTDEV_STATE_DISCONNECT_REQUEST = 0x20, +}; + +#define btdev_status_clear(flag, pad) dev[pad].status &= ~flag +#define btdev_status_set(flag, pad) dev[pad].status |= flag +#define btdev_status_check(flag, pad) (dev[pad].status & flag) + +enum eHCI { + // {{{ + /* Bluetooth HCI states for HCI_task() */ + HCI_INIT_STATE = 0, + HCI_RESET_STATE, + HCI_READBDADDR_STATE, + HCI_CONNECT_IN_STATE, + HCI_REMOTE_NAME_STATE, + HCI_CHANGE_CONNECTION, + HCI_READ_REMOTE_SUPPORTED_FEATURES, + HCI_CONNECTED_STATE, + + /* HCI OpCode Group Field (OGF) */ + HCI_OGF_LINK_CNTRL = (0x01 << 2), // OGF: Link Control Commands + HCI_OGF_LINK_POLICY = (0x02 << 2), // OGF: Link Policy Commands + HCI_OGF_CTRL_BBAND = (0x03 << 2), // OGF: Controller & Baseband Commands + HCI_OGF_INFO_PARAM = (0x04 << 2), // OGF: Informational Parameters + + /* HCI OpCode Command Field (OCF) */ + HCI_OCF_DISCONNECT = 0x06, // OGF = 0x01 + HCI_OCF_ACCEPT_CONNECTION = 0x09, // OGF = 0x01 + HCI_OCF_REJECT_CONNECTION = 0x0A, // OGF = 0x01 + HCI_OCF_CHANGE_CONNECTION_TYPE = 0x0F, // OGF = 0x01 + HCI_OCF_REMOTE_NAME = 0x19, // OGF = 0x01 + HCI_OCF_LINK_KEY_REQUEST_REPLY = 0x0B, // OGF = 0x01 + + HCI_OCF_RESET = 0x03, // OGF = 0x03 + HCI_OCF_WRITE_ACCEPT_TIMEOUT = 0x16, // OGF = 0x03 + HCI_OCF_WRITE_SCAN_ENABLE = 0x1A, // OGF = 0x03 + + HCI_OCF_READ_BDADDR = 0x09, // OGF = 0x04 + + /* HCI events managed */ + HCI_EVENT_CONNECT_COMPLETE = 0x03, + HCI_EVENT_CONNECT_REQUEST = 0x04, + HCI_EVENT_DISCONN_COMPLETE = 0x05, + HCI_EVENT_AUTHENTICATION_COMPLETE = 0x06, + HCI_EVENT_REMOTE_NAME_COMPLETE = 0x07, + HCI_EVENT_ENCRYPTION_CHANGE = 0x08, + HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE = 0x0B, + HCI_EVENT_QOS_SETUP_COMPLETE = 0x0d, // do nothing + HCI_EVENT_COMMAND_COMPLETE = 0x0e, + HCI_EVENT_COMMAND_STATUS = 0x0f, + HCI_EVENT_ROLE_CHANGED = 0x12, + HCI_EVENT_NUM_COMPLETED_PKT = 0x13, // do nothing + HCI_EVENT_PIN_CODE_REQUEST = 0x16, + HCI_EVENT_LINK_KEY_REQUEST = 0x17, + HCI_EVENT_CHANGED_CONNECTION_TYPE = 0x1D, + HCI_EVENT_PAGE_SR_CHANGED = 0x20, + HCI_EVENT_MAX_SLOT_CHANGE = 0x1B, //Max Slots Change event + + /* HCI event flags for hci_event_flag */ + HCI_FLAG_COMMAND_COMPLETE = 0x01, + HCI_FLAG_COMMAND_STATUS = 0x02, + HCI_FLAG_CONNECT_COMPLETE = 0x04, + HCI_FLAG_DISCONN_COMPLETE = 0x08, + HCI_FLAG_INCOMING_REQUEST = 0x10, + HCI_FLAG_READ_BDADDR = 0x20, + HCI_FLAG_REMOTE_NAME_COMPLETE = 0x40, + + /* HCI Scan Enable Parameters */ + SCAN_ENABLE_NOINQ_NOPAG = 0x00, + SCAN_ENABLE_ENINQ_NOPAG = 0x01, + SCAN_ENABLE_NOINQ_ENPAG = 0x02, + SCAN_ENABLE_ENINQ_ENPAG = 0x03, + // }}} +}; + +enum eL2CAP { + // {{{ + /* Bluetooth L2CAP PSM */ + L2CAP_PSM_SDP = 0x01, + L2CAP_PSM_CTRL = 0x11, // HID_Control + L2CAP_PSM_INTR = 0x13, // HID_Interrupt + + /* Bluetooth L2CAP states for L2CAP_task() */ + L2CAP_DOWN_STATE = 0, + L2CAP_INIT_STATE, + L2CAP_CONTROL_CONNECTING_STATE, + L2CAP_CONTROL_REQUEST_STATE, + L2CAP_CONTROL_CONFIGURING_STATE, + L2CAP_INTERRUPT_CONNECTING_STATE, + L2CAP_INTERRUPT_REQUEST_STATE, + L2CAP_INTERRUPT_CONFIGURING_STATE, + L2CAP_CONNECTED_STATE, + L2CAP_LED_STATE, + L2CAP_LED_STATE_END, + L2CAP_READY_STATE, + L2CAP_DISCONNECT_STATE, + + /* L2CAP event flags */ + L2CAP_EV_COMMAND_CONNECTED = 0x01, + L2CAP_EV_COMMAND_CONFIG_REQ = 0x02, + L2CAP_EV_COMMAND_CONFIGURED = 0x04, + L2CAP_EV_COMMAND_DISCONNECT_REQ = 0x08, + L2CAP_EV_INTERRUPT_CONNECTED = 0x10, + L2CAP_EV_INTERRUPT_CONFIG_REQ = 0x20, + L2CAP_EV_INTERRUPT_CONFIGURED = 0x40, + L2CAP_EV_INTERRUPT_DISCONNECT_REQ = 0x80, + + /* L2CAP signaling command */ + L2CAP_CMD_COMMAND_REJECT = 0x01, + L2CAP_CMD_CONNECTION_REQUEST = 0x02, + L2CAP_CMD_CONNECTION_RESPONSE = 0x03, + L2CAP_CMD_CONFIG_REQUEST = 0x04, + L2CAP_CMD_CONFIG_RESPONSE = 0x05, + L2CAP_CMD_DISCONNECT_REQUEST = 0x06, + L2CAP_CMD_DISCONNECT_RESPONSE = 0x07, + + /* HCI ACL Data Packet + * + * buf[0] buf[1] buf[2] buf[3] + * 0 4 8 11 12 16 24 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | HCI Handle |PB |BC | Data Total Length | HCI ACL Data Packet + * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * + * buf[4] buf[5] buf[6] buf[7] + * 0 8 16 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | Length | Channel ID | Basic L2CAP header + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * + * buf[8] buf[9] buf[10] buf[11] + * 0 8 16 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | Code | Identifier | Length | Control frame (C-frame) + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. (signaling packet format) + */ + // }}} +}; + +#define l2cap_handle_ok(handle) (((u16)(l2cap_buf[0] | (l2cap_buf[1] << 8)) & 0x0FFF) == (u16)(handle & 0x0FFF)) +#define l2cap_control_channel ((l2cap_buf[6] | (l2cap_buf[7] << 8)) == 0x0001) // Channel ID for ACL-U +#define l2cap_interrupt_channel ((l2cap_buf[6] | (l2cap_buf[7] << 8)) == dev[pad].pad->interrupt_dcid) +#define l2cap_command_channel ((l2cap_buf[6] | (l2cap_buf[7] << 8)) == dev[pad].pad->control_dcid) + +#define btstack_IMPORTS_start DECLARE_IMPORT_TABLE(btstack, 1, 1) + +void btstack_register(bt_paddrv_t *pad); +#define I_btstack_register DECLARE_IMPORT(4, btstack_register) + +void btstack_unregister(bt_paddrv_t *pad); +#define I_btstack_unregister DECLARE_IMPORT(5, btstack_unregister) + +int btstack_hid_command(bt_paddrv_t *pad, u8 *data, u8 length, int id); +#define I_btstack_hid_command DECLARE_IMPORT(6, btstack_hid_command) + +#define btstack_IMPORTS_end END_IMPORT_TABLE + +#endif diff --git a/modules/pademu/btstack/exports.tab b/modules/pademu/btstack/exports.tab new file mode 100644 index 000000000..7dec25ab5 --- /dev/null +++ b/modules/pademu/btstack/exports.tab @@ -0,0 +1,12 @@ + +DECLARE_EXPORT_TABLE(btstack, 1, 1) + DECLARE_EXPORT(_start) + DECLARE_EXPORT(_retonly) + DECLARE_EXPORT(btstack_exit) + DECLARE_EXPORT(_retonly) + DECLARE_EXPORT(btstack_register) + DECLARE_EXPORT(btstack_unregister) + DECLARE_EXPORT(btstack_hid_command) +END_EXPORT_TABLE + +void _retonly() {} diff --git a/modules/pademu/btstack/imports.lst b/modules/pademu/btstack/imports.lst new file mode 100644 index 000000000..e36f592dd --- /dev/null +++ b/modules/pademu/btstack/imports.lst @@ -0,0 +1,68 @@ +loadcore_IMPORTS_start +I_RegisterLibraryEntries +I_ReleaseLibraryEntries +I_GetLoadcoreInternalData +I_QueryLibraryEntryTable +I_SetRebootTimeLibraryHandlingMode +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_PollSema +I_DeleteSema +I_iSignalSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_StartThread +I_CreateThread +I_DeleteThread +I_DelayThread +I_GetThreadId +I_SleepThread +I_WakeupThread +I_TerminateThread +I_SetAlarm +I_CancelAlarm +thbase_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +usbd_IMPORTS_start +I_sceUsbdScanStaticDescriptor +I_sceUsbdOpenPipe +I_sceUsbdClosePipe +I_sceUsbdOpenPipeAligned +I_sceUsbdSetPrivateData +I_sceUsbdTransferPipe +I_sceUsbdRegisterLdd +usbd_IMPORTS_end + +sysclib_IMPORTS_start +I_strncmp +#ifndef USE_SMSUTILS +I_memset +I_memcpy +#endif +sysclib_IMPORTS_end + +#ifdef USE_SMSUTILS +smsutils_IMPORTS_start +I_mips_memset +I_mips_memcpy +smsutils_IMPORTS_end +#endif + +pademu_IMPORTS_start +I_pademu_connect +I_pademu_disconnect +pademu_IMPORTS_end diff --git a/modules/pademu/btstack/irx_imports.h b/modules/pademu/btstack/irx_imports.h new file mode 100644 index 000000000..280115628 --- /dev/null +++ b/modules/pademu/btstack/irx_imports.h @@ -0,0 +1,40 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "dmacman.h" +#include "intrman.h" +#include "iomanX.h" +#include "libsd.h" +#include "loadcore.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thmsgbx.h" +#include "thsemap.h" +#include "usbd.h" +#include "vblank.h" +#include "xloadcore.h" + +#include "../pademu.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/modules/pademu/ds3bt/Makefile b/modules/pademu/ds3bt/Makefile new file mode 100644 index 000000000..7147be201 --- /dev/null +++ b/modules/pademu/ds3bt/Makefile @@ -0,0 +1,22 @@ + +IOP_BIN = ds3bt.irx +IOP_OBJS = ds3bt.o imports.o +IOP_OBJS_DIR = obj.ds3bt/ + +IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%) + +IOP_CFLAGS += -Wall -fno-builtin -DUSE_SMSUTILS +IOP_LDFLAGS += -s + +all: OBJ_DIR $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +rebuild: clean all + +OBJ_DIR: + mkdir -p $(IOP_OBJS_DIR) + +include $(PS2SDK)/Defs.make +include ../Rules.make diff --git a/modules/pademu/ds3bt/ds3bt.c b/modules/pademu/ds3bt/ds3bt.c new file mode 100644 index 000000000..213427be7 --- /dev/null +++ b/modules/pademu/ds3bt/ds3bt.c @@ -0,0 +1,220 @@ +#include "irx.h" +#include "types.h" +#include "loadcore.h" +#include "stdio.h" +#include "sifrpc.h" +#include "sysclib.h" +#include "thbase.h" +#include "thsemap.h" +#include "../pademu.h" +#include "../btstack/btstack.h" +#include "ds3bt.h" + +#define DPRINTF(x...) printf(x) +//#define DPRINTF(x...) + +static u8 output_01_report[] = + { + 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + +static u8 led_patterns[][2] = + { + {0x1C, 0x02}, + {0x1A, 0x04}, + {0x16, 0x08}, + {0x0E, 0x10}, +}; + +static u8 power_level[] = + { + 0x00, 0x00, 0x02, 0x06, 0x0E, 0x1E}; + +int ds3bt_connect(bt_paddata_t *data, u8 *str, int size); +void ds3bt_init(bt_paddata_t *data, int id); +void ds3bt_read_report(bt_paddata_t *data, u8 *in, int bytes); +void ds3bt_rumble(bt_paddata_t *data); + +bt_paddrv_t ds3btdrv = {0x0040, 0x0041, ds3bt_connect, ds3bt_init, ds3bt_read_report, ds3bt_rumble}; + +static u8 press_emu = 0; + +#define MAX_DELAY 10 +#define CMD_DELAY 2 + +int ds3bt_connect(bt_paddata_t *data, u8 *str, int size) +{ + DPRINTF("BS3BT: %s \n", str); //PLAYSTATION(R)3 Controller + + if (strncmp(str, "PLAYSTATION(R)3 Controller", 26) != 0) { + return 0; + } + + return 1; +} + +void ds3bt_init(bt_paddata_t *data, int id) +{ + u8 init_buf[PS3_F4_REPORT_LEN + 2]; + + init_buf[0] = HID_THDR_SET_REPORT_FEATURE; // THdr + init_buf[1] = PS3_F4_REPORT_ID; // Report ID + init_buf[2] = 0x42; + init_buf[3] = 0x03; + init_buf[4] = 0x00; + init_buf[5] = 0x00; + + btstack_hid_command(&ds3btdrv, init_buf, PS3_F4_REPORT_LEN + 2, data->id); + + data->id = id; + data->led[0] = led_patterns[id][1]; + data->led[1] = 0; + DelayThread(CMD_DELAY); + ds3bt_rumble(data); + data->btn_delay = 0xFF; + press_emu = 0; +} + +void ds3bt_read_report(bt_paddata_t *data, u8 *in, int bytes) +{ + if (in[8] == HID_THDR_DATA_INPUT) { + ds3report_t *report = (ds3report_t *)(in + 9); + if (report->ReportID == PS3_01_REPORT_ID) { + + if (report->RightStickX == 0 && report->RightStickY == 0) // ledrumble cmd causes null report sometime + return; + + data->data[0] = ~report->ButtonStateL; + data->data[1] = ~report->ButtonStateH; + + data->data[2] = report->RightStickX; //rx + data->data[3] = report->RightStickY; //ry + data->data[4] = report->LeftStickX; //lx + data->data[5] = report->LeftStickY; //ly + + if (bytes == 21 && !press_emu) + press_emu = 1; + + if (press_emu) { //needs emulating pressure buttons + data->data[6] = report->Right * 255; //right + data->data[7] = report->Left * 255; //left + data->data[8] = report->Up * 255; //up + data->data[9] = report->Down * 255; //down + + data->data[10] = report->Triangle * 255; //triangle + data->data[11] = report->Circle * 255; //circle + data->data[12] = report->Cross * 255; //cross + data->data[13] = report->Square * 255; //square + + data->data[14] = report->L1 * 255; //L1 + data->data[15] = report->R1 * 255; //R1 + data->data[16] = report->L2 * 255; //L2 + data->data[17] = report->R2 * 255; //R2 + + report->Power = 0x05; + } else { + data->data[6] = report->PressureRight; //right + data->data[7] = report->PressureLeft; //left + data->data[8] = report->PressureUp; //up + data->data[9] = report->PressureDown; //down + + data->data[10] = report->PressureTriangle; //triangle + data->data[11] = report->PressureCircle; //circle + data->data[12] = report->PressureCross; //cross + data->data[13] = report->PressureSquare; //square + + data->data[14] = report->PressureL1; //L1 + data->data[15] = report->PressureR1; //R1 + data->data[16] = report->PressureL2; //L2 + data->data[17] = report->PressureR2; //R2 + } + + if (report->PSButtonState) { //display battery level + if (report->Select && (data->btn_delay == MAX_DELAY)) { //PS + SELECT + if (data->analog_btn < 2) //unlocked mode + data->analog_btn = !data->analog_btn; + + data->led[0] = led_patterns[data->id][(data->analog_btn & 1)]; + data->btn_delay = 1; + } else { + if (report->Power != 0xEE) + data->led[0] = power_level[report->Power]; + + if (data->btn_delay < MAX_DELAY) + data->btn_delay++; + } + } else { + data->led[0] = led_patterns[data->id][(data->analog_btn & 1)]; + + if (data->btn_delay > 0) + data->btn_delay--; + } + + if (report->Power == 0xEE) //charging + data->led[1] = 1; + else + data->led[1] = 0; + + if (data->btn_delay > 0) { + data->update_rum = 1; + } + } + } else { + DPRINTF("BS3BT: Unmanaged Input Report: THDR 0x%02X ", in[8]); + DPRINTF("BS3BT: ID 0x%02X \n", in[9]); + } +} + +void ds3bt_rumble(bt_paddata_t *data) +{ + u8 led_buf[PS3_01_REPORT_LEN + 2]; + mips_memset(led_buf, 0, sizeof(led_buf)); + mips_memcpy(led_buf + 2, output_01_report, sizeof(output_01_report)); + + if (data->fix) + led_buf[0] = 0xA2; // THdr + else + led_buf[0] = HID_THDR_SET_REPORT_OUTPUT; // THdr + + led_buf[1] = PS3_01_REPORT_ID; // Report ID + + mips_memcpy(&led_buf[2], output_01_report, sizeof(output_01_report)); // PS3_01_REPORT_LEN); + + if (data->fix) { + if (data->rrum < 5) + data->rrum = 0; + } + + led_buf[3] = 0xFE; //rt + led_buf[4] = data->rrum; //rp + led_buf[5] = 0xFE; //lt + led_buf[6] = data->lrum; //lp + + led_buf[11] = data->led[0] & 0x7F; //LED Conf + + if (data->led[1]) { //means charging, so blink + led_buf[15] = 0x32; + led_buf[20] = 0x32; + led_buf[25] = 0x32; + led_buf[30] = 0x32; + } + + btstack_hid_command(&ds3btdrv, led_buf, sizeof(output_01_report) + 2, data->id); +} + +int _start(int argc, char *argv[]) +{ + btstack_register(&ds3btdrv); + + return MODULE_RESIDENT_END; +} diff --git a/modules/pademu/ds3bt/ds3bt.h b/modules/pademu/ds3bt/ds3bt.h new file mode 100644 index 000000000..bb27de303 --- /dev/null +++ b/modules/pademu/ds3bt/ds3bt.h @@ -0,0 +1,99 @@ +#ifndef _DS3BT_H_ +#define _DS3BT_H_ + +#define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer + +enum eHID { + // {{{ + /* HID event flag */ + HID_FLAG_STATUS_REPORTED = 0x01, + HID_FLAG_BUTTONS_CHANGED = 0x02, + HID_FLAG_EXTENSION = 0x04, + HID_FLAG_COMMAND_SUCCESS = 0x08, + + /* Bluetooth HID Transaction Header (THdr) */ + HID_THDR_GET_REPORT_FEATURE = 0x43, + HID_THDR_SET_REPORT_OUTPUT = 0x52, + HID_THDR_SET_REPORT_FEATURE = 0x53, + HID_THDR_DATA_INPUT = 0xa1, + + /* Defines of various parameters for PS3 Game controller reports */ + PS3_F4_REPORT_ID = 0xF4, + PS3_F4_REPORT_LEN = 0x04, + + PS3_01_REPORT_ID = 0x01, + PS3_01_REPORT_LEN = 0x30, + + PS4_02_REPORT_ID = 0x02, + PS4_11_REPORT_ID = 0x11, + PS4_11_REPORT_LEN = 0x4D, + + // }}} +}; + +typedef struct +{ + u8 ReportID; + u8 Zero; + union + { + u8 ButtonStateL; // Main buttons Low + struct + { + u8 Select : 1; + u8 L3 : 1; + u8 R3 : 1; + u8 Start : 1; + u8 Up : 1; + u8 Right : 1; + u8 Down : 1; + u8 Left : 1; + }; + }; + union + { + u8 ButtonStateH; // Main buttons High + struct + { + u8 L2 : 1; + u8 R2 : 1; + u8 L1 : 1; + u8 R1 : 1; + u8 Triangle : 1; + u8 Circle : 1; + u8 Cross : 1; + u8 Square : 1; + }; + }; + u8 PSButtonState; // PS button + u8 Reserved1; // Unknown + u8 LeftStickX; // left Joystick X axis 0 - 255, 128 is mid + u8 LeftStickY; // left Joystick Y axis 0 - 255, 128 is mid + u8 RightStickX; // right Joystick X axis 0 - 255, 128 is mid + u8 RightStickY; // right Joystick Y axis 0 - 255, 128 is mid + u8 Reserved2[4]; // Unknown + u8 PressureUp; // digital Pad Up button Pressure 0 - 255 + u8 PressureRight; // digital Pad Right button Pressure 0 - 255 + u8 PressureDown; // digital Pad Down button Pressure 0 - 255 + u8 PressureLeft; // digital Pad Left button Pressure 0 - 255 + u8 PressureL2; // digital Pad L2 button Pressure 0 - 255 + u8 PressureR2; // digital Pad R2 button Pressure 0 - 255 + u8 PressureL1; // digital Pad L1 button Pressure 0 - 255 + u8 PressureR1; // digital Pad R1 button Pressure 0 - 255 + u8 PressureTriangle; // digital Pad Triangle button Pressure 0 - 255 + u8 PressureCircle; // digital Pad Circle button Pressure 0 - 255 + u8 PressureCross; // digital Pad Cross button Pressure 0 - 255 + u8 PressureSquare; // digital Pad Square button Pressure 0 - 255 + u8 Reserved3[3]; // Unknown + u8 Charge; // charging status ? 02 = charge, 03 = normal + u8 Power; // Battery status ? 05=full - 02=dying, 01=just before shutdown, EE=charging + u8 Connection; // Connection Type ? 14 when operating by bluetooth, 10 when operating by bluetooth with cable plugged in, 16 when bluetooh and rumble + u8 Reserved4[9]; // Unknown + s16 AccelX; + s16 AccelY; + s16 AccelZ; + s16 GyroZ; + +} __attribute__((packed)) ds3report_t; + +#endif diff --git a/modules/pademu/ds3bt/imports.lst b/modules/pademu/ds3bt/imports.lst new file mode 100644 index 000000000..b0de84ee7 --- /dev/null +++ b/modules/pademu/ds3bt/imports.lst @@ -0,0 +1,74 @@ +loadcore_IMPORTS_start +I_RegisterLibraryEntries +I_ReleaseLibraryEntries +I_GetLoadcoreInternalData +I_QueryLibraryEntryTable +I_SetRebootTimeLibraryHandlingMode +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_PollSema +I_DeleteSema +I_iSignalSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_StartThread +I_CreateThread +I_DeleteThread +I_DelayThread +I_GetThreadId +I_SleepThread +I_WakeupThread +I_TerminateThread +I_SetAlarm +I_CancelAlarm +thbase_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +usbd_IMPORTS_start +I_sceUsbdScanStaticDescriptor +I_sceUsbdOpenPipe +I_sceUsbdClosePipe +I_sceUsbdOpenPipeAligned +I_sceUsbdSetPrivateData +I_sceUsbdTransferPipe +I_sceUsbdRegisterLdd +usbd_IMPORTS_end + +sysclib_IMPORTS_start +I_strncmp +#ifndef USE_SMSUTILS +I_memset +I_memcpy +#endif +sysclib_IMPORTS_end + +#ifdef USE_SMSUTILS +smsutils_IMPORTS_start +I_mips_memset +I_mips_memcpy +smsutils_IMPORTS_end +#endif + +pademu_IMPORTS_start +I_pademu_connect +I_pademu_disconnect +pademu_IMPORTS_end + +btstack_IMPORTS_start +I_btstack_register +I_btstack_unregister +I_btstack_hid_command +btstack_IMPORTS_end diff --git a/modules/pademu/ds3bt/irx_imports.h b/modules/pademu/ds3bt/irx_imports.h new file mode 100644 index 000000000..c0508fecb --- /dev/null +++ b/modules/pademu/ds3bt/irx_imports.h @@ -0,0 +1,41 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "dmacman.h" +#include "intrman.h" +#include "iomanX.h" +#include "libsd.h" +#include "loadcore.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thmsgbx.h" +#include "thsemap.h" +#include "usbd.h" +#include "vblank.h" +#include "xloadcore.h" + +#include "../pademu.h" +#include "../btstack/btstack.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/modules/pademu/ds3usb/Makefile b/modules/pademu/ds3usb/Makefile new file mode 100644 index 000000000..49b5ceb95 --- /dev/null +++ b/modules/pademu/ds3usb/Makefile @@ -0,0 +1,22 @@ + +IOP_BIN = ds3usb.irx +IOP_OBJS = ds3usb.o imports.o +IOP_OBJS_DIR = obj.ds3usb/ + +IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%) + +IOP_CFLAGS += -Wall -fno-builtin -DUSE_SMSUTILS +IOP_LDFLAGS += -s + +all: OBJ_DIR $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +rebuild: clean all + +OBJ_DIR: + mkdir -p $(IOP_OBJS_DIR) + +include $(PS2SDK)/Defs.make +include ../Rules.make diff --git a/modules/pademu/ds3usb/ds3usb.c b/modules/pademu/ds3usb/ds3usb.c new file mode 100644 index 000000000..e8bc0d20c --- /dev/null +++ b/modules/pademu/ds3usb/ds3usb.c @@ -0,0 +1,429 @@ +#include "types.h" +#include "loadcore.h" +#include "stdio.h" +#include "sifrpc.h" +#include "sysclib.h" +#include "usbd.h" +#include "usbd_macro.h" +#include "thbase.h" +#include "thsemap.h" +#include "ds3usb.h" + +//#define DPRINTF(x...) printf(x) +#define DPRINTF(x...) + +#define REQ_USB_OUT (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) +#define REQ_USB_IN (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) + +#define MAX_PADS 4 + +static u8 output_01_report[] = + { + 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + +static u8 led_patterns[][2] = + { + {0x1C, 0x02}, + {0x1A, 0x04}, + {0x16, 0x08}, + {0x0E, 0x10}, +}; + +static u8 power_level[] = + { + 0x00, 0x00, 0x02, 0x06, 0x0E, 0x1E}; + +static u8 usb_buf[MAX_BUFFER_SIZE] __attribute((aligned(4))) = {0}; + +int usb_probe(int devId); +int usb_connect(int devId); +int usb_disconnect(int devId); + +static void usb_release(int pad); +static void usb_config_set(int result, int count, void *arg); + +UsbDriver usb_driver = {NULL, NULL, "ds3usb", usb_probe, usb_connect, usb_disconnect}; + +static void readReport(u8 *data, int pad); +static int LEDRumble(u8 *led, u8 lrum, u8 rrum, int pad); + +ds3usb_device ds3dev[MAX_PADS]; + +int usb_probe(int devId) +{ + UsbDeviceDescriptor *device = NULL; + + DPRINTF("DS3USB: probe: devId=%i\n", devId); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + if (device == NULL) { + DPRINTF("DS3USB: Error - Couldn't get device descriptor\n"); + return 0; + } + + if (device->idVendor == SONY_VID && device->idProduct == DS3_PID) + return 1; + + return 0; +} + +int usb_connect(int devId) +{ + int pad, epCount; + UsbDeviceDescriptor *device; + UsbConfigDescriptor *config; + UsbInterfaceDescriptor *interface; + UsbEndpointDescriptor *endpoint; + + DPRINTF("DS3USB: connect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (ds3dev[pad].usb_id == -1) + break; + } + + if (pad >= MAX_PADS) { + DPRINTF("DS3USB: Error - only %d device allowed !\n", MAX_PADS); + return 1; + } + + PollSema(ds3dev[pad].sema); + + ds3dev[pad].dev.id = pad; + ds3dev[pad].usb_id = devId; + ds3dev[pad].controlEndp = UsbOpenEndpoint(devId, NULL); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG); + interface = (UsbInterfaceDescriptor *)((char *)config + config->bLength); + epCount = interface->bNumEndpoints - 1; + endpoint = (UsbEndpointDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_ENDPOINT); + + do { + if (endpoint->bmAttributes == USB_ENDPOINT_XFER_INT) { + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && ds3dev[pad].interruptEndp < 0) { + ds3dev[pad].interruptEndp = UsbOpenEndpointAligned(devId, endpoint); + DPRINTF("DS3USB: register Event endpoint id =%i addr=%02X packetSize=%i\n", ds3dev[pad].interruptEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + } + + endpoint = (UsbEndpointDescriptor *)((char *)endpoint + endpoint->bLength); + + } while (epCount--); + + if (ds3dev[pad].interruptEndp < 0) { + usb_release(pad); + return 1; + } + + UsbSetDeviceConfiguration(ds3dev[pad].controlEndp, config->bConfigurationValue, usb_config_set, (void *)pad); + SignalSema(ds3dev[pad].sema); + + return 0; +} + +int usb_disconnect(int devId) +{ + u8 pad; + + DPRINTF("DS3USB: disconnect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (ds3dev[pad].usb_id == devId) { + pademu_disconnect(&ds3dev[pad].dev); + break; + } + } + + if (pad < MAX_PADS) + usb_release(pad); + + return 0; +} + +static void usb_release(int pad) +{ + PollSema(ds3dev[pad].sema); + + if (ds3dev[pad].interruptEndp >= 0) + UsbCloseEndpoint(ds3dev[pad].interruptEndp); + + ds3dev[pad].controlEndp = -1; + ds3dev[pad].interruptEndp = -1; + ds3dev[pad].usb_id = -1; + + SignalSema(ds3dev[pad].sema); +} + +static void usb_data_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("DS3USB: usb_data_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + ds3dev[pad].usb_resultcode = resultCode; + + SignalSema(ds3dev[pad].sema); +} + +static void usb_cmd_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("DS3USB: usb_cmd_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + SignalSema(ds3dev[pad].cmd_sema); +} + +static void usb_config_set(int result, int count, void *arg) +{ + int pad = (int)arg; + u8 led[2]; + + PollSema(ds3dev[pad].sema); + + usb_buf[0] = 0x42; + usb_buf[1] = 0x0C; + usb_buf[2] = 0x00; + usb_buf[3] = 0x00; + + UsbControlTransfer(ds3dev[pad].controlEndp, REQ_USB_OUT, USB_REQ_SET_REPORT, (HID_USB_GET_REPORT_FEATURE << 8) | 0xF4, 0, 4, usb_buf, NULL, NULL); + DelayThread(10000); + pademu_connect(&ds3dev[pad].dev); + led[0] = led_patterns[ds3dev[pad].dev.id][1]; + led[1] = 0; + + LEDRumble(led, 0, 0, pad); + SignalSema(ds3dev[pad].sema); +} + +#define MAX_DELAY 10 + +static void readReport(u8 *data, int pad) +{ + ds3report_t *report = (ds3report_t *)data; + if (report->ReportID == 0x01) { + if (report->RightStickX == 0 && report->RightStickY == 0) // ledrumble cmd causes null report sometime + return; + + ds3dev[pad].data[0] = ~report->ButtonStateL; + ds3dev[pad].data[1] = ~report->ButtonStateH; + + ds3dev[pad].data[2] = report->RightStickX; //rx + ds3dev[pad].data[3] = report->RightStickY; //ry + ds3dev[pad].data[4] = report->LeftStickX; //lx + ds3dev[pad].data[5] = report->LeftStickY; //ly + + ds3dev[pad].data[6] = report->PressureRight; //right + ds3dev[pad].data[7] = report->PressureLeft; //left + ds3dev[pad].data[8] = report->PressureUp; //up + ds3dev[pad].data[9] = report->PressureDown; //down + + ds3dev[pad].data[10] = report->PressureTriangle; //triangle + ds3dev[pad].data[11] = report->PressureCircle; //circle + ds3dev[pad].data[12] = report->PressureCross; //cross + ds3dev[pad].data[13] = report->PressureSquare; //square + + ds3dev[pad].data[14] = report->PressureL1; //L1 + ds3dev[pad].data[15] = report->PressureR1; //R1 + ds3dev[pad].data[16] = report->PressureL2; //L2 + ds3dev[pad].data[17] = report->PressureR2; //R2 + + if (report->PSButtonState) { //display battery level + if (report->Select && (ds3dev[pad].btn_delay == MAX_DELAY)) { //PS + SELECT + if (ds3dev[pad].analog_btn < 2) //unlocked mode + ds3dev[pad].analog_btn = !ds3dev[pad].analog_btn; + + ds3dev[pad].oldled[0] = led_patterns[pad][(ds3dev[pad].analog_btn & 1)]; + ds3dev[pad].btn_delay = 1; + } else { + if (report->Power != 0xEE) + ds3dev[pad].oldled[0] = power_level[report->Power]; + + if (ds3dev[pad].btn_delay < MAX_DELAY) + ds3dev[pad].btn_delay++; + } + } else { + ds3dev[pad].oldled[0] = led_patterns[pad][(ds3dev[pad].analog_btn & 1)]; + + if (ds3dev[pad].btn_delay > 0) + ds3dev[pad].btn_delay--; + } + + if (report->Power == 0xEE) //charging + ds3dev[pad].oldled[1] = 1; + else + ds3dev[pad].oldled[1] = 0; + + if (ds3dev[pad].btn_delay > 0) { + ds3dev[pad].update_rum = 1; + } + } +} + +static int LEDRumble(u8 *led, u8 lrum, u8 rrum, int pad) +{ + PollSema(ds3dev[pad].cmd_sema); + + mips_memset(usb_buf, 0, sizeof(usb_buf)); + mips_memcpy(usb_buf, output_01_report, sizeof(output_01_report)); + + usb_buf[1] = 0xFE; //rt + usb_buf[2] = rrum; //rp + usb_buf[3] = 0xFE; //lt + usb_buf[4] = lrum; //lp + usb_buf[9] = led[0] & 0x7F; //LED Conf + + if (led[1]) { //means charging, so blink + usb_buf[13] = 0x32; + usb_buf[18] = 0x32; + usb_buf[23] = 0x32; + usb_buf[28] = 0x32; + } + + ds3dev[pad].oldled[0] = led[0]; + ds3dev[pad].oldled[1] = led[1]; + + return UsbControlTransfer(ds3dev[pad].controlEndp, REQ_USB_OUT, USB_REQ_SET_REPORT, (HID_USB_SET_REPORT_OUTPUT << 8) | 0x01, 0, sizeof(output_01_report), usb_buf, usb_cmd_cb, (void *)pad); +} + +static unsigned int timeout(void *arg) +{ + int sema = (int)arg; + iSignalSema(sema); + return 0; +} + +static void TransferWait(int sema) +{ + iop_sys_clock_t cmd_timeout; + + cmd_timeout.lo = 200000; + cmd_timeout.hi = 0; + + if (SetAlarm(&cmd_timeout, timeout, (void *)sema) == 0) { + WaitSema(sema); + CancelAlarm(timeout, NULL); + } +} + +void ds3usb_set_rumble(u8 lrum, u8 rrum, int port) +{ + WaitSema(ds3dev[port].sema); + + ds3dev[port].update_rum = 1; + ds3dev[port].lrum = lrum; + ds3dev[port].rrum = rrum; + + SignalSema(ds3dev[port].sema); +} + +int ds3usb_get_data(u8 *dst, int size, int port) +{ + int ret = 0; + + WaitSema(ds3dev[port].sema); + + PollSema(ds3dev[port].sema); + + ret = UsbInterruptTransfer(ds3dev[port].interruptEndp, usb_buf, MAX_BUFFER_SIZE, usb_data_cb, (void *)port); + + if (ret == USB_RC_OK) { + TransferWait(ds3dev[port].sema); + if (!ds3dev[port].usb_resultcode) + readReport(usb_buf, port); + + ds3dev[port].usb_resultcode = 1; + } else { + DPRINTF("DS3USB: DS3USB_get_data usb transfer error %d\n", ret); + } + + mips_memcpy(dst, ds3dev[port].data, size); + ret = ds3dev[port].analog_btn & 1; + + if (ds3dev[port].update_rum) { + ret = LEDRumble(ds3dev[port].oldled, ds3dev[port].lrum, ds3dev[port].rrum, port); + if (ret == USB_RC_OK) + TransferWait(ds3dev[port].cmd_sema); + else + DPRINTF("DS3USB: LEDRumble usb transfer error %d\n", ret); + + ds3dev[port].update_rum = 0; + } + + SignalSema(ds3dev[port].sema); + + return ret; +} + +void ds3usb_set_mode(int mode, int lock, int port) +{ + if (lock == 3) + ds3dev[port].analog_btn = 3; + else + ds3dev[port].analog_btn = mode; +} + +void ds3usb_reset() +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) + usb_release(pad); +} + +int _start(int argc, char *argv[]) +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) { + ds3dev[pad].usb_id = -1; + ds3dev[pad].dev.id = -1; + ds3dev[pad].dev.pad_get_data = ds3usb_get_data; + ds3dev[pad].dev.pad_set_rumble = ds3usb_set_rumble; + ds3dev[pad].dev.pad_set_mode = ds3usb_set_mode; + + ds3dev[pad].oldled[0] = 0; + ds3dev[pad].oldled[1] = 0; + ds3dev[pad].lrum = 0; + ds3dev[pad].rrum = 0; + ds3dev[pad].update_rum = 1; + ds3dev[pad].sema = -1; + ds3dev[pad].cmd_sema = -1; + ds3dev[pad].controlEndp = -1; + ds3dev[pad].interruptEndp = -1; + + ds3dev[pad].data[0] = 0xFF; + ds3dev[pad].data[1] = 0xFF; + ds3dev[pad].analog_btn = 0; + + mips_memset(&ds3dev[pad].data[2], 0x7F, 4); + mips_memset(&ds3dev[pad].data[6], 0x00, 12); + + ds3dev[pad].sema = CreateMutex(IOP_MUTEX_UNLOCKED); + ds3dev[pad].cmd_sema = CreateMutex(IOP_MUTEX_UNLOCKED); + + if (ds3dev[pad].sema < 0 || ds3dev[pad].cmd_sema < 0) { + DPRINTF("DS3USB: Failed to allocate I/O semaphore.\n"); + return MODULE_NO_RESIDENT_END; + } + } + + if (UsbRegisterDriver(&usb_driver) != USB_RC_OK) { + DPRINTF("DS3USB: Error registering USB devices\n"); + return MODULE_NO_RESIDENT_END; + } + + return MODULE_RESIDENT_END; +} diff --git a/modules/pademu/ds34usb.h b/modules/pademu/ds3usb/ds3usb.h similarity index 55% rename from modules/pademu/ds34usb.h rename to modules/pademu/ds3usb/ds3usb.h index 2ab3532c5..7f25064cb 100644 --- a/modules/pademu/ds34usb.h +++ b/modules/pademu/ds3usb/ds3usb.h @@ -1,45 +1,31 @@ -#ifndef _DS34USB_H_ -#define _DS34USB_H_ +#ifndef _DS3USB_H_ +#define _DS3USB_H_ #include "irx.h" +#include "../pademu.h" -#define DS34_VID 0x054C // Sony Corporation -#define DS3_PID 0x0268 // PS3 Controller -#define DS4_PID 0x05C4 // PS4 Controller -#define DS4_PID_SLIM 0x09CC // PS4 Slim Controller - -#define DS3 0 -#define DS4 1 +#define SONY_VID 0x054C // Sony Corporation +#define DS3_PID 0x0268 // PS3 Controller #define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer -typedef struct _usb_ds34 +typedef struct { - int devId; + pad_device_t dev; + int usb_id; int sema; int cmd_sema; int controlEndp; int interruptEndp; - int outEndp; - u8 enabled; - u8 status; - u8 type; //0 - ds3, 1 - ds4 - u8 oldled[4]; //rgb for ds4 and blink + int usb_resultcode; + u8 oldled[2]; u8 lrum; u8 rrum; u8 update_rum; u8 data[18]; u8 analog_btn; u8 btn_delay; -} ds34usb_device; - -enum eDS34USBStatus { - DS34USB_STATE_DISCONNECTED = 0x00, - DS34USB_STATE_AUTHORIZED = 0x01, - DS34USB_STATE_CONFIGURED = 0x02, - DS34USB_STATE_CONNECTED = 0x04, - DS34USB_STATE_RUNNING = 0x08, -}; +} ds3usb_device; enum eHID { // {{{ @@ -67,8 +53,10 @@ enum eHID { // }}} }; -struct ds3report +typedef struct { + u8 ReportID; + u8 Zero; union { u8 ButtonStateL; // Main buttons Low @@ -128,67 +116,6 @@ struct ds3report s16 AccelZ; s16 GyroZ; -} __attribute__((packed)); - -struct ds4report -{ - u8 ReportID; - u8 LeftStickX; // left Joystick X axis 0 - 255, 128 is mid - u8 LeftStickY; // left Joystick Y axis 0 - 255, 128 is mid - u8 RightStickX; // right Joystick X axis 0 - 255, 128 is mid - u8 RightStickY; // right Joystick Y axis 0 - 255, 128 is mid - u8 Dpad : 4; // hat format, 0x08 is released, 0=N, 1=NE, 2=E, 3=SE, 4=S, 5=SW, 6=W, 7=NW - u8 Square : 1; - u8 Cross : 1; - u8 Circle : 1; - u8 Triangle : 1; - u8 L1 : 1; - u8 R1 : 1; - u8 L2 : 1; - u8 R2 : 1; - u8 Share : 1; - u8 Option : 1; - u8 L3 : 1; - u8 R3 : 1; - u8 PSButton : 1; - u8 TPad : 1; - u8 Counter1 : 6; // counts up by 1 per report - u8 PressureL2; // digital Pad L2 button Pressure 0 - 255 - u8 PressureR2; // digital Pad R2 button Pressure 0 - 255 - u8 Counter2; - u8 Counter3; - u8 Battery; // battery level from 0x00 to 0xff - s16 AccelX; - s16 AccelY; - s16 AccelZ; - s16 GyroZ; - s16 GyroY; - s16 GyroX; - u8 Reserved1[5]; // Unknown - u8 Power : 4; // from 0x0 to 0xA - charging, 0xB - charged - u8 Usb_plugged : 1; - u8 Headphones : 1; - u8 Microphone : 1; - u8 Padding : 1; - u8 Reserved2[2]; // Unknown - u8 TPpack; // number of trackpad packets (0x00 to 0x04) - u8 PackCounter; // packet counter - u8 Finger1ID : 7; // counter - u8 Finger1Active : 1; // 0 - active, 1 - unactive - u16 Finger1X : 12; // finger 1 coordinates resolution 1920x943 - u16 Finger1Y : 12; - u8 Finger2ID : 7; - u8 Finger2Active : 1; - u16 Finger2X : 12; // finger 2 coordinates resolution 1920x943 - u16 Finger2Y : 12; - -} __attribute__((packed)); - -int ds34usb_init(u8 pads, u8 options); -int ds34usb_get_status(int port); -void ds34usb_reset(); -int ds34usb_get_data(u8 *dst, int size, int port); -void ds34usb_set_rumble(u8 lrum, u8 rrum, int port); -void ds34usb_set_mode(int mode, int lock, int port); +} __attribute__((packed)) ds3report_t; #endif diff --git a/modules/pademu/ds3usb/imports.lst b/modules/pademu/ds3usb/imports.lst new file mode 100644 index 000000000..e36f592dd --- /dev/null +++ b/modules/pademu/ds3usb/imports.lst @@ -0,0 +1,68 @@ +loadcore_IMPORTS_start +I_RegisterLibraryEntries +I_ReleaseLibraryEntries +I_GetLoadcoreInternalData +I_QueryLibraryEntryTable +I_SetRebootTimeLibraryHandlingMode +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_PollSema +I_DeleteSema +I_iSignalSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_StartThread +I_CreateThread +I_DeleteThread +I_DelayThread +I_GetThreadId +I_SleepThread +I_WakeupThread +I_TerminateThread +I_SetAlarm +I_CancelAlarm +thbase_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +usbd_IMPORTS_start +I_sceUsbdScanStaticDescriptor +I_sceUsbdOpenPipe +I_sceUsbdClosePipe +I_sceUsbdOpenPipeAligned +I_sceUsbdSetPrivateData +I_sceUsbdTransferPipe +I_sceUsbdRegisterLdd +usbd_IMPORTS_end + +sysclib_IMPORTS_start +I_strncmp +#ifndef USE_SMSUTILS +I_memset +I_memcpy +#endif +sysclib_IMPORTS_end + +#ifdef USE_SMSUTILS +smsutils_IMPORTS_start +I_mips_memset +I_mips_memcpy +smsutils_IMPORTS_end +#endif + +pademu_IMPORTS_start +I_pademu_connect +I_pademu_disconnect +pademu_IMPORTS_end diff --git a/modules/pademu/ds3usb/irx_imports.h b/modules/pademu/ds3usb/irx_imports.h new file mode 100644 index 000000000..280115628 --- /dev/null +++ b/modules/pademu/ds3usb/irx_imports.h @@ -0,0 +1,40 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "dmacman.h" +#include "intrman.h" +#include "iomanX.h" +#include "libsd.h" +#include "loadcore.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thmsgbx.h" +#include "thsemap.h" +#include "usbd.h" +#include "vblank.h" +#include "xloadcore.h" + +#include "../pademu.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/modules/pademu/ds4bt/Makefile b/modules/pademu/ds4bt/Makefile new file mode 100644 index 000000000..163fa6bca --- /dev/null +++ b/modules/pademu/ds4bt/Makefile @@ -0,0 +1,22 @@ + +IOP_BIN = ds4bt.irx +IOP_OBJS = ds4bt.o imports.o +IOP_OBJS_DIR = obj.ds4bt/ + +IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%) + +IOP_CFLAGS += -Wall -fno-builtin -DUSE_SMSUTILS +IOP_LDFLAGS += -s + +all: OBJ_DIR $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +rebuild: clean all + +OBJ_DIR: + mkdir -p $(IOP_OBJS_DIR) + +include $(PS2SDK)/Defs.make +include ../Rules.make diff --git a/modules/pademu/ds4bt/ds4bt.c b/modules/pademu/ds4bt/ds4bt.c new file mode 100644 index 000000000..f8cd5bab9 --- /dev/null +++ b/modules/pademu/ds4bt/ds4bt.c @@ -0,0 +1,215 @@ +#include "irx.h" +#include "types.h" +#include "loadcore.h" +#include "stdio.h" +#include "sifrpc.h" +#include "sysclib.h" +#include "thbase.h" +#include "thsemap.h" +#include "../pademu.h" +#include "../btstack/btstack.h" +#include "ds4bt.h" + +//#define DPRINTF(x...) printf(x) +#define DPRINTF(x...) + +static u8 rgbled_patterns[][2][3] = + { + {{0x00, 0x00, 0x10}, {0x00, 0x00, 0x7F}}, // light blue/blue + {{0x00, 0x10, 0x00}, {0x00, 0x7F, 0x00}}, // light green/green/ + {{0x10, 0x10, 0x00}, {0x7F, 0x7F, 0x00}}, // light yellow/yellow + {{0x00, 0x10, 0x10}, {0x00, 0x7F, 0x7F}}, // light cyan/cyan +}; + +int ds4bt_connect(bt_paddata_t *data, u8 *str, int size); +void ds4bt_init(bt_paddata_t *data, int id); +void ds4bt_read_report(bt_paddata_t *data, u8 *in, int bytes); +void ds4bt_rumble(bt_paddata_t *data); + +bt_paddrv_t ds4btdrv = {0x0070, 0x0071, ds4bt_connect, ds4bt_init, ds4bt_read_report, ds4bt_rumble}; + +#define MAX_DELAY 10 +#define CMD_DELAY 2 + +int ds4bt_connect(bt_paddata_t *data, u8 *str, int size) +{ + if (strncmp(str, "Wireless Controller", 19) == 0) { + DPRINTF("BS4BT: %s \n", str); + return 1; + } + return 0; +} + +void ds4bt_init(bt_paddata_t *data, int id) +{ + u8 init_buf[2]; + + init_buf[0] = HID_THDR_GET_REPORT_FEATURE; // THdr + init_buf[1] = PS4_02_REPORT_ID; // Report ID + + btstack_hid_command(&ds4btdrv, init_buf, 2, data->id); + + data->id = id; + data->led[0] = rgbled_patterns[id][1][0]; + data->led[1] = rgbled_patterns[id][1][1]; + data->led[2] = rgbled_patterns[id][1][2]; + data->led[3] = 0; + DelayThread(CMD_DELAY); + ds4bt_rumble(data); + data->btn_delay = 0xFF; +} + +void ds4bt_read_report(bt_paddata_t *data, u8 *in, int bytes) +{ + if (in[8] == HID_THDR_DATA_INPUT) { + ds4report_t *report = (ds4report_t *)(in + 9); + if (report->ReportID == PS4_11_REPORT_ID) { + u8 up = 0, down = 0, left = 0, right = 0; + switch (report->Dpad) { + case 0: + up = 1; + break; + case 1: + up = 1; + right = 1; + break; + case 2: + right = 1; + break; + case 3: + down = 1; + right = 1; + break; + case 4: + down = 1; + break; + case 5: + down = 1; + left = 1; + break; + case 6: + left = 1; + break; + case 7: + up = 1; + left = 1; + break; + case 8: + up = 0; + down = 0; + left = 0; + right = 0; + break; + } + + if (bytes > 63 && report->TPad) { + if (!report->Finger1Active) { + if (report->Finger1X < 960) + report->Share = 1; + else + report->Option = 1; + } + + if (!report->Finger2Active) { + if (report->Finger2X < 960) + report->Share = 1; + else + report->Option = 1; + } + } + + data->data[0] = ~(report->Share | report->L3 << 1 | report->R3 << 2 | report->Option << 3 | up << 4 | right << 5 | down << 6 | left << 7); + data->data[1] = ~(report->L2 | report->R2 << 1 | report->L1 << 2 | report->R1 << 3 | report->Triangle << 4 | report->Circle << 5 | report->Cross << 6 | report->Square << 7); + + data->data[2] = report->RightStickX; //rx + data->data[3] = report->RightStickY; //ry + data->data[4] = report->LeftStickX; //lx + data->data[5] = report->LeftStickY; //ly + + data->data[6] = right * 255; //right + data->data[7] = left * 255; //left + data->data[8] = up * 255; //up + data->data[9] = down * 255; //down + + data->data[10] = report->Triangle * 255; //triangle + data->data[11] = report->Circle * 255; //circle + data->data[12] = report->Cross * 255; //cross + data->data[13] = report->Square * 255; //square + + data->data[14] = report->L1 * 255; //L1 + data->data[15] = report->R1 * 255; //R1 + data->data[16] = report->PressureL2; //L2 + data->data[17] = report->PressureR2; //R2 + + if (report->PSButton) { //display battery level + if (report->Share && (data->btn_delay == MAX_DELAY)) { //PS + SELECT + if (data->analog_btn < 2) //unlocked mode + data->analog_btn = !data->analog_btn; + + data->led[0] = rgbled_patterns[data->id][(data->analog_btn & 1)][0]; + data->led[1] = rgbled_patterns[data->id][(data->analog_btn & 1)][1]; + data->led[2] = rgbled_patterns[data->id][(data->analog_btn & 1)][2]; + data->btn_delay = 1; + } else { + data->led[0] = report->Battery; + data->led[1] = 0; + data->led[2] = 0; + + if (data->btn_delay < MAX_DELAY) + data->btn_delay++; + } + } else { + data->led[0] = rgbled_patterns[data->id][(data->analog_btn & 1)][0]; + data->led[1] = rgbled_patterns[data->id][(data->analog_btn & 1)][1]; + data->led[2] = rgbled_patterns[data->id][(data->analog_btn & 1)][2]; + + if (data->btn_delay > 0) + data->btn_delay--; + } + + if (report->Power != 0xB && report->Usb_plugged) //charging + data->led[3] = 1; + else + data->led[3] = 0; + + if (data->btn_delay > 0) { + data->update_rum = 1; + } + } + } else { + DPRINTF("BS4BT: Unmanaged Input Report: THDR 0x%02X ", in[8]); + DPRINTF("BS4BT: ID 0x%02X \n", in[9]); + } +} + +void ds4bt_rumble(bt_paddata_t *data) +{ + u8 led_buf[PS4_11_REPORT_LEN + 2]; + mips_memset(led_buf, 0, sizeof(led_buf)); + + led_buf[0] = HID_THDR_SET_REPORT_OUTPUT; // THdr + led_buf[1] = PS4_11_REPORT_ID; // Report ID + led_buf[2] = 0x80; //update rate 1000Hz + led_buf[4] = 0xFF; + + led_buf[7] = data->rrum * 255; + led_buf[8] = data->lrum; + + led_buf[9] = data->led[0]; //r + led_buf[10] = data->led[1]; //g + led_buf[11] = data->led[2]; //b + + if (data->led[3]) { //means charging, so blink + led_buf[12] = 0x80; // Time to flash bright (255 = 2.5 seconds) + led_buf[13] = 0x80; // Time to flash dark (255 = 2.5 seconds) + } + + btstack_hid_command(&ds4btdrv, led_buf, PS4_11_REPORT_LEN + 2, data->id); +} + +int _start(int argc, char *argv[]) +{ + btstack_register(&ds4btdrv); + + return MODULE_RESIDENT_END; +} diff --git a/modules/pademu/ds4bt/ds4bt.h b/modules/pademu/ds4bt/ds4bt.h new file mode 100644 index 000000000..7a116285d --- /dev/null +++ b/modules/pademu/ds4bt/ds4bt.h @@ -0,0 +1,89 @@ +#ifndef _DS4BT_H_ +#define _DS4BT_H_ + +#define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer + +enum eHID { + // {{{ + /* HID event flag */ + HID_FLAG_STATUS_REPORTED = 0x01, + HID_FLAG_BUTTONS_CHANGED = 0x02, + HID_FLAG_EXTENSION = 0x04, + HID_FLAG_COMMAND_SUCCESS = 0x08, + + /* Bluetooth HID Transaction Header (THdr) */ + HID_THDR_GET_REPORT_FEATURE = 0x43, + HID_THDR_SET_REPORT_OUTPUT = 0x52, + HID_THDR_SET_REPORT_FEATURE = 0x53, + HID_THDR_DATA_INPUT = 0xa1, + + /* Defines of various parameters for PS3 Game controller reports */ + PS3_F4_REPORT_ID = 0xF4, + PS3_F4_REPORT_LEN = 0x04, + + PS3_01_REPORT_ID = 0x01, + PS3_01_REPORT_LEN = 0x30, + + PS4_02_REPORT_ID = 0x02, + PS4_11_REPORT_ID = 0x11, + PS4_11_REPORT_LEN = 0x4D, + + // }}} +}; + +typedef struct +{ + u8 ReportID; + u8 Unk; + u8 ReportID2; + u8 LeftStickX; // left Joystick X axis 0 - 255, 128 is mid + u8 LeftStickY; // left Joystick Y axis 0 - 255, 128 is mid + u8 RightStickX; // right Joystick X axis 0 - 255, 128 is mid + u8 RightStickY; // right Joystick Y axis 0 - 255, 128 is mid + u8 Dpad : 4; // hat format, 0x08 is released, 0=N, 1=NE, 2=E, 3=SE, 4=S, 5=SW, 6=W, 7=NW + u8 Square : 1; + u8 Cross : 1; + u8 Circle : 1; + u8 Triangle : 1; + u8 L1 : 1; + u8 R1 : 1; + u8 L2 : 1; + u8 R2 : 1; + u8 Share : 1; + u8 Option : 1; + u8 L3 : 1; + u8 R3 : 1; + u8 PSButton : 1; + u8 TPad : 1; + u8 Counter1 : 6; // counts up by 1 per report + u8 PressureL2; // digital Pad L2 button Pressure 0 - 255 + u8 PressureR2; // digital Pad R2 button Pressure 0 - 255 + u8 Counter2; + u8 Counter3; + u8 Battery; // battery level from 0x00 to 0xff + s16 AccelX; + s16 AccelY; + s16 AccelZ; + s16 GyroZ; + s16 GyroY; + s16 GyroX; + u8 Reserved1[5]; // Unknown + u8 Power : 4; // from 0x0 to 0xA - charging, 0xB - charged + u8 Usb_plugged : 1; + u8 Headphones : 1; + u8 Microphone : 1; + u8 Padding : 1; + u8 Reserved2[2]; // Unknown + u8 TPpack; // number of trackpad packets (0x00 to 0x04) + u8 PackCounter; // packet counter + u8 Finger1ID : 7; // counter + u8 Finger1Active : 1; // 0 - active, 1 - unactive + u16 Finger1X : 12; // finger 1 coordinates resolution 1920x943 + u16 Finger1Y : 12; + u8 Finger2ID : 7; + u8 Finger2Active : 1; + u16 Finger2X : 12; // finger 2 coordinates resolution 1920x943 + u16 Finger2Y : 12; +} __attribute__((packed)) ds4report_t; + +#endif diff --git a/modules/pademu/ds4bt/imports.lst b/modules/pademu/ds4bt/imports.lst new file mode 100644 index 000000000..b0de84ee7 --- /dev/null +++ b/modules/pademu/ds4bt/imports.lst @@ -0,0 +1,74 @@ +loadcore_IMPORTS_start +I_RegisterLibraryEntries +I_ReleaseLibraryEntries +I_GetLoadcoreInternalData +I_QueryLibraryEntryTable +I_SetRebootTimeLibraryHandlingMode +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_PollSema +I_DeleteSema +I_iSignalSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_StartThread +I_CreateThread +I_DeleteThread +I_DelayThread +I_GetThreadId +I_SleepThread +I_WakeupThread +I_TerminateThread +I_SetAlarm +I_CancelAlarm +thbase_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +usbd_IMPORTS_start +I_sceUsbdScanStaticDescriptor +I_sceUsbdOpenPipe +I_sceUsbdClosePipe +I_sceUsbdOpenPipeAligned +I_sceUsbdSetPrivateData +I_sceUsbdTransferPipe +I_sceUsbdRegisterLdd +usbd_IMPORTS_end + +sysclib_IMPORTS_start +I_strncmp +#ifndef USE_SMSUTILS +I_memset +I_memcpy +#endif +sysclib_IMPORTS_end + +#ifdef USE_SMSUTILS +smsutils_IMPORTS_start +I_mips_memset +I_mips_memcpy +smsutils_IMPORTS_end +#endif + +pademu_IMPORTS_start +I_pademu_connect +I_pademu_disconnect +pademu_IMPORTS_end + +btstack_IMPORTS_start +I_btstack_register +I_btstack_unregister +I_btstack_hid_command +btstack_IMPORTS_end diff --git a/modules/pademu/ds4bt/irx_imports.h b/modules/pademu/ds4bt/irx_imports.h new file mode 100644 index 000000000..c0508fecb --- /dev/null +++ b/modules/pademu/ds4bt/irx_imports.h @@ -0,0 +1,41 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "dmacman.h" +#include "intrman.h" +#include "iomanX.h" +#include "libsd.h" +#include "loadcore.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thmsgbx.h" +#include "thsemap.h" +#include "usbd.h" +#include "vblank.h" +#include "xloadcore.h" + +#include "../pademu.h" +#include "../btstack/btstack.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/modules/pademu/ds4usb/Makefile b/modules/pademu/ds4usb/Makefile new file mode 100644 index 000000000..1b8f1980b --- /dev/null +++ b/modules/pademu/ds4usb/Makefile @@ -0,0 +1,22 @@ + +IOP_BIN = ds4usb.irx +IOP_OBJS = ds4usb.o imports.o +IOP_OBJS_DIR = obj.ds4usb/ + +IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%) + +IOP_CFLAGS += -Wall -fno-builtin -DUSE_SMSUTILS +IOP_LDFLAGS += -s + +all: OBJ_DIR $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +rebuild: clean all + +OBJ_DIR: + mkdir -p $(IOP_OBJS_DIR) + +include $(PS2SDK)/Defs.make +include ../Rules.make diff --git a/modules/pademu/ds4usb/ds4usb.c b/modules/pademu/ds4usb/ds4usb.c new file mode 100644 index 000000000..a4c1441c6 --- /dev/null +++ b/modules/pademu/ds4usb/ds4usb.c @@ -0,0 +1,479 @@ +#include "types.h" +#include "loadcore.h" +#include "stdio.h" +#include "sifrpc.h" +#include "sysclib.h" +#include "usbd.h" +#include "usbd_macro.h" +#include "thbase.h" +#include "thsemap.h" +#include "ds4usb.h" + +//#define DPRINTF(x...) printf(x) +#define DPRINTF(x...) + +#define REQ_USB_OUT (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) +#define REQ_USB_IN (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) + +#define MAX_PADS 4 + +static u8 rgbled_patterns[][2][3] = + { + {{0x00, 0x00, 0x10}, {0x00, 0x00, 0x7F}}, // light blue/blue + {{0x00, 0x10, 0x00}, {0x00, 0x7F, 0x00}}, // light green/green + {{0x10, 0x10, 0x00}, {0x7F, 0x7F, 0x00}}, // light yellow/yellow + {{0x00, 0x10, 0x10}, {0x00, 0x7F, 0x7F}}, // light cyan/cyan +}; + +static u8 usb_buf[MAX_BUFFER_SIZE + 32] __attribute((aligned(4))) = {0}; + +int usb_probe(int devId); +int usb_connect(int devId); +int usb_disconnect(int devId); + +static void usb_release(int pad); +static void usb_config_set(int result, int count, void *arg); + +UsbDriver usb_driver = {NULL, NULL, "ds4usb", usb_probe, usb_connect, usb_disconnect}; + +static void readReport(u8 *data, int pad); +static int LEDRumble(u8 *led, u8 lrum, u8 rrum, int pad); + +ds4usb_device ds4dev[MAX_PADS]; + +int usb_probe(int devId) +{ + UsbDeviceDescriptor *device = NULL; + + DPRINTF("DS4USB: probe: devId=%i\n", devId); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + if (device == NULL) { + DPRINTF("DS4USB: Error - Couldn't get device descriptor\n"); + return 0; + } + + if (device->idVendor == SONY_VID && (device->idProduct == DS4_PID || device->idProduct == DS4_PID_SLIM)) + return 1; + + return 0; +} + +int usb_connect(int devId) +{ + int pad, epCount; + UsbDeviceDescriptor *device; + UsbConfigDescriptor *config; + UsbInterfaceDescriptor *interface; + UsbEndpointDescriptor *endpoint; + + DPRINTF("DS4USB: connect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (ds4dev[pad].usb_id == -1) + break; + } + + if (pad >= MAX_PADS) { + DPRINTF("DS4USB: Error - only %d device allowed !\n", MAX_PADS); + return 1; + } + + PollSema(ds4dev[pad].sema); + + ds4dev[pad].dev.id = pad; + ds4dev[pad].usb_id = devId; + ds4dev[pad].controlEndp = UsbOpenEndpoint(devId, NULL); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG); + interface = (UsbInterfaceDescriptor *)((char *)config + config->bLength); + epCount = interface->bNumEndpoints - 1; + + if (device->idProduct == DS4_PID_SLIM) { + epCount = 20; // ds4 v2 returns interface->bNumEndpoints as 0 + } + + endpoint = (UsbEndpointDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_ENDPOINT); + + do { + if (endpoint->bmAttributes == USB_ENDPOINT_XFER_INT) { + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && ds4dev[pad].interruptEndp < 0) { + ds4dev[pad].interruptEndp = UsbOpenEndpointAligned(devId, endpoint); + DPRINTF("DS4USB: register Event endpoint id =%i addr=%02X packetSize=%i\n", ds4dev[pad].interruptEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT && ds4dev[pad].outEndp < 0) { + ds4dev[pad].outEndp = UsbOpenEndpointAligned(devId, endpoint); + DPRINTF("DS34USB: register Output endpoint id =%i addr=%02X packetSize=%i\n", ds4pad[pad].outEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + } + + endpoint = (UsbEndpointDescriptor *)((char *)endpoint + endpoint->bLength); + + } while (epCount--); + + if (ds4dev[pad].interruptEndp < 0 || ds4dev[pad].outEndp < 0) { + usb_release(pad); + return 1; + } + + UsbSetDeviceConfiguration(ds4dev[pad].controlEndp, config->bConfigurationValue, usb_config_set, (void *)pad); + SignalSema(ds4dev[pad].sema); + + return 0; +} + +int usb_disconnect(int devId) +{ + u8 pad; + + DPRINTF("DS4USB: disconnect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (ds4dev[pad].usb_id == devId) { + pademu_disconnect(&ds4dev[pad].dev); + break; + } + } + + if (pad < MAX_PADS) + usb_release(pad); + + return 0; +} + +static void usb_release(int pad) +{ + PollSema(ds4dev[pad].sema); + + if (ds4dev[pad].interruptEndp >= 0) + UsbCloseEndpoint(ds4dev[pad].interruptEndp); + + if (ds4dev[pad].outEndp >= 0) + UsbCloseEndpoint(ds4dev[pad].outEndp); + + ds4dev[pad].controlEndp = -1; + ds4dev[pad].interruptEndp = -1; + ds4dev[pad].outEndp = -1; + ds4dev[pad].usb_id = -1; + + SignalSema(ds4dev[pad].sema); +} + +static void usb_data_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("DS4USB: usb_data_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + ds4dev[pad].usb_resultcode = resultCode; + + SignalSema(ds4dev[pad].sema); +} + +static void usb_cmd_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("DS4USB: usb_cmd_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + SignalSema(ds4dev[pad].cmd_sema); +} + +static void usb_config_set(int result, int count, void *arg) +{ + int pad = (int)arg; + u8 led[4]; + + PollSema(ds4dev[pad].sema); + + pademu_connect(&ds4dev[pad].dev); + + led[0] = rgbled_patterns[ds4dev[pad].dev.id][1][0]; + led[1] = rgbled_patterns[ds4dev[pad].dev.id][1][1]; + led[2] = rgbled_patterns[ds4dev[pad].dev.id][1][2]; + led[3] = 0; + + LEDRumble(led, 0, 0, pad); + SignalSema(ds4dev[pad].sema); +} + +#define MAX_DELAY 10 + +static void readReport(u8 *data, int pad) +{ + ds4report_t *report = (ds4report_t *)data; + if (report->ReportID == 0x01) { + u8 up = 0, down = 0, left = 0, right = 0; + + switch (report->Dpad) { + case 0: + up = 1; + break; + case 1: + up = 1; + right = 1; + break; + case 2: + right = 1; + break; + case 3: + down = 1; + right = 1; + break; + case 4: + down = 1; + break; + case 5: + down = 1; + left = 1; + break; + case 6: + left = 1; + break; + case 7: + up = 1; + left = 1; + break; + case 8: + up = 0; + down = 0; + left = 0; + right = 0; + break; + } + + if (report->TPad) { + if (!report->Finger1Active) { + if (report->Finger1X < 960) + report->Share = 1; + else + report->Option = 1; + } + + if (!report->Finger2Active) { + if (report->Finger2X < 960) + report->Share = 1; + else + report->Option = 1; + } + } + + ds4dev[pad].data[0] = ~(report->Share | report->L3 << 1 | report->R3 << 2 | report->Option << 3 | up << 4 | right << 5 | down << 6 | left << 7); + ds4dev[pad].data[1] = ~(report->L2 | report->R2 << 1 | report->L1 << 2 | report->R1 << 3 | report->Triangle << 4 | report->Circle << 5 | report->Cross << 6 | report->Square << 7); + + ds4dev[pad].data[2] = report->RightStickX; //rx + ds4dev[pad].data[3] = report->RightStickY; //ry + ds4dev[pad].data[4] = report->LeftStickX; //lx + ds4dev[pad].data[5] = report->LeftStickY; //ly + + ds4dev[pad].data[6] = right * 255; //right + ds4dev[pad].data[7] = left * 255; //left + ds4dev[pad].data[8] = up * 255; //up + ds4dev[pad].data[9] = down * 255; //down + + ds4dev[pad].data[10] = report->Triangle * 255; //triangle + ds4dev[pad].data[11] = report->Circle * 255; //circle + ds4dev[pad].data[12] = report->Cross * 255; //cross + ds4dev[pad].data[13] = report->Square * 255; //square + + ds4dev[pad].data[14] = report->L1 * 255; //L1 + ds4dev[pad].data[15] = report->R1 * 255; //R1 + ds4dev[pad].data[16] = report->PressureL2; //L2 + ds4dev[pad].data[17] = report->PressureR2; //R2 + + if (report->PSButton) { //display battery level + if (report->Share && (ds4dev[pad].btn_delay == MAX_DELAY)) { //PS + Share + if (ds4dev[pad].analog_btn < 2) //unlocked mode + ds4dev[pad].analog_btn = !ds4dev[pad].analog_btn; + + ds4dev[pad].oldled[0] = rgbled_patterns[pad][(ds4dev[pad].analog_btn & 1)][0]; + ds4dev[pad].oldled[1] = rgbled_patterns[pad][(ds4dev[pad].analog_btn & 1)][1]; + ds4dev[pad].oldled[2] = rgbled_patterns[pad][(ds4dev[pad].analog_btn & 1)][2]; + ds4dev[pad].btn_delay = 1; + } else { + ds4dev[pad].oldled[0] = report->Battery; + ds4dev[pad].oldled[1] = 0; + ds4dev[pad].oldled[2] = 0; + + if (ds4dev[pad].btn_delay < MAX_DELAY) + ds4dev[pad].btn_delay++; + } + } else { + ds4dev[pad].oldled[0] = rgbled_patterns[pad][(ds4dev[pad].analog_btn & 1)][0]; + ds4dev[pad].oldled[1] = rgbled_patterns[pad][(ds4dev[pad].analog_btn & 1)][1]; + ds4dev[pad].oldled[2] = rgbled_patterns[pad][(ds4dev[pad].analog_btn & 1)][2]; + + if (ds4dev[pad].btn_delay > 0) + ds4dev[pad].btn_delay--; + } + + if (report->Power != 0xB && report->Usb_plugged) //charging + ds4dev[pad].oldled[3] = 1; + else + ds4dev[pad].oldled[3] = 0; + + if (ds4dev[pad].btn_delay > 0) { + ds4dev[pad].update_rum = 1; + } + } +} + +static int LEDRumble(u8 *led, u8 lrum, u8 rrum, int pad) +{ + PollSema(ds4dev[pad].cmd_sema); + + usb_buf[0] = 0x05; + usb_buf[1] = 0xFF; + + usb_buf[4] = rrum * 255; //ds4 has full control + usb_buf[5] = lrum; + + usb_buf[6] = led[0]; //r + usb_buf[7] = led[1]; //g + usb_buf[8] = led[2]; //b + + if (led[3]) //means charging, so blink + { + usb_buf[9] = 0x80; // Time to flash bright (255 = 2.5 seconds) + usb_buf[10] = 0x80; // Time to flash dark (255 = 2.5 seconds) + } + + ds4dev[pad].oldled[0] = led[0]; + ds4dev[pad].oldled[1] = led[1]; + ds4dev[pad].oldled[2] = led[2]; + ds4dev[pad].oldled[3] = led[3]; + + return UsbInterruptTransfer(ds4dev[pad].outEndp, usb_buf, 32, usb_cmd_cb, (void *)pad); +} + +static unsigned int timeout(void *arg) +{ + int sema = (int)arg; + iSignalSema(sema); + return 0; +} + +static void TransferWait(int sema) +{ + iop_sys_clock_t cmd_timeout; + + cmd_timeout.lo = 200000; + cmd_timeout.hi = 0; + + if (SetAlarm(&cmd_timeout, timeout, (void *)sema) == 0) { + WaitSema(sema); + CancelAlarm(timeout, NULL); + } +} + +void ds4usb_set_rumble(u8 lrum, u8 rrum, int port) +{ + WaitSema(ds4dev[port].sema); + + ds4dev[port].update_rum = 1; + ds4dev[port].lrum = lrum; + ds4dev[port].rrum = rrum; + + SignalSema(ds4dev[port].sema); +} + +int ds4usb_get_data(u8 *dst, int size, int port) +{ + int ret = 0; + + WaitSema(ds4dev[port].sema); + + PollSema(ds4dev[port].sema); + + ret = UsbInterruptTransfer(ds4dev[port].interruptEndp, usb_buf, MAX_BUFFER_SIZE, usb_data_cb, (void *)port); + + if (ret == USB_RC_OK) { + TransferWait(ds4dev[port].sema); + if (!ds4dev[port].usb_resultcode) + readReport(usb_buf, port); + + ds4dev[port].usb_resultcode = 1; + } else { + DPRINTF("DS4USB: DS4USB_get_data usb transfer error %d\n", ret); + } + + mips_memcpy(dst, ds4dev[port].data, size); + ret = ds4dev[port].analog_btn & 1; + + if (ds4dev[port].update_rum) { + ret = LEDRumble(ds4dev[port].oldled, ds4dev[port].lrum, ds4dev[port].rrum, port); + if (ret == USB_RC_OK) + TransferWait(ds4dev[port].cmd_sema); + else + DPRINTF("DS4USB: LEDRumble usb transfer error %d\n", ret); + + ds4dev[port].update_rum = 0; + } + + SignalSema(ds4dev[port].sema); + + return ret; +} + +void ds4usb_set_mode(int mode, int lock, int port) +{ + if (lock == 3) + ds4dev[port].analog_btn = 3; + else + ds4dev[port].analog_btn = mode; +} + +void ds4usb_reset() +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) + usb_release(pad); +} + +int _start(int argc, char *argv[]) +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) { + ds4dev[pad].usb_id = -1; + ds4dev[pad].dev.id = -1; + ds4dev[pad].dev.pad_get_data = ds4usb_get_data; + ds4dev[pad].dev.pad_set_rumble = ds4usb_set_rumble; + ds4dev[pad].dev.pad_set_mode = ds4usb_set_mode; + + ds4dev[pad].oldled[0] = 0; + ds4dev[pad].oldled[1] = 0; + ds4dev[pad].lrum = 0; + ds4dev[pad].rrum = 0; + ds4dev[pad].update_rum = 1; + ds4dev[pad].sema = -1; + ds4dev[pad].cmd_sema = -1; + ds4dev[pad].controlEndp = -1; + ds4dev[pad].interruptEndp = -1; + ds4dev[pad].outEndp = -1; + + ds4dev[pad].data[0] = 0xFF; + ds4dev[pad].data[1] = 0xFF; + ds4dev[pad].analog_btn = 0; + + mips_memset(&ds4dev[pad].data[2], 0x7F, 4); + mips_memset(&ds4dev[pad].data[6], 0x00, 12); + + ds4dev[pad].sema = CreateMutex(IOP_MUTEX_UNLOCKED); + ds4dev[pad].cmd_sema = CreateMutex(IOP_MUTEX_UNLOCKED); + + if (ds4dev[pad].sema < 0 || ds4dev[pad].cmd_sema < 0) { + DPRINTF("DS4USB: Failed to allocate I/O semaphore.\n"); + return MODULE_NO_RESIDENT_END; + } + } + + if (UsbRegisterDriver(&usb_driver) != USB_RC_OK) { + DPRINTF("DS4USB: Error registering USB devices\n"); + return MODULE_NO_RESIDENT_END; + } + + return MODULE_RESIDENT_END; +} diff --git a/modules/pademu/ds4usb/ds4usb.h b/modules/pademu/ds4usb/ds4usb.h new file mode 100644 index 000000000..b9f0272d7 --- /dev/null +++ b/modules/pademu/ds4usb/ds4usb.h @@ -0,0 +1,111 @@ +#ifndef _DS3USB_H_ +#define _DS3USB_H_ + +#include "irx.h" +#include "../pademu.h" + +#define SONY_VID 0x054C // Sony Corporation +#define DS4_PID 0x05C4 // PS4 Controller +#define DS4_PID_SLIM 0x09CC // PS4 Slim Controller + +#define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer + +typedef struct +{ + pad_device_t dev; + int usb_id; + int sema; + int cmd_sema; + int controlEndp; + int interruptEndp; + int outEndp; + int usb_resultcode; + u8 oldled[4]; + u8 lrum; + u8 rrum; + u8 update_rum; + u8 data[18]; + u8 analog_btn; + u8 btn_delay; +} ds4usb_device; + +enum eHID { + // {{{ + /* HID event flag */ + HID_FLAG_STATUS_REPORTED = 0x01, + HID_FLAG_BUTTONS_CHANGED = 0x02, + HID_FLAG_EXTENSION = 0x04, + HID_FLAG_COMMAND_SUCCESS = 0x08, + + /* USB HID Transaction Header (THdr) */ + HID_USB_GET_REPORT_FEATURE = 0x03, + HID_USB_SET_REPORT_OUTPUT = 0x02, + HID_USB_DATA_INPUT = 0x01, + + /* Defines of various parameters for PS3 Game controller reports */ + PS3_F4_REPORT_ID = 0xF4, + PS3_F4_REPORT_LEN = 0x04, + + PS3_01_REPORT_ID = 0x01, + PS3_01_REPORT_LEN = 0x30, + + PS4_02_REPORT_ID = 0x02, + PS4_11_REPORT_ID = 0x11, + PS4_11_REPORT_LEN = 0x4D, + // }}} +}; + +typedef struct +{ + u8 ReportID; + u8 LeftStickX; // left Joystick X axis 0 - 255, 128 is mid + u8 LeftStickY; // left Joystick Y axis 0 - 255, 128 is mid + u8 RightStickX; // right Joystick X axis 0 - 255, 128 is mid + u8 RightStickY; // right Joystick Y axis 0 - 255, 128 is mid + u8 Dpad : 4; // hat format, 0x08 is released, 0=N, 1=NE, 2=E, 3=SE, 4=S, 5=SW, 6=W, 7=NW + u8 Square : 1; + u8 Cross : 1; + u8 Circle : 1; + u8 Triangle : 1; + u8 L1 : 1; + u8 R1 : 1; + u8 L2 : 1; + u8 R2 : 1; + u8 Share : 1; + u8 Option : 1; + u8 L3 : 1; + u8 R3 : 1; + u8 PSButton : 1; + u8 TPad : 1; + u8 Counter1 : 6; // counts up by 1 per report + u8 PressureL2; // digital Pad L2 button Pressure 0 - 255 + u8 PressureR2; // digital Pad R2 button Pressure 0 - 255 + u8 Counter2; + u8 Counter3; + u8 Battery; // battery level from 0x00 to 0xff + s16 AccelX; + s16 AccelY; + s16 AccelZ; + s16 GyroZ; + s16 GyroY; + s16 GyroX; + u8 Reserved1[5]; // Unknown + u8 Power : 4; // from 0x0 to 0xA - charging, 0xB - charged + u8 Usb_plugged : 1; + u8 Headphones : 1; + u8 Microphone : 1; + u8 Padding : 1; + u8 Reserved2[2]; // Unknown + u8 TPpack; // number of trackpad packets (0x00 to 0x04) + u8 PackCounter; // packet counter + u8 Finger1ID : 7; // counter + u8 Finger1Active : 1; // 0 - active, 1 - unactive + u16 Finger1X : 12; // finger 1 coordinates resolution 1920x943 + u16 Finger1Y : 12; + u8 Finger2ID : 7; + u8 Finger2Active : 1; + u16 Finger2X : 12; // finger 2 coordinates resolution 1920x943 + u16 Finger2Y : 12; +} __attribute__((packed)) ds4report_t; + +#endif diff --git a/modules/pademu/ds4usb/imports.lst b/modules/pademu/ds4usb/imports.lst new file mode 100644 index 000000000..e36f592dd --- /dev/null +++ b/modules/pademu/ds4usb/imports.lst @@ -0,0 +1,68 @@ +loadcore_IMPORTS_start +I_RegisterLibraryEntries +I_ReleaseLibraryEntries +I_GetLoadcoreInternalData +I_QueryLibraryEntryTable +I_SetRebootTimeLibraryHandlingMode +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_PollSema +I_DeleteSema +I_iSignalSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_StartThread +I_CreateThread +I_DeleteThread +I_DelayThread +I_GetThreadId +I_SleepThread +I_WakeupThread +I_TerminateThread +I_SetAlarm +I_CancelAlarm +thbase_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +usbd_IMPORTS_start +I_sceUsbdScanStaticDescriptor +I_sceUsbdOpenPipe +I_sceUsbdClosePipe +I_sceUsbdOpenPipeAligned +I_sceUsbdSetPrivateData +I_sceUsbdTransferPipe +I_sceUsbdRegisterLdd +usbd_IMPORTS_end + +sysclib_IMPORTS_start +I_strncmp +#ifndef USE_SMSUTILS +I_memset +I_memcpy +#endif +sysclib_IMPORTS_end + +#ifdef USE_SMSUTILS +smsutils_IMPORTS_start +I_mips_memset +I_mips_memcpy +smsutils_IMPORTS_end +#endif + +pademu_IMPORTS_start +I_pademu_connect +I_pademu_disconnect +pademu_IMPORTS_end diff --git a/modules/pademu/ds4usb/irx_imports.h b/modules/pademu/ds4usb/irx_imports.h new file mode 100644 index 000000000..280115628 --- /dev/null +++ b/modules/pademu/ds4usb/irx_imports.h @@ -0,0 +1,40 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "dmacman.h" +#include "intrman.h" +#include "iomanX.h" +#include "libsd.h" +#include "loadcore.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thmsgbx.h" +#include "thsemap.h" +#include "usbd.h" +#include "vblank.h" +#include "xloadcore.h" + +#include "../pademu.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/modules/pademu/exports.tab b/modules/pademu/exports.tab index 40c6386e9..aab538ad3 100644 --- a/modules/pademu/exports.tab +++ b/modules/pademu/exports.tab @@ -5,6 +5,8 @@ DECLARE_EXPORT_TABLE(pademu, 1, 1) DECLARE_EXPORT(_exit) DECLARE_EXPORT(_retonly) DECLARE_EXPORT(pademu_hookSio2man) + DECLARE_EXPORT(pademu_connect) + DECLARE_EXPORT(pademu_disconnect) END_EXPORT_TABLE void _retonly() {} diff --git a/modules/pademu/hidusb/Makefile b/modules/pademu/hidusb/Makefile new file mode 100644 index 000000000..a1de4238d --- /dev/null +++ b/modules/pademu/hidusb/Makefile @@ -0,0 +1,22 @@ + +IOP_BIN = hidusb.irx +IOP_OBJS = hidusb.o imports.o +IOP_OBJS_DIR = obj.hidusb/ + +IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%) + +IOP_CFLAGS += -Wall -fno-builtin -DUSE_SMSUTILS +IOP_LDFLAGS += -s + +all: OBJ_DIR $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +rebuild: clean all + +OBJ_DIR: + mkdir -p $(IOP_OBJS_DIR) + +include $(PS2SDK)/Defs.make +include ../Rules.make diff --git a/modules/pademu/hidusb/hidusb.c b/modules/pademu/hidusb/hidusb.c new file mode 100644 index 000000000..0356c95c5 --- /dev/null +++ b/modules/pademu/hidusb/hidusb.c @@ -0,0 +1,511 @@ +#include "types.h" +#include "loadcore.h" +#include "stdio.h" +#include "sifrpc.h" +#include "sysclib.h" +#include "usbd.h" +#include "usbd_macro.h" +#include "thbase.h" +#include "thsemap.h" +#include "hidusb.h" + +//#define DPRINTF(x...) printf(x) +#define DPRINTF(x...) + +#define MAX_PADS 4 + +static u8 usb_buf[MAX_BUFFER_SIZE] __attribute((aligned(4))) = {0}; + +int usb_probe(int devId); +int usb_connect(int devId); +int usb_disconnect(int devId); + +static void usb_release(int pad); +static void usb_config_set(int result, int count, void *arg); + +UsbDriver usb_driver = {NULL, NULL, "hidusb", usb_probe, usb_connect, usb_disconnect}; + +int read_report_descriptor(u8 *data, int size, hidreport_t *report); +static void read_report(u8 *data, int pad); + +hidusb_device hiddev[MAX_PADS]; + +int usb_probe(int devId) +{ + UsbDeviceDescriptor *device; + UsbConfigDescriptor *config; + UsbInterfaceDescriptor *interface; + + DPRINTF("HIDUSB: probe: devId=%i\n", devId); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG); + interface = (UsbInterfaceDescriptor *)((char *)config + config->bLength); + + if (interface->bInterfaceClass != USB_CLASS_HID) + return 0; + + return 1; +} + +int usb_connect(int devId) +{ + int pad, epCount, hid_report_size; + UsbDeviceDescriptor *device; + UsbConfigDescriptor *config; + UsbInterfaceDescriptor *interface; + UsbEndpointDescriptor *endpoint; + UsbHidDescriptor *hid; + u8 buf[512]; + + DPRINTF("HIDUSB: connect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (hiddev[pad].usb_id == -1) + break; + } + + if (pad >= MAX_PADS) { + DPRINTF("HIDUSB: Error - only %d device allowed !\n", MAX_PADS); + return 1; + } + + PollSema(hiddev[pad].sema); + + hiddev[pad].dev.id = pad; + hiddev[pad].usb_id = devId; + hiddev[pad].controlEndp = UsbOpenEndpoint(devId, NULL); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG); + interface = (UsbInterfaceDescriptor *)((char *)config + config->bLength); + hid = (UsbHidDescriptor *)UsbGetDeviceStaticDescriptor(devId, interface, USB_DT_HID); + epCount = interface->bNumEndpoints - 1; + endpoint = (UsbEndpointDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_ENDPOINT); + + do { + if (endpoint->bmAttributes == USB_ENDPOINT_XFER_INT) { + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && hiddev[pad].interruptEndp < 0) { + hiddev[pad].interruptEndp = UsbOpenEndpointAligned(devId, endpoint); + DPRINTF("DS3USB: register Event endpoint id =%i addr=%02X packetSize=%i\n", hiddev[pad].interruptEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + } + + endpoint = (UsbEndpointDescriptor *)((char *)endpoint + endpoint->bLength); + + } while (epCount--); + + if (hiddev[pad].interruptEndp < 0) { + usb_release(pad); + return 1; + } + hid_report_size = (hid->Sub[0].wDescriptorLengthL | hid->Sub[0].wDescriptorLengthH << 8); + UsbControlTransfer(hiddev[pad].controlEndp, (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE), USB_REQ_GET_DESCRIPTOR, (hid->Sub[0].bDescriptorType << 8), interface->bInterfaceNumber, 512, buf, NULL, NULL); + DelayThread(10000); + read_report_descriptor(buf, hid_report_size, &hiddev[pad].rep); + UsbSetDeviceConfiguration(hiddev[pad].controlEndp, config->bConfigurationValue, usb_config_set, (void *)pad); + SignalSema(hiddev[pad].sema); + + return 0; +} + +int usb_disconnect(int devId) +{ + u8 pad; + + DPRINTF("DS3USB: disconnect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (hiddev[pad].usb_id == devId) { + pademu_disconnect(&hiddev[pad].dev); + break; + } + } + + if (pad < MAX_PADS) + usb_release(pad); + + return 0; +} + +static void usb_release(int pad) +{ + PollSema(hiddev[pad].sema); + + if (hiddev[pad].interruptEndp >= 0) + UsbCloseEndpoint(hiddev[pad].interruptEndp); + + hiddev[pad].controlEndp = -1; + hiddev[pad].interruptEndp = -1; + hiddev[pad].usb_id = -1; + hiddev[pad].lrum = 0; + hiddev[pad].rrum = 0; + hiddev[pad].update_rum = 1; + hiddev[pad].data[0] = 0xFF; + hiddev[pad].data[1] = 0xFF; + hiddev[pad].analog_btn = 0; + + mips_memset(&hiddev[pad].data[2], 0x7F, 4); + mips_memset(&hiddev[pad].data[6], 0x00, 12); + + SignalSema(hiddev[pad].sema); +} + +static void usb_data_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("DS3USB: usb_data_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + hiddev[pad].usb_resultcode = resultCode; + + SignalSema(hiddev[pad].sema); +} + +static void usb_cmd_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("DS3USB: usb_cmd_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + SignalSema(hiddev[pad].cmd_sema); +} + +static void usb_config_set(int result, int count, void *arg) +{ + int pad = (int)arg; + PollSema(hiddev[pad].sema); + DelayThread(10000); + pademu_connect(&hiddev[pad].dev); + SignalSema(hiddev[pad].sema); +} + +#define MAX_DELAY 10 + +static void read_report(u8 *data, int pad) +{ + int i, count, pos, bits; + u8 up = 0, down = 0, left = 0, right = 0, mask; + + + if (data[0] == 0x01) { + hiddev[pad].data[0] = 0x00; + hiddev[pad].data[1] = 0x00; + count = 4; + if (hiddev[pad].rep.axes.count < 4) + count = hiddev[pad].rep.axes.count; + + for (i = 0; i < count; i++) { + hiddev[pad].data[2 + i] = data[1 + (hiddev[pad].rep.axes.start_pos + hiddev[pad].rep.axes.size * i) / 8]; + } + + count = 16; + bits = 8; + if (hiddev[pad].rep.hats.count > 0) { + pos = hiddev[pad].rep.hats.start_pos; + switch ((data[1 + pos / 8] >> pos % 8) & 0x0F) { + case 0: + up = 1; + break; + case 1: + up = 1; + right = 1; + break; + case 2: + right = 1; + break; + case 3: + down = 1; + right = 1; + break; + case 4: + down = 1; + break; + case 5: + down = 1; + left = 1; + break; + case 6: + left = 1; + break; + case 7: + up = 1; + left = 1; + break; + case 8: + up = 0; + down = 0; + left = 0; + right = 0; + break; + } + hiddev[pad].data[0] = ~(up << 4 | right << 5 | down << 6 | left << 7 | 0x0F); + hiddev[pad].data[6] = right * 255; //right + hiddev[pad].data[7] = left * 255; //left + hiddev[pad].data[8] = up * 255; //up + hiddev[pad].data[9] = down * 255; //down + + count = 12; + bits = 4; + } + + if (hiddev[pad].rep.buttons.count < count) + count = hiddev[pad].rep.buttons.count; + + for (i = 0; i < count; i++) { + pos = hiddev[pad].rep.buttons.start_pos + hiddev[pad].rep.buttons.size * i; + if (i >= bits) { + //mask = ~(1 << (i - bits)); + //hiddev[pad].data[1] &= mask | (((data[1 + pos / 8] >> pos % 8) & 1) << (i - bits)); + hiddev[pad].data[1] |= (((data[1 + pos / 8] >> pos % 8) & 1) << (i - bits)); + } else { + //mask = ~(1 << i); + //hiddev[pad].data[0] &= mask | (((data[1 + pos / 8] >> pos % 8) & 1) << i); + hiddev[pad].data[0] |= (((data[1 + pos / 8] >> pos % 8) & 1) << i); + } + } + + //(report->Share | report->L3 << 1 | report->R3 << 2 | report->Option << 3 | up << 4 | right << 5 | down << 6 | left << 7); + //(report->L2 | report->R2 << 1 | report->L1 << 2 | report->R1 << 3 | report->Triangle << 4 | report->Circle << 5 | report->Cross << 6 | report->Square << 7); + + /* + hiddev[pad].data[10] = report->PressureTriangle; //triangle + hiddev[pad].data[11] = report->PressureCircle; //circle + hiddev[pad].data[12] = report->PressureCross; //cross + hiddev[pad].data[13] = report->PressureSquare; //square + + hiddev[pad].data[14] = report->PressureL1; //L1 + hiddev[pad].data[15] = report->PressureR1; //R1 + hiddev[pad].data[16] = report->PressureL2; //L2 + hiddev[pad].data[17] = report->PressureR2; //R2*/ + + /*hiddev[pad].data[0] = ~hiddev[pad].data[0]; + hiddev[pad].data[1] = ~hiddev[pad].data[1];*/ + } +} + +static unsigned int timeout(void *arg) +{ + int sema = (int)arg; + iSignalSema(sema); + return 0; +} + +static void TransferWait(int sema) +{ + iop_sys_clock_t cmd_timeout; + + cmd_timeout.lo = 200000; + cmd_timeout.hi = 0; + + if (SetAlarm(&cmd_timeout, timeout, (void *)sema) == 0) { + WaitSema(sema); + CancelAlarm(timeout, NULL); + } +} + +void hidusb_set_rumble(u8 lrum, u8 rrum, int port) +{ + WaitSema(hiddev[port].sema); + + hiddev[port].update_rum = 1; + hiddev[port].lrum = lrum; + hiddev[port].rrum = rrum; + + SignalSema(hiddev[port].sema); +} + +int hidusb_get_data(u8 *dst, int size, int port) +{ + int ret = 0; + + WaitSema(hiddev[port].sema); + + PollSema(hiddev[port].sema); + + ret = UsbInterruptTransfer(hiddev[port].interruptEndp, usb_buf, MAX_BUFFER_SIZE, usb_data_cb, (void *)port); + + if (ret == USB_RC_OK) { + TransferWait(hiddev[port].sema); + if (!hiddev[port].usb_resultcode) + read_report(usb_buf, port); + + hiddev[port].usb_resultcode = 1; + } else { + DPRINTF("HIDUSB: hidusb_get_data usb transfer error %d\n", ret); + } + + mips_memcpy(dst, hiddev[port].data, size); + ret = hiddev[port].analog_btn & 1; + + /*if (hiddev[port].update_rum) { + ret = LEDRumble(hiddev[port].oldled, hiddev[port].lrum, hiddev[port].rrum, port); + if (ret == USB_RC_OK) + TransferWait(hiddev[port].cmd_sema); + else + DPRINTF("DS3USB: LEDRumble usb transfer error %d\n", ret); + + hiddev[port].update_rum = 0; + }*/ + + SignalSema(hiddev[port].sema); + + return ret; +} + +void hidusb_set_mode(int mode, int lock, int port) +{ + if (lock == 3) + hiddev[port].analog_btn = 3; + else + hiddev[port].analog_btn = mode; +} + +void hidusb_reset() +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) + usb_release(pad); +} + +int _start(int argc, char *argv[]) +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) { + hiddev[pad].usb_id = -1; + hiddev[pad].dev.id = -1; + hiddev[pad].dev.pad_get_data = hidusb_get_data; + hiddev[pad].dev.pad_set_rumble = hidusb_set_rumble; + hiddev[pad].dev.pad_set_mode = hidusb_set_mode; + + hiddev[pad].lrum = 0; + hiddev[pad].rrum = 0; + hiddev[pad].update_rum = 1; + hiddev[pad].sema = -1; + hiddev[pad].cmd_sema = -1; + hiddev[pad].controlEndp = -1; + hiddev[pad].interruptEndp = -1; + + hiddev[pad].data[0] = 0xFF; + hiddev[pad].data[1] = 0xFF; + hiddev[pad].analog_btn = 0; + + mips_memset(&hiddev[pad].data[2], 0x7F, 4); + mips_memset(&hiddev[pad].data[6], 0x00, 12); + + hiddev[pad].sema = CreateMutex(IOP_MUTEX_UNLOCKED); + hiddev[pad].cmd_sema = CreateMutex(IOP_MUTEX_UNLOCKED); + + if (hiddev[pad].sema < 0 || hiddev[pad].cmd_sema < 0) { + DPRINTF("HIDUSB: Failed to allocate I/O semaphore.\n"); + return MODULE_NO_RESIDENT_END; + } + } + + if (UsbRegisterDriver(&usb_driver) != USB_RC_OK) { + DPRINTF("HIDUSB: Error registering USB devices\n"); + return MODULE_NO_RESIDENT_END; + } + + return MODULE_RESIDENT_END; +} + +int read_report_descriptor(u8 *data, int size, hidreport_t *report) +{ + u8 bSize, bType, bTag, bDataSize, bLongItemTag; + int dSize; + unsigned int dVal; + u8 *dPtr; + int i; + int rPos, rSize, rCount, rUsage; + + dPtr = data; + dSize = size; + + rPos = 0; + rSize = 0; + rCount = 0; + rUsage = 0; + + report->buttons.count = 0; + report->axes.count = 0; + report->hats.count = 0; + + while (dSize > 0) { + if (*dPtr != 0xFE) { //short item tag + + bSize = *dPtr & 3; + + if (bSize == 3) + bSize = 4; + + bType = (*dPtr >> 2) & 3; + bTag = (*dPtr >> 4) & 0x0F; + + for (i = 0, dVal = 0; i < bSize; i++) + dVal |= *(dPtr + 1 + i) << (8 * i); + + if (*dPtr == 0x85 && dVal == 2) //ReportID + break; + + if (*dPtr == 0x95) + rCount = dVal; + + if (*dPtr == 0x75) + rSize = dVal; + + if (*dPtr == 0x09 || *dPtr == 0x05) + rUsage = dVal; + + if (*dPtr == 0x81) { //Input + + switch (rUsage) { + case 0x09: //buttons + report->buttons.count = rCount; + report->buttons.size = rSize; + report->buttons.start_pos = rPos; + printf("Usage buttons: rCount %d rSize %d rPos %d\n", rCount, rSize, rPos); + break; + case 0x30: //x + case 0x31: //y + case 0x32: //z + case 0x33: //rx + case 0x34: //ry + case 0x35: //rz + report->axes.count = rCount; + report->axes.size = rSize; + report->axes.start_pos = rPos; + printf("Usage axes: rCount %d rSize %d rPos %d\n", rCount, rSize, rPos); + break; + case 0x39: //hats + report->hats.count = rCount; + report->hats.size = rSize; + report->hats.start_pos = rPos; + printf("Usage hats: rCount %d rSize %d rPos %d\n", rCount, rSize, rPos); + break; + } + + rPos += rSize * rCount; + rSize = 0; + rCount = 0; + rUsage = 0; + } + + dSize -= bSize + 1; + dPtr += bSize + 1; + } else { //long item tag + + bDataSize = *(dPtr + 1); + bLongItemTag = *(dPtr + 2); + + for (i = 0; i < bDataSize + 3; i++) { + dPtr++; + dSize--; + } + } + } + + return rPos; +} diff --git a/modules/pademu/hidusb/hidusb.h b/modules/pademu/hidusb/hidusb.h new file mode 100644 index 000000000..c8ff32861 --- /dev/null +++ b/modules/pademu/hidusb/hidusb.h @@ -0,0 +1,61 @@ +#ifndef _HIDUSB_H_ +#define _HIDUSB_H_ + +#include "irx.h" +#include "../pademu.h" + +#define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer + +#define USB_DT_HID 0x21 + +/* HID Descriptor (Class Specific Descriptor) */ +typedef struct +{ + u8 bDescriptorType; + u8 wDescriptorLengthL; + u8 wDescriptorLengthH; +} UsbHidSubDescriptorInfo; + +typedef struct +{ + u8 bLength; + u8 bDescriptorType; + u16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; /* Number of SubDescriptor */ + UsbHidSubDescriptorInfo Sub[0]; +} UsbHidDescriptor; + +typedef struct +{ + u16 count; + u16 size; //in bits + int start_pos; //position in report +} hiddata_t; + +typedef struct +{ + hiddata_t axes; + hiddata_t hats; + hiddata_t buttons; +} hidreport_t; + +typedef struct +{ + pad_device_t dev; + hidreport_t rep; + int usb_id; + int sema; + int cmd_sema; + int controlEndp; + int interruptEndp; + int usb_resultcode; + u8 lrum; + u8 rrum; + u8 update_rum; + u8 data[18]; + u8 analog_btn; + u8 btn_delay; +} hidusb_device; + +#endif diff --git a/modules/pademu/hidusb/imports.lst b/modules/pademu/hidusb/imports.lst new file mode 100644 index 000000000..e36f592dd --- /dev/null +++ b/modules/pademu/hidusb/imports.lst @@ -0,0 +1,68 @@ +loadcore_IMPORTS_start +I_RegisterLibraryEntries +I_ReleaseLibraryEntries +I_GetLoadcoreInternalData +I_QueryLibraryEntryTable +I_SetRebootTimeLibraryHandlingMode +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_PollSema +I_DeleteSema +I_iSignalSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_StartThread +I_CreateThread +I_DeleteThread +I_DelayThread +I_GetThreadId +I_SleepThread +I_WakeupThread +I_TerminateThread +I_SetAlarm +I_CancelAlarm +thbase_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +usbd_IMPORTS_start +I_sceUsbdScanStaticDescriptor +I_sceUsbdOpenPipe +I_sceUsbdClosePipe +I_sceUsbdOpenPipeAligned +I_sceUsbdSetPrivateData +I_sceUsbdTransferPipe +I_sceUsbdRegisterLdd +usbd_IMPORTS_end + +sysclib_IMPORTS_start +I_strncmp +#ifndef USE_SMSUTILS +I_memset +I_memcpy +#endif +sysclib_IMPORTS_end + +#ifdef USE_SMSUTILS +smsutils_IMPORTS_start +I_mips_memset +I_mips_memcpy +smsutils_IMPORTS_end +#endif + +pademu_IMPORTS_start +I_pademu_connect +I_pademu_disconnect +pademu_IMPORTS_end diff --git a/modules/pademu/hidusb/irx_imports.h b/modules/pademu/hidusb/irx_imports.h new file mode 100644 index 000000000..280115628 --- /dev/null +++ b/modules/pademu/hidusb/irx_imports.h @@ -0,0 +1,40 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "dmacman.h" +#include "intrman.h" +#include "iomanX.h" +#include "libsd.h" +#include "loadcore.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thmsgbx.h" +#include "thsemap.h" +#include "usbd.h" +#include "vblank.h" +#include "xloadcore.h" + +#include "../pademu.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/modules/pademu/pademu.c b/modules/pademu/pademu.c index 3668b498a..bb0d69c3e 100644 --- a/modules/pademu/pademu.c +++ b/modules/pademu/pademu.c @@ -6,36 +6,23 @@ Review OpenUsbLd README & LICENSE files for further details. */ -#include "pademu.h" - -#ifdef BT - -#include "ds34bt.h" - -#define PAD_INIT ds34bt_init -#define PAD_GET_STATUS ds34bt_get_status -#define PAD_RESET ds34bt_reset -#define PAD_GET_DATA ds34bt_get_data -#define PAD_SET_RUMBLE ds34bt_set_rumble -#define PAD_SET_MODE ds34bt_set_mode - -#elif defined(USB) - -#include "ds34usb.h" - -#define PAD_INIT ds34usb_init -#define PAD_GET_STATUS ds34usb_get_status -#define PAD_RESET ds34usb_reset -#define PAD_GET_DATA ds34usb_get_data -#define PAD_SET_RUMBLE ds34usb_set_rumble -#define PAD_SET_MODE ds34usb_set_mode +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#else -#error "must define mode" -#endif +#include "pademu.h" -//#define DPRINTF(x...) printf(x) -#define DPRINTF(x...) +#define DPRINTF(x...) printf(x) +//#define DPRINTF(x...) typedef struct { @@ -49,6 +36,7 @@ typedef struct u8 lrum; u8 rrum; u8 mask[4]; + pad_device_t *dev; } pad_status_t; #define DIGITAL_MODE 0x41 @@ -58,15 +46,12 @@ typedef struct #define MAX_PORTS 4 -#define PAD_STATE_RUNNING 0x08 - IRX_ID("pademu", 1, 1); PtrRegisterLibraryEntires pRegisterLibraryEntires; /* Pointer to RegisterLibraryEntires routine */ Sio2McProc pSio2man25, pSio2man51; /* Pointers to SIO2MAN routines */ pad_status_t pad[MAX_PORTS]; -static u8 pad_inited = 0; static u8 pad_enable = 0; static u8 pad_options = 0; @@ -89,6 +74,9 @@ void pademu_cmd(int port, u8 *in, u8 *out, u8 out_size); void pademu_mtap(sio2_transfer_data_t *td); +void pademu_connect(pad_device_t *dev); +void pademu_disconnect(pad_device_t *dev); + extern struct irx_export_table _exp_pademu; int _start(int argc, char *argv[]) @@ -128,7 +116,6 @@ int _start(int argc, char *argv[]) void _exit(int mode) { - PAD_RESET(); } int install_sio2hook() @@ -138,7 +125,7 @@ int install_sio2hook() /* looking for LOADCORE's library entry table */ exp = GetExportTable("loadcore", 0x100); if (exp == NULL) { - DPRINTF("Unable to find loadcore exports.\n"); + DPRINTF("PADEMU: Unable to find loadcore exports.\n"); return 0; } @@ -151,7 +138,7 @@ int install_sio2hook() /* hooking SIO2MAN's routines */ InstallSio2manHook(exp, 1); } else { - DPRINTF("SIO2MAN exports not found.\n"); + DPRINTF("PADEMU: SIO2MAN exports not found.\n"); } return 1; @@ -159,6 +146,7 @@ int install_sio2hook() void InstallSio2manHook(void *exp, int ver) { + DPRINTF("PADEMU: Install sio2man hooks \n"); /* hooking SIO2MAN entry #25 (used by MCMAN and old PADMAN) */ pSio2man25 = HookExportEntry(exp, 25, hookSio2man25); /* hooking SIO2MAN entry #51 (used by MC2_* modules and PADMAN) */ @@ -177,12 +165,12 @@ int hookRegisterLibraryEntires(iop_library_t *lib) /* hooking SIO2MAN's routines */ InstallSio2manHook(&lib[1], GetExportTableSize(&lib[1]) >= 61); } else { - DPRINTF("registering library %s failed, error %d\n", lib->name, ret); + DPRINTF("PADEMU: registering library %s failed, error %d\n", lib->name, ret); return ret; } } - DPRINTF("registering library %s\n", lib->name); + DPRINTF("PADEMU: registering library %s\n", lib->name); return pRegisterLibraryEntires(lib); } @@ -281,6 +269,33 @@ void pademu_setup(u8 ports, u8 vib) pad[i].lrum = 2; pad[i].rrum = 2; + + pad[i].dev = NULL; + } +} + +void pademu_connect(pad_device_t *dev) +{ + int i; + for (i = 0; i < MAX_PORTS; i++) { + if (pad[i].enabled && pad[i].dev == NULL) { + pad[i].dev = dev; + pad[i].dev->id = i; + DPRINTF("PADEMU: Device connected\n"); + break; + } + } +} + +void pademu_disconnect(pad_device_t *dev) +{ + int i; + for (i = 0; i < MAX_PORTS; i++) { + if (pad[i].dev == dev) { + pad[i].dev = NULL; + DPRINTF("PADEMU: Device disconnected\n"); + break; + } } } @@ -305,10 +320,6 @@ void pademu(sio2_transfer_data_t *td) td->stat6c = 0x1100; //? td->stat70 = 0x0F; //? - if (!pad_inited) { - pad_inited = PAD_INIT(pad_enable, pad_options); - } - if (port2 == 1) { //find next cmd for (cmd_size = 5; cmd_size < td->in_size - 3; cmd_size++) { @@ -321,7 +332,7 @@ void pademu(sio2_transfer_data_t *td) } if (cmd_size + 3 == td->in_size) { - DPRINTF("Second cmd not found!\n"); + DPRINTF("PADEMU: Second cmd not found!\n"); return; } @@ -370,9 +381,11 @@ void pademu_cmd(int port, u8 *in, u8 *out, u8 out_size) { u8 i; + //DPRINTF("PADEMU: sio cmd %02x port %d\n", in[1], port); + mips_memset(out, 0x00, out_size); - if (!(PAD_GET_STATUS(port) & PAD_STATE_RUNNING)) { + if (pad[port].dev == NULL) { pad[port].lrum = 2; pad[port].rrum = 2; return; @@ -408,11 +421,11 @@ void pademu_cmd(int port, u8 *in, u8 *out, u8 out_size) case 0x42: //read data if (in[1] == 0x42) { if (pad[port].vibration) { //disable/enable vibration - PAD_SET_RUMBLE(in[pad[port].lrum], in[pad[port].rrum], port); + pad[port].dev->pad_set_rumble(in[pad[port].lrum], in[pad[port].rrum], pad[port].dev->id); } } - i = PAD_GET_DATA(&out[3], out_size - 3, port); + i = pad[port].dev->pad_get_data(&out[3], out_size - 3, pad[port].dev->id); if (pad[port].mode_lock == 0) { //mode unlocked if (pad[port].mode != i) { @@ -440,7 +453,7 @@ void pademu_cmd(int port, u8 *in, u8 *out, u8 out_size) } else { pad[port].mode_id = DIGITAL_MODE; } - PAD_SET_MODE(pad[port].mode, pad[port].mode_lock, port); + pad[port].dev->pad_set_mode(pad[port].mode, pad[port].mode_lock, pad[port].dev->id); break; case 0x45: //query model and mode diff --git a/modules/pademu/pademu.h b/modules/pademu/pademu.h index 0be94e786..61860c4b0 100644 --- a/modules/pademu/pademu.h +++ b/modules/pademu/pademu.h @@ -5,19 +5,6 @@ Review OpenUsbLd README & LICENSE files for further details. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "sys_utils.h" /* type for a pointer to LOADCORE's entry RegisterLibraryEntires */ @@ -52,9 +39,31 @@ typedef struct struct _sio2_dma_arg out_dma; } sio2_transfer_data_t; +typedef struct +{ + int id; + int (*pad_get_data)(u8 *dst, int size, int id); + void (*pad_set_rumble)(u8 lrum, u8 rrum, int id); + void (*pad_set_mode)(int mode, int lock, int id); +} pad_device_t; + typedef void (*Sio2McProc)(sio2_transfer_data_t *arg); void *GetExportTable(char *libname, int version); u32 GetExportTableSize(void *table); void *GetExportEntry(void *table, u32 entry); void *HookExportEntry(void *table, u32 entry, void *func); + + +#define pademu_IMPORTS_start DECLARE_IMPORT_TABLE(pademu, 1, 1) + +void pademu_hookSio2man(sio2_transfer_data_t *td, Sio2McProc sio2proc); +#define I_pademu_hookSio2man DECLARE_IMPORT(4, pademu_hookSio2man) + +void pademu_connect(pad_device_t *dev); +#define I_pademu_connect DECLARE_IMPORT(5, pademu_connect) + +void pademu_disconnect(pad_device_t *dev); +#define I_pademu_disconnect DECLARE_IMPORT(6, pademu_disconnect) + +#define pademu_IMPORTS_end END_IMPORT_TABLE diff --git a/modules/pademu/sys_utils.c b/modules/pademu/sys_utils.c index 6fb76d10c..dd24af7e2 100644 --- a/modules/pademu/sys_utils.c +++ b/modules/pademu/sys_utils.c @@ -6,6 +6,19 @@ Review OpenUsbLd README & LICENSE files for further details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "pademu.h" /* prototype for LOADCORE's function */ diff --git a/modules/pademu/sys_utils.h b/modules/pademu/sys_utils.h index 66ff9cd26..b389e17de 100644 --- a/modules/pademu/sys_utils.h +++ b/modules/pademu/sys_utils.h @@ -32,4 +32,4 @@ void mips_memset(void *, int, unsigned); #define mips_memcpy memcpy #endif -#endif /* __MCEMU_UTILS_H */ +#endif diff --git a/modules/pademu/xbox360usb/Makefile b/modules/pademu/xbox360usb/Makefile new file mode 100644 index 000000000..8eba8e8cf --- /dev/null +++ b/modules/pademu/xbox360usb/Makefile @@ -0,0 +1,22 @@ + +IOP_BIN = xbox360usb.irx +IOP_OBJS = xbox360usb.o imports.o +IOP_OBJS_DIR = obj.xbox360usb/ + +IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%) + +IOP_CFLAGS += -Wall -fno-builtin -DUSE_SMSUTILS +IOP_LDFLAGS += -s + +all: OBJ_DIR $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +rebuild: clean all + +OBJ_DIR: + mkdir -p $(IOP_OBJS_DIR) + +include $(PS2SDK)/Defs.make +include ../Rules.make diff --git a/modules/pademu/xbox360usb/imports.lst b/modules/pademu/xbox360usb/imports.lst new file mode 100644 index 000000000..e36f592dd --- /dev/null +++ b/modules/pademu/xbox360usb/imports.lst @@ -0,0 +1,68 @@ +loadcore_IMPORTS_start +I_RegisterLibraryEntries +I_ReleaseLibraryEntries +I_GetLoadcoreInternalData +I_QueryLibraryEntryTable +I_SetRebootTimeLibraryHandlingMode +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_PollSema +I_DeleteSema +I_iSignalSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_StartThread +I_CreateThread +I_DeleteThread +I_DelayThread +I_GetThreadId +I_SleepThread +I_WakeupThread +I_TerminateThread +I_SetAlarm +I_CancelAlarm +thbase_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +usbd_IMPORTS_start +I_sceUsbdScanStaticDescriptor +I_sceUsbdOpenPipe +I_sceUsbdClosePipe +I_sceUsbdOpenPipeAligned +I_sceUsbdSetPrivateData +I_sceUsbdTransferPipe +I_sceUsbdRegisterLdd +usbd_IMPORTS_end + +sysclib_IMPORTS_start +I_strncmp +#ifndef USE_SMSUTILS +I_memset +I_memcpy +#endif +sysclib_IMPORTS_end + +#ifdef USE_SMSUTILS +smsutils_IMPORTS_start +I_mips_memset +I_mips_memcpy +smsutils_IMPORTS_end +#endif + +pademu_IMPORTS_start +I_pademu_connect +I_pademu_disconnect +pademu_IMPORTS_end diff --git a/modules/pademu/xbox360usb/irx_imports.h b/modules/pademu/xbox360usb/irx_imports.h new file mode 100644 index 000000000..280115628 --- /dev/null +++ b/modules/pademu/xbox360usb/irx_imports.h @@ -0,0 +1,40 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "dmacman.h" +#include "intrman.h" +#include "iomanX.h" +#include "libsd.h" +#include "loadcore.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thmsgbx.h" +#include "thsemap.h" +#include "usbd.h" +#include "vblank.h" +#include "xloadcore.h" + +#include "../pademu.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/modules/pademu/xbox360usb/xbox360usb.c b/modules/pademu/xbox360usb/xbox360usb.c new file mode 100644 index 000000000..28b90a344 --- /dev/null +++ b/modules/pademu/xbox360usb/xbox360usb.c @@ -0,0 +1,388 @@ +#include "types.h" +#include "loadcore.h" +#include "stdio.h" +#include "sifrpc.h" +#include "sysclib.h" +#include "usbd.h" +#include "usbd_macro.h" +#include "thbase.h" +#include "thsemap.h" +#include "xbox360usb.h" + +//#define DPRINTF(x...) printf(x) +#define DPRINTF(x...) + +#define REQ_USB_OUT (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) +#define REQ_USB_IN (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) + +#define MAX_PADS 4 + +static u8 usb_buf[MAX_BUFFER_SIZE] __attribute((aligned(4))) = {0}; + +int usb_probe(int devId); +int usb_connect(int devId); +int usb_disconnect(int devId); + +static void usb_release(int pad); +static void usb_config_set(int result, int count, void *arg); + +UsbDriver usb_driver = {NULL, NULL, "xbox360usb", usb_probe, usb_connect, usb_disconnect}; + +static void readReport(u8 *data, int pad); +static int LEDRumble(u8 *led, u8 lrum, u8 rrum, int pad); + +xbox360usb_device xbox360dev[MAX_PADS]; + +int usb_probe(int devId) +{ + UsbDeviceDescriptor *device = NULL; + + DPRINTF("XBOX360USB: probe: devId=%i\n", devId); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + if (device == NULL) { + DPRINTF("XBOX360USB: Error - Couldn't get device descriptor\n"); + return 0; + } + + if ((device->idVendor == XBOX_VID || device->idVendor == MADCATZ_VID || device->idVendor == JOYTECH_VID || device->idVendor == GAMESTOP_VID) && + (device->idProduct == XBOX_WIRED_PID || device->idProduct == MADCATZ_WIRED_PID || device->idProduct == GAMESTOP_WIRED_PID || + device->idProduct == AFTERGLOW_WIRED_PID || device->idProduct == JOYTECH_WIRED_PID)) + return 1; + + return 0; +} + +int usb_connect(int devId) +{ + int pad, epCount; + UsbDeviceDescriptor *device; + UsbConfigDescriptor *config; + UsbInterfaceDescriptor *interface; + UsbEndpointDescriptor *endpoint; + + DPRINTF("XBOX360USB: connect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (xbox360dev[pad].usb_id == -1) + break; + } + + if (pad >= MAX_PADS) { + DPRINTF("XBOX360USB: Error - only %d device allowed !\n", MAX_PADS); + return 1; + } + + PollSema(xbox360dev[pad].sema); + + xbox360dev[pad].dev.id = pad; + xbox360dev[pad].usb_id = devId; + xbox360dev[pad].controlEndp = UsbOpenEndpoint(devId, NULL); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG); + interface = (UsbInterfaceDescriptor *)((char *)config + config->bLength); + epCount = interface->bNumEndpoints - 1; + endpoint = (UsbEndpointDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_ENDPOINT); + + do { + if (endpoint->bmAttributes == USB_ENDPOINT_XFER_INT) { + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && xbox360dev[pad].interruptEndp < 0) { + xbox360dev[pad].interruptEndp = UsbOpenEndpointAligned(devId, endpoint); + DPRINTF("XBOX360USB: register Event endpoint id =%i addr=%02X packetSize=%i\n", xbox360dev[pad].interruptEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + } + + endpoint = (UsbEndpointDescriptor *)((char *)endpoint + endpoint->bLength); + + } while (epCount--); + + if (xbox360dev[pad].interruptEndp < 0) { + usb_release(pad); + return 1; + } + + UsbSetDeviceConfiguration(xbox360dev[pad].controlEndp, config->bConfigurationValue, usb_config_set, (void *)pad); + SignalSema(xbox360dev[pad].sema); + + return 0; +} + +int usb_disconnect(int devId) +{ + u8 pad; + + DPRINTF("XBOX360USB: disconnect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (xbox360dev[pad].usb_id == devId) { + pademu_disconnect(&xbox360dev[pad].dev); + break; + } + } + + if (pad < MAX_PADS) + usb_release(pad); + + return 0; +} + +static void usb_release(int pad) +{ + PollSema(xbox360dev[pad].sema); + + if (xbox360dev[pad].interruptEndp >= 0) + UsbCloseEndpoint(xbox360dev[pad].interruptEndp); + + xbox360dev[pad].controlEndp = -1; + xbox360dev[pad].interruptEndp = -1; + xbox360dev[pad].usb_id = -1; + + SignalSema(xbox360dev[pad].sema); +} + +static void usb_data_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("XBOX360USB: usb_data_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + xbox360dev[pad].usb_resultcode = resultCode; + + SignalSema(xbox360dev[pad].sema); +} + +static void usb_cmd_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("XBOX360USB: usb_cmd_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + SignalSema(xbox360dev[pad].cmd_sema); +} + +static void usb_config_set(int result, int count, void *arg) +{ + int pad = (int)arg; + u8 led[2]; + + PollSema(xbox360dev[pad].sema); + + led[0] = 0x02 + pad; + led[1] = 0; + + LEDRumble(led, 0, 0, pad); + SignalSema(xbox360dev[pad].sema); + + pademu_connect(&xbox360dev[pad].dev); +} + +#define MAX_DELAY 10 + +static void readReport(u8 *data, int pad) +{ + xbox360report_t *report = (xbox360report_t *)data; + if (report->ReportID == 0x00 && report->Length == 0x14) { + xbox360dev[pad].data[0] = ~(report->Back | report->LS << 1 | report->RS << 2 | report->Start << 3 | report->Up << 4 | report->Right << 5 | report->Down << 6 | report->Left << 7); + xbox360dev[pad].data[1] = ~((report->LeftTrigger != 0) | (report->RightTrigger != 0) << 1 | report->LB << 2 | report->RB << 3 | report->Y << 4 | report->B << 5 | report->A << 6 | report->X << 7); + + xbox360dev[pad].data[2] = report->RightStickXH + 128; //rx + xbox360dev[pad].data[3] = ~(report->RightStickYH + 128); //ry + xbox360dev[pad].data[4] = report->LeftStickXH + 128; //lx + xbox360dev[pad].data[5] = ~(report->LeftStickYH + 128); //ly + + xbox360dev[pad].data[6] = report->Right * 255; //right + xbox360dev[pad].data[7] = report->Left * 255; //left + xbox360dev[pad].data[8] = report->Up * 255; //up + xbox360dev[pad].data[9] = report->Down * 255; //down + + xbox360dev[pad].data[10] = report->Y * 255; //triangle + xbox360dev[pad].data[11] = report->B * 255; //circle + xbox360dev[pad].data[12] = report->A * 255; //cross + xbox360dev[pad].data[13] = report->X * 255; //square + + xbox360dev[pad].data[14] = report->LB * 255; //L1 + xbox360dev[pad].data[15] = report->RB * 255; //R1 + xbox360dev[pad].data[16] = report->LeftTrigger; //L2 + xbox360dev[pad].data[17] = report->RightTrigger; //R2 + + if (report->XBOX) { //display battery level + if (report->Back && (xbox360dev[pad].btn_delay == MAX_DELAY)) { //XBOX + BACK + if (xbox360dev[pad].analog_btn < 2) //unlocked mode + xbox360dev[pad].analog_btn = !xbox360dev[pad].analog_btn; + + //xbox360dev[pad].oldled[0] = led_patterns[pad][(xbox360dev[pad].analog_btn & 1)]; + xbox360dev[pad].btn_delay = 1; + } else { + //if (report->Power != 0xEE) + // xbox360dev[pad].oldled[0] = power_level[report->Power]; + + if (xbox360dev[pad].btn_delay < MAX_DELAY) + xbox360dev[pad].btn_delay++; + } + } else { + //xbox360dev[pad].oldled[0] = led_patterns[pad][(xbox360dev[pad].analog_btn & 1)]; + + if (xbox360dev[pad].btn_delay > 0) + xbox360dev[pad].btn_delay--; + } + } +} + +static int LEDRumble(u8 *led, u8 lrum, u8 rrum, int pad) +{ + int ret; + PollSema(xbox360dev[pad].cmd_sema); + + usb_buf[0] = 0x00; + usb_buf[1] = 0x08; + usb_buf[2] = 0x00; + usb_buf[3] = lrum; // big weight + usb_buf[4] = rrum; // small weight + usb_buf[5] = 0x00; + usb_buf[6] = 0x00; + usb_buf[7] = 0x00; + + ret = UsbControlTransfer(xbox360dev[pad].controlEndp, REQ_USB_OUT, USB_REQ_SET_REPORT, (HID_USB_SET_REPORT_OUTPUT << 8) | 0x01, 0, 8, usb_buf, usb_cmd_cb, (void *)pad); + if (ret == USB_RC_OK) { + usb_buf[0] = 0x01; + usb_buf[1] = 0x03; + usb_buf[2] = led[0]; + ret = UsbControlTransfer(xbox360dev[pad].controlEndp, REQ_USB_OUT, USB_REQ_SET_REPORT, (HID_USB_SET_REPORT_OUTPUT << 8) | 0x01, 0, 3, usb_buf, usb_cmd_cb, (void *)pad); + } + + xbox360dev[pad].oldled[0] = led[0]; + xbox360dev[pad].oldled[1] = led[1]; + + return ret; +} + +static unsigned int timeout(void *arg) +{ + int sema = (int)arg; + iSignalSema(sema); + return 0; +} + +static void TransferWait(int sema) +{ + iop_sys_clock_t cmd_timeout; + + cmd_timeout.lo = 200000; + cmd_timeout.hi = 0; + + if (SetAlarm(&cmd_timeout, timeout, (void *)sema) == 0) { + WaitSema(sema); + CancelAlarm(timeout, NULL); + } +} + +void xbox360usb_set_rumble(u8 lrum, u8 rrum, int port) +{ + WaitSema(xbox360dev[port].sema); + + xbox360dev[port].update_rum = 1; + xbox360dev[port].lrum = lrum; + xbox360dev[port].rrum = rrum; + + SignalSema(xbox360dev[port].sema); +} + +int xbox360usb_get_data(u8 *dst, int size, int port) +{ + int ret = 0; + + WaitSema(xbox360dev[port].sema); + + PollSema(xbox360dev[port].sema); + + ret = UsbInterruptTransfer(xbox360dev[port].interruptEndp, usb_buf, MAX_BUFFER_SIZE, usb_data_cb, (void *)port); + + if (ret == USB_RC_OK) { + TransferWait(xbox360dev[port].sema); + if (!xbox360dev[port].usb_resultcode) + readReport(usb_buf, port); + + xbox360dev[port].usb_resultcode = 1; + } else { + DPRINTF("XBOX360USB: XBOX360USB_get_data usb transfer error %d\n", ret); + } + + mips_memcpy(dst, xbox360dev[port].data, size); + ret = xbox360dev[port].analog_btn & 1; + + if (xbox360dev[port].update_rum) { + ret = LEDRumble(xbox360dev[port].oldled, xbox360dev[port].lrum, xbox360dev[port].rrum, port); + if (ret == USB_RC_OK) + TransferWait(xbox360dev[port].cmd_sema); + else + DPRINTF("XBOX360USB: LEDRumble usb transfer error %d\n", ret); + + xbox360dev[port].update_rum = 0; + } + + SignalSema(xbox360dev[port].sema); + + return ret; +} + +void xbox360usb_set_mode(int mode, int lock, int port) +{ + if (lock == 3) + xbox360dev[port].analog_btn = 3; + else + xbox360dev[port].analog_btn = mode; +} + +void xbox360usb_reset() +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) + usb_release(pad); +} + +int _start(int argc, char *argv[]) +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) { + xbox360dev[pad].usb_id = -1; + xbox360dev[pad].dev.id = -1; + xbox360dev[pad].dev.pad_get_data = xbox360usb_get_data; + xbox360dev[pad].dev.pad_set_rumble = xbox360usb_set_rumble; + xbox360dev[pad].dev.pad_set_mode = xbox360usb_set_mode; + + xbox360dev[pad].oldled[0] = 0; + xbox360dev[pad].oldled[1] = 0; + xbox360dev[pad].lrum = 0; + xbox360dev[pad].rrum = 0; + xbox360dev[pad].update_rum = 1; + xbox360dev[pad].sema = -1; + xbox360dev[pad].cmd_sema = -1; + xbox360dev[pad].controlEndp = -1; + xbox360dev[pad].interruptEndp = -1; + + xbox360dev[pad].data[0] = 0xFF; + xbox360dev[pad].data[1] = 0xFF; + xbox360dev[pad].analog_btn = 0; + + mips_memset(&xbox360dev[pad].data[2], 0x7F, 4); + mips_memset(&xbox360dev[pad].data[6], 0x00, 12); + + xbox360dev[pad].sema = CreateMutex(IOP_MUTEX_UNLOCKED); + xbox360dev[pad].cmd_sema = CreateMutex(IOP_MUTEX_UNLOCKED); + + if (xbox360dev[pad].sema < 0 || xbox360dev[pad].cmd_sema < 0) { + DPRINTF("XBOX360USB: Failed to allocate I/O semaphore.\n"); + return MODULE_NO_RESIDENT_END; + } + } + + if (UsbRegisterDriver(&usb_driver) != USB_RC_OK) { + DPRINTF("XBOX360USB: Error registering USB devices\n"); + return MODULE_NO_RESIDENT_END; + } + + return MODULE_RESIDENT_END; +} diff --git a/modules/pademu/xbox360usb/xbox360usb.h b/modules/pademu/xbox360usb/xbox360usb.h new file mode 100644 index 000000000..50ff796cc --- /dev/null +++ b/modules/pademu/xbox360usb/xbox360usb.h @@ -0,0 +1,141 @@ +#ifndef _XBOX360USB_H_ +#define _XBOX360USB_H_ + +#include "irx.h" +#include "../pademu.h" + +#define XBOX_VID 0x045E // Microsoft Corporation +#define MADCATZ_VID 0x1BAD // For unofficial Mad Catz controllers +#define JOYTECH_VID 0x162E // For unofficial Joytech controllers +#define GAMESTOP_VID 0x0E6F // Gamestop controller + +#define XBOX_WIRED_PID 0x028E // Microsoft 360 Wired controller +#define XBOX_WIRELESS_PID 0x028F // Wireless controller only support charging +#define XBOX_WIRELESS_RECEIVER_PID 0x0719 // Microsoft Wireless Gaming Receiver +#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID 0x0291 // Third party Wireless Gaming Receiver +#define MADCATZ_WIRED_PID 0xF016 // Mad Catz wired controller +#define JOYTECH_WIRED_PID 0xBEEF // For Joytech wired controller +#define GAMESTOP_WIRED_PID 0x0401 // Gamestop wired controller +#define AFTERGLOW_WIRED_PID 0x0213 // Afterglow wired controller - it uses the same VID as a Gamestop controller + +#define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer + +typedef struct +{ + pad_device_t dev; + int usb_id; + int sema; + int cmd_sema; + int controlEndp; + int interruptEndp; + int usb_resultcode; + u8 oldled[2]; + u8 lrum; + u8 rrum; + u8 update_rum; + u8 data[18]; + u8 analog_btn; + u8 btn_delay; +} xbox360usb_device; + +enum eHID { + // {{{ + /* HID event flag */ + HID_FLAG_STATUS_REPORTED = 0x01, + HID_FLAG_BUTTONS_CHANGED = 0x02, + HID_FLAG_EXTENSION = 0x04, + HID_FLAG_COMMAND_SUCCESS = 0x08, + + /* USB HID Transaction Header (THdr) */ + HID_USB_GET_REPORT_FEATURE = 0x03, + HID_USB_SET_REPORT_OUTPUT = 0x02, + HID_USB_DATA_INPUT = 0x01, + + /* Defines of various parameters for PS3 Game controller reports */ + PS3_F4_REPORT_ID = 0xF4, + PS3_F4_REPORT_LEN = 0x04, + + PS3_01_REPORT_ID = 0x01, + PS3_01_REPORT_LEN = 0x30, + + PS4_02_REPORT_ID = 0x02, + PS4_11_REPORT_ID = 0x11, + PS4_11_REPORT_LEN = 0x4D, + // }}} +}; + +typedef struct +{ + u8 ReportID; + u8 Length; //0x14 + union + { + u8 ButtonStateL; // Main buttons Low + struct + { + u8 Up : 1; + u8 Down : 1; + u8 Left : 1; + u8 Right : 1; + u8 Start : 1; + u8 Back : 1; + u8 LS : 1; + u8 RS : 1; + }; + }; + union + { + u8 ButtonStateH; // Main buttons High + struct + { + u8 LB : 1; + u8 RB : 1; + u8 XBOX : 1; + u8 Dummy1 : 1; + u8 A : 1; + u8 B : 1; + u8 X : 1; + u8 Y : 1; + }; + }; + u8 LeftTrigger; + u8 RightTrigger; + union + { + u16 LeftStickX; + struct + { + u8 LeftStickXL; + u8 LeftStickXH; + }; + }; + union + { + u16 LeftStickY; + struct + { + u8 LeftStickYL; + u8 LeftStickYH; + }; + }; + union + { + u16 RightStickX; + struct + { + u8 RightStickXL; + u8 RightStickXH; + }; + }; + union + { + u16 RightStickY; + struct + { + u8 RightStickYL; + u8 RightStickYH; + }; + }; +} __attribute__((packed)) xbox360report_t; + +#endif diff --git a/modules/pademu/xboxoneusb/Makefile b/modules/pademu/xboxoneusb/Makefile new file mode 100644 index 000000000..fa6c97469 --- /dev/null +++ b/modules/pademu/xboxoneusb/Makefile @@ -0,0 +1,22 @@ + +IOP_BIN = xboxoneusb.irx +IOP_OBJS = xboxoneusb.o imports.o +IOP_OBJS_DIR = obj.xboxoneusb/ + +IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%) + +IOP_CFLAGS += -Wall -fno-builtin -DUSE_SMSUTILS +IOP_LDFLAGS += -s + +all: OBJ_DIR $(IOP_BIN) + +clean: + rm -f $(IOP_BIN) $(IOP_OBJS) + +rebuild: clean all + +OBJ_DIR: + mkdir -p $(IOP_OBJS_DIR) + +include $(PS2SDK)/Defs.make +include ../Rules.make diff --git a/modules/pademu/xboxoneusb/imports.lst b/modules/pademu/xboxoneusb/imports.lst new file mode 100644 index 000000000..e36f592dd --- /dev/null +++ b/modules/pademu/xboxoneusb/imports.lst @@ -0,0 +1,68 @@ +loadcore_IMPORTS_start +I_RegisterLibraryEntries +I_ReleaseLibraryEntries +I_GetLoadcoreInternalData +I_QueryLibraryEntryTable +I_SetRebootTimeLibraryHandlingMode +loadcore_IMPORTS_end + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_PollSema +I_DeleteSema +I_iSignalSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_StartThread +I_CreateThread +I_DeleteThread +I_DelayThread +I_GetThreadId +I_SleepThread +I_WakeupThread +I_TerminateThread +I_SetAlarm +I_CancelAlarm +thbase_IMPORTS_end + +intrman_IMPORTS_start +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +usbd_IMPORTS_start +I_sceUsbdScanStaticDescriptor +I_sceUsbdOpenPipe +I_sceUsbdClosePipe +I_sceUsbdOpenPipeAligned +I_sceUsbdSetPrivateData +I_sceUsbdTransferPipe +I_sceUsbdRegisterLdd +usbd_IMPORTS_end + +sysclib_IMPORTS_start +I_strncmp +#ifndef USE_SMSUTILS +I_memset +I_memcpy +#endif +sysclib_IMPORTS_end + +#ifdef USE_SMSUTILS +smsutils_IMPORTS_start +I_mips_memset +I_mips_memcpy +smsutils_IMPORTS_end +#endif + +pademu_IMPORTS_start +I_pademu_connect +I_pademu_disconnect +pademu_IMPORTS_end diff --git a/modules/pademu/xboxoneusb/irx_imports.h b/modules/pademu/xboxoneusb/irx_imports.h new file mode 100644 index 000000000..280115628 --- /dev/null +++ b/modules/pademu/xboxoneusb/irx_imports.h @@ -0,0 +1,40 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# $Id$ +# Defines all IRX imports. +*/ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + +/* Please keep these in alphabetical order! */ +#include "dmacman.h" +#include "intrman.h" +#include "iomanX.h" +#include "libsd.h" +#include "loadcore.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "sysmem.h" +#include "thbase.h" +#include "thevent.h" +#include "thmsgbx.h" +#include "thsemap.h" +#include "usbd.h" +#include "vblank.h" +#include "xloadcore.h" + +#include "../pademu.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/modules/pademu/xboxoneusb/xboxoneusb.c b/modules/pademu/xboxoneusb/xboxoneusb.c new file mode 100644 index 000000000..89c02e827 --- /dev/null +++ b/modules/pademu/xboxoneusb/xboxoneusb.c @@ -0,0 +1,380 @@ +#include "types.h" +#include "loadcore.h" +#include "stdio.h" +#include "sifrpc.h" +#include "sysclib.h" +#include "usbd.h" +#include "usbd_macro.h" +#include "thbase.h" +#include "thsemap.h" +#include "xboxoneusb.h" + +//#define DPRINTF(x...) printf(x) +#define DPRINTF(x...) + +#define REQ_USB_OUT (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) +#define REQ_USB_IN (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) + +#define MAX_PADS 4 + +static u8 usb_buf[MAX_BUFFER_SIZE + 32] __attribute((aligned(4))) = {0}; + +int usb_probe(int devId); +int usb_connect(int devId); +int usb_disconnect(int devId); + +static void usb_release(int pad); +static void usb_config_set(int result, int count, void *arg); + +UsbDriver usb_driver = {NULL, NULL, "xboxoneusb", usb_probe, usb_connect, usb_disconnect}; + +static void readReport(u8 *data, int pad); +static int Rumble(u8 lrum, u8 rrum, int pad); + +xboxoneusb_device xboxonedev[MAX_PADS]; +static u8 cmdcnt = 0; + +int usb_probe(int devId) +{ + UsbDeviceDescriptor *device = NULL; + + DPRINTF("XBOXONEUSB: probe: devId=%i\n", devId); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + if (device == NULL) { + DPRINTF("XBOXONEUSB: Error - Couldn't get device descriptor\n"); + return 0; + } + + if ((device->idVendor == XBOX_VID || device->idVendor == XBOX_VID2 || device->idVendor == XBOX_VID3 || device->idVendor == XBOX_VID4 || device->idVendor == XBOX_VID5 || device->idVendor == XBOX_VID6) && + (device->idProduct == XBOX_ONE_PID1 || device->idProduct == XBOX_ONE_PID2 || device->idProduct == XBOX_ONE_PID3 || device->idProduct == XBOX_ONE_PID4 || + device->idProduct == XBOX_ONE_PID5 || device->idProduct == XBOX_ONE_PID6 || device->idProduct == XBOX_ONE_PID7 || device->idProduct == XBOX_ONE_PID8 || + device->idProduct == XBOX_ONE_PID9 || device->idProduct == XBOX_ONE_PID10 || device->idProduct == XBOX_ONE_PID11 || device->idProduct == XBOX_ONE_PID12 || device->idProduct == XBOX_ONE_PID13)) + return 1; + + return 0; +} + +int usb_connect(int devId) +{ + int pad, epCount; + UsbDeviceDescriptor *device; + UsbConfigDescriptor *config; + UsbInterfaceDescriptor *interface; + UsbEndpointDescriptor *endpoint; + + DPRINTF("XBOXONEUSB: connect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (xboxonedev[pad].usb_id == -1) + break; + } + + if (pad >= MAX_PADS) { + DPRINTF("XBOXONEUSB: Error - only %d device allowed !\n", MAX_PADS); + return 1; + } + + PollSema(xboxonedev[pad].sema); + + xboxonedev[pad].dev.id = pad; + xboxonedev[pad].usb_id = devId; + xboxonedev[pad].controlEndp = UsbOpenEndpoint(devId, NULL); + + device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE); + config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG); + interface = (UsbInterfaceDescriptor *)((char *)config + config->bLength); + epCount = interface->bNumEndpoints - 1; + endpoint = (UsbEndpointDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_ENDPOINT); + + do { + if (endpoint->bmAttributes == USB_ENDPOINT_XFER_INT) { + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && xboxonedev[pad].interruptEndp < 0) { + xboxonedev[pad].interruptEndp = UsbOpenEndpointAligned(devId, endpoint); + xboxonedev[pad].endin = endpoint; + DPRINTF("XBOXONEUSB: register Event endpoint id =%i addr=%02X packetSize=%i\n", xboxonedev[pad].interruptEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT && xboxonedev[pad].outEndp < 0) { + xboxonedev[pad].outEndp = UsbOpenEndpointAligned(devId, endpoint); + xboxonedev[pad].endout = endpoint; + DPRINTF("DS34USB: register Output endpoint id =%i addr=%02X packetSize=%i\n", xboxonepad[pad].outEndp, endpoint->bEndpointAddress, (unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB); + } + } + + endpoint = (UsbEndpointDescriptor *)((char *)endpoint + endpoint->bLength); + + } while (epCount--); + + if (xboxonedev[pad].interruptEndp < 0 || xboxonedev[pad].outEndp < 0) { + usb_release(pad); + return 1; + } + + UsbSetDeviceConfiguration(xboxonedev[pad].controlEndp, config->bConfigurationValue, usb_config_set, (void *)pad); + SignalSema(xboxonedev[pad].sema); + + return 0; +} + +int usb_disconnect(int devId) +{ + u8 pad; + + DPRINTF("XBOXONEUSB: disconnect: devId=%i\n", devId); + + for (pad = 0; pad < MAX_PADS; pad++) { + if (xboxonedev[pad].usb_id == devId) { + pademu_disconnect(&xboxonedev[pad].dev); + break; + } + } + + if (pad < MAX_PADS) + usb_release(pad); + + return 0; +} + +static void usb_release(int pad) +{ + PollSema(xboxonedev[pad].sema); + + if (xboxonedev[pad].interruptEndp >= 0) + UsbCloseEndpoint(xboxonedev[pad].interruptEndp); + + if (xboxonedev[pad].outEndp >= 0) + UsbCloseEndpoint(xboxonedev[pad].outEndp); + + xboxonedev[pad].controlEndp = -1; + xboxonedev[pad].interruptEndp = -1; + xboxonedev[pad].outEndp = -1; + xboxonedev[pad].usb_id = -1; + + SignalSema(xboxonedev[pad].sema); +} + +static void usb_data_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("XBOXONEUSB: usb_data_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + xboxonedev[pad].usb_resultcode = resultCode; + + SignalSema(xboxonedev[pad].sema); +} + +static void usb_cmd_cb(int resultCode, int bytes, void *arg) +{ + int pad = (int)arg; + + //DPRINTF("XBOXONEUSB: usb_cmd_cb: res %d, bytes %d, arg %p \n", resultCode, bytes, arg); + + SignalSema(xboxonedev[pad].cmd_sema); +} + +static void usb_config_set(int result, int count, void *arg) +{ + int pad = (int)arg; + + PollSema(xboxonedev[pad].sema); + + cmdcnt = 0; + usb_buf[0] = 0x05; + usb_buf[1] = 0x20; + usb_buf[2] = cmdcnt++; + usb_buf[3] = 0x01; + usb_buf[4] = 0x00; + UsbInterruptTransfer(xboxonedev[pad].outEndp, usb_buf, 5, NULL, NULL); + DelayThread(10000); + + SignalSema(xboxonedev[pad].sema); + + pademu_connect(&xboxonedev[pad].dev); +} + +#define MAX_DELAY 10 + +static void readReport(u8 *data, int pad) +{ + xboxonereport_t *report = (xboxonereport_t *)data; + if (report->ReportID == 0x20) { + xboxonedev[pad].data[0] = ~(report->Back | report->LS << 1 | report->RS << 2 | report->Start << 3 | report->Up << 4 | report->Right << 5 | report->Down << 6 | report->Left << 7); + xboxonedev[pad].data[1] = ~((report->LeftTriggerH != 0) | (report->RightTriggerH != 0) << 1 | report->LB << 2 | report->RB << 3 | report->Y << 4 | report->B << 5 | report->A << 6 | report->X << 7); + + xboxonedev[pad].data[2] = report->RightStickXH + 128; //rx + xboxonedev[pad].data[3] = ~(report->RightStickYH + 128); //ry + xboxonedev[pad].data[4] = report->LeftStickXH + 128; //lx + xboxonedev[pad].data[5] = ~(report->LeftStickYH + 128); //ly + + xboxonedev[pad].data[6] = report->Right * 255; //right + xboxonedev[pad].data[7] = report->Left * 255; //left + xboxonedev[pad].data[8] = report->Up * 255; //up + xboxonedev[pad].data[9] = report->Down * 255; //down + + xboxonedev[pad].data[10] = report->Y * 255; //triangle + xboxonedev[pad].data[11] = report->B * 255; //circle + xboxonedev[pad].data[12] = report->A * 255; //cross + xboxonedev[pad].data[13] = report->X * 255; //square + + xboxonedev[pad].data[14] = report->LB * 255; //L1 + xboxonedev[pad].data[15] = report->RB * 255; //R1 + xboxonedev[pad].data[16] = report->LeftTriggerH; //L2 + xboxonedev[pad].data[17] = report->RightTriggerH; //R2 + } +} + +static int Rumble(u8 lrum, u8 rrum, int pad) +{ + PollSema(xboxonedev[pad].cmd_sema); + + usb_buf[0] = 0x09; + usb_buf[1] = 0x00; + usb_buf[2] = cmdcnt++; + usb_buf[3] = 0x09; // Substructure (what substructure rest of this packet has) + usb_buf[4] = 0x00; // Mode + usb_buf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R) + usb_buf[6] = 0x00; // lT force + usb_buf[7] = 0x00; // rT force + usb_buf[8] = lrum; // L force + usb_buf[9] = rrum; // R force + usb_buf[10] = 0x80; // Length of pulse + usb_buf[11] = 0x00; // Off period + usb_buf[12] = 0x00; // Repeat count + + return UsbInterruptTransfer(xboxonedev[pad].outEndp, usb_buf, 13, usb_cmd_cb, (void *)pad); +} + +static unsigned int timeout(void *arg) +{ + int sema = (int)arg; + iSignalSema(sema); + return 0; +} + +static void TransferWait(int sema) +{ + iop_sys_clock_t cmd_timeout; + + cmd_timeout.lo = 200000; + cmd_timeout.hi = 0; + + if (SetAlarm(&cmd_timeout, timeout, (void *)sema) == 0) { + WaitSema(sema); + CancelAlarm(timeout, NULL); + } +} + +void xboxoneusb_set_rumble(u8 lrum, u8 rrum, int port) +{ + WaitSema(xboxonedev[port].sema); + + xboxonedev[port].update_rum = 1; + xboxonedev[port].lrum = lrum; + xboxonedev[port].rrum = rrum; + + SignalSema(xboxonedev[port].sema); +} + +int xboxoneusb_get_data(u8 *dst, int size, int port) +{ + int ret = 0; + + WaitSema(xboxonedev[port].sema); + + PollSema(xboxonedev[port].sema); + + ret = UsbInterruptTransfer(xboxonedev[port].interruptEndp, usb_buf, MAX_BUFFER_SIZE, usb_data_cb, (void *)port); + + if (ret == USB_RC_OK) { + TransferWait(xboxonedev[port].sema); + if (!xboxonedev[port].usb_resultcode) + readReport(usb_buf, port); + + xboxonedev[port].usb_resultcode = 1; + } else { + UsbCloseEndpoint(xboxonedev[port].interruptEndp); + xboxonedev[port].interruptEndp = UsbOpenEndpointAligned(xboxonedev[port].usb_id, xboxonedev[port].endin); + DPRINTF("XBOXONEUSB: XBOXONEUSB_get_data usb transfer error %d\n", ret); + } + + mips_memcpy(dst, xboxonedev[port].data, size); + ret = xboxonedev[port].analog_btn & 1; + + if (xboxonedev[port].update_rum) { + ret = Rumble(xboxonedev[port].lrum, xboxonedev[port].rrum, port); + if (ret == USB_RC_OK) { + TransferWait(xboxonedev[port].cmd_sema); + } else { + UsbCloseEndpoint(xboxonedev[port].outEndp); + xboxonedev[port].outEndp = UsbOpenEndpointAligned(xboxonedev[port].usb_id, xboxonedev[port].endout); + DPRINTF("XBOXONEUSB: LEDRumble usb transfer error %d\n", ret); + } + + xboxonedev[port].update_rum = 0; + } + + SignalSema(xboxonedev[port].sema); + + return ret; +} + +void xboxoneusb_set_mode(int mode, int lock, int port) +{ + if (lock == 3) + xboxonedev[port].analog_btn = 3; + else + xboxonedev[port].analog_btn = mode; +} + +void xboxoneusb_reset() +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) + usb_release(pad); +} + +int _start(int argc, char *argv[]) +{ + int pad; + + for (pad = 0; pad < MAX_PADS; pad++) { + xboxonedev[pad].usb_id = -1; + xboxonedev[pad].dev.id = -1; + xboxonedev[pad].dev.pad_get_data = xboxoneusb_get_data; + xboxonedev[pad].dev.pad_set_rumble = xboxoneusb_set_rumble; + xboxonedev[pad].dev.pad_set_mode = xboxoneusb_set_mode; + + xboxonedev[pad].lrum = 0; + xboxonedev[pad].rrum = 0; + xboxonedev[pad].update_rum = 1; + xboxonedev[pad].sema = -1; + xboxonedev[pad].cmd_sema = -1; + xboxonedev[pad].controlEndp = -1; + xboxonedev[pad].interruptEndp = -1; + xboxonedev[pad].outEndp = -1; + + xboxonedev[pad].data[0] = 0xFF; + xboxonedev[pad].data[1] = 0xFF; + xboxonedev[pad].analog_btn = 0; + + mips_memset(&xboxonedev[pad].data[2], 0x7F, 4); + mips_memset(&xboxonedev[pad].data[6], 0x00, 12); + + xboxonedev[pad].sema = CreateMutex(IOP_MUTEX_UNLOCKED); + xboxonedev[pad].cmd_sema = CreateMutex(IOP_MUTEX_UNLOCKED); + + if (xboxonedev[pad].sema < 0 || xboxonedev[pad].cmd_sema < 0) { + DPRINTF("XBOXONEUSB: Failed to allocate I/O semaphore.\n"); + return MODULE_NO_RESIDENT_END; + } + } + + if (UsbRegisterDriver(&usb_driver) != USB_RC_OK) { + DPRINTF("XBOXONEUSB: Error registering USB devices\n"); + return MODULE_NO_RESIDENT_END; + } + + return MODULE_RESIDENT_END; +} diff --git a/modules/pademu/xboxoneusb/xboxoneusb.h b/modules/pademu/xboxoneusb/xboxoneusb.h new file mode 100644 index 000000000..72c95b6b2 --- /dev/null +++ b/modules/pademu/xboxoneusb/xboxoneusb.h @@ -0,0 +1,172 @@ +#ifndef _DS3USB_H_ +#define _DS3USB_H_ + +#include "irx.h" +#include "usbd.h" +#include "../pademu.h" + +#define XBOX_VID 0x045E // Microsoft Corporation + +#define XBOX_ONE_PID1 0x02D1 // Microsoft X-Box One pad +#define XBOX_ONE_PID2 0x02DD // Microsoft X-Box One pad (Firmware 2015) +#define XBOX_ONE_PID3 0x02E3 // Microsoft X-Box One Elite pad +#define XBOX_ONE_PID4 0x02EA // Microsoft X-Box One S pad +#define XBOX_ONE_PID13 0x0B0A // Microsoft X-Box One Adaptive Controller + +// Unofficial controllers +#define XBOX_VID2 0x0738 // Mad Catz +#define XBOX_VID3 0x0E6F // Afterglow +#define XBOX_VID4 0x0F0D // HORIPAD ONE +#define XBOX_VID5 0x1532 // Razer +#define XBOX_VID6 0x24C6 // PowerA + +#define XBOX_ONE_PID5 0x4A01 // Mad Catz FightStick TE 2 - might have different mapping for triggers? +#define XBOX_ONE_PID6 0x0139 // Afterglow Prismatic Wired Controller +#define XBOX_ONE_PID7 0x0146 // Rock Candy Wired Controller for Xbox One +#define XBOX_ONE_PID8 0x0067 // HORIPAD ONE +#define XBOX_ONE_PID9 0x0A03 // Razer Wildcat +#define XBOX_ONE_PID10 0x541A // PowerA Xbox One Mini Wired Controller +#define XBOX_ONE_PID11 0x542A // Xbox ONE spectra +#define XBOX_ONE_PID12 0x543A // PowerA Xbox One wired controller + +#define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer + +typedef struct +{ + pad_device_t dev; + int usb_id; + int sema; + int cmd_sema; + int controlEndp; + int interruptEndp; + int outEndp; + int usb_resultcode; + UsbEndpointDescriptor *endin; + UsbEndpointDescriptor *endout; + u8 lrum; + u8 rrum; + u8 update_rum; + u8 data[18]; + u8 analog_btn; + u8 btn_delay; +} xboxoneusb_device; + +enum eHID { + // {{{ + /* HID event flag */ + HID_FLAG_STATUS_REPORTED = 0x01, + HID_FLAG_BUTTONS_CHANGED = 0x02, + HID_FLAG_EXTENSION = 0x04, + HID_FLAG_COMMAND_SUCCESS = 0x08, + + /* USB HID Transaction Header (THdr) */ + HID_USB_GET_REPORT_FEATURE = 0x03, + HID_USB_SET_REPORT_OUTPUT = 0x02, + HID_USB_DATA_INPUT = 0x01, + + /* Defines of various parameters for PS3 Game controller reports */ + PS3_F4_REPORT_ID = 0xF4, + PS3_F4_REPORT_LEN = 0x04, + + PS3_01_REPORT_ID = 0x01, + PS3_01_REPORT_LEN = 0x30, + + PS4_02_REPORT_ID = 0x02, + PS4_11_REPORT_ID = 0x11, + PS4_11_REPORT_LEN = 0x4D, + // }}} +}; + +typedef struct +{ + u8 ReportID; // 0x20 + u8 Zero; + u16 id; + + union + { + u8 ButtonStateL; // Main buttons Low + struct + { + u8 Sync : 1; + u8 Dummy1 : 1; + u8 Start : 1; + u8 Back : 1; + u8 A : 1; + u8 B : 1; + u8 X : 1; + u8 Y : 1; + }; + }; + union + { + u8 ButtonStateH; // Main buttons High + struct + { + u8 Up : 1; + u8 Down : 1; + u8 Left : 1; + u8 Right : 1; + u8 LB : 1; + u8 RB : 1; + u8 LS : 1; + u8 RS : 1; + }; + }; + union + { + u16 LeftTrigger; + struct + { + u8 LeftTriggerL; + u8 LeftTriggerH; + }; + }; + union + { + u16 RightTrigger; + struct + { + u8 RightTriggerL; + u8 RightTriggerH; + }; + }; + union + { + s16 LeftStickX; + struct + { + u8 LeftStickXL; + u8 LeftStickXH; + }; + }; + union + { + s16 LeftStickY; + struct + { + u8 LeftStickYL; + u8 LeftStickYH; + }; + }; + union + { + s16 RightStickX; + struct + { + u8 RightStickXL; + u8 RightStickXH; + }; + }; + union + { + s16 RightStickY; + struct + { + u8 RightStickYL; + u8 RightStickYH; + }; + }; +} __attribute__((packed)) xboxonereport_t; + +#endif diff --git a/src/dialogs.c b/src/dialogs.c index d777787a9..6bbd81aed 100644 --- a/src/dialogs.c +++ b/src/dialogs.c @@ -532,9 +532,11 @@ struct UIItem diaPadEmuConfig[] = { {UI_BOOL, PADCFG_PADEMU_ENABLE, 1, 1, _STR_HINT_PADEMU_ENABLE, 0, 0, {.intvalue = {1, 1}}}, {UI_BREAK}, - {UI_LABEL, 0, 1, 1, -1, -45, 0, {.label = {NULL, _STR_PADEMU_MODE}}}, + {UI_LABEL, 0, 1, 1, -1, -45, 0, {.label = {NULL, _STR_PADEMU_MODULES}}}, {UI_SPACER}, - {UI_ENUM, PADCFG_PADEMU_MODE, 1, 1, _STR_HINT_PADEMU_MODE, 0, 0, {.intvalue = {1, 1}}}, + {UI_ENUM, PADCFG_PADEMU_MODULES_LIST, 1, 1, _STR_HINT_PADEMU_MODULES, 0, 0, {.intvalue = {1, 1}}}, + {UI_SPACER}, + {UI_BOOL, PADCFG_PADEMU_MODULES_SET, 1, 1, _STR_HINT_PADEMU_MODULES_SET, 0, 0, {.intvalue = {1, 1}}}, {UI_BREAK}, {UI_LABEL, 0, 1, 1, -1, -45, 0, {.label = {NULL, _STR_MTAP_ENABLE}}}, diff --git a/src/guigame.c b/src/guigame.c index c8d7b9077..fa07047ce 100644 --- a/src/guigame.c +++ b/src/guigame.c @@ -38,6 +38,7 @@ static int CheatMode; #ifdef PADEMU static int EnablePadEmu; static int PadEmuSettings; +static int PadEmuModules; #endif static char hexid[32]; @@ -506,8 +507,8 @@ static char *ver_to_str(u8 *str, u8 ma, u16 mi) static int guiGamePadEmuUpdater(int modified) { - int PadEmuMode, PadPort, PadEmuVib, PadEmuPort, PadEmuMtap, PadEmuMtapPort, PadEmuWorkaround; - static int oldPadPort; + int PadEmuModulesList, PadPort, PadEmuVib, PadEmuPort, PadEmuMtap, PadEmuMtapPort, PadEmuWorkaround, PadEmuModulesSet; + static int oldPadPort, oldPadEmuModulesList; int previousSource = gPadEmuSource; diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_SOURCE, &gPadEmuSource); @@ -524,7 +525,8 @@ static int guiGamePadEmuUpdater(int modified) } diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_ENABLE, &EnablePadEmu); - diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_MODE, &PadEmuMode); + diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_MODULES_LIST, &PadEmuModulesList); + diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_MODULES_SET, &PadEmuModulesSet); diaGetInt(diaPadEmuConfig, PADCFG_PADPORT, &PadPort); diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_PORT, &PadEmuPort); diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_VIB, &PadEmuVib); @@ -536,32 +538,23 @@ static int guiGamePadEmuUpdater(int modified) diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_MTAP, EnablePadEmu); diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_MTAP_PORT, PadEmuMtap); - diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_MODE, EnablePadEmu); + diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_MODULES_LIST, EnablePadEmu); + diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_MODULES_SET, EnablePadEmu); diaSetEnabled(diaPadEmuConfig, PADCFG_PADPORT, EnablePadEmu); diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_VIB, PadEmuPort & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC, (PadEmuMode == 1) & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC, (PadEmuMode == 1) & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PAIR, (PadEmuMode == 1) & EnablePadEmu); - - diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC_STR, (PadEmuMode == 1) & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC_STR, (PadEmuMode == 1) & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PAIR_STR, (PadEmuMode == 1) & EnablePadEmu); - - diaSetVisible(diaPadEmuConfig, PADCFG_BTINFO, (PadEmuMode == 1) & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, (PadEmuMode == 1) & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND_STR, (PadEmuMode == 1) & EnablePadEmu); + diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, EnablePadEmu); if (modified) { if (PadEmuMtap) { diaSetEnum(diaPadEmuConfig, PADCFG_PADPORT, PadEmuPorts_enums[PadEmuMtapPort]); diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, (PadPort == 0) & EnablePadEmu); - PadEmuSettings |= 0x00000E00; + PadEmuSettings |= 0x0000000E; } else { diaSetEnum(diaPadEmuConfig, PADCFG_PADPORT, PadEmuPorts_enums[0]); diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, EnablePadEmu); - PadEmuSettings &= 0xFFFF03FF; + PadEmuSettings &= 0xFFFFFF03; if (PadPort > 1) { PadPort = 0; diaSetInt(diaPadEmuConfig, PADCFG_PADPORT, PadPort); @@ -569,51 +562,58 @@ static int guiGamePadEmuUpdater(int modified) } if (PadPort != oldPadPort) { - diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_PORT, (PadEmuSettings >> (8 + PadPort)) & 1); - diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_VIB, (PadEmuSettings >> (16 + PadPort)) & 1); + diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_PORT, (PadEmuSettings >> (PadPort)) & 1); + diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_VIB, (PadEmuSettings >> (8 + PadPort)) & 1); oldPadPort = PadPort; } + + if (PadEmuModulesList != oldPadEmuModulesList) { + diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_MODULES_SET, (PadEmuModules >> PadEmuModulesList) & 1); + + oldPadEmuModulesList = PadEmuModulesList; + } } - PadEmuSettings |= PadEmuMode | (PadEmuPort << (8 + PadPort)) | (PadEmuVib << (16 + PadPort)) | (PadEmuMtap << 24) | ((PadEmuMtapPort - 1) << 25) | (PadEmuWorkaround << 26); - PadEmuSettings &= (~(!PadEmuMode) & ~(!PadEmuPort << (8 + PadPort)) & ~(!PadEmuVib << (16 + PadPort)) & ~(!PadEmuMtap << 24) & ~(!(PadEmuMtapPort - 1) << 25) & ~(!PadEmuWorkaround << 26)); + PadEmuSettings |= (PadEmuPort << (PadPort)) | (PadEmuVib << (8 + PadPort)) | (PadEmuMtap << 16) | ((PadEmuMtapPort - 1) << 17) | (PadEmuWorkaround << 18); + PadEmuSettings &= (~(!PadEmuPort << (PadPort)) & ~(!PadEmuVib << (8 + PadPort)) & ~(!PadEmuMtap << 16) & ~(!(PadEmuMtapPort - 1) << 17) & ~(!PadEmuWorkaround << 18)); + + PadEmuModules |= PadEmuModulesSet << PadEmuModulesList; + PadEmuModules &= ~(!PadEmuModulesSet << PadEmuModulesList); - if (PadEmuMode == 1) { - if (ds34bt_get_status(0) & DS34BT_STATE_USB_CONFIGURED) { - if (dg_discon) { + if (ds34bt_get_status(0) & DS34BT_STATE_USB_CONFIGURED) { + if (dg_discon) { + dgmacset = 0; + dg_discon = 0; + } + if (!dgmacset) { + if (ds34bt_get_bdaddr(dg_mac)) { + dgmacset = 1; + diaSetLabel(diaPadEmuConfig, PADCFG_USBDG_MAC, bdaddr_to_str(dg_mac, dg_str)); + } else { dgmacset = 0; - dg_discon = 0; } - if (!dgmacset) { - if (ds34bt_get_bdaddr(dg_mac)) { - dgmacset = 1; - diaSetLabel(diaPadEmuConfig, PADCFG_USBDG_MAC, bdaddr_to_str(dg_mac, dg_str)); - } else { - dgmacset = 0; - } - } - } else { - dg_discon = 1; } + } else { + dg_discon = 1; + } - if (!dgmacset) { - diaSetLabel(diaPadEmuConfig, PADCFG_USBDG_MAC, _l(_STR_NOT_CONNECTED)); - } + if (!dgmacset) { + diaSetLabel(diaPadEmuConfig, PADCFG_USBDG_MAC, _l(_STR_NOT_CONNECTED)); + } - if (ds34usb_get_status(0) & DS34USB_STATE_RUNNING) { - if (!ds3macset) { - if (ds34usb_get_bdaddr(0, ds3_mac)) { - ds3macset = 1; - diaSetLabel(diaPadEmuConfig, PADCFG_PAD_MAC, bdaddr_to_str(ds3_mac, ds3_str)); - } else { - ds3macset = 0; - } + if (ds34usb_get_status(0) & DS34USB_STATE_RUNNING) { + if (!ds3macset) { + if (ds34usb_get_bdaddr(0, ds3_mac)) { + ds3macset = 1; + diaSetLabel(diaPadEmuConfig, PADCFG_PAD_MAC, bdaddr_to_str(ds3_mac, ds3_str)); + } else { + ds3macset = 0; } - } else { - diaSetLabel(diaPadEmuConfig, PADCFG_PAD_MAC, _l(_STR_NOT_CONNECTED)); - ds3macset = 0; } + } else { + diaSetLabel(diaPadEmuConfig, PADCFG_PAD_MAC, _l(_STR_NOT_CONNECTED)); + ds3macset = 0; } return 0; @@ -674,36 +674,24 @@ static int guiGamePadEmuInfoUpdater(int modified) void guiGameShowPadEmuConfig(void) { const char *settingsSource[] = {_l(_STR_GLOBAL_SETTINGS), _l(_STR_PERGAME_SETTINGS), NULL}; - const char *PadEmuModes[] = {_l(_STR_DS34USB_MODE), _l(_STR_DS34BT_MODE), NULL}; + const char *PadEmuModulesList[] = {"DualShock 3 USB", "DualShock 3 BT", "DualShock 4 USB", "DualShock 4 BT", "XBOX 360 USB", "XBOX ONE USB", "HID USB", NULL}; int PadEmuMtap, PadEmuMtapPort, i; diaSetEnum(diaPadEmuConfig, PADCFG_PADEMU_SOURCE, settingsSource); - diaSetEnum(diaPadEmuConfig, PADCFG_PADEMU_MODE, PadEmuModes); + diaSetEnum(diaPadEmuConfig, PADCFG_PADEMU_MODULES_LIST, PadEmuModulesList); - PadEmuMtap = (PadEmuSettings >> 24) & 1; - PadEmuMtapPort = ((PadEmuSettings >> 25) & 1) + 1; + PadEmuMtap = (PadEmuSettings >> 16) & 1; + PadEmuMtapPort = ((PadEmuSettings >> 17) & 1) + 1; diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC, PadEmuSettings & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC, PadEmuSettings & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PAIR, PadEmuSettings & EnablePadEmu); - - diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC_STR, PadEmuSettings & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC_STR, PadEmuSettings & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PAIR_STR, PadEmuSettings & EnablePadEmu); - - diaSetVisible(diaPadEmuConfig, PADCFG_BTINFO, PadEmuSettings & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, PadEmuSettings & EnablePadEmu); - diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND_STR, PadEmuSettings & EnablePadEmu); - if (PadEmuMtap) { diaSetEnum(diaPadEmuConfig, PADCFG_PADPORT, PadEmuPorts_enums[PadEmuMtapPort]); - PadEmuSettings |= 0x00000E00; + PadEmuSettings |= 0x0000000E; } else { diaSetEnum(diaPadEmuConfig, PADCFG_PADPORT, PadEmuPorts_enums[0]); - PadEmuSettings &= 0xFFFF03FF; + PadEmuSettings &= 0xFFFFFF03; } int result = -1; @@ -883,9 +871,15 @@ int guiGameSaveConfig(config_set_t *configSet, item_list_t *support) result = configSetInt(configSet, CONFIG_ITEM_PADEMUSETTINGS, PadEmuSettings); else configRemoveKey(configSet, CONFIG_ITEM_PADEMUSETTINGS); + + if (PadEmuModules != 0) + result = configSetInt(configSet, CONFIG_ITEM_PADEMUMODULES, PadEmuModules); + else + configRemoveKey(configSet, CONFIG_ITEM_PADEMUMODULES); } else if (gPadEmuSource == SETTINGS_GLOBAL) { configSetInt(configGame, CONFIG_ITEM_ENABLEPADEMU, EnablePadEmu); configSetInt(configGame, CONFIG_ITEM_PADEMUSETTINGS, PadEmuSettings); + configSetInt(configGame, CONFIG_ITEM_PADEMUMODULES, PadEmuModules); } #endif @@ -924,6 +918,7 @@ void guiGameRemoveGlobalSettings(config_set_t *configGame) // PADEMU configRemoveKey(configGame, CONFIG_ITEM_ENABLEPADEMU); configRemoveKey(configGame, CONFIG_ITEM_PADEMUSETTINGS); + configRemoveKey(configGame, CONFIG_ITEM_PADEMUMODULES); #endif saveConfig(CONFIG_GAME, 0); } @@ -956,6 +951,7 @@ void guiGameRemoveSettings(config_set_t *configSet) configRemoveKey(configSet, CONFIG_ITEM_PADEMUSOURCE); configRemoveKey(configSet, CONFIG_ITEM_ENABLEPADEMU); configRemoveKey(configSet, CONFIG_ITEM_PADEMUSETTINGS); + configRemoveKey(configSet, CONFIG_ITEM_PADEMUMODULES); #endif // VMC configRemoveVMC(configSet, 0); @@ -1041,11 +1037,13 @@ static void guiGameLoadPadEmuConfig(config_set_t *configSet, config_set_t *confi { EnablePadEmu = 0; PadEmuSettings = 0; + PadEmuModules = 0; // set global settings. gPadEmuSource = 0; configGetInt(configGame, CONFIG_ITEM_ENABLEPADEMU, &EnablePadEmu); configGetInt(configGame, CONFIG_ITEM_PADEMUSETTINGS, &PadEmuSettings); + configGetInt(configGame, CONFIG_ITEM_PADEMUMODULES, &PadEmuModules); // override global with per-game settings if available and selected. configGetInt(configSet, CONFIG_ITEM_PADEMUSOURCE, &gPadEmuSource); @@ -1054,21 +1052,22 @@ static void guiGameLoadPadEmuConfig(config_set_t *configSet, config_set_t *confi EnablePadEmu = 0; if (!configGetInt(configSet, CONFIG_ITEM_PADEMUSETTINGS, &PadEmuSettings)) PadEmuSettings = 0; + if (!configGetInt(configSet, CONFIG_ITEM_PADEMUMODULES, &PadEmuModules)) + PadEmuModules = 0; } // set gui settings. - int PadEmuMtap = (PadEmuSettings >> 24) & 1; - int PadEmuMtapPort = ((PadEmuSettings >> 25) & 1) + 1; + int PadEmuMtap = (PadEmuSettings >> 16) & 1; + int PadEmuMtapPort = ((PadEmuSettings >> 17) & 1) + 1; diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_SOURCE, gPadEmuSource); diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_ENABLE, EnablePadEmu); - diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_MODE, PadEmuSettings & 0xFF); diaSetInt(diaPadEmuConfig, PADCFG_PADPORT, 0); - diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_PORT, (PadEmuSettings >> 8) & 1); - diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_VIB, (PadEmuSettings >> 16) & 1); + diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_PORT, (PadEmuSettings)&1); + diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_VIB, (PadEmuSettings >> 8) & 1); diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_MTAP, PadEmuMtap); diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_MTAP_PORT, PadEmuMtapPort); - diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, ((PadEmuSettings >> 26) & 1)); + diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, ((PadEmuSettings >> 18) & 1)); } #endif diff --git a/src/lang.c b/src/lang.c index b2c5b32c5..9656f62ed 100644 --- a/src/lang.c +++ b/src/lang.c @@ -220,10 +220,9 @@ static char *internalEnglish[LANG_STR_COUNT] = { "Pad Emulator Settings", "Enable Pad Emulator", "Turns on/off Pad Emulator for selected game.", - "Pad Emulator Mode", - "Select Pad Emulator mode.", - "DualShock3/4 USB", - "DualShock3/4 BT", + "Select modules to load", + "Select which pademu modules to load.", + "Select/Unselect pademu module.", "Settings For Port:", "Select Pad Emulator port for settings.", "Enable Emulation", diff --git a/src/supportbase.c b/src/supportbase.c index 1c176e658..229b06e9e 100644 --- a/src/supportbase.c +++ b/src/supportbase.c @@ -587,13 +587,16 @@ int sbPrepare(base_game_info_t *game, config_set_t *configSet, int size_cdvdman, gPadEmuSource = 0; gEnablePadEmu = 0; gPadEmuSettings = 0; + gPadEmuModules = 0; if (configGetInt(configSet, CONFIG_ITEM_PADEMUSOURCE, &gPadEmuSource)) { configGetInt(configSet, CONFIG_ITEM_ENABLEPADEMU, &gEnablePadEmu); configGetInt(configSet, CONFIG_ITEM_PADEMUSETTINGS, &gPadEmuSettings); + configGetInt(configSet, CONFIG_ITEM_PADEMUMODULES, &gPadEmuModules); } else { configGetInt(configGame, CONFIG_ITEM_ENABLEPADEMU, &gEnablePadEmu); configGetInt(configGame, CONFIG_ITEM_PADEMUSETTINGS, &gPadEmuSettings); + configGetInt(configGame, CONFIG_ITEM_PADEMUMODULES, &gPadEmuModules); } #endif diff --git a/src/system.c b/src/system.c index 04a658db5..65023b8db 100644 --- a/src/system.c +++ b/src/system.c @@ -485,13 +485,47 @@ static unsigned int sendIrxKernelRAM(const char *startup, const char *mode_str, } #ifdef PADEMU + int btstack_loaded = 0; if (gEnablePadEmu) { - if (gPadEmuSettings & 0xFF) { - irxptr_tab[modcount].info = size_bt_pademu_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_PADEMU); - irxptr_tab[modcount++].ptr = (void *)&bt_pademu_irx; - } else { - irxptr_tab[modcount].info = size_usb_pademu_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_PADEMU); - irxptr_tab[modcount++].ptr = (void *)&usb_pademu_irx; + irxptr_tab[modcount].info = size_pademu_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_PADEMU); + irxptr_tab[modcount++].ptr = (void *)&pademu_irx; + if (gPadEmuModules & (1 << 0)) { + irxptr_tab[modcount].info = size_ds3usb_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_DS3USB); + irxptr_tab[modcount++].ptr = (void *)&ds3usb_irx; + } + if (gPadEmuModules & (1 << 1)) { + if (!btstack_loaded) { + irxptr_tab[modcount].info = size_btstack_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_BTSTACK); + irxptr_tab[modcount++].ptr = (void *)&btstack_irx; + btstack_loaded = 1; + } + irxptr_tab[modcount].info = size_ds3bt_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_DS3BT); + irxptr_tab[modcount++].ptr = (void *)&ds3bt_irx; + } + if (gPadEmuModules & (1 << 2)) { + irxptr_tab[modcount].info = size_ds4usb_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_DS4USB); + irxptr_tab[modcount++].ptr = (void *)&ds4usb_irx; + } + if (gPadEmuModules & (1 << 3)) { + if (!btstack_loaded) { + irxptr_tab[modcount].info = size_btstack_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_BTSTACK); + irxptr_tab[modcount++].ptr = (void *)&btstack_irx; + btstack_loaded = 1; + } + irxptr_tab[modcount].info = size_ds4bt_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_DS4BT); + irxptr_tab[modcount++].ptr = (void *)&ds4bt_irx; + } + if (gPadEmuModules & (1 << 4)) { + irxptr_tab[modcount].info = size_xbox360usb_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_XBOX360USB); + irxptr_tab[modcount++].ptr = (void *)&xbox360usb_irx; + } + if (gPadEmuModules & (1 << 5)) { + irxptr_tab[modcount].info = size_xboxoneusb_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_XBOXONEUSB); + irxptr_tab[modcount++].ptr = (void *)&xboxoneusb_irx; + } + if (gPadEmuModules & (1 << 6)) { + irxptr_tab[modcount].info = size_hidusb_irx | SET_OPL_MOD_ID(OPL_MODULE_ID_HIDUSB); + irxptr_tab[modcount++].ptr = (void *)&hidusb_irx; } } #endif @@ -773,8 +807,8 @@ void sysLaunchLoaderElf(const char *filename, const char *mode_str, int size_cdv sprintf(KernelConfig, "%u %u", (unsigned int)eeloadCopy, (unsigned int)initUserMemory); #ifdef PADEMU -#define PADEMU_SPECIFIER " %d, %u" -#define PADEMU_ARGUMENT , gEnablePadEmu, (unsigned int)(gPadEmuSettings >> 8) +#define PADEMU_SPECIFIER " %d, %u %u" +#define PADEMU_ARGUMENT , gEnablePadEmu, (unsigned int)gPadEmuSettings, (unsigned int)gPadEmuModules #else #define PADEMU_SPECIFIER #define PADEMU_ARGUMENT