static void hid_manager_device_attached(void* context, IOReturn result, void* sender, IOHIDDeviceRef device) { char device_name[1024]; CFStringRef device_name_ref; struct apple_pad_connection* connection = (struct apple_pad_connection*)calloc(1, sizeof(*connection)); connection->device = device; connection->slot = MAX_PLAYERS; IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone); IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); IOHIDDeviceRegisterRemovalCallback(device, hid_device_removed, connection); device_name_ref = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); CFStringGetCString(device_name_ref, device_name, sizeof(device_name), kCFStringEncodingUTF8); connection->slot = apple_joypad_connect(device_name, connection); if (apple_joypad_has_interface(connection->slot)) IOHIDDeviceRegisterInputReportCallback(device, connection->data + 1, sizeof(connection->data) - 1, hid_device_report, connection); else IOHIDDeviceRegisterInputValueCallback(device, hid_device_input_callback, connection); if (device_name[0] != '\0') { strlcpy(g_settings.input.device_names[connection->slot], device_name, sizeof(g_settings.input.device_names)); input_config_autoconfigure_joypad(connection->slot, device_name, apple_joypad.ident); RARCH_LOG("Port %d: %s.\n", connection->slot, device_name); } }
static void hid_manager_device_attached(void* context, IOReturn result, void* sender, IOHIDDeviceRef device) { struct apple_pad_connection* connection = calloc(1, sizeof(struct apple_pad_connection)); connection->device = device; connection->slot = MAX_PLAYERS; IOHIDDeviceOpen(device, kIOHIDOptionsTypeNone); IOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); IOHIDDeviceRegisterRemovalCallback(device, hid_device_removed, connection); CFStringRef device_name_ref = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); char device_name[1024]; CFStringGetCString(device_name_ref, device_name, sizeof(device_name), kCFStringEncodingUTF8); connection->slot = apple_joypad_connect(device_name, connection); if (apple_joypad_has_interface(connection->slot)) IOHIDDeviceRegisterInputReportCallback(device, connection->data + 1, sizeof(connection->data) - 1, hid_device_report, connection); else IOHIDDeviceRegisterInputValueCallback(device, hid_device_input_callback, connection); }
void btpad_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { bd_addr_t event_addr; if (packet_type == HCI_EVENT_PACKET) { switch (packet[0]) { case BTSTACK_EVENT_STATE: { RARCH_LOG("BTstack: HCI State %d\n", packet[2]); switch (packet[2]) { case HCI_STATE_WORKING: btpad_queue_reset(); btpad_queue_hci_read_bd_addr(); bt_send_cmd_ptr(l2cap_register_service_ptr, PSM_HID_CONTROL, 672); // TODO: Where did I get 672 for mtu? bt_send_cmd_ptr(l2cap_register_service_ptr, PSM_HID_INTERRUPT, 672); btpad_queue_hci_inquiry(HCI_INQUIRY_LAP, 3, 1); btpad_queue_run(1); break; case HCI_STATE_HALTING: btpad_close_all_connections(); CFRunLoopStop(CFRunLoopGetCurrent()); break; } } break; case HCI_EVENT_COMMAND_STATUS: btpad_queue_run(packet[3]); break; case HCI_EVENT_COMMAND_COMPLETE: { btpad_queue_run(packet[2]); if (COMMAND_COMPLETE_EVENT(packet, (*hci_read_bd_addr_ptr))) { bt_flip_addr_ptr(event_addr, &packet[6]); if (!packet[5]) RARCH_LOG("BTpad: Local address is %s\n", bd_addr_to_str_ptr(event_addr)); else RARCH_LOG("BTpad: Failed to get local address (Status: %02X)\n", packet[5]); } } break; case HCI_EVENT_INQUIRY_RESULT: { if (packet[2]) { bt_flip_addr_ptr(event_addr, &packet[3]); struct apple_pad_connection* connection = (struct apple_pad_connection*)btpad_find_empty_connection(); if (connection) { RARCH_LOG("BTpad: Inquiry found device\n"); memset(connection, 0, sizeof(struct apple_pad_connection)); memcpy(connection->address, event_addr, sizeof(bd_addr_t)); connection->has_address = true; connection->state = BTPAD_CONNECTING; bt_send_cmd_ptr(l2cap_create_channel_ptr, connection->address, PSM_HID_CONTROL); bt_send_cmd_ptr(l2cap_create_channel_ptr, connection->address, PSM_HID_INTERRUPT); } } } break; case HCI_EVENT_INQUIRY_COMPLETE: { // This must be turned off during gameplay as it causes a ton of lag inquiry_running = !inquiry_off; if (inquiry_running) btpad_queue_hci_inquiry(HCI_INQUIRY_LAP, 3, 1); } break; case L2CAP_EVENT_CHANNEL_OPENED: { bt_flip_addr_ptr(event_addr, &packet[3]); const uint16_t handle = READ_BT_16(packet, 9); const uint16_t psm = READ_BT_16(packet, 11); const uint16_t channel_id = READ_BT_16(packet, 13); struct apple_pad_connection* connection = (struct apple_pad_connection*)btpad_find_connection_for(handle, event_addr); if (!packet[2]) { if (!connection) { RARCH_LOG("BTpad: Got L2CAP 'Channel Opened' event for unrecognized device\n"); break; } RARCH_LOG("BTpad: L2CAP channel opened: (PSM: %02X)\n", psm); connection->handle = handle; if (psm == PSM_HID_CONTROL) connection->channels[0] = channel_id; else if (psm == PSM_HID_INTERRUPT) connection->channels[1] = channel_id; else RARCH_LOG("BTpad: Got unknown L2CAP PSM, ignoring (PSM: %02X)\n", psm); if (connection->channels[0] && connection->channels[1]) { RARCH_LOG("BTpad: Got both L2CAP channels, requesting name\n"); btpad_queue_hci_remote_name_request(connection->address, 0, 0, 0); } } else RARCH_LOG("BTpad: Got failed L2CAP 'Channel Opened' event (PSM: %02X, Status: %02X)\n", psm, packet[2]); } break; case L2CAP_EVENT_INCOMING_CONNECTION: { bt_flip_addr_ptr(event_addr, &packet[2]); const uint16_t handle = READ_BT_16(packet, 8); const uint32_t psm = READ_BT_16(packet, 10); const uint32_t channel_id = READ_BT_16(packet, 12); struct apple_pad_connection* connection = (struct apple_pad_connection*)btpad_find_connection_for(handle, event_addr); if (!connection) { connection = btpad_find_empty_connection(); if (connection) { RARCH_LOG("BTpad: Got new incoming connection\n"); memset(connection, 0, sizeof(struct apple_pad_connection)); memcpy(connection->address, event_addr, sizeof(bd_addr_t)); connection->has_address = true; connection->handle = handle; connection->state = BTPAD_CONNECTING; } else break; } RARCH_LOG("BTpad: Incoming L2CAP connection (PSM: %02X)\n", psm); bt_send_cmd_ptr(l2cap_accept_connection_ptr, channel_id); } break; case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: { bt_flip_addr_ptr(event_addr, &packet[3]); struct apple_pad_connection* connection = (struct apple_pad_connection*)btpad_find_connection_for(0, event_addr); if (!connection) { RARCH_LOG("BTpad: Got unexpected remote name, ignoring\n"); break; } RARCH_LOG("BTpad: Got %.200s\n", (char*)&packet[9]); connection->slot = apple_joypad_connect((char*)packet + 9, connection); connection->state = BTPAD_CONNECTED; } break; case HCI_EVENT_PIN_CODE_REQUEST: RARCH_LOG("BTpad: Sending WiiMote PIN\n"); bt_flip_addr_ptr(event_addr, &packet[2]); btpad_queue_hci_pin_code_request_reply(event_addr, &packet[2]); break; case HCI_EVENT_DISCONNECTION_COMPLETE: { const uint32_t handle = READ_BT_16(packet, 3); if (!packet[2]) { struct apple_pad_connection* connection = (struct apple_pad_connection*)btpad_find_connection_for(handle, 0); if (connection) { connection->handle = 0; apple_joypad_disconnect(connection->slot); btpad_close_connection(connection); } } else RARCH_LOG("BTpad: Got failed 'Disconnection Complete' event (Status: %02X)\n", packet[2]); } break; case L2CAP_EVENT_SERVICE_REGISTERED: { if (packet[2]) RARCH_LOG("BTpad: Got failed 'Service Registered' event (PSM: %02X, Status: %02X)\n", READ_BT_16(packet, 3), packet[2]); } break; } } else if (packet_type == L2CAP_DATA_PACKET) { int i; for (i = 0; i < MAX_PLAYERS; i ++) { struct apple_pad_connection* connection = (struct apple_pad_connection*)&g_connections[i]; if (connection->state == BTPAD_CONNECTED && (connection->channels[0] == channel || connection->channels[1] == channel)) apple_joypad_packet(connection->slot, packet, size); } } }