/**************************************************************************** * Copyright (C) 2014 FIX94 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ /* WiiU Pro Controller Documentation from TeHaxor69 */ #include #include #include #include #include #include "wupc_structs.h" #include "wupc/wupc.h" extern __typeof(wiiuse_register) __real_wiiuse_register; static vu32 __WUPC_ChannelsUsed = 0; static conf_pads __WUPC_Devices; static struct WUPCStat *__WUPC_Connected[4]; static struct WUPCStat __WUPC_Status[CONF_PAD_MAX_REGISTERED]; static struct WUPCData __WUPC_PadData[4]; static struct WUPCButtons __WUPC_PadButtons[4]; static u32 __WUPC_Inited = 0; static const u8 __WUPC_LEDState[] = { 0x10, 0x20, 0x40, 0x80 }; #define CHAN_MAX 4 #define TRANSFER_CALIBRATE 0 #define TRANSFER_DONE 1 static void __WUPC_SetLED(struct bte_pcb *sock, u32 state) { u8 buf[2]; buf[0] = 0x11; buf[1] = __WUPC_LEDState[state]; bte_senddata(sock,buf,2); } static s32 __WUPC_HandleData(void *arg,void *buffer,u16 len) { struct WUPCStat *stat = (struct WUPCStat*)arg; u32 chan = stat->channel; if(*(u8*)buffer == 0x3D && len == 22) { if(stat->transferstate == TRANSFER_CALIBRATE) { stat->xAxisLmid = bswap16(*(u16*)(((u8*)buffer)+1)); stat->xAxisRmid = bswap16(*(u16*)(((u8*)buffer)+3)); stat->yAxisLmid = bswap16(*(u16*)(((u8*)buffer)+5)); stat->yAxisRmid = bswap16(*(u16*)(((u8*)buffer)+7)); stat->transferstate = TRANSFER_DONE; } __WUPC_PadData[chan].xAxisL = bswap16(*(u16*)(((u8*)buffer)+1)) - stat->xAxisLmid; __WUPC_PadData[chan].xAxisR = bswap16(*(u16*)(((u8*)buffer)+3)) - stat->xAxisRmid; __WUPC_PadData[chan].yAxisL = bswap16(*(u16*)(((u8*)buffer)+5)) - stat->yAxisLmid; __WUPC_PadData[chan].yAxisR = bswap16(*(u16*)(((u8*)buffer)+7)) - stat->yAxisRmid; __WUPC_PadData[chan].button = ~(*(u16*)(((u8*)buffer)+9)) << 16; u8 extradata = ~(*(((u8*)buffer)+11)); __WUPC_PadData[chan].battery = (extradata >> 4) & 0x7; __WUPC_PadData[chan].extra = extradata & 0xF; } return ERR_OK; } static s32 __WUPC_HandleConnect(void *arg,struct bte_pcb *pcb,u8 err) { struct WUPCStat *stat = (struct WUPCStat*)arg; if(__WUPC_ChannelsUsed >= CHAN_MAX) { bte_disconnect(pcb); return err; } stat->channel = __WUPC_ChannelsUsed; stat->rumble = 0; __WUPC_SetLED(pcb, __WUPC_ChannelsUsed); u8 buf[3]; buf[0] = 0x12; buf[1] = 0x00; buf[2] = 0x3D; bte_senddata(pcb,buf,3); stat->transferstate = TRANSFER_CALIBRATE; __WUPC_Connected[__WUPC_ChannelsUsed] = stat; __WUPC_ChannelsUsed++; return err; } static s32 __WUPC_HandleDisconnect(void *arg,struct bte_pcb *pcb __attribute__((unused)),u8 err) { struct WUPCStat *stat = (struct WUPCStat*)arg; if(__WUPC_ChannelsUsed) __WUPC_ChannelsUsed--; u32 i; for(i = stat->channel; i < 3; ++i) __WUPC_Connected[i] = __WUPC_Connected[i+1]; __WUPC_Connected[3] = NULL; for(i = 0; i < CONF_PAD_MAX_REGISTERED; ++i) { if(__WUPC_Status[i].channel > stat->channel && __WUPC_Status[i].channel != CHAN_MAX) { __WUPC_Status[i].channel--; __WUPC_SetLED(__WUPC_Status[i].sock, __WUPC_Status[i].channel); } } stat->channel = CHAN_MAX; return err; } int __WUPC_RegisterPad(struct WUPCStat *stat, struct bd_addr *_bdaddr) { stat->channel = CHAN_MAX; stat->bdaddr = *_bdaddr; if(stat->sock == NULL) { stat->sock = bte_new(); if(stat->sock == NULL) return ERR_OK; } bte_arg(stat->sock, stat); bte_received(stat->sock, __WUPC_HandleData); bte_disconnected(stat->sock, __WUPC_HandleDisconnect); bte_registerdeviceasync(stat->sock, _bdaddr, __WUPC_HandleConnect); return ERR_OK; } int __wrap_wiiuse_register(struct wiimote_listen_t *wml, struct bd_addr *bdaddr, struct wiimote_t *(*assign_cb)(struct bd_addr *bdaddr)) { if(__WUPC_Inited) { u8 got_addr[] = { bdaddr->addr[5], bdaddr->addr[4], bdaddr->addr[3], bdaddr->addr[2], bdaddr->addr[1], bdaddr->addr[0] }; u32 i; for(i = 0; i < __WUPC_Devices.num_registered; ++i) { if(strstr(__WUPC_Devices.registered[i].name, "-UC") != NULL) { u8 *cur_bdaddr = __WUPC_Devices.registered[i].bdaddr; if(memcmp(cur_bdaddr, got_addr, 6) == 0) return __WUPC_RegisterPad(&__WUPC_Status[i], bdaddr); } } } return __real_wiiuse_register(wml,bdaddr,assign_cb); } void WUPC_Init() { if(__WUPC_Inited == 1) return; if(CONF_GetPadDevices(&__WUPC_Devices) < 0) return; u32 i; for(i = 0; i < CHAN_MAX; ++i) __WUPC_Connected[i] = NULL; for(i = 0; i < CONF_PAD_MAX_REGISTERED; ++i) __WUPC_Status[i].channel = CHAN_MAX; __WUPC_ChannelsUsed = 0; __WUPC_Inited = 1; } void WUPC_Disconnect(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return; bte_disconnect(__WUPC_Connected[chan]->sock); } void WUPC_Shutdown() { if(__WUPC_Inited == 0) return; __WUPC_Inited = 0; u32 i; for(i = 0; i < CONF_PAD_MAX_REGISTERED; ++i) { if(__WUPC_Status[i].channel != CHAN_MAX) bte_disconnect(__WUPC_Status[i].sock); } } struct WUPCData *WUPC_Data(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return NULL; return &__WUPC_PadData[chan]; } void WUPC_Rumble(u8 chan, bool rumble) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return; u8 buf[2]; buf[0] = 0x11; buf[1] = __WUPC_LEDState[chan] | rumble; bte_senddata(__WUPC_Connected[chan]->sock,buf,2); } u32 WUPC_UpdateButtonStats() { u32 newstate, oldstate; u32 i, ret = 0; for(i = 0; i < CHAN_MAX; ++i) { if(__WUPC_Connected[i] == NULL) return ret; newstate = __WUPC_PadData[i].button | (__WUPC_PadData[i].extra & WUPC_EXTRA_BUTTON_RSTICK) | (__WUPC_PadData[i].extra & WUPC_EXTRA_BUTTON_LSTICK); oldstate = __WUPC_PadButtons[i].state; __WUPC_PadButtons[i].state = newstate; __WUPC_PadButtons[i].up = oldstate & ~newstate; __WUPC_PadButtons[i].down = newstate & (newstate ^ oldstate); ret |= (1<= CHAN_MAX || __WUPC_Connected[chan] == NULL) return 0; return __WUPC_PadButtons[chan].up; } u32 WUPC_ButtonsDown(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return 0; return __WUPC_PadButtons[chan].down; } u32 WUPC_ButtonsHeld(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return 0; return __WUPC_PadButtons[chan].state; } s16 WUPC_lStickX(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return 0; return __WUPC_PadData[chan].xAxisL; } s16 WUPC_lStickY(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return 0; return __WUPC_PadData[chan].yAxisL; } s16 WUPC_rStickX(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return 0; return __WUPC_PadData[chan].xAxisR; } s16 WUPC_rStickY(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return 0; return __WUPC_PadData[chan].yAxisR; } u8 WUPC_extra(u8 chan) { if(chan >= CHAN_MAX || __WUPC_Connected[chan] == NULL) return 0; return __WUPC_PadData[chan].extra; }