/* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis * * RetroArch 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 Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch 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 RetroArch. * If not, see . */ #include "hid_device_driver.h" #define DS3_ACTIVATION_REPORT_ID 0xf4 #define DS3_RUMBLE_REPORT_ID 0x01 typedef struct ds3_instance { void *handle; joypad_connection_t *pad; int slot; bool led_set; uint32_t buttons; uint16_t motors[2]; uint8_t data[64]; } ds3_instance_t; static uint8_t activation_packet[] = { #if defined(IOS) 0x53, 0xF4, #elif defined(HAVE_WIIUSB_HID) 0x02, #endif 0x42, 0x0c, 0x00, 0x00 }; #if defined(WIIU) #define PACKET_OFFSET 2 #elif defined(HAVE_WIIUSB_HID) #define PACKET_OFFSET 1 #else #define PACKET_OFFSET 0 #endif #define LED_OFFSET 11 #define MOTOR1_OFFSET 4 #define MOTOR2_OFFSET 6 static uint8_t control_packet[] = { 0x52, 0x01, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 int control_packet_size = sizeof(control_packet); extern pad_connection_interface_t ds3_pad_connection; static void print_error(const char *fmt, int32_t errcode) { int16_t err1, err2; err1 = errcode & 0x0000ffff; err2 = ((errcode & 0xffff0000) >> 16); RARCH_ERR(fmt, err1, err2); } static void update_pad_state(ds3_instance_t *instance); static int32_t send_activation_packet(ds3_instance_t *instance) { int32_t result; #if defined(WIIU) result = HID_SET_REPORT(instance->handle, HID_REPORT_FEATURE, DS3_ACTIVATION_REPORT_ID, activation_packet, sizeof(activation_packet)); #else HID_SEND_CONTROL(instance->handle, activation_packet, sizeof(activation_packet)); #endif if(result < 0) print_error("[ds3]: activation packet failed (%d:%d)\n", result); return result; } static uint32_t set_protocol(ds3_instance_t *instance, int protocol) { uint32_t result = 0; #if defined(WIIU) result = HID_SET_PROTOCOL(instance->handle, 1); if(result) print_error("[ds3]: set protocol failed (%d:%d)\n", result); #endif return result; } static int32_t send_control_packet(ds3_instance_t *instance) { uint8_t packet_buffer[control_packet_size]; int32_t result = 0; memcpy(packet_buffer, control_packet, control_packet_size); packet_buffer[LED_OFFSET] = 0; if(instance->pad) { packet_buffer[LED_OFFSET] = 1 << ((instance->slot % 4) + 1); } packet_buffer[MOTOR1_OFFSET] = instance->motors[1] >> 8; packet_buffer[MOTOR2_OFFSET] = instance->motors[0] >> 8; #if defined(HAVE_WIIUSB_HID) packet_buffer[1] = 0x03; #endif #if defined(WIIU) result = HID_SET_REPORT(instance->handle, HID_REPORT_OUTPUT, DS3_RUMBLE_REPORT_ID, packet_buffer+PACKET_OFFSET, control_packet_size-PACKET_OFFSET); if(result < 0) print_error("[ds3]: send control packet failed: (%d:%d)\n", result); #else HID_SEND_CONTROL(instance->handle, packet_buffer+PACKET_OFFSET, control_packet_size-PACKET_OFFSET); #endif /* WIIU */ return result; } static void *ds3_init(void *handle) { ds3_instance_t *instance; int errors = 0; RARCH_LOG("[ds3]: init\n"); instance = (ds3_instance_t *)calloc(1, sizeof(ds3_instance_t)); if(!instance) goto error; memset(instance, 0, sizeof(ds3_instance_t)); instance->handle = handle; RARCH_LOG("[ds3]: setting protocol\n"); /* if(set_protocol(instance, 1)) errors++; */ set_protocol(instance, 1); RARCH_LOG("[ds3]: sending control packet\n"); if(send_control_packet(instance) < 0) errors++; RARCH_LOG("[ds3]: sending activation packet\n"); if(send_activation_packet(instance) < 0) errors++; if(errors) goto error; instance->pad = hid_pad_register(instance, &ds3_pad_connection); if(!instance->pad) goto error; RARCH_LOG("[ds3]: init complete.\n"); return instance; error: RARCH_ERR("[ds3]: init failed.\n"); if(instance) free(instance); return NULL; } static void ds3_free(void *data) { ds3_instance_t *instance = (ds3_instance_t *)data; if(instance) free(instance); } static void ds3_handle_packet(void *data, uint8_t *packet, size_t size) { ds3_instance_t *instance = (ds3_instance_t *)data; if(!instance || !instance->pad) return; instance->pad->iface->packet_handler(data, packet, size); } static bool ds3_detect(uint16_t vendor_id, uint16_t product_id) { return vendor_id == VID_SONY && product_id == PID_SONY_DS3; } hid_device_t ds3_hid_device = { ds3_init, ds3_free, ds3_handle_packet, ds3_detect, "Sony DualShock 3" }; /** * pad interface implementation */ static void *ds3_pad_init(void *data, uint32_t slot, hid_driver_t *driver) { ds3_instance_t *pad = (ds3_instance_t *)data; pad->slot = slot; return data; } static void ds3_pad_deinit(void *data) { ds3_instance_t *pad = (ds3_instance_t *)data; } static void ds3_get_buttons(void *data, input_bits_t *state) { ds3_instance_t *pad = (ds3_instance_t *)data; if(pad) { BITS_COPY16_PTR(state, pad->buttons); if(pad->buttons & 0x10000) BIT256_SET_PTR(state, RARCH_MENU_TOGGLE); } else { BIT256_CLEAR_ALL_PTR(state); } } static void ds3_packet_handler(void *data, uint8_t *packet, uint16_t size) { ds3_instance_t *instance = (ds3_instance_t *)data; if(instance->pad && !instance->led_set) { send_control_packet(instance); instance->led_set = true; } if(size > control_packet_size) { RARCH_ERR("[ds3]: Expecting packet to be %d but was %d\n", control_packet_size, size); return; } memcpy(instance->data, packet, size); update_pad_state(instance); } static void update_pad_state(ds3_instance_t *instance) { uint32_t i, pressed_keys; static const uint32_t button_mapping[17] = { RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_DEVICE_ID_JOYPAD_L3, RETRO_DEVICE_ID_JOYPAD_R3, RETRO_DEVICE_ID_JOYPAD_START, RETRO_DEVICE_ID_JOYPAD_UP, RETRO_DEVICE_ID_JOYPAD_RIGHT, RETRO_DEVICE_ID_JOYPAD_DOWN, RETRO_DEVICE_ID_JOYPAD_LEFT, RETRO_DEVICE_ID_JOYPAD_L2, RETRO_DEVICE_ID_JOYPAD_R2, RETRO_DEVICE_ID_JOYPAD_L, RETRO_DEVICE_ID_JOYPAD_R, RETRO_DEVICE_ID_JOYPAD_X, RETRO_DEVICE_ID_JOYPAD_A, RETRO_DEVICE_ID_JOYPAD_B, RETRO_DEVICE_ID_JOYPAD_Y, 16 /* PS button */ }; instance->buttons = 0; pressed_keys = instance->data[2]|(instance->data[3] << 8)|((instance->data[4] & 0x01) << 16); for(i = 0; i < 17; i++) instance->buttons |= (pressed_keys & (1 << i)) ? (1 << button_mapping[i]) : 0; } static void ds3_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) { ds3_instance_t *pad = (ds3_instance_t *)data; } static int16_t ds3_get_axis(void *data, unsigned axis) { ds3_instance_t *pad = (ds3_instance_t *)data; int16_t val; if(!pad || axis >= 4) return 0; val = (pad->data[6+axis] << 8) - 0x8000; // val = (pad->data[7+axis] << 8) - 0x8000; return (val > 0x1000 || val < -0x1000) ? 0 : val; } static const char *ds3_get_name(void *data) { ds3_instance_t *pad = (ds3_instance_t *)data; return "Sony DualShock 3"; } static bool ds3_button(void *data, uint16_t joykey) { ds3_instance_t *pad = (ds3_instance_t *)data; if(!pad || joykey > 31) return false; return pad->buttons & (1 << joykey); } pad_connection_interface_t ds3_pad_connection = { ds3_pad_init, ds3_pad_deinit, ds3_packet_handler, ds3_set_rumble, ds3_get_buttons, ds3_get_axis, ds3_get_name, ds3_button };