mirror of
https://github.com/libretro/RetroArch
synced 2025-04-15 14:42:27 +00:00
(BTStack) Commit btstack deps to repo
This commit is contained in:
parent
5790dbbc61
commit
b2096c6bb0
76
ios/RetroArch/input/BTStack/btstack/bt_control.h
Normal file
76
ios/RetroArch/input/BTStack/btstack/bt_control.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* bt_control.h
|
||||
*
|
||||
* BT Control API -- allows BT Daemon to initialize and control differnt hardware
|
||||
*
|
||||
* Created by Matthias Ringwald on 5/19/09.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
POWER_WILL_SLEEP = 1,
|
||||
POWER_WILL_WAKE_UP
|
||||
} POWER_NOTIFICATION_t;
|
||||
|
||||
typedef struct {
|
||||
int (*on) (void *config); // <-- turn BT module on and configure
|
||||
int (*off) (void *config); // <-- turn BT module off
|
||||
int (*sleep)(void *config); // <-- put BT module to sleep - only to be called after ON
|
||||
int (*wake) (void *config); // <-- wake BT module from sleep - only to be called after SLEEP
|
||||
int (*valid)(void *config); // <-- test if hardware can be supported
|
||||
const char * (*name) (void *config); // <-- return hardware name
|
||||
|
||||
/** support for UART baud rate changes - cmd has to be stored in hci_cmd_buffer
|
||||
* @return have command
|
||||
*/
|
||||
int (*baudrate_cmd)(void * config, uint32_t baudrate, uint8_t *hci_cmd_buffer);
|
||||
|
||||
/** support custom init sequences after RESET command - cmd has to be stored in hci_cmd_buffer
|
||||
* @return have command
|
||||
*/
|
||||
int (*next_cmd)(void *config, uint8_t * hci_cmd_buffer);
|
||||
|
||||
void (*register_for_power_notifications)(void (*cb)(POWER_NOTIFICATION_t event));
|
||||
|
||||
void (*hw_error)(void);
|
||||
} bt_control_t;
|
58
ios/RetroArch/input/BTStack/btstack/bt_control_iphone.h
Normal file
58
ios/RetroArch/input/BTStack/btstack/bt_control_iphone.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* bt_control_iphone.h
|
||||
*
|
||||
* BT Control API implementation for the iPhone and the iPod Touch
|
||||
*
|
||||
* Created by Matthias Ringwald on 5/19/09.
|
||||
*/
|
||||
|
||||
#include "bt_control.h"
|
||||
|
||||
extern bt_control_t bt_control_iphone;
|
||||
|
||||
int bt_control_iphone_power_management_enabled(void);
|
||||
int bt_control_iphone_power_management_supported(void);
|
||||
|
||||
int iphone_system_bt_enabled(void);
|
||||
void iphone_system_bt_set_enabled(int enabled);
|
||||
|
||||
int iphone_system_has_csr(void);
|
||||
|
||||
|
||||
|
731
ios/RetroArch/input/BTStack/btstack/bt_control_iphone.m
Normal file
731
ios/RetroArch/input/BTStack/btstack/bt_control_iphone.m
Normal file
@ -0,0 +1,731 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* bt_control_iphone.c
|
||||
*
|
||||
* control Bluetooth module using BlueTool
|
||||
*
|
||||
* Created by Matthias Ringwald on 5/19/09.
|
||||
* PowerManagement implementation by Jens David, DG1KJD on 20110801.
|
||||
*
|
||||
* Bluetooth Toggle by BigBoss
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "bt_control_iphone.h"
|
||||
#include "hci_transport.h"
|
||||
#include "hci.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> // open
|
||||
#include <stdlib.h> // system, random, srandom
|
||||
#include <stdio.h> // sscanf, printf
|
||||
#include <string.h> // strcpy, strcat, strncmp
|
||||
#include <sys/utsname.h> // uname
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../SpringBoardAccess/SpringBoardAccess.h"
|
||||
|
||||
// minimal IOKit
|
||||
#include <Availability.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
// compile issue fix
|
||||
#undef NSEC_PER_USEC
|
||||
#undef USEC_PER_SEC
|
||||
#undef NSEC_PER_SEC
|
||||
// end of fix
|
||||
|
||||
#include <mach/mach.h>
|
||||
#define IOKIT
|
||||
#include <device/device_types.h>
|
||||
|
||||
// costants
|
||||
#define sys_iokit err_system(0x38)
|
||||
#define sub_iokit_common err_sub(0)
|
||||
|
||||
#define iokit_common_msg(message) (UInt32)(sys_iokit|sub_iokit_common|message)
|
||||
|
||||
#define kIOMessageCanDevicePowerOff iokit_common_msg(0x200)
|
||||
#define kIOMessageDeviceWillPowerOff iokit_common_msg(0x210)
|
||||
#define kIOMessageDeviceWillNotPowerOff iokit_common_msg(0x220)
|
||||
#define kIOMessageDeviceHasPoweredOn iokit_common_msg(0x230)
|
||||
#define kIOMessageCanSystemPowerOff iokit_common_msg(0x240)
|
||||
#define kIOMessageSystemWillPowerOff iokit_common_msg(0x250)
|
||||
#define kIOMessageSystemWillNotPowerOff iokit_common_msg(0x260)
|
||||
#define kIOMessageCanSystemSleep iokit_common_msg(0x270)
|
||||
#define kIOMessageSystemWillSleep iokit_common_msg(0x280)
|
||||
#define kIOMessageSystemWillNotSleep iokit_common_msg(0x290)
|
||||
#define kIOMessageSystemHasPoweredOn iokit_common_msg(0x300)
|
||||
#define kIOMessageSystemWillRestart iokit_common_msg(0x310)
|
||||
#define kIOMessageSystemWillPowerOn iokit_common_msg(0x320)
|
||||
|
||||
// types
|
||||
typedef io_object_t io_connect_t;
|
||||
typedef io_object_t io_service_t;
|
||||
typedef kern_return_t IOReturn;
|
||||
typedef struct IONotificationPort * IONotificationPortRef;
|
||||
|
||||
// prototypes
|
||||
kern_return_t IOMasterPort( mach_port_t bootstrapPort, mach_port_t * masterPort );
|
||||
CFMutableDictionaryRef IOServiceNameMatching(const char * name );
|
||||
CFTypeRef IORegistryEntrySearchCFProperty(mach_port_t entry, const io_name_t plane,
|
||||
CFStringRef key, CFAllocatorRef allocator, UInt32 options );
|
||||
mach_port_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching );
|
||||
kern_return_t IOObjectRelease(mach_port_t object);
|
||||
|
||||
typedef void (*IOServiceInterestCallback)(void * refcon, io_service_t service, uint32_t messageType, void * messageArgument);
|
||||
io_connect_t IORegisterForSystemPower (void * refcon, IONotificationPortRef * thePortRef,
|
||||
IOServiceInterestCallback callback, io_object_t * notifier );
|
||||
IOReturn IODeregisterForSystemPower (io_object_t *notifier);
|
||||
CFRunLoopSourceRef IONotificationPortGetRunLoopSource(IONotificationPortRef notify );
|
||||
IOReturn IOAllowPowerChange ( io_connect_t kernelPort, long notificationID );
|
||||
IOReturn IOCancelPowerChange ( io_connect_t kernelPort, long notificationID );
|
||||
|
||||
// local globals
|
||||
static io_connect_t root_port = 0; // a reference to the Root Power Domain IOService
|
||||
static int power_notification_pipe_fds[2];
|
||||
static data_source_t power_notification_ds;
|
||||
|
||||
static void (*power_notification_callback)(POWER_NOTIFICATION_t event) = NULL;
|
||||
|
||||
#define BUFF_LEN 80
|
||||
static char buffer[BUFF_LEN+1];
|
||||
|
||||
// local mac address and default transport speed from IORegistry
|
||||
static bd_addr_t local_mac_address;
|
||||
static uint32_t transport_speed;
|
||||
|
||||
static int power_management_active = 0;
|
||||
static char *os3xBlueTool = "BlueTool";
|
||||
static char *os4xBlueTool = "/usr/local/bin/BlueToolH4";
|
||||
|
||||
/**
|
||||
* check OS version for >= 4.0
|
||||
*/
|
||||
static int iphone_os_at_least_40(){
|
||||
return kCFCoreFoundationVersionNumber >= 550.32;
|
||||
}
|
||||
|
||||
/**
|
||||
* get machine name
|
||||
*/
|
||||
static struct utsname unmae_info;
|
||||
static char *get_machine_name(void){
|
||||
uname(&unmae_info);
|
||||
return unmae_info.machine;
|
||||
}
|
||||
|
||||
/**
|
||||
* on iPhone/iPod touch
|
||||
*/
|
||||
static int iphone_valid(void *config){
|
||||
char * machine = get_machine_name();
|
||||
if (!strncmp("iPod1", machine, strlen("iPod1"))) return 0; // 1st gen touch no BT
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char * iphone_name(void *config){
|
||||
return get_machine_name();
|
||||
}
|
||||
|
||||
// Get BD_ADDR from IORegistry
|
||||
static void ioregistry_get_info() {
|
||||
mach_port_t mp;
|
||||
IOMasterPort(MACH_PORT_NULL,&mp);
|
||||
CFMutableDictionaryRef bt_matching = IOServiceNameMatching("bluetooth");
|
||||
mach_port_t bt_service = IOServiceGetMatchingService(mp, bt_matching);
|
||||
|
||||
// local-mac-address
|
||||
CFTypeRef local_mac_address_ref = IORegistryEntrySearchCFProperty(bt_service,"IODeviceTree",CFSTR("local-mac-address"), kCFAllocatorDefault, 1);
|
||||
CFDataGetBytes(local_mac_address_ref,CFRangeMake(0,CFDataGetLength(local_mac_address_ref)),local_mac_address); // buffer needs to be unsigned char
|
||||
|
||||
// transport-speed
|
||||
CFTypeRef transport_speed_ref = IORegistryEntrySearchCFProperty(bt_service,"IODeviceTree",CFSTR("transport-speed"), kCFAllocatorDefault, 1);
|
||||
int transport_speed_len = CFDataGetLength(transport_speed_ref);
|
||||
CFDataGetBytes(transport_speed_ref,CFRangeMake(0,transport_speed_len), (uint8_t*) &transport_speed); // buffer needs to be unsigned char
|
||||
|
||||
IOObjectRelease(bt_service);
|
||||
|
||||
// dump info
|
||||
log_info("local-mac-address: %s\n", bd_addr_to_str(local_mac_address));
|
||||
log_info("transport-speed: %u\n", transport_speed);
|
||||
}
|
||||
|
||||
static int iphone_has_csr(){
|
||||
// construct script path from device name
|
||||
char *machine = get_machine_name();
|
||||
if (strncmp(machine, "iPhone1,", strlen("iPhone1,")) == 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iphone_write_string(int fd, char *string){
|
||||
int len = strlen(string);
|
||||
write(fd, string, len);
|
||||
}
|
||||
|
||||
static void iphone_csr_set_pskey(int fd, int key, int value){
|
||||
int len = sprintf(buffer, "csr -p 0x%04x=0x%04x\n", key, value);
|
||||
write(fd, buffer, len);
|
||||
}
|
||||
|
||||
static void iphone_csr_set_bd_addr(int fd){
|
||||
int len = sprintf(buffer,"\ncsr -p 0x0001=0x00%.2x,0x%.2x%.2x,0x00%.2x,0x%.2x%.2x\n",
|
||||
local_mac_address[3], local_mac_address[4], local_mac_address[5], local_mac_address[2], local_mac_address[0], local_mac_address[1]);
|
||||
write(fd, buffer, len);
|
||||
}
|
||||
|
||||
static void iphone_csr_set_baud(int fd, int baud){
|
||||
// calculate baud rate (assume rate is multiply of 100)
|
||||
uint32_t baud_key = (4096 * (baud/100) + 4999) / 10000;
|
||||
iphone_csr_set_pskey(fd, 0x01be, baud_key);
|
||||
}
|
||||
|
||||
static void iphone_bcm_set_bd_addr(int fd){
|
||||
int len = sprintf(buffer, "bcm -a %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_address[0], local_mac_address[1],
|
||||
local_mac_address[2], local_mac_address[3], local_mac_address[4], local_mac_address[5]);
|
||||
write(fd, buffer, len);
|
||||
}
|
||||
|
||||
static void iphone_bcm_set_baud(int fd, int baud){
|
||||
int len = sprintf(buffer, "bcm -b %u\n", baud);
|
||||
write(fd, buffer, len);
|
||||
}
|
||||
|
||||
// OS 3.x
|
||||
static int iphone_write_initscript (int output, int baudrate){
|
||||
|
||||
// construct script path from device name
|
||||
strcpy(buffer, "/etc/bluetool/");
|
||||
char *machine = get_machine_name();
|
||||
strcat(buffer, machine);
|
||||
if (iphone_has_csr()){
|
||||
strcat(buffer, ".init.script");
|
||||
} else {
|
||||
strcat(buffer, ".boot.script");
|
||||
}
|
||||
|
||||
// open script
|
||||
int input = open(buffer, O_RDONLY);
|
||||
|
||||
int pos = 0;
|
||||
int mirror = 0;
|
||||
int store = 1;
|
||||
while (1){
|
||||
int chars = read(input, &buffer[pos], 1);
|
||||
|
||||
// end-of-line
|
||||
if (chars == 0 || buffer[pos]=='\n' || buffer[pos]== '\r'){
|
||||
if (store) {
|
||||
// stored characters
|
||||
write(output, buffer, pos+chars);
|
||||
}
|
||||
if (mirror) {
|
||||
write(output, "\n", 1);
|
||||
}
|
||||
pos = 0;
|
||||
mirror = 0;
|
||||
store = 1;
|
||||
if (chars) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// mirror
|
||||
if (mirror){
|
||||
write(output, &buffer[pos], 1);
|
||||
}
|
||||
|
||||
// store
|
||||
if (store) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
// iPhone - set BD_ADDR after "csr -i"
|
||||
if (store == 1 && pos == 6 && strncmp(buffer, "csr -i", 6) == 0) {
|
||||
store = 0;
|
||||
write(output, buffer, pos); // "csr -i"
|
||||
iphone_csr_set_bd_addr(output);
|
||||
}
|
||||
|
||||
// iPod2,1
|
||||
// check for "bcm -X" cmds
|
||||
if (store == 1 && pos == 6){
|
||||
if (strncmp(buffer, "bcm -", 5) == 0) {
|
||||
store = 0;
|
||||
switch (buffer[5]){
|
||||
case 'a': // BT Address
|
||||
iphone_bcm_set_bd_addr(output);
|
||||
mirror = 0;
|
||||
break;
|
||||
case 'b': // baud rate command OS 2.x
|
||||
case 'B': // baud rate command OS 3.x
|
||||
iphone_bcm_set_baud(output, baudrate);
|
||||
mirror = 0;
|
||||
break;
|
||||
case 's': // sleep mode - replace with "wake" command?
|
||||
iphone_write_string(output, "wake on\n");
|
||||
mirror = 0;
|
||||
break;
|
||||
default: // other "bcm -X" command
|
||||
write(output, buffer, pos);
|
||||
mirror = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iPhone1,1 & iPhone 2,1: OS 3.x
|
||||
// check for "csr -B" and "csr -T"
|
||||
if (store == 1 && pos == 6){
|
||||
if (strncmp(buffer, "csr -", 5) == 0) {
|
||||
switch(buffer[5]){
|
||||
case 'T': // Transport Mode
|
||||
store = 0;
|
||||
break;
|
||||
case 'B': // Baud rate
|
||||
iphone_csr_set_baud(output, baudrate);
|
||||
store = 0;
|
||||
break;
|
||||
default: // wait for full command
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iPhone1,1 & iPhone 2,1: OS 2.x
|
||||
// check for "csr -p 0x1234=0x5678" (20)
|
||||
if (store == 1 && pos == 20) {
|
||||
int pskey, value;
|
||||
store = 0;
|
||||
if (sscanf(buffer, "csr -p 0x%x=0x%x", &pskey, &value) == 2){
|
||||
switch (pskey) {
|
||||
case 0x01f9: // UART MODE
|
||||
case 0x01c1: // Configure H5 mode
|
||||
mirror = 0;
|
||||
break;
|
||||
case 0x01be: // PSKEY_UART_BAUD_RATE
|
||||
iphone_csr_set_baud(output, baudrate);
|
||||
mirror = 0;
|
||||
break;
|
||||
default:
|
||||
// anything else: dump buffer and start forwarding
|
||||
write(output, buffer, pos);
|
||||
mirror = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
write(output, buffer, pos);
|
||||
mirror = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// close input
|
||||
close(input);
|
||||
|
||||
#ifdef USE_POWERMANAGEMENT
|
||||
if (iphone_has_csr()) {
|
||||
/* CSR BT module: deactivated since it didn't work on iPhone 3G, 3.1.3
|
||||
the first few packets didn't get received when iPhone is sleeping.
|
||||
That's quite possible since Apple uses H5, but BTstack uses H4
|
||||
H5's packet retransmission should take care of that */
|
||||
// iphone_write_string(output, "msleep 50\n");
|
||||
// iphone_write_string(output, "csr -p 0x01ca=0x0031\n");
|
||||
// iphone_write_string(output, "msleep 50\n");
|
||||
// iphone_write_string(output, "csr -p 0x01c7=0x0001,0x01f4,0x0005,0x0020\n");
|
||||
} else {
|
||||
/* BCM BT module, deactivated since untested for now */
|
||||
// iphone_write_string(output, "bcm -s 0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x01\n");
|
||||
// iphone_write_string(output, "msleep 50\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// OS 4.x and higher
|
||||
static void iphone_write_configscript(int fd, int baudrate){
|
||||
iphone_write_string(fd, "device -D -S\n");
|
||||
if (iphone_has_csr()) {
|
||||
iphone_csr_set_bd_addr(fd);
|
||||
if (baudrate) {
|
||||
iphone_csr_set_baud(fd, baudrate);
|
||||
}
|
||||
iphone_write_string(fd, "csr -r\n");
|
||||
#ifdef USE_POWERMANAGEMENT
|
||||
/* CSR BT module: deactivated since untested, but it most likely won't work
|
||||
see comments in 3.x init sequence above */
|
||||
// iphone_write_string(fd, "msleep 50\n");
|
||||
// iphone_write_string(fd, "csr -p 0x01ca=0x0031\n");
|
||||
// iphone_write_string(fd, "msleep 50\n");
|
||||
// iphone_write_string(fd, "csr -p 0x01c7=0x0001,0x01f4,0x0005,0x0020\n");
|
||||
#endif
|
||||
} else {
|
||||
iphone_bcm_set_bd_addr(fd);
|
||||
if (baudrate) {
|
||||
iphone_bcm_set_baud(fd, baudrate);
|
||||
iphone_write_string(fd, "msleep 200\n");
|
||||
}
|
||||
#ifdef USE_POWERMANAGEMENT
|
||||
// power management only active on 4.x with BCM (iPhone 3GS and higher, all iPads, iPod touch 3G and higher)
|
||||
iphone_write_string(fd, "bcm -s 0x01,0x00,0x00,0x01,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x01\n");
|
||||
iphone_write_string(fd, "msleep 50\n");
|
||||
#endif
|
||||
}
|
||||
iphone_write_string(fd, "quit\n");
|
||||
}
|
||||
|
||||
static int iphone_on (void *transport_config){
|
||||
// hci_uart_config->baudrate_init == 0, if using native speed
|
||||
|
||||
log_info("iphone_on: entered\n");
|
||||
|
||||
int err = 0;
|
||||
|
||||
hci_uart_config_t * hci_uart_config = (hci_uart_config_t*) transport_config;
|
||||
|
||||
// get local-mac-addr and transport-speed from IORegistry
|
||||
ioregistry_get_info();
|
||||
|
||||
#ifdef USE_RANDOM_BD_ADDR
|
||||
// While developing an app that emulates a Bluetooth HID keyboard, we've learnt
|
||||
// that in (some versions/driver combinations of) Windows XP, information about
|
||||
// a device with incorrect SDP descriptions are stored forever.
|
||||
//
|
||||
// To continue development, this option was added that picks a random BD_ADDR
|
||||
// on start to trick windows in giving us a fresh start on each try.
|
||||
//
|
||||
// Use with caution!
|
||||
|
||||
srandom(time(NULL));
|
||||
bt_store_32(local_mac_address, 0, random());
|
||||
bt_store_16(local_mac_address, 4, random());
|
||||
#endif
|
||||
|
||||
if (iphone_system_bt_enabled()){
|
||||
perror("iphone_on: System Bluetooth enabled!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// unload BTServer
|
||||
log_info("iphone_on: unload BTServer\n");
|
||||
err = system ("launchctl unload /System/Library/LaunchDaemons/com.apple.BTServer.plist");
|
||||
|
||||
// check for os version >= 4.0
|
||||
int os4x = iphone_os_at_least_40();
|
||||
|
||||
// OS 4.0
|
||||
char * bluetool = os3xBlueTool;
|
||||
if (os4x) {
|
||||
bluetool = os4xBlueTool;
|
||||
}
|
||||
|
||||
// quick test if Bluetooth UART can be opened - and try to start cleanly
|
||||
int fd = open(hci_uart_config->device_name, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (fd > 0) {
|
||||
// everything's fine
|
||||
close(fd);
|
||||
} else {
|
||||
// no way!
|
||||
log_error( "bt_control.c:iphone_on(): Failed to open '%s', trying killall %s\n", hci_uart_config->device_name, bluetool);
|
||||
system("killall -9 BlueToolH4");
|
||||
system("killall -9 BlueTool");
|
||||
sleep(3);
|
||||
|
||||
// try again
|
||||
fd = open(hci_uart_config->device_name, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (fd > 0){
|
||||
close(fd);
|
||||
} else {
|
||||
log_error( "bt_control.c:iphone_on(): Failed to open '%s' again, trying killall BTServer and killall %s\n", hci_uart_config->device_name, bluetool);
|
||||
system("killall -9 BTServer");
|
||||
system("killall -9 BlueToolH4");
|
||||
system("killall -9 BlueTool");
|
||||
sleep(3);
|
||||
}
|
||||
}
|
||||
|
||||
// basic config on 4.0+
|
||||
if (os4x) {
|
||||
sprintf(buffer, "%s -F boot", bluetool);
|
||||
system(buffer);
|
||||
sprintf(buffer, "%s -F init", bluetool);
|
||||
system(buffer);
|
||||
}
|
||||
|
||||
// advanced config - set bd addr, use custom baud rate, enable deep sleep
|
||||
FILE * outputFile = popen(bluetool, "r+");
|
||||
setvbuf(outputFile, NULL, _IONBF, 0);
|
||||
int output = fileno(outputFile);
|
||||
|
||||
if (os4x) {
|
||||
// 4.x - send custom config
|
||||
iphone_write_configscript(output, hci_uart_config->baudrate_init);
|
||||
} else {
|
||||
// 3.x - modify original script on the fly
|
||||
iphone_write_initscript(output, hci_uart_config->baudrate_init);
|
||||
}
|
||||
|
||||
// log output
|
||||
fflush(outputFile);
|
||||
int singlechar = 0;
|
||||
char linebuf[80];
|
||||
int pos = 0;
|
||||
while (singlechar != EOF) {
|
||||
singlechar = fgetc(outputFile);
|
||||
linebuf[pos] = singlechar;
|
||||
if (singlechar != EOF && singlechar != '\n' && singlechar != '\r' && pos < 78) {
|
||||
pos++;
|
||||
continue;
|
||||
};
|
||||
linebuf[pos] = 0;
|
||||
if (pos > 0) {
|
||||
log_info("%s", linebuf);
|
||||
}
|
||||
pos = 0;
|
||||
};
|
||||
err = pclose(outputFile);
|
||||
|
||||
#ifdef USE_POWERMANAGEMENT
|
||||
power_management_active = bt_control_iphone_power_management_supported();
|
||||
|
||||
// if baud == 0, we're using system default: set in transport config
|
||||
if (hci_uart_config->baudrate_init == 0) {
|
||||
hci_uart_config->baudrate_init = transport_speed;
|
||||
}
|
||||
#endif
|
||||
|
||||
// if we sleep for about 3 seconds, we miss a strage packet... but we don't care
|
||||
// sleep(3);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int iphone_off (void *config){
|
||||
|
||||
/*
|
||||
char *machine = get_machine_name();
|
||||
if (strncmp(machine, "iPad", strlen("iPad")) == 0) {
|
||||
// put iPad Bluetooth into deep sleep
|
||||
system ("echo \"wake off\n quit\" | BlueTool");
|
||||
} else {
|
||||
// power off for iPhone and iPod
|
||||
system ("echo \"power off\n quit\" | BlueTool");
|
||||
}
|
||||
*/
|
||||
// power off (all models)
|
||||
log_info("iphone_off: turn off using BlueTool\n");
|
||||
system ("echo \"power off\nquit\" | BlueTool");
|
||||
|
||||
// kill Apple BTServer as it gets confused and fails to start anyway
|
||||
// system("killall BTServer");
|
||||
|
||||
// reload BTServer
|
||||
log_info("iphone_off: reload BTServer\n");
|
||||
system ("launchctl load /System/Library/LaunchDaemons/com.apple.BTServer.plist");
|
||||
log_info("iphone_off: done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iphone_sleep(void *config){
|
||||
|
||||
// will sleep by itself
|
||||
if (power_management_active) return 0;
|
||||
|
||||
// put Bluetooth into deep sleep
|
||||
system ("echo \"wake off\nquit\" | BlueTool");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iphone_wake(void *config){
|
||||
|
||||
// will wake by itself
|
||||
if (power_management_active) return 0;
|
||||
|
||||
// wake up Bluetooth module
|
||||
system ("echo \"wake on\nquit\" | BlueTool");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void MySleepCallBack( void * refCon, io_service_t service, natural_t messageType, void * messageArgument ) {
|
||||
|
||||
char data;
|
||||
log_info( "messageType %08lx, arg %08lx\n", (long unsigned int)messageType, (long unsigned int)messageArgument);
|
||||
switch ( messageType ) {
|
||||
case kIOMessageCanSystemSleep:
|
||||
/* Idle sleep is about to kick in. This message will not be sent for forced sleep.
|
||||
Applications have a chance to prevent sleep by calling IOCancelPowerChange.
|
||||
Most applications should not prevent idle sleep.
|
||||
|
||||
Power Management waits up to 30 seconds for you to either allow or deny idle sleep.
|
||||
If you don't acknowledge this power change by calling either IOAllowPowerChange
|
||||
or IOCancelPowerChange, the system will wait 30 seconds then go to sleep.
|
||||
*/
|
||||
|
||||
// Uncomment to cancel idle sleep
|
||||
// IOCancelPowerChange( root_port, (long)messageArgument );
|
||||
// we will allow idle sleep
|
||||
IOAllowPowerChange( root_port, (long)messageArgument );
|
||||
break;
|
||||
|
||||
case kIOMessageSystemWillSleep:
|
||||
/* The system WILL go to sleep. If you do not call IOAllowPowerChange or
|
||||
IOCancelPowerChange to acknowledge this message, sleep will be
|
||||
delayed by 30 seconds.
|
||||
|
||||
NOTE: If you call IOCancelPowerChange to deny sleep it returns kIOReturnSuccess,
|
||||
however the system WILL still go to sleep.
|
||||
*/
|
||||
|
||||
// notify main thread
|
||||
data = POWER_WILL_SLEEP;
|
||||
write(power_notification_pipe_fds[1], &data, 1);
|
||||
|
||||
// don't allow power change, even when power management is active
|
||||
// BTstack needs to disable discovery mode during sleep to save power
|
||||
|
||||
// if (!power_management_active) break;
|
||||
// IOAllowPowerChange( root_port, (long)messageArgument );
|
||||
|
||||
break;
|
||||
|
||||
case kIOMessageSystemWillPowerOn:
|
||||
|
||||
data = POWER_WILL_WAKE_UP;
|
||||
write(power_notification_pipe_fds[1], &data, 1);
|
||||
break;
|
||||
|
||||
case kIOMessageSystemHasPoweredOn:
|
||||
//System has finished waking up...
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int power_notification_process(struct data_source *ds) {
|
||||
|
||||
if (!power_notification_callback) return -1;
|
||||
|
||||
// get token
|
||||
char token;
|
||||
int bytes_read = read(power_notification_pipe_fds[0], &token, 1);
|
||||
if (bytes_read != 1) return -1;
|
||||
|
||||
log_info("power_notification_process: %u\n", token);
|
||||
|
||||
power_notification_callback( (POWER_NOTIFICATION_t) token );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* assumption: called only once, cb != null
|
||||
*/
|
||||
void iphone_register_for_power_notifications(void (*cb)(POWER_NOTIFICATION_t event)){
|
||||
|
||||
// handle IOKIT power notifications - http://developer.apple.com/library/mac/#qa/qa2004/qa1340.html
|
||||
IONotificationPortRef notifyPortRef = NULL; // notification port allocated by IORegisterForSystemPower
|
||||
io_object_t notifierObject = 0; // notifier object, used to deregister later
|
||||
root_port = IORegisterForSystemPower(NULL, ¬ifyPortRef, MySleepCallBack, ¬ifierObject);
|
||||
if (!root_port) {
|
||||
log_info("IORegisterForSystemPower failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// add the notification port to this thread's runloop
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes);
|
||||
|
||||
//
|
||||
power_notification_callback = cb;
|
||||
|
||||
// create pipe to deliver power notification to main run loop
|
||||
pipe(power_notification_pipe_fds);
|
||||
|
||||
// set up data source handler
|
||||
power_notification_ds.fd = power_notification_pipe_fds[0];
|
||||
power_notification_ds.process = power_notification_process;
|
||||
run_loop_add_data_source(&power_notification_ds);
|
||||
}
|
||||
|
||||
int bt_control_iphone_power_management_supported(void){
|
||||
// only supported on Broadcom chipsets with iOS 4.0+
|
||||
if ( iphone_has_csr()) return 0;
|
||||
if (!iphone_os_at_least_40()) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// direct access
|
||||
int bt_control_iphone_power_management_enabled(void){
|
||||
return power_management_active;
|
||||
}
|
||||
|
||||
// single instance
|
||||
bt_control_t bt_control_iphone = {
|
||||
.on = iphone_on,
|
||||
.off = iphone_off,
|
||||
.sleep = iphone_sleep,
|
||||
.wake = iphone_wake,
|
||||
.valid = iphone_valid,
|
||||
.name = iphone_name,
|
||||
.register_for_power_notifications = iphone_register_for_power_notifications
|
||||
};
|
||||
|
||||
int iphone_system_bt_enabled(void){
|
||||
return SBA_getBluetoothEnabled();
|
||||
}
|
||||
|
||||
void iphone_system_bt_set_enabled(int enabled)
|
||||
{
|
||||
SBA_setBluetoothEnabled(enabled);
|
||||
sleep(2); // give change a chance
|
||||
}
|
||||
|
||||
int iphone_system_has_csr(void){
|
||||
return iphone_has_csr();
|
||||
}
|
135
ios/RetroArch/input/BTStack/btstack/btstack.c
Normal file
135
ios/RetroArch/input/BTStack/btstack/btstack.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* btstack.c
|
||||
*
|
||||
* Created by Matthias Ringwald on 7/1/09.
|
||||
*
|
||||
* BTstack client API
|
||||
*/
|
||||
|
||||
#include <btstack/btstack.h>
|
||||
|
||||
#include "l2cap.h"
|
||||
#include "socket_connection.h"
|
||||
#include <btstack/run_loop.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// static uint8_t hci_cmd_buffer[3+255]; // HCI Command Header + max payload
|
||||
static uint8_t hci_cmd_buffer[HCI_ACL_BUFFER_SIZE]; // BTstack command packets are not size restricted
|
||||
|
||||
static connection_t *btstack_connection = NULL;
|
||||
|
||||
/** prototypes & dummy functions */
|
||||
static void dummy_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){};
|
||||
static int btstack_packet_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t size);
|
||||
|
||||
/** local globals :) */
|
||||
static void (*client_packet_handler)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) = dummy_handler;
|
||||
|
||||
static const char * daemon_tcp_address = NULL;
|
||||
static uint16_t daemon_tcp_port = BTSTACK_PORT;
|
||||
|
||||
// optional: if called before bt_open, TCP socket is used instead of local unix socket
|
||||
// note: address is not copied and must be valid during bt_open
|
||||
void bt_use_tcp(const char * address, uint16_t port){
|
||||
daemon_tcp_address = address;
|
||||
daemon_tcp_port = port;
|
||||
}
|
||||
|
||||
// init BTstack library
|
||||
int bt_open(){
|
||||
|
||||
socket_connection_register_packet_callback(btstack_packet_handler);
|
||||
|
||||
// BTdaemon
|
||||
if (daemon_tcp_address) {
|
||||
btstack_connection = socket_connection_open_tcp(daemon_tcp_address,daemon_tcp_port);
|
||||
} else {
|
||||
btstack_connection = socket_connection_open_unix();
|
||||
}
|
||||
if (!btstack_connection) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// stop using BTstack library
|
||||
int bt_close(){
|
||||
return socket_connection_close_tcp(btstack_connection);
|
||||
}
|
||||
|
||||
// send hci cmd packet
|
||||
int bt_send_cmd(const hci_cmd_t *cmd, ...){
|
||||
va_list argptr;
|
||||
va_start(argptr, cmd);
|
||||
uint16_t len = hci_create_cmd_internal(hci_cmd_buffer, cmd, argptr);
|
||||
va_end(argptr);
|
||||
socket_connection_send_packet(btstack_connection, HCI_COMMAND_DATA_PACKET, 0, hci_cmd_buffer, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int btstack_packet_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t size){
|
||||
// printf("BTstack client handler: packet type %u, data[0] %x\n", packet_type, data[0]);
|
||||
(*client_packet_handler)(packet_type, channel, data, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// register packet handler
|
||||
btstack_packet_handler_t bt_register_packet_handler(btstack_packet_handler_t handler){
|
||||
btstack_packet_handler_t old_handler = client_packet_handler;
|
||||
client_packet_handler = handler;
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
void bt_send_l2cap(uint16_t source_cid, uint8_t *data, uint16_t len){
|
||||
// send
|
||||
socket_connection_send_packet(btstack_connection, L2CAP_DATA_PACKET, source_cid, data, len);
|
||||
}
|
||||
|
||||
void bt_send_rfcomm(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){
|
||||
// send
|
||||
socket_connection_send_packet(btstack_connection, RFCOMM_DATA_PACKET, rfcomm_cid, data, len);
|
||||
}
|
||||
|
||||
void bt_send_acl(uint8_t * data, uint16_t len){
|
||||
// send
|
||||
socket_connection_send_packet(btstack_connection, HCI_ACL_DATA_PACKET, 0, data, len);
|
||||
}
|
@ -40,9 +40,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hci_cmds.h"
|
||||
#include "run_loop.h"
|
||||
#include "utils.h"
|
||||
#include <btstack/hci_cmds.h>
|
||||
#include <btstack/run_loop.h>
|
||||
#include <btstack/utils.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
336
ios/RetroArch/input/BTStack/btstack/btstack_memory.c
Normal file
336
ios/RetroArch/input/BTStack/btstack/btstack_memory.c
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* btstsack_memory.h
|
||||
*
|
||||
* @brief BTstack memory management via configurable memory pools
|
||||
*
|
||||
* @note code semi-atuomatically generated by btstack_memory_generator.py
|
||||
*
|
||||
*/
|
||||
|
||||
#include "btstack_memory.h"
|
||||
#include <btstack/memory_pool.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "hci.h"
|
||||
#include "l2cap.h"
|
||||
#include "rfcomm.h"
|
||||
|
||||
// MARK: hci_connection_t
|
||||
#ifdef MAX_NO_HCI_CONNECTIONS
|
||||
#if MAX_NO_HCI_CONNECTIONS > 0
|
||||
static hci_connection_t hci_connection_storage[MAX_NO_HCI_CONNECTIONS];
|
||||
static memory_pool_t hci_connection_pool;
|
||||
void * btstack_memory_hci_connection_get(void){
|
||||
return memory_pool_get(&hci_connection_pool);
|
||||
}
|
||||
void btstack_memory_hci_connection_free(void *hci_connection){
|
||||
memory_pool_free(&hci_connection_pool, hci_connection);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_hci_connection_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_hci_connection_free(void *hci_connection){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_hci_connection_get(void){
|
||||
return malloc(sizeof(hci_connection_t));
|
||||
}
|
||||
void btstack_memory_hci_connection_free(void *hci_connection){
|
||||
free(hci_connection);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: l2cap_service_t
|
||||
#ifdef MAX_NO_L2CAP_SERVICES
|
||||
#if MAX_NO_L2CAP_SERVICES > 0
|
||||
static l2cap_service_t l2cap_service_storage[MAX_NO_L2CAP_SERVICES];
|
||||
static memory_pool_t l2cap_service_pool;
|
||||
void * btstack_memory_l2cap_service_get(void){
|
||||
return memory_pool_get(&l2cap_service_pool);
|
||||
}
|
||||
void btstack_memory_l2cap_service_free(void *l2cap_service){
|
||||
memory_pool_free(&l2cap_service_pool, l2cap_service);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_l2cap_service_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_l2cap_service_free(void *l2cap_service){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_l2cap_service_get(void){
|
||||
return malloc(sizeof(l2cap_service_t));
|
||||
}
|
||||
void btstack_memory_l2cap_service_free(void *l2cap_service){
|
||||
free(l2cap_service);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: l2cap_channel_t
|
||||
#ifdef MAX_NO_L2CAP_CHANNELS
|
||||
#if MAX_NO_L2CAP_CHANNELS > 0
|
||||
static l2cap_channel_t l2cap_channel_storage[MAX_NO_L2CAP_CHANNELS];
|
||||
static memory_pool_t l2cap_channel_pool;
|
||||
void * btstack_memory_l2cap_channel_get(void){
|
||||
return memory_pool_get(&l2cap_channel_pool);
|
||||
}
|
||||
void btstack_memory_l2cap_channel_free(void *l2cap_channel){
|
||||
memory_pool_free(&l2cap_channel_pool, l2cap_channel);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_l2cap_channel_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_l2cap_channel_free(void *l2cap_channel){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_l2cap_channel_get(void){
|
||||
return malloc(sizeof(l2cap_channel_t));
|
||||
}
|
||||
void btstack_memory_l2cap_channel_free(void *l2cap_channel){
|
||||
free(l2cap_channel);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: rfcomm_multiplexer_t
|
||||
#ifdef MAX_NO_RFCOMM_MULTIPLEXERS
|
||||
#if MAX_NO_RFCOMM_MULTIPLEXERS > 0
|
||||
static rfcomm_multiplexer_t rfcomm_multiplexer_storage[MAX_NO_RFCOMM_MULTIPLEXERS];
|
||||
static memory_pool_t rfcomm_multiplexer_pool;
|
||||
void * btstack_memory_rfcomm_multiplexer_get(void){
|
||||
return memory_pool_get(&rfcomm_multiplexer_pool);
|
||||
}
|
||||
void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){
|
||||
memory_pool_free(&rfcomm_multiplexer_pool, rfcomm_multiplexer);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_rfcomm_multiplexer_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_rfcomm_multiplexer_get(void){
|
||||
return malloc(sizeof(rfcomm_multiplexer_t));
|
||||
}
|
||||
void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){
|
||||
free(rfcomm_multiplexer);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: rfcomm_service_t
|
||||
#ifdef MAX_NO_RFCOMM_SERVICES
|
||||
#if MAX_NO_RFCOMM_SERVICES > 0
|
||||
static rfcomm_service_t rfcomm_service_storage[MAX_NO_RFCOMM_SERVICES];
|
||||
static memory_pool_t rfcomm_service_pool;
|
||||
void * btstack_memory_rfcomm_service_get(void){
|
||||
return memory_pool_get(&rfcomm_service_pool);
|
||||
}
|
||||
void btstack_memory_rfcomm_service_free(void *rfcomm_service){
|
||||
memory_pool_free(&rfcomm_service_pool, rfcomm_service);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_rfcomm_service_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_rfcomm_service_free(void *rfcomm_service){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_rfcomm_service_get(void){
|
||||
return malloc(sizeof(rfcomm_service_t));
|
||||
}
|
||||
void btstack_memory_rfcomm_service_free(void *rfcomm_service){
|
||||
free(rfcomm_service);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: rfcomm_channel_t
|
||||
#ifdef MAX_NO_RFCOMM_CHANNELS
|
||||
#if MAX_NO_RFCOMM_CHANNELS > 0
|
||||
static rfcomm_channel_t rfcomm_channel_storage[MAX_NO_RFCOMM_CHANNELS];
|
||||
static memory_pool_t rfcomm_channel_pool;
|
||||
void * btstack_memory_rfcomm_channel_get(void){
|
||||
return memory_pool_get(&rfcomm_channel_pool);
|
||||
}
|
||||
void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){
|
||||
memory_pool_free(&rfcomm_channel_pool, rfcomm_channel);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_rfcomm_channel_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_rfcomm_channel_get(void){
|
||||
return malloc(sizeof(rfcomm_channel_t));
|
||||
}
|
||||
void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){
|
||||
free(rfcomm_channel);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: db_mem_device_name_t
|
||||
#ifdef MAX_NO_DB_MEM_DEVICE_NAMES
|
||||
#if MAX_NO_DB_MEM_DEVICE_NAMES > 0
|
||||
static db_mem_device_name_t db_mem_device_name_storage[MAX_NO_DB_MEM_DEVICE_NAMES];
|
||||
static memory_pool_t db_mem_device_name_pool;
|
||||
void * btstack_memory_db_mem_device_name_get(void){
|
||||
return memory_pool_get(&db_mem_device_name_pool);
|
||||
}
|
||||
void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){
|
||||
memory_pool_free(&db_mem_device_name_pool, db_mem_device_name);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_db_mem_device_name_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_db_mem_device_name_get(void){
|
||||
return malloc(sizeof(db_mem_device_name_t));
|
||||
}
|
||||
void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){
|
||||
free(db_mem_device_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: db_mem_device_link_key_t
|
||||
#ifdef MAX_NO_DB_MEM_DEVICE_LINK_KEYS
|
||||
#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0
|
||||
static db_mem_device_link_key_t db_mem_device_link_key_storage[MAX_NO_DB_MEM_DEVICE_LINK_KEYS];
|
||||
static memory_pool_t db_mem_device_link_key_pool;
|
||||
void * btstack_memory_db_mem_device_link_key_get(void){
|
||||
return memory_pool_get(&db_mem_device_link_key_pool);
|
||||
}
|
||||
void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){
|
||||
memory_pool_free(&db_mem_device_link_key_pool, db_mem_device_link_key);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_db_mem_device_link_key_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_db_mem_device_link_key_get(void){
|
||||
return malloc(sizeof(db_mem_device_link_key_t));
|
||||
}
|
||||
void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){
|
||||
free(db_mem_device_link_key);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: db_mem_service_t
|
||||
#ifdef MAX_NO_DB_MEM_SERVICES
|
||||
#if MAX_NO_DB_MEM_SERVICES > 0
|
||||
static db_mem_service_t db_mem_service_storage[MAX_NO_DB_MEM_SERVICES];
|
||||
static memory_pool_t db_mem_service_pool;
|
||||
void * btstack_memory_db_mem_service_get(void){
|
||||
return memory_pool_get(&db_mem_service_pool);
|
||||
}
|
||||
void btstack_memory_db_mem_service_free(void *db_mem_service){
|
||||
memory_pool_free(&db_mem_service_pool, db_mem_service);
|
||||
}
|
||||
#else
|
||||
void * btstack_memory_db_mem_service_get(void){
|
||||
return NULL;
|
||||
}
|
||||
void btstack_memory_db_mem_service_free(void *db_mem_service){
|
||||
};
|
||||
#endif
|
||||
#elif defined(HAVE_MALLOC)
|
||||
void * btstack_memory_db_mem_service_get(void){
|
||||
return malloc(sizeof(db_mem_service_t));
|
||||
}
|
||||
void btstack_memory_db_mem_service_free(void *db_mem_service){
|
||||
free(db_mem_service);
|
||||
}
|
||||
#endif
|
||||
|
||||
// init
|
||||
void btstack_memory_init(void){
|
||||
#if MAX_NO_HCI_CONNECTIONS > 0
|
||||
memory_pool_create(&hci_connection_pool, hci_connection_storage, MAX_NO_HCI_CONNECTIONS, sizeof(hci_connection_t));
|
||||
#endif
|
||||
#if MAX_NO_L2CAP_SERVICES > 0
|
||||
memory_pool_create(&l2cap_service_pool, l2cap_service_storage, MAX_NO_L2CAP_SERVICES, sizeof(l2cap_service_t));
|
||||
#endif
|
||||
#if MAX_NO_L2CAP_CHANNELS > 0
|
||||
memory_pool_create(&l2cap_channel_pool, l2cap_channel_storage, MAX_NO_L2CAP_CHANNELS, sizeof(l2cap_channel_t));
|
||||
#endif
|
||||
#if MAX_NO_RFCOMM_MULTIPLEXERS > 0
|
||||
memory_pool_create(&rfcomm_multiplexer_pool, rfcomm_multiplexer_storage, MAX_NO_RFCOMM_MULTIPLEXERS, sizeof(rfcomm_multiplexer_t));
|
||||
#endif
|
||||
#if MAX_NO_RFCOMM_SERVICES > 0
|
||||
memory_pool_create(&rfcomm_service_pool, rfcomm_service_storage, MAX_NO_RFCOMM_SERVICES, sizeof(rfcomm_service_t));
|
||||
#endif
|
||||
#if MAX_NO_RFCOMM_CHANNELS > 0
|
||||
memory_pool_create(&rfcomm_channel_pool, rfcomm_channel_storage, MAX_NO_RFCOMM_CHANNELS, sizeof(rfcomm_channel_t));
|
||||
#endif
|
||||
#if MAX_NO_DB_MEM_DEVICE_NAMES > 0
|
||||
memory_pool_create(&db_mem_device_name_pool, db_mem_device_name_storage, MAX_NO_DB_MEM_DEVICE_NAMES, sizeof(db_mem_device_name_t));
|
||||
#endif
|
||||
#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0
|
||||
memory_pool_create(&db_mem_device_link_key_pool, db_mem_device_link_key_storage, MAX_NO_DB_MEM_DEVICE_LINK_KEYS, sizeof(db_mem_device_link_key_t));
|
||||
#endif
|
||||
#if MAX_NO_DB_MEM_SERVICES > 0
|
||||
memory_pool_create(&db_mem_service_pool, db_mem_service_storage, MAX_NO_DB_MEM_SERVICES, sizeof(db_mem_service_t));
|
||||
#endif
|
||||
}
|
73
ios/RetroArch/input/BTStack/btstack/btstack_memory.h
Normal file
73
ios/RetroArch/input/BTStack/btstack/btstack_memory.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* btstsack_memory.h
|
||||
*
|
||||
* @brief BTstack memory management via configurable memory pools
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void btstack_memory_init(void);
|
||||
|
||||
void * btstack_memory_hci_connection_get(void);
|
||||
void btstack_memory_hci_connection_free(void *hci_connection);
|
||||
void * btstack_memory_l2cap_service_get(void);
|
||||
void btstack_memory_l2cap_service_free(void *l2cap_service);
|
||||
void * btstack_memory_l2cap_channel_get(void);
|
||||
void btstack_memory_l2cap_channel_free(void *l2cap_channel);
|
||||
void * btstack_memory_rfcomm_multiplexer_get(void);
|
||||
void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer);
|
||||
void * btstack_memory_rfcomm_service_get(void);
|
||||
void btstack_memory_rfcomm_service_free(void *rfcomm_service);
|
||||
void * btstack_memory_rfcomm_channel_get(void);
|
||||
void btstack_memory_rfcomm_channel_free(void *rfcomm_channel);
|
||||
void * btstack_memory_db_mem_device_name_get(void);
|
||||
void btstack_memory_db_mem_device_name_free(void *db_mem_device_name);
|
||||
void * btstack_memory_db_mem_device_link_key_get(void);
|
||||
void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key);
|
||||
void * btstack_memory_db_mem_service_get(void);
|
||||
void btstack_memory_db_mem_service_free(void *db_mem_service);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
800
ios/RetroArch/input/BTStack/btstack/daemon.c
Normal file
800
ios/RetroArch/input/BTStack/btstack/daemon.c
Normal file
@ -0,0 +1,800 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* daemon.c
|
||||
*
|
||||
* Created by Matthias Ringwald on 7/1/09.
|
||||
*
|
||||
* BTstack background daemon
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include <btstack/btstack.h>
|
||||
#include <btstack/linked_list.h>
|
||||
#include <btstack/run_loop.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "hci.h"
|
||||
#include "hci_dump.h"
|
||||
#include "hci_transport.h"
|
||||
#include "l2cap.h"
|
||||
#include "rfcomm.h"
|
||||
#include "sdp.h"
|
||||
#include "socket_connection.h"
|
||||
|
||||
#ifdef USE_BLUETOOL
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "bt_control_iphone.h"
|
||||
#include <notify.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_SPRINGBOARD
|
||||
#include "platform_iphone.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TRANSPORT_USB
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#endif
|
||||
|
||||
#define DAEMON_NO_ACTIVE_CLIENT_TIMEOUT 10000
|
||||
|
||||
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
// connection
|
||||
connection_t * connection;
|
||||
|
||||
// power mode
|
||||
HCI_POWER_MODE power_mode;
|
||||
|
||||
// discoverable
|
||||
uint8_t discoverable;
|
||||
|
||||
} client_state_t;
|
||||
|
||||
// MARK: prototypes
|
||||
static void dummy_bluetooth_status_handler(BLUETOOTH_STATE state);
|
||||
static client_state_t * client_for_connection(connection_t *connection);
|
||||
static int clients_require_power_on(void);
|
||||
static int clients_require_discoverable(void);
|
||||
static void clients_clear_power_request(void);
|
||||
static void start_power_off_timer(void);
|
||||
static void stop_power_off_timer(void);
|
||||
|
||||
// MARK: globals
|
||||
static hci_transport_t * transport;
|
||||
static hci_uart_config_t config;
|
||||
static timer_source_t timeout;
|
||||
static uint8_t timeout_active = 0;
|
||||
static int power_management_sleep = 0;
|
||||
static linked_list_t clients = NULL; // list of connected clients
|
||||
static void (*bluetooth_status_handler)(BLUETOOTH_STATE state) = dummy_bluetooth_status_handler;
|
||||
|
||||
static int global_enable = 0;
|
||||
|
||||
static remote_device_db_t const * remote_device_db = NULL;
|
||||
static int rfcomm_channel_generator = 1;
|
||||
|
||||
static int loggingEnabled;
|
||||
|
||||
static void dummy_bluetooth_status_handler(BLUETOOTH_STATE state){
|
||||
log_info("Bluetooth status: %u\n", state);
|
||||
};
|
||||
|
||||
static void daemon_no_connections_timeout(struct timer *ts){
|
||||
if (clients_require_power_on()) return; // false alarm :)
|
||||
log_info("No active client connection for %u seconds -> POWER OFF\n", DAEMON_NO_ACTIVE_CLIENT_TIMEOUT/1000);
|
||||
hci_power_control(HCI_POWER_OFF);
|
||||
}
|
||||
|
||||
static int btstack_command_handler(connection_t *connection, uint8_t *packet, uint16_t size){
|
||||
|
||||
bd_addr_t addr;
|
||||
uint16_t cid;
|
||||
uint16_t psm;
|
||||
uint16_t service_channel;
|
||||
uint16_t mtu;
|
||||
uint8_t reason;
|
||||
uint8_t rfcomm_channel;
|
||||
uint8_t rfcomm_credits;
|
||||
uint32_t service_record_handle;
|
||||
client_state_t *client;
|
||||
|
||||
// BTstack internal commands - 16 Bit OpCode, 8 Bit ParamLen, Params...
|
||||
switch (READ_CMD_OCF(packet)){
|
||||
case BTSTACK_GET_STATE:
|
||||
log_info("BTSTACK_GET_STATE");
|
||||
hci_emit_state();
|
||||
break;
|
||||
case BTSTACK_SET_POWER_MODE:
|
||||
log_info("BTSTACK_SET_POWER_MODE %u", packet[3]);
|
||||
// track client power requests
|
||||
client = client_for_connection(connection);
|
||||
if (!client) break;
|
||||
client->power_mode = packet[3];
|
||||
// handle merged state
|
||||
if (!clients_require_power_on()){
|
||||
start_power_off_timer();
|
||||
} else if (!power_management_sleep) {
|
||||
stop_power_off_timer();
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
}
|
||||
break;
|
||||
case BTSTACK_GET_VERSION:
|
||||
log_info("BTSTACK_GET_VERSION");
|
||||
hci_emit_btstack_version();
|
||||
break;
|
||||
#ifdef USE_BLUETOOL
|
||||
case BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED:
|
||||
log_info("BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED %u", packet[3]);
|
||||
iphone_system_bt_set_enabled(packet[3]);
|
||||
hci_emit_system_bluetooth_enabled(iphone_system_bt_enabled());
|
||||
break;
|
||||
|
||||
case BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED:
|
||||
log_info("BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED");
|
||||
hci_emit_system_bluetooth_enabled(iphone_system_bt_enabled());
|
||||
break;
|
||||
#else
|
||||
case BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED:
|
||||
case BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED:
|
||||
hci_emit_system_bluetooth_enabled(0);
|
||||
break;
|
||||
#endif
|
||||
case BTSTACK_SET_DISCOVERABLE:
|
||||
log_info("BTSTACK_SET_DISCOVERABLE discoverable %u)", packet[3]);
|
||||
// track client discoverable requests
|
||||
client = client_for_connection(connection);
|
||||
if (!client) break;
|
||||
client->discoverable = packet[3];
|
||||
// merge state
|
||||
hci_discoverable_control(clients_require_discoverable());
|
||||
break;
|
||||
case BTSTACK_SET_BLUETOOTH_ENABLED:
|
||||
log_info("BTSTACK_SET_BLUETOOTH_ENABLED: %u\n", packet[3]);
|
||||
if (packet[3]) {
|
||||
// global enable
|
||||
global_enable = 1;
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
} else {
|
||||
global_enable = 0;
|
||||
clients_clear_power_request();
|
||||
hci_power_control(HCI_POWER_OFF);
|
||||
}
|
||||
break;
|
||||
case L2CAP_CREATE_CHANNEL_MTU:
|
||||
bt_flip_addr(addr, &packet[3]);
|
||||
psm = READ_BT_16(packet, 9);
|
||||
mtu = READ_BT_16(packet, 11);
|
||||
l2cap_create_channel_internal( connection, NULL, addr, psm, mtu);
|
||||
break;
|
||||
case L2CAP_CREATE_CHANNEL:
|
||||
bt_flip_addr(addr, &packet[3]);
|
||||
psm = READ_BT_16(packet, 9);
|
||||
l2cap_create_channel_internal( connection, NULL, addr, psm, 150); // until r865
|
||||
break;
|
||||
case L2CAP_DISCONNECT:
|
||||
cid = READ_BT_16(packet, 3);
|
||||
reason = packet[5];
|
||||
l2cap_disconnect_internal(cid, reason);
|
||||
break;
|
||||
case L2CAP_REGISTER_SERVICE:
|
||||
psm = READ_BT_16(packet, 3);
|
||||
mtu = READ_BT_16(packet, 5);
|
||||
l2cap_register_service_internal(connection, NULL, psm, mtu);
|
||||
break;
|
||||
case L2CAP_UNREGISTER_SERVICE:
|
||||
psm = READ_BT_16(packet, 3);
|
||||
l2cap_unregister_service_internal(connection, psm);
|
||||
break;
|
||||
case L2CAP_ACCEPT_CONNECTION:
|
||||
cid = READ_BT_16(packet, 3);
|
||||
l2cap_accept_connection_internal(cid);
|
||||
break;
|
||||
case L2CAP_DECLINE_CONNECTION:
|
||||
cid = READ_BT_16(packet, 3);
|
||||
reason = packet[7];
|
||||
l2cap_decline_connection_internal(cid, reason);
|
||||
break;
|
||||
case RFCOMM_CREATE_CHANNEL:
|
||||
bt_flip_addr(addr, &packet[3]);
|
||||
rfcomm_channel = packet[9];
|
||||
rfcomm_create_channel_internal( connection, &addr, rfcomm_channel );
|
||||
break;
|
||||
case RFCOMM_CREATE_CHANNEL_WITH_CREDITS:
|
||||
bt_flip_addr(addr, &packet[3]);
|
||||
rfcomm_channel = packet[9];
|
||||
rfcomm_credits = packet[10];
|
||||
rfcomm_create_channel_with_initial_credits_internal( connection, &addr, rfcomm_channel, rfcomm_credits );
|
||||
break;
|
||||
case RFCOMM_DISCONNECT:
|
||||
cid = READ_BT_16(packet, 3);
|
||||
reason = packet[5];
|
||||
rfcomm_disconnect_internal(cid);
|
||||
break;
|
||||
case RFCOMM_REGISTER_SERVICE:
|
||||
rfcomm_channel = packet[3];
|
||||
mtu = READ_BT_16(packet, 4);
|
||||
rfcomm_register_service_internal(connection, rfcomm_channel, mtu);
|
||||
break;
|
||||
case RFCOMM_REGISTER_SERVICE_WITH_CREDITS:
|
||||
rfcomm_channel = packet[3];
|
||||
mtu = READ_BT_16(packet, 4);
|
||||
rfcomm_credits = packet[6];
|
||||
rfcomm_register_service_with_initial_credits_internal(connection, rfcomm_channel, mtu, rfcomm_credits);
|
||||
break;
|
||||
case RFCOMM_UNREGISTER_SERVICE:
|
||||
service_channel = READ_BT_16(packet, 3);
|
||||
rfcomm_unregister_service_internal(service_channel);
|
||||
break;
|
||||
case RFCOMM_ACCEPT_CONNECTION:
|
||||
cid = READ_BT_16(packet, 3);
|
||||
rfcomm_accept_connection_internal(cid);
|
||||
break;
|
||||
case RFCOMM_DECLINE_CONNECTION:
|
||||
cid = READ_BT_16(packet, 3);
|
||||
reason = packet[7];
|
||||
rfcomm_decline_connection_internal(cid);
|
||||
break;
|
||||
case RFCOMM_GRANT_CREDITS:
|
||||
cid = READ_BT_16(packet, 3);
|
||||
rfcomm_credits = packet[5];
|
||||
rfcomm_grant_credits(cid, rfcomm_credits);
|
||||
break;
|
||||
case RFCOMM_PERSISTENT_CHANNEL: {
|
||||
if (remote_device_db) {
|
||||
// enforce \0
|
||||
packet[3+248] = 0;
|
||||
rfcomm_channel = remote_device_db->persistent_rfcomm_channel((char*)&packet[3]);
|
||||
} else {
|
||||
// NOTE: hack for non-iOS platforms
|
||||
rfcomm_channel = rfcomm_channel_generator++;
|
||||
}
|
||||
log_info("RFCOMM_EVENT_PERSISTENT_CHANNEL %u", rfcomm_channel);
|
||||
uint8_t event[4];
|
||||
event[0] = RFCOMM_EVENT_PERSISTENT_CHANNEL;
|
||||
event[1] = sizeof(event) - 2;
|
||||
event[2] = 0;
|
||||
event[3] = rfcomm_channel;
|
||||
hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
||||
socket_connection_send_packet(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event));
|
||||
break;
|
||||
}
|
||||
|
||||
case SDP_REGISTER_SERVICE_RECORD:
|
||||
log_info("SDP_REGISTER_SERVICE_RECORD size %u\n", size);
|
||||
sdp_register_service_internal(connection, &packet[3]);
|
||||
break;
|
||||
case SDP_UNREGISTER_SERVICE_RECORD:
|
||||
service_record_handle = READ_BT_32(packet, 3);
|
||||
log_info("SDP_UNREGISTER_SERVICE_RECORD handle 0x%x ", service_record_handle);
|
||||
sdp_unregister_service_internal(connection, service_record_handle);
|
||||
break;
|
||||
default:
|
||||
log_error("Error: command %u not implemented\n:", READ_CMD_OCF(packet));
|
||||
break;
|
||||
}
|
||||
|
||||
// verbose log info on command before dumped command unknown to PacketLogger or Wireshark
|
||||
hci_dump_packet( HCI_COMMAND_DATA_PACKET, 1, packet, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daemon_client_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length){
|
||||
|
||||
int err = 0;
|
||||
client_state_t * client;
|
||||
|
||||
switch (packet_type){
|
||||
case HCI_COMMAND_DATA_PACKET:
|
||||
if (READ_CMD_OGF(data) != OGF_BTSTACK) {
|
||||
// HCI Command
|
||||
hci_send_cmd_packet(data, length);
|
||||
} else {
|
||||
// BTstack command
|
||||
btstack_command_handler(connection, data, length);
|
||||
}
|
||||
break;
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
err = hci_send_acl_packet(data, length);
|
||||
break;
|
||||
case L2CAP_DATA_PACKET:
|
||||
// process l2cap packet...
|
||||
err = l2cap_send_internal(channel, data, length);
|
||||
if (err == BTSTACK_ACL_BUFFERS_FULL) {
|
||||
l2cap_block_new_credits(1);
|
||||
}
|
||||
break;
|
||||
case RFCOMM_DATA_PACKET:
|
||||
// process l2cap packet...
|
||||
err = rfcomm_send_internal(channel, data, length);
|
||||
break;
|
||||
case DAEMON_EVENT_PACKET:
|
||||
switch (data[0]) {
|
||||
case DAEMON_EVENT_CONNECTION_OPENED:
|
||||
log_info("DAEMON_EVENT_CONNECTION_OPENED %p\n",connection);
|
||||
|
||||
client = malloc(sizeof(client_state_t));
|
||||
if (!client) break; // fail
|
||||
client->connection = connection;
|
||||
client->power_mode = HCI_POWER_OFF;
|
||||
client->discoverable = 0;
|
||||
linked_list_add(&clients, (linked_item_t *) client);
|
||||
break;
|
||||
case DAEMON_EVENT_CONNECTION_CLOSED:
|
||||
log_info("DAEMON_EVENT_CONNECTION_CLOSED %p\n",connection);
|
||||
sdp_unregister_services_for_connection(connection);
|
||||
rfcomm_close_connection(connection);
|
||||
l2cap_close_connection(connection);
|
||||
client = client_for_connection(connection);
|
||||
if (!client) break;
|
||||
linked_list_remove(&clients, (linked_item_t *) client);
|
||||
free(client);
|
||||
// update discoverable mode
|
||||
hci_discoverable_control(clients_require_discoverable());
|
||||
// start power off, if last active client
|
||||
if (!clients_require_power_on()){
|
||||
start_power_off_timer();
|
||||
}
|
||||
break;
|
||||
case DAEMON_NR_CONNECTIONS_CHANGED:
|
||||
log_info("Nr Connections changed, new %u\n",data[1]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (err) {
|
||||
log_info("Daemon Handler: err %d\n", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static void daemon_set_logging_enabled(int enabled){
|
||||
if (enabled && !loggingEnabled){
|
||||
// use logger: format HCI_DUMP_PACKETLOGGER, HCI_DUMP_BLUEZ or HCI_DUMP_STDOUT
|
||||
hci_dump_open("/tmp/hci_dump.pklg", HCI_DUMP_PACKETLOGGER);
|
||||
}
|
||||
if (!enabled && loggingEnabled){
|
||||
hci_dump_close();
|
||||
}
|
||||
loggingEnabled = enabled;
|
||||
}
|
||||
|
||||
// local cache used to manage UI status
|
||||
static HCI_STATE hci_state = HCI_STATE_OFF;
|
||||
static int num_connections = 0;
|
||||
static void update_ui_status(void){
|
||||
if (hci_state != HCI_STATE_WORKING) {
|
||||
bluetooth_status_handler(BLUETOOTH_OFF);
|
||||
} else {
|
||||
if (num_connections) {
|
||||
bluetooth_status_handler(BLUETOOTH_ACTIVE);
|
||||
} else {
|
||||
bluetooth_status_handler(BLUETOOTH_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_SPRINGBOARD
|
||||
static void preferences_changed_callback(void){
|
||||
int logging = platform_iphone_logging_enabled();
|
||||
log_info("Logging enabled: %u\n", logging);
|
||||
daemon_set_logging_enabled(logging);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void deamon_status_event_handler(uint8_t *packet, uint16_t size){
|
||||
|
||||
uint8_t update_status = 0;
|
||||
|
||||
// handle state event
|
||||
switch (packet[0]) {
|
||||
case BTSTACK_EVENT_STATE:
|
||||
hci_state = packet[2];
|
||||
log_info("New state: %u\n", hci_state);
|
||||
update_status = 1;
|
||||
break;
|
||||
case BTSTACK_EVENT_NR_CONNECTIONS_CHANGED:
|
||||
num_connections = packet[2];
|
||||
log_info("New nr connections: %u\n", num_connections);
|
||||
update_status = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// choose full bluetooth state
|
||||
if (update_status) {
|
||||
update_ui_status();
|
||||
}
|
||||
}
|
||||
|
||||
static void daemon_retry_parked(void){
|
||||
|
||||
// socket_connection_retry_parked is not reentrant
|
||||
static int retry_mutex = 0;
|
||||
|
||||
// lock mutex
|
||||
if (retry_mutex) return;
|
||||
retry_mutex = 1;
|
||||
|
||||
// ... try sending again
|
||||
socket_connection_retry_parked();
|
||||
|
||||
if (!socket_connection_has_parked_connections()){
|
||||
l2cap_block_new_credits(0);
|
||||
}
|
||||
|
||||
// unlock mutex
|
||||
retry_mutex = 0;
|
||||
}
|
||||
|
||||
static void daemon_packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
switch (packet_type) {
|
||||
case HCI_EVENT_PACKET:
|
||||
deamon_status_event_handler(packet, size);
|
||||
switch (packet[0]){
|
||||
case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:
|
||||
// ACL buffer freed...
|
||||
daemon_retry_parked();
|
||||
// no need to tell clients
|
||||
return;
|
||||
case RFCOMM_EVENT_CREDITS:
|
||||
// RFCOMM CREDITS received...
|
||||
daemon_retry_parked();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case DAEMON_EVENT_PACKET:
|
||||
switch (packet[0]){
|
||||
case DAEMON_EVENT_NEW_RFCOMM_CREDITS:
|
||||
daemon_retry_parked();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (connection) {
|
||||
socket_connection_send_packet(connection, packet_type, channel, packet, size);
|
||||
} else {
|
||||
socket_connection_send_packet_all(packet_type, channel, packet, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void power_notification_callback(POWER_NOTIFICATION_t notification){
|
||||
switch (notification) {
|
||||
case POWER_WILL_SLEEP:
|
||||
// let's sleep
|
||||
power_management_sleep = 1;
|
||||
hci_power_control(HCI_POWER_SLEEP);
|
||||
break;
|
||||
case POWER_WILL_WAKE_UP:
|
||||
// assume that all clients use Bluetooth -> if connection, start Bluetooth
|
||||
power_management_sleep = 0;
|
||||
if (clients_require_power_on()) {
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void daemon_sigint_handler(int param){
|
||||
|
||||
#ifdef USE_BLUETOOL
|
||||
// notify daemons
|
||||
notify_post("ch.ringwald.btstack.stopped");
|
||||
#endif
|
||||
|
||||
log_info(" <= SIGINT received, shutting down..\n");
|
||||
|
||||
hci_power_control( HCI_POWER_OFF);
|
||||
hci_close();
|
||||
|
||||
log_info("Good bye, see you.\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: manage power off timer
|
||||
|
||||
#define USE_POWER_OFF_TIMER
|
||||
|
||||
static void stop_power_off_timer(void){
|
||||
#ifdef USE_POWER_OFF_TIMER
|
||||
if (timeout_active) {
|
||||
run_loop_remove_timer(&timeout);
|
||||
timeout_active = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void start_power_off_timer(void){
|
||||
#ifdef USE_POWER_OFF_TIMER
|
||||
stop_power_off_timer();
|
||||
run_loop_set_timer(&timeout, DAEMON_NO_ACTIVE_CLIENT_TIMEOUT);
|
||||
run_loop_add_timer(&timeout);
|
||||
timeout_active = 1;
|
||||
#else
|
||||
hci_power_control(HCI_POWER_OFF);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: manage list of clients
|
||||
|
||||
|
||||
static client_state_t * client_for_connection(connection_t *connection) {
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) clients; it ; it = it->next){
|
||||
client_state_t * client_state = (client_state_t *) it;
|
||||
if (client_state->connection == connection) {
|
||||
return client_state;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void clients_clear_power_request(void){
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) clients; it ; it = it->next){
|
||||
client_state_t * client_state = (client_state_t *) it;
|
||||
client_state->power_mode = HCI_POWER_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
static int clients_require_power_on(void){
|
||||
|
||||
if (global_enable) return 1;
|
||||
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) clients; it ; it = it->next){
|
||||
client_state_t * client_state = (client_state_t *) it;
|
||||
if (client_state->power_mode == HCI_POWER_ON) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clients_require_discoverable(void){
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) clients; it ; it = it->next){
|
||||
client_state_t * client_state = (client_state_t *) it;
|
||||
if (client_state->discoverable) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(const char * name) {
|
||||
log_info("%s, BTstack background daemon\n", name);
|
||||
log_info("usage: %s [-h|--help] [--tcp]\n", name);
|
||||
log_info(" -h|--help display this usage\n");
|
||||
log_info(" --tcp use TCP server socket instead of local unix socket\n");
|
||||
}
|
||||
|
||||
#ifdef USE_BLUETOOL
|
||||
static void * run_loop_thread(void *context){
|
||||
run_loop_execute();
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main (int argc, char * const * argv){
|
||||
|
||||
static int tcp_flag = 0;
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{ "tcp", no_argument, &tcp_flag, 1 },
|
||||
{ "help", no_argument, 0, 0 },
|
||||
{ 0,0,0,0 } // This is a filler for -1
|
||||
};
|
||||
|
||||
int c;
|
||||
int option_index = -1;
|
||||
|
||||
c = getopt_long(argc, argv, "h", long_options, &option_index);
|
||||
|
||||
if (c == -1) break; // no more option
|
||||
|
||||
// treat long parameter first
|
||||
if (option_index == -1) {
|
||||
switch (c) {
|
||||
case '?':
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (option_index) {
|
||||
case 1:
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make stdout unbuffered
|
||||
setbuf(stdout, NULL);
|
||||
log_error("BTdaemon started\n");
|
||||
|
||||
// handle CTRL-c
|
||||
signal(SIGINT, daemon_sigint_handler);
|
||||
// handle SIGTERM - suggested for launchd
|
||||
signal(SIGTERM, daemon_sigint_handler);
|
||||
// handle SIGPIPE
|
||||
struct sigaction act;
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigemptyset (&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
sigaction (SIGPIPE, &act, NULL);
|
||||
|
||||
bt_control_t * control = NULL;
|
||||
|
||||
#ifdef HAVE_TRANSPORT_H4
|
||||
config.device_name = UART_DEVICE;
|
||||
config.baudrate_init = UART_SPEED;
|
||||
config.baudrate_main = 0;
|
||||
config.flowcontrol = 1;
|
||||
#if defined(USE_BLUETOOL) && defined(USE_POWERMANAGEMENT)
|
||||
if (bt_control_iphone_power_management_supported()){
|
||||
// use default (max) UART baudrate over netraph interface
|
||||
config.baudrate_init = 0;
|
||||
transport = hci_transport_h4_iphone_instance();
|
||||
} else {
|
||||
transport = hci_transport_h4_instance();
|
||||
}
|
||||
#else
|
||||
transport = hci_transport_h4_instance();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TRANSPORT_USB
|
||||
transport = hci_transport_usb_instance();
|
||||
#endif
|
||||
|
||||
#ifdef USE_BLUETOOL
|
||||
control = &bt_control_iphone;
|
||||
#endif
|
||||
|
||||
#if defined(USE_BLUETOOL) && defined(USE_POWERMANAGEMENT)
|
||||
if (bt_control_iphone_power_management_supported()){
|
||||
hci_transport_h4_iphone_set_enforce_wake_device("/dev/btwake");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SPRINGBOARD
|
||||
bluetooth_status_handler = platform_iphone_status_handler;
|
||||
platform_iphone_register_window_manager_restart(update_ui_status);
|
||||
platform_iphone_register_preferences_changed(preferences_changed_callback);
|
||||
#endif
|
||||
|
||||
#ifdef REMOTE_DEVICE_DB
|
||||
remote_device_db = &REMOTE_DEVICE_DB;
|
||||
#endif
|
||||
|
||||
run_loop_init(RUN_LOOP_POSIX);
|
||||
|
||||
// init power management notifications
|
||||
if (control && control->register_for_power_notifications){
|
||||
control->register_for_power_notifications(power_notification_callback);
|
||||
}
|
||||
|
||||
// logging
|
||||
loggingEnabled = 0;
|
||||
int newLoggingEnabled = 1;
|
||||
#ifdef USE_BLUETOOL
|
||||
// iPhone has toggle in Preferences.app
|
||||
newLoggingEnabled = platform_iphone_logging_enabled();
|
||||
#endif
|
||||
daemon_set_logging_enabled(newLoggingEnabled);
|
||||
|
||||
// init HCI
|
||||
hci_init(transport, &config, control, remote_device_db);
|
||||
|
||||
// init L2CAP
|
||||
l2cap_init();
|
||||
l2cap_register_packet_handler(daemon_packet_handler);
|
||||
timeout.process = daemon_no_connections_timeout;
|
||||
|
||||
#ifdef HAVE_RFCOMM
|
||||
log_info("config.h: HAVE_RFCOMM\n");
|
||||
rfcomm_init();
|
||||
rfcomm_register_packet_handler(daemon_packet_handler);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SDP
|
||||
sdp_init();
|
||||
sdp_register_packet_handler(daemon_packet_handler);
|
||||
#endif
|
||||
|
||||
#ifdef USE_LAUNCHD
|
||||
socket_connection_create_launchd();
|
||||
#else
|
||||
// create server
|
||||
if (tcp_flag) {
|
||||
socket_connection_create_tcp(BTSTACK_PORT);
|
||||
} else {
|
||||
socket_connection_create_unix(BTSTACK_UNIX);
|
||||
}
|
||||
#endif
|
||||
socket_connection_register_packet_callback(daemon_client_handler);
|
||||
|
||||
#ifdef USE_BLUETOOL
|
||||
// notify daemons
|
||||
notify_post("ch.ringwald.btstack.started");
|
||||
|
||||
// spawn thread to have BTstack run loop on new thread, while main thread is used to keep CFRunLoop
|
||||
pthread_t run_loop;
|
||||
pthread_create(&run_loop, NULL, &run_loop_thread, NULL);
|
||||
|
||||
// needed to receive notifications
|
||||
CFRunLoopRun();
|
||||
#endif
|
||||
|
||||
// go!
|
||||
run_loop_execute();
|
||||
return 0;
|
||||
}
|
76
ios/RetroArch/input/BTStack/btstack/debug.h
Normal file
76
ios/RetroArch/input/BTStack/btstack/debug.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* debug.h
|
||||
*
|
||||
* allow to funnel debug & error messages
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "hci_dump.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef ENABLE_LOG_DEBUG
|
||||
#ifdef HAVE_HCI_DUMP
|
||||
#define log_debug(format, ...) hci_dump_log(format, ## __VA_ARGS__)
|
||||
#else
|
||||
#define log_debug(format, ...) printf(format, ## __VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define log_debug(...)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LOG_INFO
|
||||
#ifdef HAVE_HCI_DUMP
|
||||
#define log_info(format, ...) hci_dump_log(format, ## __VA_ARGS__)
|
||||
#else
|
||||
#define log_info(format, ...) printf(format, ## __VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define log_info(...)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LOG_ERROR
|
||||
#ifdef HAVE_HCI_DUMP
|
||||
#define log_error(format, ...) hci_dump_log(format, ## __VA_ARGS__)
|
||||
#else
|
||||
#define log_error(format, ...) printf(format, ## __VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define log_error(...)
|
||||
#endif
|
43
ios/RetroArch/input/BTStack/btstack/hal_cpu.h
Normal file
43
ios/RetroArch/input/BTStack/btstack/hal_cpu.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2011 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hal_cpu.h
|
||||
*
|
||||
* Low power mode for MCU requires that IRQs can be first blocked
|
||||
* and then unblocked while entering low power mode atomically
|
||||
*/
|
||||
|
||||
void hal_cpu_disable_irqs(void);
|
||||
void hal_cpu_enable_irqs(void);
|
||||
void hal_cpu_enable_irqs_and_sleep(void);
|
||||
|
||||
|
45
ios/RetroArch/input/BTStack/btstack/hal_tick.h
Normal file
45
ios/RetroArch/input/BTStack/btstack/hal_tick.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2011 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hal_tick.h
|
||||
*
|
||||
* Hardware abstraction layer for periodic ticks
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void hal_tick_init(void);
|
||||
void hal_tick_set_handler(void (*tick_handler)(void));
|
||||
int hal_tick_get_tick_period_in_ms(void);
|
52
ios/RetroArch/input/BTStack/btstack/hal_uart_dma.h
Normal file
52
ios/RetroArch/input/BTStack/btstack/hal_uart_dma.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2011 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hal_uart_dma.h
|
||||
*
|
||||
* Hardware abstraction layer that provides
|
||||
* - blockwise IRQ-driven read/write
|
||||
* - CSR IRQs
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void hal_uart_dma_init(void);
|
||||
void hal_uart_dma_set_block_received( void (*block_handler)(void));
|
||||
void hal_uart_dma_set_block_sent( void (*block_handler)(void));
|
||||
void hal_uart_dma_set_csr_irq_handler( void (*csr_irq_handler)(void));
|
||||
int hal_uart_dma_set_baud(uint32_t baud);
|
||||
void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t length);
|
||||
void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t len);
|
||||
void hal_uart_dma_set_sleep(uint8_t sleep);
|
1420
ios/RetroArch/input/BTStack/btstack/hci.c
Normal file
1420
ios/RetroArch/input/BTStack/btstack/hci.c
Normal file
File diff suppressed because it is too large
Load Diff
359
ios/RetroArch/input/BTStack/btstack/hci.h
Normal file
359
ios/RetroArch/input/BTStack/btstack/hci.h
Normal file
@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci.h
|
||||
*
|
||||
* Created by Matthias Ringwald on 4/29/09.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <btstack/hci_cmds.h>
|
||||
#include <btstack/utils.h>
|
||||
#include "hci_transport.h"
|
||||
#include "bt_control.h"
|
||||
#include "remote_device_db.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// packet header sizes
|
||||
#define HCI_CMD_HEADER_SIZE 3
|
||||
#define HCI_ACL_HEADER_SIZE 4
|
||||
#define HCI_SCO_HEADER_SIZE 3
|
||||
#define HCI_EVENT_HEADER_SIZE 2
|
||||
|
||||
// packet sizes (max payload)
|
||||
#define HCI_ACL_DM1_SIZE 17
|
||||
#define HCI_ACL_DH1_SIZE 27
|
||||
#define HCI_ACL_2DH1_SIZE 54
|
||||
#define HCI_ACL_3DH1_SIZE 83
|
||||
#define HCI_ACL_DM3_SIZE 121
|
||||
#define HCI_ACL_DH3_SIZE 183
|
||||
#define HCI_ACL_DM5_SIZE 224
|
||||
#define HCI_ACL_DH5_SIZE 339
|
||||
#define HCI_ACL_2DH3_SIZE 367
|
||||
#define HCI_ACL_3DH3_SIZE 552
|
||||
#define HCI_ACL_2DH5_SIZE 679
|
||||
#define HCI_ACL_3DH5_SIZE 1021
|
||||
|
||||
#define HCI_EVENT_PAYLOAD_SIZE 255
|
||||
#define HCI_CMD_PAYLOAD_SIZE 255
|
||||
|
||||
// packet buffer sizes
|
||||
// HCI_ACL_PAYLOAD_SIZE is configurable and defined in config.h
|
||||
#define HCI_EVENT_BUFFER_SIZE (HCI_EVENT_HEADER_SIZE + HCI_EVENT_PAYLOAD_SIZE)
|
||||
#define HCI_CMD_BUFFER_SIZE (HCI_CMD_HEADER_SIZE + HCI_CMD_PAYLOAD_SIZE)
|
||||
#define HCI_ACL_BUFFER_SIZE (HCI_ACL_HEADER_SIZE + HCI_ACL_PAYLOAD_SIZE)
|
||||
|
||||
// size of hci buffers, big enough for command, event, or acl packet without H4 packet type
|
||||
// @note cmd buffer is bigger than event buffer
|
||||
#if HCI_ACL_BUFFER_SIZE > HCI_CMD_BUFFER_SIZE
|
||||
#define HCI_PACKET_BUFFER_SIZE HCI_ACL_BUFFER_SIZE
|
||||
#else
|
||||
#define HCI_PACKET_BUFFER_SIZE HCI_CMD_BUFFER_SIZE
|
||||
#endif
|
||||
|
||||
// OGFs
|
||||
#define OGF_LINK_CONTROL 0x01
|
||||
#define OGF_LINK_POLICY 0x02
|
||||
#define OGF_CONTROLLER_BASEBAND 0x03
|
||||
#define OGF_INFORMATIONAL_PARAMETERS 0x04
|
||||
#define OGF_LE_CONTROLLER 0x08
|
||||
#define OGF_BTSTACK 0x3d
|
||||
#define OGF_VENDOR 0x3f
|
||||
|
||||
// cmds for BTstack
|
||||
// get state: @returns HCI_STATE
|
||||
#define BTSTACK_GET_STATE 0x01
|
||||
|
||||
// set power mode: @param HCI_POWER_MODE
|
||||
#define BTSTACK_SET_POWER_MODE 0x02
|
||||
|
||||
// set capture mode: @param on
|
||||
#define BTSTACK_SET_ACL_CAPTURE_MODE 0x03
|
||||
|
||||
// get BTstack version
|
||||
#define BTSTACK_GET_VERSION 0x04
|
||||
|
||||
// get system Bluetooth state
|
||||
#define BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED 0x05
|
||||
|
||||
// set system Bluetooth state
|
||||
#define BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED 0x06
|
||||
|
||||
// enable inquiry scan for this client
|
||||
#define BTSTACK_SET_DISCOVERABLE 0x07
|
||||
|
||||
// set global Bluetooth state
|
||||
#define BTSTACK_SET_BLUETOOTH_ENABLED 0x08
|
||||
|
||||
// create l2cap channel: @param bd_addr(48), psm (16)
|
||||
#define L2CAP_CREATE_CHANNEL 0x20
|
||||
|
||||
// disconnect l2cap disconnect, @param channel(16), reason(8)
|
||||
#define L2CAP_DISCONNECT 0x21
|
||||
|
||||
// register l2cap service: @param psm(16), mtu (16)
|
||||
#define L2CAP_REGISTER_SERVICE 0x22
|
||||
|
||||
// unregister l2cap disconnect, @param psm(16)
|
||||
#define L2CAP_UNREGISTER_SERVICE 0x23
|
||||
|
||||
// accept connection @param bd_addr(48), dest cid (16)
|
||||
#define L2CAP_ACCEPT_CONNECTION 0x24
|
||||
|
||||
// decline l2cap disconnect,@param bd_addr(48), dest cid (16), reason(8)
|
||||
#define L2CAP_DECLINE_CONNECTION 0x25
|
||||
|
||||
// create l2cap channel: @param bd_addr(48), psm (16), mtu (16)
|
||||
#define L2CAP_CREATE_CHANNEL_MTU 0x26
|
||||
|
||||
// register SDP Service Record: service record (size)
|
||||
#define SDP_REGISTER_SERVICE_RECORD 0x30
|
||||
|
||||
// unregister SDP Service Record
|
||||
#define SDP_UNREGISTER_SERVICE_RECORD 0x31
|
||||
|
||||
// RFCOMM "HCI" Commands
|
||||
#define RFCOMM_CREATE_CHANNEL 0x40
|
||||
#define RFCOMM_DISCONNECT 0x41
|
||||
#define RFCOMM_REGISTER_SERVICE 0x42
|
||||
#define RFCOMM_UNREGISTER_SERVICE 0x43
|
||||
#define RFCOMM_ACCEPT_CONNECTION 0x44
|
||||
#define RFCOMM_DECLINE_CONNECTION 0x45
|
||||
#define RFCOMM_PERSISTENT_CHANNEL 0x46
|
||||
#define RFCOMM_CREATE_CHANNEL_WITH_CREDITS 0x47
|
||||
#define RFCOMM_REGISTER_SERVICE_WITH_CREDITS 0x48
|
||||
#define RFCOMM_GRANT_CREDITS 0x49
|
||||
|
||||
//
|
||||
#define IS_COMMAND(packet, command) (READ_BT_16(packet,0) == command.opcode)
|
||||
|
||||
// data: event(8)
|
||||
#define DAEMON_EVENT_CONNECTION_OPENED 0x50
|
||||
|
||||
// data: event(8)
|
||||
#define DAEMON_EVENT_CONNECTION_CLOSED 0x51
|
||||
|
||||
// data: event(8), nr_connections(8)
|
||||
#define DAEMON_NR_CONNECTIONS_CHANGED 0x52
|
||||
|
||||
// data: event(8)
|
||||
#define DAEMON_EVENT_NEW_RFCOMM_CREDITS 0x53
|
||||
|
||||
// data: event()
|
||||
#define DAEMON_EVENT_HCI_PACKET_SENT 0x54
|
||||
|
||||
/**
|
||||
* Connection State
|
||||
*/
|
||||
typedef enum {
|
||||
AUTH_FLAGS_NONE = 0x00,
|
||||
RECV_LINK_KEY_REQUEST = 0x01,
|
||||
HANDLE_LINK_KEY_REQUEST = 0x02,
|
||||
SENT_LINK_KEY_REPLY = 0x04,
|
||||
SENT_LINK_KEY_NEGATIVE_REQUEST = 0x08,
|
||||
RECV_LINK_KEY_NOTIFICATION = 0x10,
|
||||
RECV_PIN_CODE_REQUEST = 0x20,
|
||||
SENT_PIN_CODE_REPLY = 0x40,
|
||||
SENT_PIN_CODE_NEGATIVE_REPLY = 0x80
|
||||
} hci_authentication_flags_t;
|
||||
|
||||
typedef enum {
|
||||
SENT_CREATE_CONNECTION = 1,
|
||||
RECEIVED_CONNECTION_REQUEST,
|
||||
ACCEPTED_CONNECTION_REQUEST,
|
||||
REJECTED_CONNECTION_REQUEST,
|
||||
OPEN,
|
||||
SENT_DISCONNECT
|
||||
} CONNECTION_STATE;
|
||||
|
||||
typedef enum {
|
||||
BLUETOOTH_OFF = 1,
|
||||
BLUETOOTH_ON,
|
||||
BLUETOOTH_ACTIVE
|
||||
} BLUETOOTH_STATE;
|
||||
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
// remote side
|
||||
bd_addr_t address;
|
||||
|
||||
// module handle
|
||||
hci_con_handle_t con_handle;
|
||||
|
||||
// state
|
||||
CONNECTION_STATE state;
|
||||
|
||||
// errands
|
||||
hci_authentication_flags_t authentication_flags;
|
||||
|
||||
timer_source_t timeout;
|
||||
|
||||
#ifdef HAVE_TIME
|
||||
// timer
|
||||
struct timeval timestamp;
|
||||
#endif
|
||||
#ifdef HAVE_TICK
|
||||
uint32_t timestamp; // timeout in system ticks
|
||||
#endif
|
||||
|
||||
// ACL packet recombination - ACL Header + ACL payload
|
||||
uint8_t acl_recombination_buffer[4 + HCI_ACL_BUFFER_SIZE];
|
||||
uint16_t acl_recombination_pos;
|
||||
uint16_t acl_recombination_length;
|
||||
|
||||
// number ACL packets sent to controller
|
||||
uint8_t num_acl_packets_sent;
|
||||
|
||||
} hci_connection_t;
|
||||
|
||||
/**
|
||||
* main data structure
|
||||
*/
|
||||
typedef struct {
|
||||
// transport component with configuration
|
||||
hci_transport_t * hci_transport;
|
||||
void * config;
|
||||
|
||||
// hardware power controller
|
||||
bt_control_t * control;
|
||||
|
||||
// list of existing baseband connections
|
||||
linked_list_t connections;
|
||||
|
||||
// single buffer for HCI Command assembly
|
||||
uint8_t hci_packet_buffer[HCI_PACKET_BUFFER_SIZE]; // opcode (16), len(8)
|
||||
|
||||
/* host to controller flow control */
|
||||
uint8_t num_cmd_packets;
|
||||
// uint8_t total_num_cmd_packets;
|
||||
uint8_t total_num_acl_packets;
|
||||
uint16_t acl_data_packet_length;
|
||||
|
||||
// usable packet types given acl_data_packet_length and HCI_ACL_BUFFER_SIZE
|
||||
uint16_t packet_types;
|
||||
|
||||
/* callback to L2CAP layer */
|
||||
void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size);
|
||||
|
||||
/* remote device db */
|
||||
remote_device_db_t const*remote_device_db;
|
||||
|
||||
/* hci state machine */
|
||||
HCI_STATE state;
|
||||
uint8_t substate;
|
||||
uint8_t cmds_ready;
|
||||
|
||||
uint8_t discoverable;
|
||||
uint8_t connectable;
|
||||
|
||||
/* buffer for scan enable cmd - 0xff no change */
|
||||
uint8_t new_scan_enable_value;
|
||||
|
||||
// buffer for single connection decline
|
||||
uint8_t decline_reason;
|
||||
bd_addr_t decline_addr;
|
||||
|
||||
} hci_stack_t;
|
||||
|
||||
// create and send hci command packets based on a template and a list of parameters
|
||||
uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...);
|
||||
uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr);
|
||||
|
||||
// set up HCI
|
||||
void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db);
|
||||
void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
|
||||
void hci_close(void);
|
||||
|
||||
// power and inquriy scan control
|
||||
int hci_power_control(HCI_POWER_MODE mode);
|
||||
void hci_discoverable_control(uint8_t enable);
|
||||
void hci_connectable_control(uint8_t enable);
|
||||
|
||||
/**
|
||||
* run the hci control loop once
|
||||
*/
|
||||
void hci_run(void);
|
||||
|
||||
// create and send hci command packets based on a template and a list of parameters
|
||||
int hci_send_cmd(const hci_cmd_t *cmd, ...);
|
||||
|
||||
// send complete CMD packet
|
||||
int hci_send_cmd_packet(uint8_t *packet, int size);
|
||||
|
||||
// send ACL packet
|
||||
int hci_send_acl_packet(uint8_t *packet, int size);
|
||||
|
||||
// non-blocking UART driver needs
|
||||
int hci_can_send_packet_now(uint8_t packet_type);
|
||||
|
||||
hci_connection_t * connection_for_handle(hci_con_handle_t con_handle);
|
||||
uint8_t hci_number_outgoing_packets(hci_con_handle_t handle);
|
||||
uint8_t hci_number_free_acl_slots(void);
|
||||
int hci_authentication_active_for_handle(hci_con_handle_t handle);
|
||||
void hci_drop_link_key_for_bd_addr(bd_addr_t *addr);
|
||||
uint16_t hci_max_acl_data_packet_length(void);
|
||||
uint16_t hci_usable_acl_packet_types(void);
|
||||
uint8_t* hci_get_outgoing_acl_packet_buffer(void);
|
||||
|
||||
//
|
||||
void hci_emit_state(void);
|
||||
void hci_emit_connection_complete(hci_connection_t *conn, uint8_t status);
|
||||
void hci_emit_l2cap_check_timeout(hci_connection_t *conn);
|
||||
void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason);
|
||||
void hci_emit_nr_connections_changed(void);
|
||||
void hci_emit_hci_open_failed(void);
|
||||
void hci_emit_btstack_version(void);
|
||||
void hci_emit_system_bluetooth_enabled(uint8_t enabled);
|
||||
void hci_emit_remote_name_cached(bd_addr_t *addr, device_name_t *name);
|
||||
void hci_emit_discoverable_enabled(uint8_t enabled);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
679
ios/RetroArch/input/BTStack/btstack/hci_cmds.c
Normal file
679
ios/RetroArch/input/BTStack/btstack/hci_cmds.c
Normal file
@ -0,0 +1,679 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_cmds.c
|
||||
*
|
||||
* Created by Matthias Ringwald on 7/23/09.
|
||||
*/
|
||||
|
||||
#include <btstack/hci_cmds.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <btstack/sdp_util.h>
|
||||
#include "config.h"
|
||||
#include "hci.h"
|
||||
|
||||
// calculate combined ogf/ocf value
|
||||
#define OPCODE(ogf, ocf) (ocf | ogf << 10)
|
||||
|
||||
/**
|
||||
* construct HCI Command based on template
|
||||
*
|
||||
* Format:
|
||||
* 1,2,3,4: one to four byte value
|
||||
* H: HCI connection handle
|
||||
* B: Bluetooth Baseband Address (BD_ADDR)
|
||||
* E: Extended Inquiry Result
|
||||
* N: Name up to 248 chars, \0 terminated
|
||||
* P: 16 byte Pairing code
|
||||
* S: Service Record (Data Element Sequence)
|
||||
*/
|
||||
uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr){
|
||||
|
||||
hci_cmd_buffer[0] = cmd->opcode & 0xff;
|
||||
hci_cmd_buffer[1] = cmd->opcode >> 8;
|
||||
int pos = 3;
|
||||
|
||||
const char *format = cmd->format;
|
||||
uint16_t word;
|
||||
uint32_t longword;
|
||||
uint8_t * ptr;
|
||||
while (*format) {
|
||||
switch(*format) {
|
||||
case '1': // 8 bit value
|
||||
case '2': // 16 bit value
|
||||
case 'H': // hci_handle
|
||||
word = va_arg(argptr, int); // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
|
||||
hci_cmd_buffer[pos++] = word & 0xff;
|
||||
if (*format == '2') {
|
||||
hci_cmd_buffer[pos++] = word >> 8;
|
||||
} else if (*format == 'H') {
|
||||
// TODO implement opaque client connection handles
|
||||
// pass module handle for now
|
||||
hci_cmd_buffer[pos++] = word >> 8;
|
||||
}
|
||||
break;
|
||||
case '3':
|
||||
case '4':
|
||||
longword = va_arg(argptr, uint32_t);
|
||||
// longword = va_arg(argptr, int);
|
||||
hci_cmd_buffer[pos++] = longword;
|
||||
hci_cmd_buffer[pos++] = longword >> 8;
|
||||
hci_cmd_buffer[pos++] = longword >> 16;
|
||||
if (*format == '4'){
|
||||
hci_cmd_buffer[pos++] = longword >> 24;
|
||||
}
|
||||
break;
|
||||
case 'B': // bt-addr
|
||||
ptr = va_arg(argptr, uint8_t *);
|
||||
hci_cmd_buffer[pos++] = ptr[5];
|
||||
hci_cmd_buffer[pos++] = ptr[4];
|
||||
hci_cmd_buffer[pos++] = ptr[3];
|
||||
hci_cmd_buffer[pos++] = ptr[2];
|
||||
hci_cmd_buffer[pos++] = ptr[1];
|
||||
hci_cmd_buffer[pos++] = ptr[0];
|
||||
break;
|
||||
case 'E': // Extended Inquiry Information 240 octets
|
||||
ptr = va_arg(argptr, uint8_t *);
|
||||
memcpy(&hci_cmd_buffer[pos], ptr, 240);
|
||||
pos += 240;
|
||||
break;
|
||||
case 'N': { // UTF-8 string, null terminated
|
||||
ptr = va_arg(argptr, uint8_t *);
|
||||
uint16_t len = strlen((const char*) ptr);
|
||||
if (len > 248) {
|
||||
len = 248;
|
||||
}
|
||||
memcpy(&hci_cmd_buffer[pos], ptr, len);
|
||||
if (len < 248) {
|
||||
// fill remaining space with zeroes
|
||||
memset(&hci_cmd_buffer[pos+len], 0, 248-len);
|
||||
}
|
||||
pos += 248;
|
||||
break;
|
||||
}
|
||||
case 'P': // 16 byte PIN code or link key
|
||||
ptr = va_arg(argptr, uint8_t *);
|
||||
memcpy(&hci_cmd_buffer[pos], ptr, 16);
|
||||
pos += 16;
|
||||
break;
|
||||
#ifdef HAVE_BLE
|
||||
case 'A': // 31 bytes advertising data
|
||||
ptr = va_arg(argptr, uint8_t *);
|
||||
memcpy(&hci_cmd_buffer[pos], ptr, 31);
|
||||
pos += 31;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_SDP
|
||||
case 'S': { // Service Record (Data Element Sequence)
|
||||
ptr = va_arg(argptr, uint8_t *);
|
||||
uint16_t len = de_get_len(ptr);
|
||||
memcpy(&hci_cmd_buffer[pos], ptr, len);
|
||||
pos += len;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
format++;
|
||||
};
|
||||
hci_cmd_buffer[2] = pos - 3;
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* construct HCI Command based on template
|
||||
*
|
||||
* mainly calls hci_create_cmd_internal
|
||||
*/
|
||||
uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...){
|
||||
va_list argptr;
|
||||
va_start(argptr, cmd);
|
||||
uint16_t len = hci_create_cmd_internal(hci_cmd_buffer, cmd, argptr);
|
||||
va_end(argptr);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Link Control Commands
|
||||
*/
|
||||
const hci_cmd_t hci_inquiry = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x01), "311"
|
||||
// LAP, Inquiry length, Num_responses
|
||||
};
|
||||
const hci_cmd_t hci_inquiry_cancel = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x02), ""
|
||||
// no params
|
||||
};
|
||||
const hci_cmd_t hci_create_connection = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x05), "B21121"
|
||||
// BD_ADDR, Packet_Type, Page_Scan_Repetition_Mode, Reserved, Clock_Offset, Allow_Role_Switch
|
||||
};
|
||||
const hci_cmd_t hci_disconnect = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x06), "H1"
|
||||
// Handle, Reason: 0x05, 0x13-0x15, 0x1a, 0x29
|
||||
// see Errors Codes in BT Spec Part D
|
||||
};
|
||||
const hci_cmd_t hci_create_connection_cancel = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x08), "B"
|
||||
// BD_ADDR
|
||||
};
|
||||
const hci_cmd_t hci_accept_connection_request = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x09), "B1"
|
||||
// BD_ADDR, Role: become master, stay slave
|
||||
};
|
||||
const hci_cmd_t hci_reject_connection_request = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x0a), "B1"
|
||||
// BD_ADDR, reason e.g. CONNECTION REJECTED DUE TO LIMITED RESOURCES (0x0d)
|
||||
};
|
||||
const hci_cmd_t hci_link_key_request_reply = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x0b), "BP"
|
||||
// BD_ADDR, LINK_KEY
|
||||
};
|
||||
const hci_cmd_t hci_link_key_request_negative_reply = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x0c), "B"
|
||||
// BD_ADDR
|
||||
};
|
||||
const hci_cmd_t hci_pin_code_request_reply = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x0d), "B1P"
|
||||
// BD_ADDR, pin length, PIN: c-string
|
||||
};
|
||||
const hci_cmd_t hci_pin_code_request_negative_reply = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x0e), "B"
|
||||
// BD_ADDR
|
||||
};
|
||||
const hci_cmd_t hci_authentication_requested = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x11), "H"
|
||||
// Handle
|
||||
};
|
||||
const hci_cmd_t hci_set_connection_encryption = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x13), "H1"
|
||||
// Handle, Encryption_Enable
|
||||
};
|
||||
const hci_cmd_t hci_change_connection_link_key = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x15), "H"
|
||||
// Handle
|
||||
};
|
||||
const hci_cmd_t hci_remote_name_request = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x19), "B112"
|
||||
// BD_ADDR, Page_Scan_Repetition_Mode, Reserved, Clock_Offset
|
||||
};
|
||||
const hci_cmd_t hci_remote_name_request_cancel = {
|
||||
OPCODE(OGF_LINK_CONTROL, 0x1A), "B"
|
||||
// BD_ADDR
|
||||
};
|
||||
|
||||
/**
|
||||
* Link Policy Commands
|
||||
*/
|
||||
const hci_cmd_t hci_sniff_mode = {
|
||||
OPCODE(OGF_LINK_POLICY, 0x03), "H2222"
|
||||
// handle, Sniff_Max_Interval, Sniff_Min_Interval, Sniff_Attempt, Sniff_Timeout:
|
||||
};
|
||||
const hci_cmd_t hci_qos_setup = {
|
||||
OPCODE(OGF_LINK_POLICY, 0x07), "H114444"
|
||||
// handle, flags, service_type, token rate (bytes/s), peak bandwith (bytes/s),
|
||||
// latency (us), delay_variation (us)
|
||||
};
|
||||
const hci_cmd_t hci_role_discovery = {
|
||||
OPCODE(OGF_LINK_POLICY, 0x09), "H"
|
||||
// handle
|
||||
};
|
||||
const hci_cmd_t hci_switch_role_command= {
|
||||
OPCODE(OGF_LINK_POLICY, 0x0b), "B1"
|
||||
// BD_ADDR, role: {0=master,1=slave}
|
||||
};
|
||||
const hci_cmd_t hci_read_link_policy_settings = {
|
||||
OPCODE(OGF_LINK_POLICY, 0x0c), "H"
|
||||
// handle
|
||||
};
|
||||
const hci_cmd_t hci_write_link_policy_settings = {
|
||||
OPCODE(OGF_LINK_POLICY, 0x0d), "H2"
|
||||
// handle, settings
|
||||
};
|
||||
|
||||
/**
|
||||
* Controller & Baseband Commands
|
||||
*/
|
||||
const hci_cmd_t hci_set_event_mask = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x01), "44"
|
||||
// event_mask lower 4 octets, higher 4 bytes
|
||||
};
|
||||
const hci_cmd_t hci_reset = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x03), ""
|
||||
// no params
|
||||
};
|
||||
const hci_cmd_t hci_delete_stored_link_key = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x12), "B1"
|
||||
// BD_ADDR, Delete_All_Flag
|
||||
};
|
||||
const hci_cmd_t hci_write_local_name = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x13), "N"
|
||||
// Local name (UTF-8, Null Terminated, max 248 octets)
|
||||
};
|
||||
const hci_cmd_t hci_write_page_timeout = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x18), "2"
|
||||
// Page_Timeout * 0.625 ms
|
||||
};
|
||||
const hci_cmd_t hci_write_scan_enable = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x1A), "1"
|
||||
// Scan_enable: no, inq, page, inq+page
|
||||
};
|
||||
const hci_cmd_t hci_write_authentication_enable = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x20), "1"
|
||||
// Authentication_Enable
|
||||
};
|
||||
const hci_cmd_t hci_write_class_of_device = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x24), "3"
|
||||
// Class of Device
|
||||
};
|
||||
const hci_cmd_t hci_read_num_broadcast_retransmissions = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x29), ""
|
||||
};
|
||||
const hci_cmd_t hci_write_num_broadcast_retransmissions = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x2a), "1"
|
||||
// Num broadcast retransmissions (e.g. 0 for a single broadcast)
|
||||
};
|
||||
const hci_cmd_t hci_host_buffer_size = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x33), "2122"
|
||||
// Host_ACL_Data_Packet_Length:, Host_Synchronous_Data_Packet_Length:, Host_Total_Num_ACL_Data_Packets:, Host_Total_Num_Synchronous_Data_Packets:
|
||||
};
|
||||
const hci_cmd_t hci_read_link_supervision_timeout = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x36), "H"
|
||||
// handle
|
||||
};
|
||||
const hci_cmd_t hci_write_link_supervision_timeout = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x37), "H2"
|
||||
// handle, Range for N: 0x0001 Ð 0xFFFF Time (Range: 0.625ms Ð 40.9 sec)
|
||||
};
|
||||
const hci_cmd_t hci_write_inquiry_mode = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x45), "1"
|
||||
// Inquiry mode: 0x00 = standard, 0x01 = with RSSI, 0x02 = extended
|
||||
};
|
||||
const hci_cmd_t hci_write_extended_inquiry_response = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x52), "1E"
|
||||
// FEC_Required, Exstended Inquiry Response
|
||||
};
|
||||
const hci_cmd_t hci_write_simple_pairing_mode = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x56), "1"
|
||||
// mode: 0 = off, 1 = on
|
||||
};
|
||||
const hci_cmd_t hci_read_le_host_supported = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x6c), ""
|
||||
// params: none
|
||||
// return: status, le supported host, simultaneous le host
|
||||
};
|
||||
const hci_cmd_t hci_write_le_host_supported = {
|
||||
OPCODE(OGF_CONTROLLER_BASEBAND, 0x6d), "11"
|
||||
// param: le supported host, simultaneous le host
|
||||
// return: status
|
||||
};
|
||||
|
||||
/**
|
||||
* Informational Parameters
|
||||
*/
|
||||
const hci_cmd_t hci_read_local_supported_features = {
|
||||
OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x03), ""
|
||||
// no params
|
||||
};
|
||||
const hci_cmd_t hci_read_buffer_size = {
|
||||
OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x05), ""
|
||||
// no params
|
||||
};
|
||||
const hci_cmd_t hci_read_bd_addr = {
|
||||
OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x09), ""
|
||||
// no params
|
||||
};
|
||||
|
||||
#ifdef HAVE_BLE
|
||||
/**
|
||||
* Low Energy Commands
|
||||
*/
|
||||
const hci_cmd_t hci_le_set_event_mask = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x01), "44"
|
||||
// params: event_mask lower 4 octets, higher 4 bytes
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_read_buffer_size = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x02), ""
|
||||
// params: none
|
||||
// return: status, le acl data packet len (16), total num le acl data packets(8)
|
||||
};
|
||||
const hci_cmd_t hci_le_read_supported_features = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x03), ""
|
||||
// params: none
|
||||
// return: LE_Features See [Vol 6] Part B, Section 4.6
|
||||
};
|
||||
const hci_cmd_t hci_le_set_random_address = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x05), "B"
|
||||
// params: random device address
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_set_advertising_parameters = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x06), "22111B11"
|
||||
// param: min advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec
|
||||
// param: max advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec
|
||||
// param: advertising type (enum from 0): ADV_IND, ADC_DIRECT_IND, ADV_SCAN_IND, ADV_NONCONN_IND
|
||||
// param: own address type (enum from 0): public device address, random device address
|
||||
// param: direct address type (enum from 0): public device address, random device address
|
||||
// param: direct address - public or random address of device to be connecteed
|
||||
// param: advertising channel map (flags): chan_37(1), chan_38(2), chan_39(4)
|
||||
// param: advertising filter policy (enum from 0): scan any conn any, scan whitelist, con any, scan any conn whitelist, scan whitelist, con whitelist
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_read_advertising_channel_tx_power = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x07), ""
|
||||
// params: none
|
||||
// return: status, level [-20,10] signed int (8), units dBm
|
||||
};
|
||||
const hci_cmd_t hci_le_set_advertising_data= {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x08), "1A"
|
||||
// param: advertising data len
|
||||
// param: advertising data (31 bytes)
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_set_scan_response_data= {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x09), "1A"
|
||||
// param: scan response data len
|
||||
// param: scan response data (31 bytes)
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_set_advertise_enable = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x0a), "1"
|
||||
// params: avertise enable: off (0), on (1)
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_set_scan_parameters = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x0b), "12211"
|
||||
// param: le scan type: passive (0), active (1)
|
||||
// param: le scan interval [0x0004,0x4000], unit: 0.625 msec
|
||||
// param: le scan window [0x0004,0x4000], unit: 0.625 msec
|
||||
// param: own address type: public (0), random (1)
|
||||
// param: scanning filter policy: any (0), only whitelist (1)
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_set_scan_enable = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x0c), "11"
|
||||
// param: le scan enable: disabled (0), enabled (1)
|
||||
// param: filter duplices: disabled (0), enabled (1)
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_create_connection= {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x0d), "2211B1222222"
|
||||
// param: le scan interval, [0x0004, 0x4000], unit: 0.625 msec
|
||||
// param: le scan window, [0x0004, 0x4000], unit: 0.625 msec
|
||||
// param: initiator filter policy: peer address type + peer address (0), whitelist (1)
|
||||
// param: peer address type: public (0), random (1)
|
||||
// param: peer address
|
||||
// param: own address type: public (0), random (1)
|
||||
// param: conn interval min, [0x0006, 0x0c80], unit: 1.25 msec
|
||||
// param: conn interval max, [0x0006, 0x0c80], unit: 1.25 msec
|
||||
// param: conn latency, number of connection events [0x0000, 0x01f4]
|
||||
// param: supervision timeout, [0x000a, 0x0c80], unit: 10 msec
|
||||
// param: minimum CE length, [0x0000, 0xffff], unit: 0.625 msec
|
||||
// return: none -> le create connection complete event
|
||||
};
|
||||
const hci_cmd_t hci_le_create_connection_cancel = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x0e), ""
|
||||
// params: none
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_read_white_list_size = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x0f), ""
|
||||
// params: none
|
||||
// return: status, number of entries in controller whitelist
|
||||
};
|
||||
const hci_cmd_t hci_le_clear_white_list = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x10), ""
|
||||
// params: none
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_add_device_to_whitelist = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x11), "1B"
|
||||
// param: address type: public (0), random (1)
|
||||
// param: address
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_remove_device_from_whitelist = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x12), "1B"
|
||||
// param: address type: public (0), random (1)
|
||||
// param: address
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_connection_update = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x13), "H222222"
|
||||
// param: conn handle
|
||||
// param: conn interval min, [0x0006,0x0c80], unit: 1.25 msec
|
||||
// param: conn interval max, [0x0006,0x0c80], unit: 1.25 msec
|
||||
// param: conn latency, [0x0000,0x03e8], number of connection events
|
||||
// param: supervision timeout, [0x000a,0x0c80], unit: 10 msec
|
||||
// param: minimum CE length, [0x0000,0xffff], unit: 0.625 msec
|
||||
// param: maximum CE length, [0x0000,0xffff], unit: 0.625 msec
|
||||
// return: none -> le connection update complete event
|
||||
};
|
||||
const hci_cmd_t hci_le_set_host_channel_classification = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x14), "41"
|
||||
// param: channel map 37 bit, split into first 32 and higher 5 bits
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_read_channel_map = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x15), "H"
|
||||
// params: connection handle
|
||||
// return: status, connection handle, channel map (5 bytes, 37 used)
|
||||
};
|
||||
const hci_cmd_t hci_le_read_remote_used_features = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x16), "H"
|
||||
// params: connection handle
|
||||
// return: none -> le read remote used features complete event
|
||||
};
|
||||
const hci_cmd_t hci_le_encrypt = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x17), "PP"
|
||||
// param: key (128) for AES-128
|
||||
// param: plain text (128)
|
||||
// return: status, encrypted data (128)
|
||||
};
|
||||
const hci_cmd_t hci_le_rand = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x18), ""
|
||||
// params: none
|
||||
// return: status, random number (64)
|
||||
};
|
||||
const hci_cmd_t hci_le_start_encryption = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x19), "H442P"
|
||||
// param: connection handle
|
||||
// param: 64 bit random number lower 32 bit
|
||||
// param: 64 bit random number higher 32 bit
|
||||
// param: encryption diversifier (16)
|
||||
// param: long term key (128)
|
||||
// return: none -> encryption changed or encryption key refresh complete event
|
||||
};
|
||||
const hci_cmd_t hci_le_long_term_key_request_reply = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x1a), "HP"
|
||||
// param: connection handle
|
||||
// param: long term key (128)
|
||||
// return: status, connection handle
|
||||
};
|
||||
const hci_cmd_t hci_le_long_term_key_negative_reply = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x1b), "H"
|
||||
// param: connection handle
|
||||
// return: status, connection handle
|
||||
};
|
||||
const hci_cmd_t hci_le_read_supported_states = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x1c), "H"
|
||||
// param: none
|
||||
// return: status, LE states (64)
|
||||
};
|
||||
const hci_cmd_t hci_le_receiver_test = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x1d), "1"
|
||||
// param: rx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_transmitter_test = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x1e), "111"
|
||||
// param: tx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2
|
||||
// param: lengh of test payload [0x00,0x25]
|
||||
// param: packet payload [0,7] different patterns
|
||||
// return: status
|
||||
};
|
||||
const hci_cmd_t hci_le_test_end = {
|
||||
OPCODE(OGF_LE_CONTROLLER, 0x1f), "1"
|
||||
// params: none
|
||||
// return: status, number of packets (8)
|
||||
};
|
||||
#endif
|
||||
|
||||
// BTstack commands
|
||||
const hci_cmd_t btstack_get_state = {
|
||||
OPCODE(OGF_BTSTACK, BTSTACK_GET_STATE), ""
|
||||
// no params ->
|
||||
};
|
||||
|
||||
const hci_cmd_t btstack_set_power_mode = {
|
||||
OPCODE(OGF_BTSTACK, BTSTACK_SET_POWER_MODE), "1"
|
||||
// mode: 0 = off, 1 = on
|
||||
};
|
||||
|
||||
const hci_cmd_t btstack_set_acl_capture_mode = {
|
||||
OPCODE(OGF_BTSTACK, BTSTACK_SET_ACL_CAPTURE_MODE), "1"
|
||||
// mode: 0 = off, 1 = on
|
||||
};
|
||||
|
||||
const hci_cmd_t btstack_get_version = {
|
||||
OPCODE(OGF_BTSTACK, BTSTACK_GET_VERSION), ""
|
||||
};
|
||||
|
||||
const hci_cmd_t btstack_get_system_bluetooth_enabled = {
|
||||
OPCODE(OGF_BTSTACK, BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED), ""
|
||||
};
|
||||
|
||||
const hci_cmd_t btstack_set_system_bluetooth_enabled = {
|
||||
OPCODE(OGF_BTSTACK, BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED), "1"
|
||||
};
|
||||
|
||||
const hci_cmd_t btstack_set_discoverable = {
|
||||
OPCODE(OGF_BTSTACK, BTSTACK_SET_DISCOVERABLE), "1"
|
||||
};
|
||||
|
||||
const hci_cmd_t btstack_set_bluetooth_enabled = {
|
||||
// only used by btstack config
|
||||
OPCODE(OGF_BTSTACK, BTSTACK_SET_BLUETOOTH_ENABLED), "1"
|
||||
};
|
||||
|
||||
const hci_cmd_t l2cap_create_channel = {
|
||||
OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL), "B2"
|
||||
// @param bd_addr(48), psm (16)
|
||||
};
|
||||
const hci_cmd_t l2cap_create_channel_mtu = {
|
||||
OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL_MTU), "B22"
|
||||
// @param bd_addr(48), psm (16), mtu (16)
|
||||
};
|
||||
const hci_cmd_t l2cap_disconnect = {
|
||||
OPCODE(OGF_BTSTACK, L2CAP_DISCONNECT), "21"
|
||||
// @param channel(16), reason(8)
|
||||
};
|
||||
const hci_cmd_t l2cap_register_service = {
|
||||
OPCODE(OGF_BTSTACK, L2CAP_REGISTER_SERVICE), "22"
|
||||
// @param psm (16), mtu (16)
|
||||
};
|
||||
const hci_cmd_t l2cap_unregister_service = {
|
||||
OPCODE(OGF_BTSTACK, L2CAP_UNREGISTER_SERVICE), "2"
|
||||
// @param psm (16)
|
||||
};
|
||||
const hci_cmd_t l2cap_accept_connection = {
|
||||
OPCODE(OGF_BTSTACK, L2CAP_ACCEPT_CONNECTION), "2"
|
||||
// @param source cid (16)
|
||||
};
|
||||
const hci_cmd_t l2cap_decline_connection = {
|
||||
OPCODE(OGF_BTSTACK, L2CAP_DECLINE_CONNECTION), "21"
|
||||
// @param source cid (16), reason(8)
|
||||
};
|
||||
const hci_cmd_t sdp_register_service_record = {
|
||||
OPCODE(OGF_BTSTACK, SDP_REGISTER_SERVICE_RECORD), "S"
|
||||
// @param service record handle (DES)
|
||||
};
|
||||
const hci_cmd_t sdp_unregister_service_record = {
|
||||
OPCODE(OGF_BTSTACK, SDP_UNREGISTER_SERVICE_RECORD), "4"
|
||||
// @param service record handle (32)
|
||||
};
|
||||
|
||||
// create rfcomm channel: @param bd_addr(48), channel (8)
|
||||
const hci_cmd_t rfcomm_create_channel = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL), "B1"
|
||||
};
|
||||
// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8)
|
||||
const hci_cmd_t rfcomm_create_channel_with_initial_credits = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL_WITH_CREDITS), "B121"
|
||||
};
|
||||
// grant credits: @param rfcomm_cid(16), credits (8)
|
||||
const hci_cmd_t rfcomm_grants_credits= {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_GRANT_CREDITS), "21"
|
||||
};
|
||||
// disconnect rfcomm disconnect, @param rfcomm_cid(16), reason(8)
|
||||
const hci_cmd_t rfcomm_disconnect = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_DISCONNECT), "21"
|
||||
};
|
||||
|
||||
// register rfcomm service: @param channel(8), mtu (16)
|
||||
const hci_cmd_t rfcomm_register_service = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE), "12"
|
||||
};
|
||||
// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
|
||||
const hci_cmd_t rfcomm_register_service_with_initial_credits = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE_WITH_CREDITS), "121"
|
||||
};
|
||||
|
||||
// unregister rfcomm service, @param service_channel(16)
|
||||
const hci_cmd_t rfcomm_unregister_service = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_UNREGISTER_SERVICE), "2"
|
||||
};
|
||||
// accept connection @param source cid (16)
|
||||
const hci_cmd_t rfcomm_accept_connection = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_ACCEPT_CONNECTION), "2"
|
||||
};
|
||||
// decline connection @param source cid (16)
|
||||
const hci_cmd_t rfcomm_decline_connection = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_DECLINE_CONNECTION), "21"
|
||||
};
|
||||
// request persisten rfcomm channel number for named service
|
||||
const hci_cmd_t rfcomm_persistent_channel_for_service = {
|
||||
OPCODE(OGF_BTSTACK, RFCOMM_PERSISTENT_CHANNEL), "N"
|
||||
};
|
||||
|
||||
// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
|
||||
extern const hci_cmd_t rfcomm_register_service_with_initial_credits;
|
239
ios/RetroArch/input/BTStack/btstack/hci_dump.c
Normal file
239
ios/RetroArch/input/BTStack/btstack/hci_dump.c
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_dump.c
|
||||
*
|
||||
* Dump HCI trace in various formats:
|
||||
*
|
||||
* - BlueZ's hcidump format
|
||||
* - Apple's PacketLogger
|
||||
* - stdout hexdump
|
||||
*
|
||||
* Created by Matthias Ringwald on 5/26/09.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "hci_dump.h"
|
||||
#include "hci.h"
|
||||
#include "hci_transport.h"
|
||||
#include <btstack/hci_cmds.h>
|
||||
|
||||
#ifndef EMBEDDED
|
||||
#include <fcntl.h> // open
|
||||
#include <arpa/inet.h> // hton..
|
||||
#include <unistd.h> // write
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h> // for timestamps
|
||||
#include <sys/stat.h> // for mode flags
|
||||
#include <stdarg.h> // for va_list
|
||||
#endif
|
||||
|
||||
// BLUEZ hcidump
|
||||
typedef struct {
|
||||
uint16_t len;
|
||||
uint8_t in;
|
||||
uint8_t pad;
|
||||
uint32_t ts_sec;
|
||||
uint32_t ts_usec;
|
||||
uint8_t packet_type;
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((packed))
|
||||
#endif
|
||||
hcidump_hdr;
|
||||
|
||||
// APPLE PacketLogger
|
||||
typedef struct {
|
||||
uint32_t len;
|
||||
uint32_t ts_sec;
|
||||
uint32_t ts_usec;
|
||||
uint8_t type; // 0xfc for note
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((packed))
|
||||
#endif
|
||||
pktlog_hdr;
|
||||
|
||||
#ifndef EMBEDDED
|
||||
static int dump_file = -1;
|
||||
static int dump_format;
|
||||
static hcidump_hdr header_bluez;
|
||||
static pktlog_hdr header_packetlogger;
|
||||
static char time_string[40];
|
||||
static int max_nr_packets = -1;
|
||||
static int nr_packets = 0;
|
||||
static char log_message_buffer[256];
|
||||
#endif
|
||||
|
||||
void hci_dump_open(char *filename, hci_dump_format_t format){
|
||||
#ifndef EMBEDDED
|
||||
dump_format = format;
|
||||
if (dump_format == HCI_DUMP_STDOUT) {
|
||||
dump_file = fileno(stdout);
|
||||
} else {
|
||||
dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef EMBEDDED
|
||||
void hci_dump_set_max_packets(int packets){
|
||||
max_nr_packets = packets;
|
||||
}
|
||||
#endif
|
||||
|
||||
void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
|
||||
#ifndef EMBEDDED
|
||||
|
||||
if (dump_file < 0) return; // not activated yet
|
||||
|
||||
// don't grow bigger than max_nr_packets
|
||||
if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
|
||||
if (nr_packets >= max_nr_packets){
|
||||
lseek(dump_file, 0, SEEK_SET);
|
||||
ftruncate(dump_file, 0);
|
||||
nr_packets = 0;
|
||||
}
|
||||
nr_packets++;
|
||||
}
|
||||
|
||||
// get time
|
||||
struct timeval curr_time;
|
||||
struct tm* ptm;
|
||||
gettimeofday(&curr_time, NULL);
|
||||
|
||||
switch (dump_format){
|
||||
case HCI_DUMP_STDOUT: {
|
||||
/* Obtain the time of day, and convert it to a tm struct. */
|
||||
ptm = localtime (&curr_time.tv_sec);
|
||||
/* Format the date and time, down to a single second. */
|
||||
strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
|
||||
/* Compute milliseconds from microseconds. */
|
||||
uint16_t milliseconds = curr_time.tv_usec / 1000;
|
||||
/* Print the formatted time, in seconds, followed by a decimal point
|
||||
and the milliseconds. */
|
||||
printf ("%s.%03u] ", time_string, milliseconds);
|
||||
switch (packet_type){
|
||||
case HCI_COMMAND_DATA_PACKET:
|
||||
printf("CMD => ");
|
||||
break;
|
||||
case HCI_EVENT_PACKET:
|
||||
printf("EVT <= ");
|
||||
break;
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
if (in) {
|
||||
printf("ACL <= ");
|
||||
} else {
|
||||
printf("ACL => ");
|
||||
}
|
||||
break;
|
||||
case LOG_MESSAGE_PACKET:
|
||||
// assume buffer is big enough
|
||||
packet[len] = 0;
|
||||
printf("LOG -- %s\n", (char*) packet);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
hexdump(packet, len);
|
||||
break;
|
||||
}
|
||||
|
||||
case HCI_DUMP_BLUEZ:
|
||||
bt_store_16( (uint8_t *) &header_bluez.len, 0, 1 + len);
|
||||
header_bluez.in = in;
|
||||
header_bluez.pad = 0;
|
||||
bt_store_32( (uint8_t *) &header_bluez.ts_sec, 0, curr_time.tv_sec);
|
||||
bt_store_32( (uint8_t *) &header_bluez.ts_usec, 0, curr_time.tv_usec);
|
||||
header_bluez.packet_type = packet_type;
|
||||
write (dump_file, &header_bluez, sizeof(hcidump_hdr) );
|
||||
write (dump_file, packet, len );
|
||||
break;
|
||||
|
||||
case HCI_DUMP_PACKETLOGGER:
|
||||
header_packetlogger.len = htonl( sizeof(pktlog_hdr) - 4 + len);
|
||||
header_packetlogger.ts_sec = htonl(curr_time.tv_sec);
|
||||
header_packetlogger.ts_usec = htonl(curr_time.tv_usec);
|
||||
switch (packet_type){
|
||||
case HCI_COMMAND_DATA_PACKET:
|
||||
header_packetlogger.type = 0x00;
|
||||
break;
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
if (in) {
|
||||
header_packetlogger.type = 0x03;
|
||||
} else {
|
||||
header_packetlogger.type = 0x02;
|
||||
}
|
||||
break;
|
||||
case HCI_EVENT_PACKET:
|
||||
header_packetlogger.type = 0x01;
|
||||
break;
|
||||
case LOG_MESSAGE_PACKET:
|
||||
header_packetlogger.type = 0xfc;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
write (dump_file, &header_packetlogger, sizeof(pktlog_hdr) );
|
||||
write (dump_file, packet, len );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void hci_dump_log(const char * format, ...){
|
||||
#ifndef EMBEDDED
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
|
||||
hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len);
|
||||
va_end(argptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hci_dump_close(){
|
||||
#ifndef EMBEDDED
|
||||
close(dump_file);
|
||||
dump_file = -1;
|
||||
#endif
|
||||
}
|
||||
|
59
ios/RetroArch/input/BTStack/btstack/hci_dump.h
Normal file
59
ios/RetroArch/input/BTStack/btstack/hci_dump.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_dump.h
|
||||
*
|
||||
* Dump HCI trace as BlueZ's hcidump format, Apple's PacketLogger, or stdout
|
||||
*
|
||||
* Created by Matthias Ringwald on 5/26/09.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
HCI_DUMP_BLUEZ = 0,
|
||||
HCI_DUMP_PACKETLOGGER,
|
||||
HCI_DUMP_STDOUT
|
||||
} hci_dump_format_t;
|
||||
|
||||
void hci_dump_open(char *filename, hci_dump_format_t format);
|
||||
void hci_dump_set_max_packets(int packets); // -1 for unlimited
|
||||
void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len);
|
||||
void hci_dump_log(const char * format, ...);
|
||||
void hci_dump_close(void);
|
88
ios/RetroArch/input/BTStack/btstack/hci_transport.h
Normal file
88
ios/RetroArch/input/BTStack/btstack/hci_transport.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_transport.h
|
||||
*
|
||||
* HCI Transport API -- allows BT Daemon to use different transport protcols
|
||||
*
|
||||
* Created by Matthias Ringwald on 4/29/09.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <btstack/run_loop.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* HCI packet types */
|
||||
typedef struct {
|
||||
int (*open)(void *transport_config);
|
||||
int (*close)(void *transport_config);
|
||||
int (*send_packet)(uint8_t packet_type, uint8_t *packet, int size);
|
||||
void (*register_packet_handler)(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
|
||||
const char * (*get_transport_name)(void);
|
||||
// custom extension for UART transport implementations
|
||||
int (*set_baudrate)(uint32_t baudrate);
|
||||
// support async transport layers, e.g. IRQ driven without buffers
|
||||
int (*can_send_packet_now)(uint8_t packet_type);
|
||||
} hci_transport_t;
|
||||
|
||||
typedef struct {
|
||||
const char *device_name;
|
||||
uint32_t baudrate_init; // initial baud rate
|
||||
uint32_t baudrate_main; // = 0: same as initial baudrate
|
||||
int flowcontrol; //
|
||||
} hci_uart_config_t;
|
||||
|
||||
|
||||
// inline various hci_transport_X.h files
|
||||
extern hci_transport_t * hci_transport_h4_instance(void);
|
||||
extern hci_transport_t * hci_transport_h4_dma_instance(void);
|
||||
extern hci_transport_t * hci_transport_h4_iphone_instance(void);
|
||||
extern hci_transport_t * hci_transport_h5_instance(void);
|
||||
extern hci_transport_t * hci_transport_usb_instance(void);
|
||||
|
||||
// support for "enforece wake device" in h4 - used by iOS power management
|
||||
extern void hci_transport_h4_iphone_set_enforce_wake_device(char *path);
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
298
ios/RetroArch/input/BTStack/btstack/hci_transport_h4.c
Normal file
298
ios/RetroArch/input/BTStack/btstack/hci_transport_h4.c
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_h4_transport.c
|
||||
*
|
||||
* HCI Transport API implementation for basic H4 protocol over POSIX
|
||||
*
|
||||
* Created by Matthias Ringwald on 4/29/09.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <termios.h> /* POSIX terminal control definitions */
|
||||
#include <fcntl.h> /* File control definitions */
|
||||
#include <unistd.h> /* UNIX standard function definitions */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "hci.h"
|
||||
#include "hci_transport.h"
|
||||
#include "hci_dump.h"
|
||||
|
||||
static int h4_process(struct data_source *ds);
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
|
||||
static hci_uart_config_t *hci_uart_config;
|
||||
|
||||
typedef enum {
|
||||
H4_W4_PACKET_TYPE,
|
||||
H4_W4_EVENT_HEADER,
|
||||
H4_W4_ACL_HEADER,
|
||||
H4_W4_PAYLOAD,
|
||||
} H4_STATE;
|
||||
|
||||
typedef struct hci_transport_h4 {
|
||||
hci_transport_t transport;
|
||||
data_source_t *ds;
|
||||
int uart_fd; // different from ds->fd for HCI reader thread
|
||||
/* power management support, e.g. used by iOS */
|
||||
timer_source_t sleep_timer;
|
||||
} hci_transport_h4_t;
|
||||
|
||||
|
||||
// single instance
|
||||
static hci_transport_h4_t * hci_transport_h4 = NULL;
|
||||
|
||||
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
|
||||
|
||||
// packet reader state machine
|
||||
static H4_STATE h4_state;
|
||||
static int bytes_to_read;
|
||||
static int read_pos;
|
||||
|
||||
static uint8_t hci_packet[1+HCI_PACKET_BUFFER_SIZE]; // packet type + max(acl header + acl payload, event header + event data)
|
||||
|
||||
static int h4_open(void *transport_config){
|
||||
hci_uart_config = (hci_uart_config_t*) transport_config;
|
||||
struct termios toptions;
|
||||
int flags = O_RDWR | O_NOCTTY;
|
||||
int fd = open(hci_uart_config->device_name, flags);
|
||||
if (fd == -1) {
|
||||
perror("init_serialport: Unable to open port ");
|
||||
perror(hci_uart_config->device_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tcgetattr(fd, &toptions) < 0) {
|
||||
perror("init_serialport: Couldn't get term attributes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
speed_t brate = hci_uart_config->baudrate_init; // let you override switch below if needed
|
||||
switch(hci_uart_config->baudrate_init) {
|
||||
case 57600: brate=B57600; break;
|
||||
case 115200: brate=B115200; break;
|
||||
#ifdef B230400
|
||||
case 230400: brate=B230400; break;
|
||||
#endif
|
||||
#ifdef B460800
|
||||
case 460800: brate=B460800; break;
|
||||
#endif
|
||||
#ifdef B921600
|
||||
case 921600: brate=B921600; break;
|
||||
#endif
|
||||
}
|
||||
cfsetspeed(&toptions, brate);
|
||||
|
||||
// 8N1
|
||||
toptions.c_cflag &= ~PARENB;
|
||||
toptions.c_cflag &= ~CSTOPB;
|
||||
toptions.c_cflag &= ~CSIZE;
|
||||
toptions.c_cflag |= CS8;
|
||||
|
||||
if (hci_uart_config->flowcontrol) {
|
||||
// with flow control
|
||||
toptions.c_cflag |= CRTSCTS;
|
||||
} else {
|
||||
// no flow control
|
||||
toptions.c_cflag &= ~CRTSCTS;
|
||||
}
|
||||
|
||||
toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
|
||||
toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
|
||||
|
||||
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
|
||||
toptions.c_oflag &= ~OPOST; // make raw
|
||||
|
||||
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
|
||||
toptions.c_cc[VMIN] = 1;
|
||||
toptions.c_cc[VTIME] = 0;
|
||||
|
||||
if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
|
||||
perror("init_serialport: Couldn't set term attributes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// set up data_source
|
||||
hci_transport_h4->ds = malloc(sizeof(data_source_t));
|
||||
if (!hci_transport_h4->ds) return -1;
|
||||
hci_transport_h4->uart_fd = fd;
|
||||
hci_transport_h4->ds->fd = fd;
|
||||
hci_transport_h4->ds->process = h4_process;
|
||||
run_loop_add_data_source(hci_transport_h4->ds);
|
||||
|
||||
// init state machine
|
||||
bytes_to_read = 1;
|
||||
h4_state = H4_W4_PACKET_TYPE;
|
||||
read_pos = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h4_close(void *transport_config){
|
||||
// first remove run loop handler
|
||||
run_loop_remove_data_source(hci_transport_h4->ds);
|
||||
|
||||
// close device
|
||||
close(hci_transport_h4->ds->fd);
|
||||
|
||||
// free struct
|
||||
free(hci_transport_h4->ds);
|
||||
hci_transport_h4->ds = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h4_send_packet(uint8_t packet_type, uint8_t * packet, int size){
|
||||
if (hci_transport_h4->ds == NULL) return -1;
|
||||
if (hci_transport_h4->uart_fd == 0) return -1;
|
||||
|
||||
hci_dump_packet( (uint8_t) packet_type, 0, packet, size);
|
||||
char *data = (char*) packet;
|
||||
int bytes_written = write(hci_transport_h4->uart_fd, &packet_type, 1);
|
||||
while (bytes_written < 1) {
|
||||
usleep(5000);
|
||||
bytes_written = write(hci_transport_h4->uart_fd, &packet_type, 1);
|
||||
};
|
||||
while (size > 0) {
|
||||
int bytes_written = write(hci_transport_h4->uart_fd, data, size);
|
||||
if (bytes_written < 0) {
|
||||
usleep(5000);
|
||||
continue;
|
||||
}
|
||||
data += bytes_written;
|
||||
size -= bytes_written;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
||||
packet_handler = handler;
|
||||
}
|
||||
|
||||
static void h4_deliver_packet(void){
|
||||
if (read_pos < 3) return; // sanity check
|
||||
hci_dump_packet( hci_packet[0], 1, &hci_packet[1], read_pos-1);
|
||||
packet_handler(hci_packet[0], &hci_packet[1], read_pos-1);
|
||||
|
||||
h4_state = H4_W4_PACKET_TYPE;
|
||||
read_pos = 0;
|
||||
bytes_to_read = 1;
|
||||
}
|
||||
|
||||
static void h4_statemachine(void){
|
||||
switch (h4_state) {
|
||||
|
||||
case H4_W4_PACKET_TYPE:
|
||||
if (hci_packet[0] == HCI_EVENT_PACKET){
|
||||
bytes_to_read = HCI_EVENT_HEADER_SIZE;
|
||||
h4_state = H4_W4_EVENT_HEADER;
|
||||
} else if (hci_packet[0] == HCI_ACL_DATA_PACKET){
|
||||
bytes_to_read = HCI_ACL_HEADER_SIZE;
|
||||
h4_state = H4_W4_ACL_HEADER;
|
||||
} else {
|
||||
log_error("h4_process: invalid packet type 0x%02x\n", hci_packet[0]);
|
||||
read_pos = 0;
|
||||
bytes_to_read = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case H4_W4_EVENT_HEADER:
|
||||
bytes_to_read = hci_packet[2];
|
||||
h4_state = H4_W4_PAYLOAD;
|
||||
break;
|
||||
|
||||
case H4_W4_ACL_HEADER:
|
||||
bytes_to_read = READ_BT_16( hci_packet, 3);
|
||||
h4_state = H4_W4_PAYLOAD;
|
||||
break;
|
||||
|
||||
case H4_W4_PAYLOAD:
|
||||
h4_deliver_packet();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int h4_process(struct data_source *ds) {
|
||||
if (hci_transport_h4->uart_fd == 0) return -1;
|
||||
|
||||
int read_now = bytes_to_read;
|
||||
|
||||
// read up to bytes_to_read data in
|
||||
ssize_t bytes_read = read(hci_transport_h4->uart_fd, &hci_packet[read_pos], read_now);
|
||||
// printf("h4_process: bytes read %u\n", bytes_read);
|
||||
if (bytes_read < 0) {
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
// hexdump(&hci_packet[read_pos], bytes_read);
|
||||
|
||||
bytes_to_read -= bytes_read;
|
||||
read_pos += bytes_read;
|
||||
if (bytes_to_read > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
h4_statemachine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * h4_get_transport_name(void){
|
||||
return "H4";
|
||||
}
|
||||
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
|
||||
}
|
||||
|
||||
// get h4 singleton
|
||||
hci_transport_t * hci_transport_h4_instance() {
|
||||
if (hci_transport_h4 == NULL) {
|
||||
hci_transport_h4 = malloc( sizeof(hci_transport_h4_t));
|
||||
hci_transport_h4->ds = NULL;
|
||||
hci_transport_h4->transport.open = h4_open;
|
||||
hci_transport_h4->transport.close = h4_close;
|
||||
hci_transport_h4->transport.send_packet = h4_send_packet;
|
||||
hci_transport_h4->transport.register_packet_handler = h4_register_packet_handler;
|
||||
hci_transport_h4->transport.get_transport_name = h4_get_transport_name;
|
||||
hci_transport_h4->transport.set_baudrate = NULL;
|
||||
hci_transport_h4->transport.can_send_packet_now = NULL;
|
||||
}
|
||||
return (hci_transport_t *) hci_transport_h4;
|
||||
}
|
313
ios/RetroArch/input/BTStack/btstack/hci_transport_h4_dma.c
Normal file
313
ios/RetroArch/input/BTStack/btstack/hci_transport_h4_dma.c
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_h4_transport_dma.c
|
||||
*
|
||||
* HCI Transport implementation for basic H4 protocol for blocking UART write and IRQ-driven blockwise RX
|
||||
*
|
||||
* Created by Matthias Ringwald on 4/29/09.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "hci.h"
|
||||
#include "hci_dump.h"
|
||||
#include "hci_transport.h"
|
||||
#include <btstack/run_loop.h>
|
||||
|
||||
#include <btstack/hal_uart_dma.h>
|
||||
|
||||
typedef enum {
|
||||
H4_W4_PACKET_TYPE = 1,
|
||||
H4_W4_EVENT_HEADER,
|
||||
H4_W4_ACL_HEADER,
|
||||
H4_W4_PAYLOAD,
|
||||
H4_PACKET_RECEIVED
|
||||
} H4_STATE;
|
||||
|
||||
typedef enum {
|
||||
TX_IDLE = 1,
|
||||
TX_W4_HEADER_SENT,
|
||||
TX_W4_PACKET_SENT,
|
||||
TX_DONE
|
||||
} TX_STATE;
|
||||
|
||||
typedef struct hci_transport_h4 {
|
||||
hci_transport_t transport;
|
||||
data_source_t *ds;
|
||||
} hci_transport_h4_t;
|
||||
|
||||
// prototypes
|
||||
static int h4_process(struct data_source *ds);
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
|
||||
static void h4_block_received(void);
|
||||
static void h4_block_sent(void);
|
||||
static int h4_open(void *transport_config);
|
||||
static int h4_close();
|
||||
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
|
||||
static int h4_send_packet(uint8_t packet_type, uint8_t *packet, int size);
|
||||
static const char * h4_get_transport_name();
|
||||
static int h4_set_baudrate(uint32_t baudrate);
|
||||
static int h4_can_send_packet_now(uint8_t packet_type);
|
||||
|
||||
// packet reader state machine
|
||||
static H4_STATE h4_state;
|
||||
static int read_pos;
|
||||
static int bytes_to_read;
|
||||
static uint8_t hci_packet[HCI_PACKET_BUFFER_SIZE]; // bigger than largest packet
|
||||
|
||||
// tx state
|
||||
static TX_STATE tx_state;
|
||||
static uint8_t *tx_data;
|
||||
static uint16_t tx_len;
|
||||
|
||||
// static hci_transport_h4_t * hci_transport_h4 = NULL;
|
||||
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
|
||||
|
||||
static data_source_t hci_transport_h4_dma_ds = {
|
||||
.process = h4_process
|
||||
};
|
||||
|
||||
static hci_transport_h4_t hci_transport_h4_dma = {
|
||||
.ds = &hci_transport_h4_dma_ds,
|
||||
.transport.open = h4_open,
|
||||
.transport.close = h4_close,
|
||||
.transport.send_packet = h4_send_packet,
|
||||
.transport.register_packet_handler = h4_register_packet_handler,
|
||||
.transport.get_transport_name = h4_get_transport_name,
|
||||
.transport.set_baudrate = h4_set_baudrate,
|
||||
.transport.can_send_packet_now = h4_can_send_packet_now
|
||||
};
|
||||
|
||||
static void h4_init_sm(void){
|
||||
h4_state = H4_W4_PACKET_TYPE;
|
||||
read_pos = 0;
|
||||
bytes_to_read = 1;
|
||||
hal_uart_dma_receive_block(hci_packet, bytes_to_read);
|
||||
}
|
||||
|
||||
|
||||
static int h4_open(void *transport_config){
|
||||
|
||||
// open uart
|
||||
hal_uart_dma_init();
|
||||
hal_uart_dma_set_block_received(h4_block_received);
|
||||
hal_uart_dma_set_block_sent(h4_block_sent);
|
||||
|
||||
// set up data_source
|
||||
run_loop_add_data_source(&hci_transport_h4_dma_ds);
|
||||
|
||||
//
|
||||
h4_init_sm();
|
||||
tx_state = TX_IDLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h4_close(){
|
||||
// first remove run loop handler
|
||||
run_loop_remove_data_source(&hci_transport_h4_dma_ds);
|
||||
|
||||
// close device
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h4_block_received(void){
|
||||
|
||||
read_pos += bytes_to_read;
|
||||
|
||||
// act
|
||||
switch (h4_state) {
|
||||
case H4_W4_PACKET_TYPE:
|
||||
switch (hci_packet[0]) {
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
h4_state = H4_W4_ACL_HEADER;
|
||||
bytes_to_read = HCI_ACL_HEADER_SIZE;
|
||||
break;
|
||||
case HCI_EVENT_PACKET:
|
||||
h4_state = H4_W4_EVENT_HEADER;
|
||||
bytes_to_read = HCI_EVENT_HEADER_SIZE;
|
||||
break;
|
||||
default:
|
||||
log_error("h4_process: invalid packet type 0x%02x\r\n", hci_packet[0]);
|
||||
read_pos = 0;
|
||||
h4_state = H4_W4_PACKET_TYPE;
|
||||
bytes_to_read = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case H4_W4_EVENT_HEADER:
|
||||
bytes_to_read = hci_packet[2];
|
||||
if (bytes_to_read == 0) {
|
||||
h4_state = H4_PACKET_RECEIVED;
|
||||
break;
|
||||
}
|
||||
h4_state = H4_W4_PAYLOAD;
|
||||
break;
|
||||
|
||||
case H4_W4_ACL_HEADER:
|
||||
bytes_to_read = READ_BT_16( hci_packet, 3);
|
||||
if (bytes_to_read == 0) {
|
||||
h4_state = H4_PACKET_RECEIVED;
|
||||
break;
|
||||
}
|
||||
h4_state = H4_W4_PAYLOAD;
|
||||
break;
|
||||
|
||||
case H4_W4_PAYLOAD:
|
||||
h4_state = H4_PACKET_RECEIVED;
|
||||
bytes_to_read = 0;
|
||||
// trigger run loop
|
||||
embedded_trigger();
|
||||
break;
|
||||
|
||||
default:
|
||||
bytes_to_read = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// read next block
|
||||
if (bytes_to_read) {
|
||||
hal_uart_dma_receive_block(&hci_packet[read_pos], bytes_to_read);
|
||||
}
|
||||
}
|
||||
|
||||
static void h4_block_sent(void){
|
||||
switch (tx_state){
|
||||
case TX_W4_HEADER_SENT:
|
||||
tx_state = TX_W4_PACKET_SENT;
|
||||
// h4 packet type + actual packet
|
||||
hal_uart_dma_send_block(tx_data, tx_len);
|
||||
break;
|
||||
case TX_W4_PACKET_SENT:
|
||||
tx_state = TX_DONE;
|
||||
// trigger run loop
|
||||
embedded_trigger();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
||||
packet_handler = handler;
|
||||
}
|
||||
|
||||
// #define DUMP
|
||||
|
||||
#ifdef DUMP
|
||||
static void dump(uint8_t *data, uint16_t len){
|
||||
int i;
|
||||
for (i=0; i<len;i++){
|
||||
printf("%02X ", ((uint8_t *)data)[i]);
|
||||
}
|
||||
printf("\n\r");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int h4_process(struct data_source *ds) {
|
||||
|
||||
// notify about packet sent
|
||||
if (tx_state == TX_DONE){
|
||||
// reset state
|
||||
tx_state = TX_IDLE;
|
||||
uint8_t event = DAEMON_EVENT_HCI_PACKET_SENT;
|
||||
packet_handler(HCI_EVENT_PACKET, &event, 1);
|
||||
}
|
||||
|
||||
if (h4_state != H4_PACKET_RECEIVED) return 0;
|
||||
|
||||
// log packet
|
||||
#ifdef DUMP
|
||||
printf("RX: ");
|
||||
dump(hci_packet, read_pos);
|
||||
#endif
|
||||
|
||||
packet_handler(hci_packet[0], &hci_packet[1], read_pos-1);
|
||||
|
||||
h4_init_sm();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h4_send_packet(uint8_t packet_type, uint8_t *packet, int size){
|
||||
|
||||
// write in progress
|
||||
if (tx_state != TX_IDLE) {
|
||||
log_error("h4_send_packet with tx_state = %u, type %u, data %02x %02x %02x", tx_state, packet_type, packet[0], packet[1], packet[2]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DUMP
|
||||
printf("TX: %02x ", packet_type);
|
||||
dump(packet, size);
|
||||
#endif
|
||||
|
||||
tx_data = packet;
|
||||
tx_len = size;
|
||||
|
||||
tx_state = TX_W4_HEADER_SENT;
|
||||
hal_uart_dma_send_block(&packet_type, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h4_set_baudrate(uint32_t baudrate){
|
||||
printf("h4_set_baudrate - set baud %lu\n\r", baudrate);
|
||||
return hal_uart_dma_set_baud(baudrate);
|
||||
}
|
||||
|
||||
static int h4_can_send_packet_now(uint8_t packet_type){
|
||||
return tx_state == TX_IDLE;
|
||||
|
||||
}
|
||||
|
||||
static const char * h4_get_transport_name(){
|
||||
return "H4_DMA";
|
||||
}
|
||||
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
|
||||
}
|
||||
|
||||
// get h4 singleton
|
||||
hci_transport_t * hci_transport_h4_dma_instance() {
|
||||
return (hci_transport_t *) &hci_transport_h4_dma;
|
||||
}
|
@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_h4_transport_dma.c
|
||||
*
|
||||
* HCI Transport implementation of H4 protocol with eHCILL support
|
||||
* for IRQ-driven blockwise RX and TX, and IRQ callback on CTS toggle
|
||||
*
|
||||
* Based on information found at http://e2e.ti.com/support/low_power_rf/f/660/t/134855.aspx
|
||||
*
|
||||
* Created by Matthias Ringwald on 9/16/11.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "hci.h"
|
||||
#include "hci_dump.h"
|
||||
#include "hci_transport.h"
|
||||
#include <btstack/run_loop.h>
|
||||
|
||||
#include <btstack/hal_uart_dma.h>
|
||||
|
||||
// #define DUMP
|
||||
|
||||
// eHCILL commands (+interal CTS signal)
|
||||
#define EHCILL_GO_TO_SLEEP_IND 0x030
|
||||
#define EHCILL_GO_TO_SLEEP_ACK 0x031
|
||||
#define EHCILL_WAKE_UP_IND 0x032
|
||||
#define EHCILL_WAKE_UP_ACK 0x033
|
||||
#define EHCILL_CTS_SIGNAL 0x034
|
||||
|
||||
typedef enum {
|
||||
H4_W4_PACKET_TYPE = 1,
|
||||
H4_W4_EVENT_HEADER,
|
||||
H4_W4_ACL_HEADER,
|
||||
H4_W4_PAYLOAD,
|
||||
H4_PACKET_RECEIVED
|
||||
} H4_STATE;
|
||||
|
||||
typedef enum {
|
||||
TX_IDLE = 1,
|
||||
TX_W4_WAKEUP, // eHCILL only
|
||||
TX_W4_HEADER_SENT,
|
||||
TX_W4_PACKET_SENT,
|
||||
TX_W4_EHCILL_SENT,
|
||||
TX_DONE
|
||||
} TX_STATE;
|
||||
|
||||
typedef enum {
|
||||
EHCILL_STATE_SLEEP = 1,
|
||||
EHCILL_STATE_W4_ACK,
|
||||
EHCILL_STATE_AWAKE
|
||||
} EHCILL_STATE;
|
||||
|
||||
typedef struct hci_transport_h4 {
|
||||
hci_transport_t transport;
|
||||
data_source_t *ds;
|
||||
} hci_transport_h4_t;
|
||||
|
||||
// prototypes
|
||||
static int h4_process(struct data_source *ds);
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
|
||||
static void h4_block_received(void);
|
||||
static void h4_block_sent(void);
|
||||
static int h4_open(void *transport_config);
|
||||
static int h4_close();
|
||||
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
|
||||
static const char * h4_get_transport_name();
|
||||
static int h4_set_baudrate(uint32_t baudrate);
|
||||
static int h4_can_send_packet_now(uint8_t packet_type);
|
||||
|
||||
static int ehcill_send_packet(uint8_t packet_type, uint8_t *packet, int size);
|
||||
static void ehcill_uart_dma_receive_block(uint8_t *buffer, uint16_t size);
|
||||
static int ehcill_sleep_mode_active(void);
|
||||
static void ehcill_handle(uint8_t action);
|
||||
static void ehcill_cts_irq_handler();
|
||||
|
||||
// eCHILL: state machine
|
||||
static EHCILL_STATE ehcill_state = EHCILL_STATE_AWAKE;
|
||||
static uint8_t * ehcill_defer_rx_buffer;
|
||||
static uint16_t ehcill_defer_rx_size = 0;
|
||||
static uint8_t ehcill_command_to_send;
|
||||
|
||||
// H4: packet reader state machine
|
||||
static H4_STATE h4_state;
|
||||
static int read_pos;
|
||||
static int bytes_to_read;
|
||||
static uint8_t hci_packet[HCI_PACKET_BUFFER_SIZE]; // bigger than largest packet
|
||||
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
|
||||
|
||||
// H4: tx state
|
||||
static TX_STATE tx_state;
|
||||
static uint8_t * tx_data;
|
||||
static uint16_t tx_len;
|
||||
static uint8_t tx_packet_type;
|
||||
|
||||
// data source used in run_loop
|
||||
static data_source_t hci_transport_h4_dma_ds = {
|
||||
.process = h4_process
|
||||
};
|
||||
|
||||
// hci_transport for use by hci
|
||||
static const hci_transport_h4_t hci_transport_h4_ehcill_dma = {
|
||||
.ds = &hci_transport_h4_dma_ds,
|
||||
.transport.open = h4_open,
|
||||
.transport.close = h4_close,
|
||||
.transport.send_packet = ehcill_send_packet,
|
||||
.transport.register_packet_handler = h4_register_packet_handler,
|
||||
.transport.get_transport_name = h4_get_transport_name,
|
||||
.transport.set_baudrate = h4_set_baudrate,
|
||||
.transport.can_send_packet_now = h4_can_send_packet_now
|
||||
};
|
||||
|
||||
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
|
||||
}
|
||||
|
||||
static const char * h4_get_transport_name(){
|
||||
return "H4_EHCILL_DMA";
|
||||
}
|
||||
|
||||
// get h4 singleton
|
||||
hci_transport_t * hci_transport_h4_dma_instance() {
|
||||
return (hci_transport_t *) &hci_transport_h4_ehcill_dma;
|
||||
}
|
||||
|
||||
static void h4_rx_init_sm(void){
|
||||
h4_state = H4_W4_PACKET_TYPE;
|
||||
read_pos = 0;
|
||||
bytes_to_read = 1;
|
||||
ehcill_uart_dma_receive_block(hci_packet, bytes_to_read);
|
||||
}
|
||||
|
||||
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
||||
packet_handler = handler;
|
||||
}
|
||||
|
||||
static int h4_open(void *transport_config){
|
||||
|
||||
// open uart
|
||||
hal_uart_dma_init();
|
||||
hal_uart_dma_set_block_received(h4_block_received);
|
||||
hal_uart_dma_set_block_sent(h4_block_sent);
|
||||
hal_uart_dma_set_csr_irq_handler(ehcill_cts_irq_handler);
|
||||
|
||||
// set up data_source
|
||||
run_loop_add_data_source(&hci_transport_h4_dma_ds);
|
||||
|
||||
// init state machiens
|
||||
h4_rx_init_sm();
|
||||
tx_state = TX_IDLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h4_set_baudrate(uint32_t baudrate){
|
||||
log_info("h4_set_baudrate - set baud %lu\n", baudrate);
|
||||
return hal_uart_dma_set_baud(baudrate);
|
||||
}
|
||||
|
||||
static int h4_close(){
|
||||
// first remove run loop handler
|
||||
run_loop_remove_data_source(&hci_transport_h4_dma_ds);
|
||||
|
||||
// stop IRQ
|
||||
hal_uart_dma_set_csr_irq_handler(NULL);
|
||||
|
||||
// close device
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h4_block_received(void){
|
||||
|
||||
read_pos += bytes_to_read;
|
||||
|
||||
// act
|
||||
switch (h4_state) {
|
||||
case H4_W4_PACKET_TYPE:
|
||||
switch (hci_packet[0]) {
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
h4_state = H4_W4_ACL_HEADER;
|
||||
bytes_to_read = HCI_ACL_HEADER_SIZE;
|
||||
break;
|
||||
case HCI_EVENT_PACKET:
|
||||
h4_state = H4_W4_EVENT_HEADER;
|
||||
bytes_to_read = HCI_EVENT_HEADER_SIZE;
|
||||
break;
|
||||
case EHCILL_GO_TO_SLEEP_IND:
|
||||
case EHCILL_GO_TO_SLEEP_ACK:
|
||||
case EHCILL_WAKE_UP_IND:
|
||||
case EHCILL_WAKE_UP_ACK:
|
||||
ehcill_handle(hci_packet[0]);
|
||||
read_pos = 0;
|
||||
bytes_to_read = 1;
|
||||
break;
|
||||
default:
|
||||
log_error("h4_process: invalid packet type 0x%02x\n", hci_packet[0]);
|
||||
read_pos = 0;
|
||||
bytes_to_read = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case H4_W4_EVENT_HEADER:
|
||||
bytes_to_read = hci_packet[2];
|
||||
if (bytes_to_read) {
|
||||
h4_state = H4_W4_PAYLOAD;
|
||||
break;
|
||||
}
|
||||
h4_state = H4_PACKET_RECEIVED;
|
||||
break;
|
||||
|
||||
case H4_W4_ACL_HEADER:
|
||||
bytes_to_read = READ_BT_16( hci_packet, 3);
|
||||
if (bytes_to_read) {
|
||||
h4_state = H4_W4_PAYLOAD;
|
||||
break;
|
||||
}
|
||||
h4_state = H4_PACKET_RECEIVED;
|
||||
break;
|
||||
|
||||
case H4_W4_PAYLOAD:
|
||||
h4_state = H4_PACKET_RECEIVED;
|
||||
bytes_to_read = 0;
|
||||
|
||||
// trigger run loop - necessary for use in low power modes
|
||||
embedded_trigger();
|
||||
break;
|
||||
|
||||
default:
|
||||
bytes_to_read = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// read next block
|
||||
if (bytes_to_read) {
|
||||
ehcill_uart_dma_receive_block(&hci_packet[read_pos], bytes_to_read);
|
||||
}
|
||||
}
|
||||
|
||||
static int h4_can_send_packet_now(uint8_t packet_type){
|
||||
return tx_state == TX_IDLE;
|
||||
|
||||
}
|
||||
|
||||
static void h4_block_sent(void){
|
||||
switch (tx_state){
|
||||
case TX_W4_HEADER_SENT:
|
||||
tx_state = TX_W4_PACKET_SENT;
|
||||
// h4 packet type + actual packet
|
||||
hal_uart_dma_send_block(tx_data, tx_len);
|
||||
break;
|
||||
case TX_W4_PACKET_SENT:
|
||||
// send pending ehcill command
|
||||
if (ehcill_command_to_send){
|
||||
tx_state = TX_W4_EHCILL_SENT;
|
||||
hal_uart_dma_send_block(&ehcill_command_to_send, 1);
|
||||
break;
|
||||
}
|
||||
tx_state = TX_DONE;
|
||||
// trigger run loop
|
||||
embedded_trigger();
|
||||
break;
|
||||
case TX_W4_EHCILL_SENT:
|
||||
if (ehcill_command_to_send == EHCILL_GO_TO_SLEEP_ACK) {
|
||||
// UART not needed after EHCILL_GO_TO_SLEEP_ACK was sent
|
||||
hal_uart_dma_set_sleep(1);
|
||||
}
|
||||
ehcill_command_to_send = 0;
|
||||
tx_state = TX_DONE;
|
||||
// trigger run loop
|
||||
embedded_trigger();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DUMP
|
||||
static void dump(uint8_t *data, uint16_t len){
|
||||
int i;
|
||||
if (len > 5) len = 8;
|
||||
for (i=0; i<len;i++){
|
||||
printf("%02X ", ((uint8_t *)data)[i]);
|
||||
}
|
||||
printf("\n\r");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int h4_process(struct data_source *ds) {
|
||||
|
||||
// notify about packet sent
|
||||
if (tx_state == TX_DONE){
|
||||
// reset state
|
||||
tx_state = TX_IDLE;
|
||||
uint8_t event = DAEMON_EVENT_HCI_PACKET_SENT;
|
||||
packet_handler(HCI_EVENT_PACKET, &event, 1);
|
||||
}
|
||||
|
||||
if (h4_state != H4_PACKET_RECEIVED) return 0;
|
||||
|
||||
// log packet
|
||||
#ifdef DUMP
|
||||
printf("RX: ");
|
||||
dump(hci_packet, read_pos);
|
||||
#endif
|
||||
|
||||
packet_handler(hci_packet[0], &hci_packet[1], read_pos-1);
|
||||
|
||||
h4_rx_init_sm();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
|
||||
int ehcill_sleep_mode_active(void){
|
||||
return ehcill_state == EHCILL_STATE_SLEEP;
|
||||
}
|
||||
|
||||
static void ehcill_cts_irq_handler(){
|
||||
ehcill_handle(EHCILL_CTS_SIGNAL);
|
||||
}
|
||||
|
||||
static void ehcill_schedule_ecill_command(uint8_t command){
|
||||
// reply with HCILL_SLEEP_ACK
|
||||
ehcill_command_to_send = command;
|
||||
switch (tx_state){
|
||||
case TX_IDLE:
|
||||
case TX_DONE:
|
||||
tx_state = TX_W4_EHCILL_SENT;
|
||||
hal_uart_dma_send_block(&ehcill_command_to_send, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ehcill_handle(uint8_t action){
|
||||
int size;
|
||||
|
||||
// printf("ehcill_handle: %x, state %u, defer_rx %u \n\r", action, ehcill_state, ehcill_defer_rx_size);
|
||||
switch(ehcill_state){
|
||||
case EHCILL_STATE_AWAKE:
|
||||
switch(action){
|
||||
case EHCILL_GO_TO_SLEEP_IND:
|
||||
|
||||
// 1. set RTS high - already done by BT RX ISR
|
||||
// 2. enable CTS - CTS always enabled
|
||||
|
||||
ehcill_state = EHCILL_STATE_SLEEP;
|
||||
log_info("RX: EHCILL_GO_TO_SLEEP_IND\n");
|
||||
ehcill_schedule_ecill_command(EHCILL_GO_TO_SLEEP_ACK);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EHCILL_STATE_SLEEP:
|
||||
switch(action){
|
||||
case EHCILL_CTS_SIGNAL:
|
||||
|
||||
// re-activate rx (also clears RTS)
|
||||
if (!ehcill_defer_rx_size) break;
|
||||
|
||||
// UART needed again
|
||||
hal_uart_dma_set_sleep(0);
|
||||
|
||||
log_info ("Re-activate rx\n");
|
||||
size = ehcill_defer_rx_size;
|
||||
ehcill_defer_rx_size = 0;
|
||||
hal_uart_dma_receive_block(ehcill_defer_rx_buffer, size);
|
||||
break;
|
||||
|
||||
case EHCILL_WAKE_UP_IND:
|
||||
|
||||
ehcill_state = EHCILL_STATE_AWAKE;
|
||||
log_info("RX: EHCILL_GO_TO_SLEEP_IND\n");
|
||||
ehcill_schedule_ecill_command(EHCILL_WAKE_UP_ACK);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EHCILL_STATE_W4_ACK:
|
||||
switch(action){
|
||||
case EHCILL_WAKE_UP_IND:
|
||||
case EHCILL_WAKE_UP_ACK:
|
||||
|
||||
log_info("RX: EHCILL_WAKE_UP_IND or ACK\n");
|
||||
|
||||
tx_state = TX_W4_HEADER_SENT;
|
||||
hal_uart_dma_send_block(&tx_packet_type, 1);
|
||||
ehcill_state = EHCILL_STATE_AWAKE;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int ehcill_send_packet(uint8_t packet_type, uint8_t *packet, int size){
|
||||
|
||||
// write in progress
|
||||
if (tx_state != TX_IDLE) {
|
||||
log_error("h4_send_packet with tx_state = %u, type %u, data %02x %02x %02x\n", tx_state, packet_type, packet[0], packet[1], packet[2]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DUMP
|
||||
printf("TX: %02x ", packet_type);
|
||||
dump(packet, size);
|
||||
#endif
|
||||
|
||||
tx_packet_type = packet_type;
|
||||
tx_data = packet;
|
||||
tx_len = size;
|
||||
|
||||
if (!ehcill_sleep_mode_active()){
|
||||
tx_state = TX_W4_HEADER_SENT;
|
||||
hal_uart_dma_send_block(&packet_type, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// UART needed again
|
||||
hal_uart_dma_set_sleep(0);
|
||||
|
||||
// update state
|
||||
tx_state = TX_W4_WAKEUP;
|
||||
ehcill_state = EHCILL_STATE_W4_ACK;
|
||||
|
||||
// wake up
|
||||
log_info("RX: SLEEP\n");
|
||||
log_info("TX: EHCILL_WAKE_UP_IND\n");
|
||||
ehcill_command_to_send = EHCILL_WAKE_UP_IND;
|
||||
hal_uart_dma_send_block(&ehcill_command_to_send, 1);
|
||||
|
||||
if (!ehcill_defer_rx_size){
|
||||
log_error("ERROR: NO RX REQUEST PENDING\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// receive request, clears RTS
|
||||
int rx_size = ehcill_defer_rx_size;
|
||||
ehcill_defer_rx_size = 0;
|
||||
hal_uart_dma_receive_block(ehcill_defer_rx_buffer, rx_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ehcill_uart_dma_receive_block(uint8_t *buffer, uint16_t size){
|
||||
if (!ehcill_sleep_mode_active()){
|
||||
ehcill_defer_rx_size = 0;
|
||||
hal_uart_dma_receive_block(buffer, size);
|
||||
return;
|
||||
}
|
||||
|
||||
// store receive request for later
|
||||
ehcill_defer_rx_buffer = buffer;
|
||||
ehcill_defer_rx_size = size;
|
||||
}
|
379
ios/RetroArch/input/BTStack/btstack/hci_transport_h4_iphone.c
Normal file
379
ios/RetroArch/input/BTStack/btstack/hci_transport_h4_iphone.c
Normal file
@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_h4_transport_iphone.c
|
||||
*
|
||||
* HCI Transport API implementation for basic H4 protocol
|
||||
* with extension for "enforced wake device" using the netgraph styele
|
||||
* interface to the Bluetooth UART on iOS
|
||||
*
|
||||
* This is only tested on newer devices with Broadcom chipsets
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define SOCKET_DEVICE "com.apple.uart.bluetooth"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#ifndef PF_NETGRAPH
|
||||
#define PF_NETGRAPH 32 /* normally in sys/socket.h */
|
||||
#endif
|
||||
#define NG_CONTROL 2 /* from freeBSD sys/ng_socket.h */
|
||||
#define SOCK_IOCTL_VAL 0xC0644E03 /* from reversing BTServer */
|
||||
#define GETSOCKOPT_VAL 0x402C7413 /* from reversing BTServer */
|
||||
#define SETSOCKOPT_VAL 0x802c7414 /* from reversing BTServer */
|
||||
|
||||
// don't enforce wake after 3s idle
|
||||
#define HCI_WAKE_TIMER_MS 3000
|
||||
#define HCI_WAKE_DURATION 10000
|
||||
|
||||
#include <termios.h> /* POSIX terminal control definitions */
|
||||
#include <fcntl.h> /* File control definitions */
|
||||
#include <unistd.h> /* UNIX standard function definitions */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "hci.h"
|
||||
#include "hci_transport.h"
|
||||
#include "hci_dump.h"
|
||||
|
||||
static int h4_process(struct data_source *ds);
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
|
||||
static hci_uart_config_t *hci_uart_config;
|
||||
|
||||
static void h4_enforce_wake_on(void);
|
||||
static void h4_enforce_wake_off(void);
|
||||
static void h4_enforce_wake_timeout(struct timer *ts);
|
||||
|
||||
typedef enum {
|
||||
H4_W4_PACKET_TYPE,
|
||||
H4_W4_EVENT_HEADER,
|
||||
H4_W4_ACL_HEADER,
|
||||
H4_W4_PAYLOAD,
|
||||
} H4_STATE;
|
||||
|
||||
typedef struct hci_transport_h4 {
|
||||
hci_transport_t transport;
|
||||
data_source_t *ds;
|
||||
int uart_fd; // different from ds->fd for HCI reader thread
|
||||
/* power management support, e.g. used by iOS */
|
||||
timer_source_t sleep_timer;
|
||||
} hci_transport_h4_t;
|
||||
|
||||
|
||||
/* NG sockaddr definition */
|
||||
struct sockaddr_ng {
|
||||
uint8_t sg_len; /* total length */
|
||||
uint8_t sg_family; /* address family */
|
||||
uint16_t sg_subtype;
|
||||
uint32_t sg_node;
|
||||
uint32_t sg_null;
|
||||
uint8_t sg_dummy[20];
|
||||
};
|
||||
|
||||
// single instance
|
||||
static hci_transport_h4_t * hci_transport_h4 = NULL;
|
||||
|
||||
// enforced wake support
|
||||
static char * enforce_wake_device = NULL;
|
||||
static int enforce_wake_fd = 0;
|
||||
|
||||
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
|
||||
|
||||
// packet reader state machine
|
||||
static H4_STATE h4_state;
|
||||
static int bytes_to_read;
|
||||
static int read_pos;
|
||||
|
||||
static uint8_t hci_packet[1+HCI_PACKET_BUFFER_SIZE]; // packet type + max(acl header + acl payload, event header + event data)
|
||||
|
||||
static int h4_open(void *transport_config)
|
||||
{
|
||||
hci_uart_config = (hci_uart_config_t *) transport_config;
|
||||
|
||||
int fd = socket(PF_NETGRAPH, SOCK_STREAM, NG_CONTROL);
|
||||
if (fd < 0) {
|
||||
perror("socket(HCI_IF)");
|
||||
goto err_out0;
|
||||
}
|
||||
|
||||
// get node address
|
||||
struct ioctl_arg_t {
|
||||
uint32_t result;
|
||||
char socket_name[96];
|
||||
} ioctl_arg;
|
||||
memset((void *) &ioctl_arg, 0x00, sizeof(struct ioctl_arg_t));
|
||||
strcpy((char *) &ioctl_arg.socket_name, SOCKET_DEVICE);
|
||||
if (ioctl(fd, SOCK_IOCTL_VAL, &ioctl_arg) != 0) {
|
||||
perror("ioctl(fd_sock, SOCK_IOCTL_VAL)");
|
||||
goto err_out1;
|
||||
}
|
||||
|
||||
// setup sock addr struct
|
||||
struct sockaddr_ng sock_addr;
|
||||
sock_addr.sg_len = sizeof(struct sockaddr_ng);
|
||||
sock_addr.sg_family = PF_NETGRAPH;
|
||||
sock_addr.sg_subtype = 0x02;
|
||||
sock_addr.sg_node = ioctl_arg.result;
|
||||
sock_addr.sg_null = 0;
|
||||
|
||||
// connect
|
||||
if (connect(fd, (const struct sockaddr *) &sock_addr, sizeof(struct sockaddr_ng)) != 0) {
|
||||
perror("connect(fd_sock)");
|
||||
goto err_out2;
|
||||
}
|
||||
|
||||
// configure UART
|
||||
struct termios toptions;
|
||||
socklen_t toptions_len = sizeof(struct termios);
|
||||
if (getsockopt(fd, SO_ACCEPTCONN, GETSOCKOPT_VAL, &toptions, &toptions_len) != 0) {
|
||||
perror("getsockopt(fd_sock)");
|
||||
goto err_out3;
|
||||
}
|
||||
cfmakeraw(&toptions);
|
||||
speed_t brate = (speed_t) hci_uart_config->baudrate_init;
|
||||
cfsetspeed(&toptions, brate);
|
||||
toptions.c_iflag |= IGNPAR;
|
||||
toptions.c_cflag = 0x00038b00;
|
||||
if (setsockopt(fd, SO_ACCEPTCONN, SETSOCKOPT_VAL, &toptions, toptions_len) != 0) {
|
||||
perror("setsockopt(fd_sock)");
|
||||
goto err_out4;
|
||||
}
|
||||
|
||||
// set up data_source
|
||||
hci_transport_h4->ds = malloc(sizeof(data_source_t));
|
||||
if (!hci_transport_h4->ds) return -1;
|
||||
hci_transport_h4->uart_fd = fd;
|
||||
|
||||
hci_transport_h4->ds->fd = fd;
|
||||
hci_transport_h4->ds->process = h4_process;
|
||||
run_loop_add_data_source(hci_transport_h4->ds);
|
||||
|
||||
// init state machine
|
||||
bytes_to_read = 1;
|
||||
h4_state = H4_W4_PACKET_TYPE;
|
||||
read_pos = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out4:
|
||||
err_out3:
|
||||
err_out2:
|
||||
err_out1:
|
||||
close(fd);
|
||||
err_out0:
|
||||
fprintf(stderr, "h4_open error\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int h4_close(){
|
||||
// first remove run loop handler
|
||||
run_loop_remove_data_source(hci_transport_h4->ds);
|
||||
|
||||
// close device
|
||||
close(hci_transport_h4->ds->fd);
|
||||
|
||||
// let module sleep
|
||||
h4_enforce_wake_off();
|
||||
|
||||
// free struct
|
||||
free(hci_transport_h4->ds);
|
||||
hci_transport_h4->ds = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h4_send_packet(uint8_t packet_type, uint8_t * packet, int size){
|
||||
if (hci_transport_h4->ds == NULL) return -1;
|
||||
if (hci_transport_h4->uart_fd == 0) return -1;
|
||||
|
||||
// wake Bluetooth module
|
||||
h4_enforce_wake_on();
|
||||
|
||||
hci_dump_packet( (uint8_t) packet_type, 0, packet, size);
|
||||
char *data = (char*) packet;
|
||||
int bytes_written = write(hci_transport_h4->uart_fd, &packet_type, 1);
|
||||
while (bytes_written < 1) {
|
||||
usleep(5000);
|
||||
bytes_written = write(hci_transport_h4->uart_fd, &packet_type, 1);
|
||||
};
|
||||
while (size > 0) {
|
||||
int bytes_written = write(hci_transport_h4->uart_fd, data, size);
|
||||
if (bytes_written < 0) {
|
||||
usleep(5000);
|
||||
continue;
|
||||
}
|
||||
data += bytes_written;
|
||||
size -= bytes_written;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h4_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
||||
packet_handler = handler;
|
||||
}
|
||||
|
||||
static void h4_deliver_packet(void){
|
||||
if (read_pos < 3) return; // sanity check
|
||||
hci_dump_packet( hci_packet[0], 1, &hci_packet[1], read_pos-1);
|
||||
packet_handler(hci_packet[0], &hci_packet[1], read_pos-1);
|
||||
|
||||
h4_state = H4_W4_PACKET_TYPE;
|
||||
read_pos = 0;
|
||||
bytes_to_read = 1;
|
||||
}
|
||||
|
||||
static void h4_statemachine(void){
|
||||
switch (h4_state) {
|
||||
|
||||
case H4_W4_PACKET_TYPE:
|
||||
if (hci_packet[0] == HCI_EVENT_PACKET){
|
||||
bytes_to_read = HCI_EVENT_HEADER_SIZE;
|
||||
h4_state = H4_W4_EVENT_HEADER;
|
||||
} else if (hci_packet[0] == HCI_ACL_DATA_PACKET){
|
||||
bytes_to_read = HCI_ACL_HEADER_SIZE;
|
||||
h4_state = H4_W4_ACL_HEADER;
|
||||
} else {
|
||||
log_error("h4_process: invalid packet type 0x%02x\n", hci_packet[0]);
|
||||
read_pos = 0;
|
||||
bytes_to_read = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case H4_W4_EVENT_HEADER:
|
||||
bytes_to_read = hci_packet[2];
|
||||
h4_state = H4_W4_PAYLOAD;
|
||||
break;
|
||||
|
||||
case H4_W4_ACL_HEADER:
|
||||
bytes_to_read = READ_BT_16( hci_packet, 3);
|
||||
h4_state = H4_W4_PAYLOAD;
|
||||
break;
|
||||
|
||||
case H4_W4_PAYLOAD:
|
||||
h4_deliver_packet();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int h4_process(struct data_source *ds) {
|
||||
if (hci_transport_h4->uart_fd == 0) return -1;
|
||||
|
||||
int read_now = bytes_to_read;
|
||||
// if (read_now > 100) {
|
||||
// read_now = 100;
|
||||
// }
|
||||
|
||||
// read up to bytes_to_read data in
|
||||
ssize_t bytes_read = read(hci_transport_h4->uart_fd, &hci_packet[read_pos], read_now);
|
||||
// printf("h4_process: bytes read %u\n", bytes_read);
|
||||
if (bytes_read < 0) {
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
// hexdump(&hci_packet[read_pos], bytes_read);
|
||||
|
||||
bytes_to_read -= bytes_read;
|
||||
read_pos += bytes_read;
|
||||
if (bytes_to_read > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
h4_statemachine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h4_enforce_wake_on(void)
|
||||
{
|
||||
if (!enforce_wake_device) return;
|
||||
|
||||
if (!enforce_wake_fd) {
|
||||
enforce_wake_fd = open(enforce_wake_device, O_RDWR);
|
||||
usleep(HCI_WAKE_DURATION); // wait until device is ready
|
||||
}
|
||||
run_loop_remove_timer(&hci_transport_h4->sleep_timer);
|
||||
run_loop_set_timer(&hci_transport_h4->sleep_timer, HCI_WAKE_TIMER_MS);
|
||||
hci_transport_h4->sleep_timer.process = h4_enforce_wake_timeout;
|
||||
run_loop_add_timer(&hci_transport_h4->sleep_timer);
|
||||
}
|
||||
|
||||
static void h4_enforce_wake_off(void)
|
||||
{
|
||||
run_loop_remove_timer(&hci_transport_h4->sleep_timer);
|
||||
|
||||
if (enforce_wake_fd) {
|
||||
close(enforce_wake_fd);
|
||||
enforce_wake_fd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void h4_enforce_wake_timeout(struct timer *ts)
|
||||
{
|
||||
h4_enforce_wake_off();
|
||||
}
|
||||
|
||||
static const char * h4_get_transport_name(void){
|
||||
return "H4_IPHONE";
|
||||
}
|
||||
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
|
||||
}
|
||||
|
||||
// get h4 singleton
|
||||
hci_transport_t * hci_transport_h4_iphone_instance() {
|
||||
if (hci_transport_h4 == NULL) {
|
||||
hci_transport_h4 = malloc( sizeof(hci_transport_h4_t));
|
||||
hci_transport_h4->ds = NULL;
|
||||
hci_transport_h4->transport.open = h4_open;
|
||||
hci_transport_h4->transport.close = h4_close;
|
||||
hci_transport_h4->transport.send_packet = h4_send_packet;
|
||||
hci_transport_h4->transport.register_packet_handler = h4_register_packet_handler;
|
||||
hci_transport_h4->transport.get_transport_name = h4_get_transport_name;
|
||||
hci_transport_h4->transport.set_baudrate = NULL;
|
||||
hci_transport_h4->transport.can_send_packet_now = NULL;
|
||||
}
|
||||
return (hci_transport_t *) hci_transport_h4;
|
||||
}
|
||||
|
||||
void hci_transport_h4_iphone_set_enforce_wake_device(char *path){
|
||||
enforce_wake_device = path;
|
||||
}
|
313
ios/RetroArch/input/BTStack/btstack/hci_transport_h5.c
Normal file
313
ios/RetroArch/input/BTStack/btstack/hci_transport_h5.c
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_transport_h5.c
|
||||
*
|
||||
* HCI Transport API implementation for basic H5 protocol
|
||||
*
|
||||
* Created by Matthias Ringwald on 4/29/09.
|
||||
*/
|
||||
|
||||
#include <termios.h> /* POSIX terminal control definitions */
|
||||
#include <fcntl.h> /* File control definitions */
|
||||
#include <unistd.h> /* UNIX standard function definitions */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hci.h"
|
||||
#include "hci_transport.h"
|
||||
#include "hci_dump.h"
|
||||
|
||||
|
||||
typedef struct hci_transport_h5 {
|
||||
hci_transport_t transport;
|
||||
data_source_t *ds;
|
||||
} hci_transport_h5_t;
|
||||
|
||||
// h5 slip state machine
|
||||
typedef enum {
|
||||
unknown = 1,
|
||||
decoding,
|
||||
x_c0,
|
||||
x_db
|
||||
} H5_STATE;
|
||||
|
||||
typedef struct h5_slip {
|
||||
state_t state;
|
||||
uint16_t length;
|
||||
uint8_t data[HCI_ACL_BUFFER_SIZE];
|
||||
} h5_slip_t;
|
||||
|
||||
// Global State
|
||||
static h5_slip_t read_sm;
|
||||
static h5_slip_t write_sm;
|
||||
static int bt_filedesc = 0;
|
||||
|
||||
// single instance
|
||||
static hci_transport_h5_t * hci_transport_h5 = NULL;
|
||||
|
||||
static int h5_process(struct data_source *ds);
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, int size);
|
||||
static hci_uart_config_t *hci_uart_config;
|
||||
|
||||
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, int size) = dummy_handler;
|
||||
|
||||
// prototypes
|
||||
static int h5_open(void *transport_config){
|
||||
hci_uart_config = (hci_uart_config_t*) transport_config;
|
||||
struct termios toptions;
|
||||
int fd = open(hci_uart_config->device_name, O_RDWR | O_NOCTTY | O_NDELAY);
|
||||
if (fd == -1) {
|
||||
perror("init_serialport: Unable to open port ");
|
||||
perror(hci_uart_config->device_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tcgetattr(fd, &toptions) < 0) {
|
||||
perror("init_serialport: Couldn't get term attributes");
|
||||
return -1;
|
||||
}
|
||||
speed_t brate = hci_uart_config->baudrate; // let you override switch below if needed
|
||||
switch(hci_uart_config->baudrate) {
|
||||
case 57600: brate=B57600; break;
|
||||
case 115200: brate=B115200; break;
|
||||
#ifdef B230400
|
||||
case 230400: brate=B230400; break;
|
||||
#endif
|
||||
#ifdef B460800
|
||||
case 460800: brate=B460800; break;
|
||||
#endif
|
||||
#ifdef B921600
|
||||
case 921600: brate=B921600; break;
|
||||
#endif
|
||||
}
|
||||
cfsetispeed(&toptions, brate);
|
||||
cfsetospeed(&toptions, brate);
|
||||
|
||||
// 8N1
|
||||
toptions.c_cflag &= ~PARENB;
|
||||
toptions.c_cflag &= ~CSTOPB;
|
||||
toptions.c_cflag &= ~CSIZE;
|
||||
toptions.c_cflag |= CS8;
|
||||
|
||||
if (hci_uart_config->flowcontrol) {
|
||||
// with flow control
|
||||
toptions.c_cflag |= CRTSCTS;
|
||||
} else {
|
||||
// no flow control
|
||||
toptions.c_cflag &= ~CRTSCTS;
|
||||
}
|
||||
|
||||
toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
|
||||
toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
|
||||
|
||||
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
|
||||
toptions.c_oflag &= ~OPOST; // make raw
|
||||
|
||||
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
|
||||
toptions.c_cc[VMIN] = 1;
|
||||
toptions.c_cc[VTIME] = 0;
|
||||
|
||||
if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
|
||||
perror("init_serialport: Couldn't set term attributes");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// set up data_source
|
||||
hci_transport_h5->ds = malloc(sizeof(data_source_t));
|
||||
if (!hci_transport_h5) return -1;
|
||||
hci_transport_h5->ds->fd = fd;
|
||||
hci_transport_h5->ds->process = h5_process;
|
||||
run_loop_add_data_source(hci_transport_h5->ds);
|
||||
|
||||
// init state machine
|
||||
h5_slip_init( &read_sm);
|
||||
h5_slip_init( &write_sm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_close(){
|
||||
// first remove run loop handler
|
||||
run_loop_remove_data_source(hci_transport_h5->ds);
|
||||
|
||||
// close device
|
||||
close(hci_transport_h5->ds->fd);
|
||||
free(hci_transport_h5->ds);
|
||||
|
||||
// free struct
|
||||
hci_transport_h5->ds = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_send_packet(uint8_t packet_type, uint8_t *packet, int size){
|
||||
if (hci_transport_h5->ds == NULL) return -1;
|
||||
if (hci_transport_h5->ds->fd == 0) return -1;
|
||||
char *data = (char*) packet;
|
||||
|
||||
hci_dump_packet( (uint8_t) packet_type, 0, packet, size);
|
||||
|
||||
write(hci_transport_h5->ds->fd, &packet_type, 1);
|
||||
while (size > 0) {
|
||||
int bytes_written = write(hci_transport_h5->ds->fd, data, size);
|
||||
if (bytes_written < 0) {
|
||||
usleep(5000);
|
||||
continue;
|
||||
}
|
||||
data += bytes_written;
|
||||
size -= bytes_written;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void h5_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, int size)){
|
||||
packet_handler = handler;
|
||||
}
|
||||
|
||||
static void h5_slip_init( h5_slip_t * sm){
|
||||
sm->state = unknown;
|
||||
}
|
||||
|
||||
static void h5_slip_process( h5_slip_t * sm, uint8_t input, uint8_t in){
|
||||
uint8_t type;
|
||||
switch (sm->state) {
|
||||
case unknown:
|
||||
if (input == 0xc0){
|
||||
sm->length = 0;
|
||||
sm->state = x_c0;
|
||||
}
|
||||
break;
|
||||
case x_c0:
|
||||
switch (input ){
|
||||
case 0xc0:
|
||||
break;
|
||||
case 0xdb:
|
||||
sm->state = x_db;
|
||||
break;
|
||||
default:
|
||||
sm->data[sm->length] = input;
|
||||
++sm->length;
|
||||
sm->state = decoding;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case decoding:
|
||||
switch (input ){
|
||||
case 0xc0:
|
||||
sm->state = unknown;
|
||||
// packet done - check if valid HCI packet
|
||||
if (sm->length < 6) break;
|
||||
type = sm->data[1] & 0x0f;
|
||||
if (type < 1 || type > 4) break;
|
||||
hci_dump_packet( type, in, &sm->data[4], sm->length-4-2); // -4 header, -2 crc for reliable
|
||||
switch (type) {
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
acl_packet_handler( &sm->data[4], sm->length-4-2);
|
||||
break;
|
||||
case HCI_EVENT_PACKET:
|
||||
event_packet_handler( &sm->data[4], sm->length-4-2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xdb:
|
||||
sm->state = x_db;
|
||||
break;
|
||||
default:
|
||||
sm->data[sm->length] = input;
|
||||
++sm->length;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case x_db:
|
||||
switch (input) {
|
||||
case 0xdc:
|
||||
sm->data[sm->length] = 0xc0;
|
||||
++sm->length;
|
||||
sm->state = decoding;
|
||||
break;
|
||||
case 0xdd:
|
||||
sm->data[sm->length] = 0xdb;
|
||||
++sm->length;
|
||||
sm->state = decoding;
|
||||
break;
|
||||
default:
|
||||
sm->state = unknown;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int h5_process(struct data_source *ds) {
|
||||
if (hci_transport_h5->ds->fd == 0) return -1;
|
||||
|
||||
// read up to bytes_to_read data in
|
||||
uint8_t data;
|
||||
while (1) {
|
||||
int bytes_read = read(hci_transport_h5->ds->fd, &data, 1);
|
||||
if (bytes_read < 1) break;
|
||||
h5_slip_process( &read_sm, data, 1);
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * h5_get_transport_name(){
|
||||
return "H5";
|
||||
}
|
||||
|
||||
static void dummy_handler(uint8_t *packet, int size){
|
||||
}
|
||||
|
||||
// get h5 singleton
|
||||
hci_transport_t * hci_transport_h5_instance() {
|
||||
if (hci_transport_h5 == NULL) {
|
||||
hci_transport_h5 = malloc( sizeof(hci_transport_h5_t));
|
||||
hci_transport_h5->ds = NULL;
|
||||
hci_transport_h5->transport.open = h5_open;
|
||||
hci_transport_h5->transport.close = h5_close;
|
||||
hci_transport_h5->transport.send_packet = h5_send_packet;
|
||||
hci_transport_h5->transport.register_packet_handler = h5_register_event_packet_handler;
|
||||
hci_transport_h5->transport.get_transport_name = h5_get_transport_name;
|
||||
hci_transport_h5->transport.set_baudrate = NULL;
|
||||
hci_transport_h5->transport.can_send_packet_now = NULL;
|
||||
}
|
||||
return (hci_transport_t *) hci_transport_h5;
|
||||
}
|
573
ios/RetroArch/input/BTStack/btstack/hci_transport_usb.c
Normal file
573
ios/RetroArch/input/BTStack/btstack/hci_transport_usb.c
Normal file
@ -0,0 +1,573 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* hci_transport_usb.c
|
||||
*
|
||||
* HCI Transport API implementation for USB
|
||||
*
|
||||
* Created by Matthias Ringwald on 7/5/09.
|
||||
*/
|
||||
|
||||
// delock bt class 2 - csr
|
||||
// 0a12:0001 (bus 27, device 2)
|
||||
|
||||
// Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size
|
||||
// HCI Commands 0 0 0x00 Control 8/16/32/64
|
||||
// HCI Events 0 0 0x81 Interrupt (IN) 16
|
||||
// ACL Data 0 0 0x82 Bulk (IN) 32/64
|
||||
// ACL Data 0 0 0x02 Bulk (OUT) 32/64
|
||||
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h> /* UNIX standard function definitions */
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "hci.h"
|
||||
#include "hci_transport.h"
|
||||
#include "hci_dump.h"
|
||||
|
||||
// prototypes
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
|
||||
static int usb_close(void *transport_config);
|
||||
|
||||
enum {
|
||||
LIB_USB_CLOSED = 0,
|
||||
LIB_USB_OPENED,
|
||||
LIB_USB_DEVICE_OPENDED,
|
||||
LIB_USB_KERNEL_DETACHED,
|
||||
LIB_USB_INTERFACE_CLAIMED,
|
||||
LIB_USB_TRANSFERS_ALLOCATED
|
||||
} libusb_state = LIB_USB_CLOSED;
|
||||
|
||||
// single instance
|
||||
static hci_transport_t * hci_transport_usb = NULL;
|
||||
|
||||
static void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size) = dummy_handler;
|
||||
|
||||
// libusb
|
||||
#if !USB_VENDOR_ID || !USB_PRODUCT_ID
|
||||
static struct libusb_device_descriptor desc;
|
||||
static libusb_device * dev;
|
||||
#endif
|
||||
static libusb_device_handle * handle;
|
||||
|
||||
#define ASYNC_BUFFERS 4
|
||||
|
||||
static struct libusb_transfer *event_in_transfer[ASYNC_BUFFERS];
|
||||
static struct libusb_transfer *bulk_in_transfer[ASYNC_BUFFERS];
|
||||
|
||||
static uint8_t hci_event_in_buffer[ASYNC_BUFFERS][HCI_ACL_BUFFER_SIZE]; // bigger than largest packet
|
||||
static uint8_t hci_bulk_in_buffer[ASYNC_BUFFERS][HCI_ACL_BUFFER_SIZE]; // bigger than largest packet
|
||||
|
||||
// For (ab)use as a linked list of received packets
|
||||
static struct libusb_transfer *handle_packet;
|
||||
|
||||
static int doing_pollfds;
|
||||
static timer_source_t usb_timer;
|
||||
static int usb_timer_active;
|
||||
|
||||
// endpoint addresses
|
||||
static int event_in_addr;
|
||||
static int acl_in_addr;
|
||||
static int acl_out_addr;
|
||||
|
||||
#if !USB_VENDOR_ID || !USB_PRODUCT_ID
|
||||
void scan_for_bt_endpoints(void) {
|
||||
int r;
|
||||
|
||||
// get endpoints from interface descriptor
|
||||
struct libusb_config_descriptor *config_descriptor;
|
||||
r = libusb_get_active_config_descriptor(dev, &config_descriptor);
|
||||
log_info("configuration: %u interfaces\n", config_descriptor->bNumInterfaces);
|
||||
|
||||
const struct libusb_interface *interface = config_descriptor->interface;
|
||||
const struct libusb_interface_descriptor * interface0descriptor = interface->altsetting;
|
||||
log_info("interface 0: %u endpoints\n", interface0descriptor->bNumEndpoints);
|
||||
|
||||
const struct libusb_endpoint_descriptor *endpoint = interface0descriptor->endpoint;
|
||||
|
||||
for (r=0;r<interface0descriptor->bNumEndpoints;r++,endpoint++){
|
||||
log_info("endpoint %x, attributes %x\n", endpoint->bEndpointAddress, endpoint->bmAttributes);
|
||||
|
||||
if ((endpoint->bmAttributes & 0x3) == LIBUSB_TRANSFER_TYPE_INTERRUPT){
|
||||
event_in_addr = endpoint->bEndpointAddress;
|
||||
log_info("Using 0x%2.2X for HCI Events\n", event_in_addr);
|
||||
}
|
||||
if ((endpoint->bmAttributes & 0x3) == LIBUSB_TRANSFER_TYPE_BULK){
|
||||
if (endpoint->bEndpointAddress & 0x80) {
|
||||
acl_in_addr = endpoint->bEndpointAddress;
|
||||
log_info("Using 0x%2.2X for ACL Data In\n", acl_in_addr);
|
||||
} else {
|
||||
acl_out_addr = endpoint->bEndpointAddress;
|
||||
log_info("Using 0x%2.2X for ACL Data Out\n", acl_out_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static libusb_device * scan_for_bt_device(libusb_device **devs) {
|
||||
int i = 0;
|
||||
while ((dev = devs[i++]) != NULL) {
|
||||
int r = libusb_get_device_descriptor(dev, &desc);
|
||||
if (r < 0) {
|
||||
log_error("failed to get device descriptor");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_info("%04x:%04x (bus %d, device %d) - class %x subclass %x protocol %x \n",
|
||||
desc.idVendor, desc.idProduct,
|
||||
libusb_get_bus_number(dev), libusb_get_device_address(dev),
|
||||
desc.bDeviceClass, desc.bDeviceSubClass, desc.bDeviceProtocol);
|
||||
|
||||
// @TODO: detect BT USB Dongle based on character and not by id
|
||||
// The class code (bDeviceClass) is 0xE0 – Wireless Controller.
|
||||
// The SubClass code (bDeviceSubClass) is 0x01 – RF Controller.
|
||||
// The Protocol code (bDeviceProtocol) is 0x01 – Bluetooth programming.
|
||||
// if (desc.bDeviceClass == 0xe0 && desc.bDeviceSubClass == 0x01 && desc.bDeviceProtocol == 0x01){
|
||||
if (desc.bDeviceClass == 0xE0 && desc.bDeviceSubClass == 0x01
|
||||
&& desc.bDeviceProtocol == 0x01) {
|
||||
log_info("BT Dongle found.\n");
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void LIBUSB_CALL async_callback(struct libusb_transfer *transfer)
|
||||
{
|
||||
int r;
|
||||
//log_info("in async_callback %d\n", transfer->endpoint);
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED ||
|
||||
(transfer->status == LIBUSB_TRANSFER_TIMED_OUT && transfer->actual_length > 0)) {
|
||||
if (handle_packet == NULL) {
|
||||
handle_packet = transfer;
|
||||
} else {
|
||||
// Walk to end of list and add current packet there
|
||||
struct libusb_transfer *temp = handle_packet;
|
||||
|
||||
while (temp->user_data) {
|
||||
temp = temp->user_data;
|
||||
}
|
||||
temp->user_data = transfer;
|
||||
}
|
||||
} else {
|
||||
// No usable data, just resubmit packet
|
||||
if (libusb_state == LIB_USB_TRANSFERS_ALLOCATED) {
|
||||
r = libusb_submit_transfer(transfer);
|
||||
|
||||
if (r) {
|
||||
log_error("Error re-submitting transfer %d\n", r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_process_ds(struct data_source *ds) {
|
||||
struct timeval tv;
|
||||
int r;
|
||||
|
||||
//log_info("in usb_process_ds\n");
|
||||
|
||||
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1;
|
||||
|
||||
// always handling an event as we're called when data is ready
|
||||
memset(&tv, 0, sizeof(struct timeval));
|
||||
libusb_handle_events_timeout(NULL, &tv);
|
||||
|
||||
// Handle any packet in the order that they were received
|
||||
while (handle_packet) {
|
||||
void * next = handle_packet->user_data;
|
||||
//log_info("handle packet %x, endpoint", handle_packet, handle_packet->endpoint);
|
||||
|
||||
if (handle_packet->endpoint == event_in_addr) {
|
||||
hci_dump_packet( HCI_EVENT_PACKET, 1, handle_packet-> buffer,
|
||||
handle_packet->actual_length);
|
||||
packet_handler(HCI_EVENT_PACKET, handle_packet-> buffer,
|
||||
handle_packet->actual_length);
|
||||
}
|
||||
|
||||
if (handle_packet->endpoint == acl_in_addr) {
|
||||
hci_dump_packet( HCI_ACL_DATA_PACKET, 1, handle_packet-> buffer,
|
||||
handle_packet->actual_length);
|
||||
packet_handler(HCI_ACL_DATA_PACKET, handle_packet-> buffer,
|
||||
handle_packet->actual_length);
|
||||
}
|
||||
|
||||
// Re-submit transfer
|
||||
if (libusb_state == LIB_USB_TRANSFERS_ALLOCATED) {
|
||||
handle_packet->user_data = NULL;
|
||||
r = libusb_submit_transfer(handle_packet);
|
||||
|
||||
if (r) {
|
||||
log_error("Error re-submitting transfer %d\n", r);
|
||||
}
|
||||
}
|
||||
|
||||
// Move to next in the list of packets to handle
|
||||
if (next) {
|
||||
handle_packet = next;
|
||||
} else {
|
||||
handle_packet = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_process_ts(timer_source_t *timer) {
|
||||
struct timeval tv, now;
|
||||
long msec;
|
||||
|
||||
//log_info("in usb_process_ts\n");
|
||||
|
||||
// Deactivate timer
|
||||
run_loop_remove_timer(&usb_timer);
|
||||
usb_timer_active = 0;
|
||||
|
||||
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return;
|
||||
|
||||
// actuall handled the packet in the pollfds function
|
||||
usb_process_ds((struct data_source *) NULL);
|
||||
|
||||
// Compute the amount of time until next event is due
|
||||
gettimeofday(&now, NULL);
|
||||
msec = (now.tv_sec - tv.tv_sec) * 1000;
|
||||
msec = (now.tv_usec - tv.tv_usec) / 1000;
|
||||
|
||||
// Maximum wait time, async packet can come in earlier than timeout
|
||||
if (msec > 10) msec = 10;
|
||||
|
||||
// Activate timer
|
||||
run_loop_set_timer(&usb_timer, msec);
|
||||
run_loop_add_timer(&usb_timer);
|
||||
usb_timer_active = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int usb_open(void *transport_config){
|
||||
int r,c;
|
||||
#if !USB_VENDOR_ID || !USB_PRODUCT_ID
|
||||
libusb_device * aDev;
|
||||
libusb_device **devs;
|
||||
ssize_t cnt;
|
||||
#endif
|
||||
|
||||
handle_packet = NULL;
|
||||
|
||||
// default endpoint addresses
|
||||
event_in_addr = 0x81; // EP1, IN interrupt
|
||||
acl_in_addr = 0x82; // EP2, IN bulk
|
||||
acl_out_addr = 0x02; // EP2, OUT bulk
|
||||
|
||||
// USB init
|
||||
r = libusb_init(NULL);
|
||||
if (r < 0) return -1;
|
||||
|
||||
libusb_state = LIB_USB_OPENED;
|
||||
|
||||
// configure debug level
|
||||
libusb_set_debug(0,3);
|
||||
|
||||
#if USB_VENDOR_ID && USB_PRODUCT_ID
|
||||
// Use a specified device
|
||||
log_info("Want vend: %04x, prod: %04x\n", USB_VENDOR_ID, USB_PRODUCT_ID);
|
||||
handle = libusb_open_device_with_vid_pid(NULL, USB_VENDOR_ID, USB_PRODUCT_ID);
|
||||
|
||||
if (!handle){
|
||||
log_error("libusb_open_device_with_vid_pid failed!\n");
|
||||
usb_close(handle);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
// Scan system for an appropriate device
|
||||
log_info("Scanning for a device");
|
||||
cnt = libusb_get_device_list(NULL, &devs);
|
||||
if (cnt < 0) {
|
||||
usb_close(handle);
|
||||
return -1;
|
||||
}
|
||||
// Find BT modul
|
||||
aDev = scan_for_bt_device(devs);
|
||||
if (!aDev){
|
||||
libusb_free_device_list(devs, 1);
|
||||
usb_close(handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev = aDev;
|
||||
r = libusb_open(dev, &handle);
|
||||
|
||||
libusb_free_device_list(devs, 1);
|
||||
|
||||
if (r < 0) {
|
||||
usb_close(handle);
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
log_info("libusb open %d, handle %p\n", r, handle);
|
||||
libusb_state = LIB_USB_OPENED;
|
||||
|
||||
// Detach OS driver (not possible for OS X)
|
||||
#ifndef __APPLE__
|
||||
r = libusb_kernel_driver_active(handle, 0);
|
||||
if (r < 0) {
|
||||
log_error("libusb_kernel_driver_active error %d\n", r);
|
||||
usb_close(handle);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (r == 1) {
|
||||
r = libusb_detach_kernel_driver(handle, 0);
|
||||
if (r < 0) {
|
||||
log_error("libusb_detach_kernel_driver error %d\n", r);
|
||||
usb_close(handle);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
log_info("libusb_detach_kernel_driver\n");
|
||||
#endif
|
||||
libusb_state = LIB_USB_KERNEL_DETACHED;
|
||||
|
||||
// reserve access to device
|
||||
log_info("claiming interface 0...\n");
|
||||
r = libusb_claim_interface(handle, 0);
|
||||
if (r < 0) {
|
||||
log_error("Error claiming interface %d\n", r);
|
||||
usb_close(handle);
|
||||
return r;
|
||||
}
|
||||
|
||||
libusb_state = LIB_USB_INTERFACE_CLAIMED;
|
||||
log_info("claimed interface 0\n");
|
||||
|
||||
#if !USB_VENDOR_ID || !USB_PRODUCT_ID
|
||||
scan_for_bt_endpoints();
|
||||
#endif
|
||||
|
||||
// allocate transfer handlers
|
||||
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
|
||||
event_in_transfer[c] = libusb_alloc_transfer(0); // 0 isochronous transfers Events
|
||||
bulk_in_transfer[c] = libusb_alloc_transfer(0); // 0 isochronous transfers ACL in
|
||||
|
||||
if ( !event_in_transfer[c] || !bulk_in_transfer[c] ) {
|
||||
usb_close(handle);
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
libusb_state = LIB_USB_TRANSFERS_ALLOCATED;
|
||||
|
||||
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
|
||||
// configure event_in handlers
|
||||
libusb_fill_interrupt_transfer(event_in_transfer[c], handle, event_in_addr,
|
||||
hci_event_in_buffer[c], HCI_ACL_BUFFER_SIZE, async_callback, NULL, 2000) ;
|
||||
|
||||
r = libusb_submit_transfer(event_in_transfer[c]);
|
||||
if (r) {
|
||||
log_error("Error submitting interrupt transfer %d\n", r);
|
||||
usb_close(handle);
|
||||
return r;
|
||||
}
|
||||
|
||||
// configure bulk_in handlers
|
||||
libusb_fill_bulk_transfer(bulk_in_transfer[c], handle, acl_in_addr,
|
||||
hci_bulk_in_buffer[c], HCI_ACL_BUFFER_SIZE, async_callback, NULL, 2000) ;
|
||||
|
||||
r = libusb_submit_transfer(bulk_in_transfer[c]);
|
||||
if (r) {
|
||||
log_error("Error submitting bulk in transfer %d\n", r);
|
||||
usb_close(handle);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for pollfds functionality
|
||||
doing_pollfds = libusb_pollfds_handle_timeouts(NULL);
|
||||
|
||||
if (doing_pollfds) {
|
||||
log_info("Async using pollfds:\n");
|
||||
|
||||
const struct libusb_pollfd ** pollfd = libusb_get_pollfds(NULL);
|
||||
for (r = 0 ; pollfd[r] ; r++) {
|
||||
data_source_t *ds = malloc(sizeof(data_source_t));
|
||||
ds->fd = pollfd[r]->fd;
|
||||
ds->process = usb_process_ds;
|
||||
run_loop_add_data_source(ds);
|
||||
log_info("%u: %p fd: %u, events %x\n", r, pollfd[r], pollfd[r]->fd, pollfd[r]->events);
|
||||
}
|
||||
} else {
|
||||
log_info("Async using timers:\n");
|
||||
|
||||
usb_timer.process = usb_process_ts;
|
||||
run_loop_set_timer(&usb_timer, 100);
|
||||
run_loop_add_timer(&usb_timer);
|
||||
usb_timer_active = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int usb_close(void *transport_config){
|
||||
int c;
|
||||
// @TODO: remove all run loops!
|
||||
|
||||
switch (libusb_state){
|
||||
case LIB_USB_CLOSED:
|
||||
break;
|
||||
|
||||
case LIB_USB_TRANSFERS_ALLOCATED:
|
||||
libusb_state = LIB_USB_INTERFACE_CLAIMED;
|
||||
|
||||
if(usb_timer_active) {
|
||||
run_loop_remove_timer(&usb_timer);
|
||||
usb_timer_active = 0;
|
||||
}
|
||||
|
||||
// Cancel any asynchronous transfers
|
||||
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
|
||||
libusb_cancel_transfer(event_in_transfer[c]);
|
||||
libusb_cancel_transfer(bulk_in_transfer[c]);
|
||||
}
|
||||
|
||||
/* TODO - find a better way to ensure that all transfers have completed */
|
||||
struct timeval tv;
|
||||
memset(&tv, 0, sizeof(struct timeval));
|
||||
libusb_handle_events_timeout(NULL, &tv);
|
||||
|
||||
case LIB_USB_INTERFACE_CLAIMED:
|
||||
for (c = 0 ; c < ASYNC_BUFFERS ; c++) {
|
||||
if (event_in_transfer[c]) libusb_free_transfer(event_in_transfer[c]);
|
||||
if (bulk_in_transfer[c]) libusb_free_transfer(bulk_in_transfer[c]);
|
||||
}
|
||||
|
||||
libusb_release_interface(handle, 0);
|
||||
|
||||
case LIB_USB_KERNEL_DETACHED:
|
||||
#ifndef __APPLE__
|
||||
libusb_attach_kernel_driver (handle, 0);
|
||||
#endif
|
||||
case LIB_USB_DEVICE_OPENDED:
|
||||
libusb_close(handle);
|
||||
|
||||
case LIB_USB_OPENED:
|
||||
libusb_exit(NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_send_cmd_packet(uint8_t *packet, int size){
|
||||
int r;
|
||||
|
||||
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1;
|
||||
|
||||
hci_dump_packet( HCI_COMMAND_DATA_PACKET, 0, packet, size);
|
||||
|
||||
// Use synchronous call to sent out command
|
||||
r = libusb_control_transfer(handle,
|
||||
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
|
||||
0, 0, 0, packet, size, 2000);
|
||||
|
||||
if (r < 0 || r !=size ) {
|
||||
log_error("Error submitting control transfer %d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_send_acl_packet(uint8_t *packet, int size){
|
||||
int r, t;
|
||||
|
||||
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) return -1;
|
||||
|
||||
hci_dump_packet( HCI_ACL_DATA_PACKET, 0, packet, size);
|
||||
|
||||
// Use synchronous call to sent out data
|
||||
r = libusb_bulk_transfer(handle, acl_out_addr, packet, size, &t, 1000);
|
||||
if(r < 0){
|
||||
log_error("Error submitting data transfer");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
|
||||
switch (packet_type){
|
||||
case HCI_COMMAND_DATA_PACKET:
|
||||
return usb_send_cmd_packet(packet, size);
|
||||
case HCI_ACL_DATA_PACKET:
|
||||
return usb_send_acl_packet(packet, size);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
||||
log_info("registering packet handler\n");
|
||||
packet_handler = handler;
|
||||
}
|
||||
|
||||
static const char * usb_get_transport_name(void){
|
||||
return "USB";
|
||||
}
|
||||
|
||||
static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
|
||||
}
|
||||
|
||||
// get usb singleton
|
||||
hci_transport_t * hci_transport_usb_instance() {
|
||||
if (!hci_transport_usb) {
|
||||
hci_transport_usb = malloc( sizeof(hci_transport_t));
|
||||
hci_transport_usb->open = usb_open;
|
||||
hci_transport_usb->close = usb_close;
|
||||
hci_transport_usb->send_packet = usb_send_packet;
|
||||
hci_transport_usb->register_packet_handler = usb_register_packet_handler;
|
||||
hci_transport_usb->get_transport_name = usb_get_transport_name;
|
||||
hci_transport_usb->set_baudrate = NULL;
|
||||
hci_transport_usb->can_send_packet_now = NULL;
|
||||
}
|
||||
return hci_transport_usb;
|
||||
}
|
1157
ios/RetroArch/input/BTStack/btstack/l2cap.c
Normal file
1157
ios/RetroArch/input/BTStack/btstack/l2cap.c
Normal file
File diff suppressed because it is too large
Load Diff
201
ios/RetroArch/input/BTStack/btstack/l2cap.h
Normal file
201
ios/RetroArch/input/BTStack/btstack/l2cap.h
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* l2cap.h
|
||||
*
|
||||
* Logical Link Control and Adaption Protocl (L2CAP)
|
||||
*
|
||||
* Created by Matthias Ringwald on 5/16/09.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hci.h"
|
||||
#include "l2cap_signaling.h"
|
||||
#include <btstack/utils.h>
|
||||
#include <btstack/btstack.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define L2CAP_SIG_ID_INVALID 0
|
||||
|
||||
#define L2CAP_HEADER_SIZE 4
|
||||
|
||||
// size of HCI ACL + L2CAP Header for regular data packets (8)
|
||||
#define COMPLETE_L2CAP_HEADER (HCI_ACL_HEADER_SIZE + L2CAP_HEADER_SIZE)
|
||||
|
||||
// minimum signaling MTU
|
||||
#define L2CAP_MINIMAL_MTU 48
|
||||
#define L2CAP_DEFAULT_MTU 672
|
||||
|
||||
// check L2CAP MTU
|
||||
#if (L2CAP_MINIMAL_MTU + L2CAP_HEADER_SIZE) > HCI_ACL_PAYLOAD_SIZE
|
||||
#error "HCI_ACL_PAYLOAD_SIZE too small for minimal L2CAP MTU of 48 bytes"
|
||||
#endif
|
||||
|
||||
// L2CAP Fixed Channel IDs
|
||||
#define L2CAP_CID_SIGNALING 0x0001
|
||||
#define L2CAP_CID_CONNECTIONLESS_CHANNEL 0x0002
|
||||
#define L2CAP_CID_ATTRIBUTE_PROTOCOL 0x0004
|
||||
#define L2CAP_CID_SIGNALING_LE 0x0005
|
||||
#define L2CAP_CID_SECURITY_MANAGER_PROTOCOL 0x0006
|
||||
|
||||
void l2cap_init(void);
|
||||
void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size));
|
||||
void l2cap_create_channel_internal(void * connection, btstack_packet_handler_t packet_handler, bd_addr_t address, uint16_t psm, uint16_t mtu);
|
||||
void l2cap_disconnect_internal(uint16_t local_cid, uint8_t reason);
|
||||
uint16_t l2cap_get_remote_mtu_for_local_cid(uint16_t local_cid);
|
||||
uint16_t l2cap_max_mtu(void);
|
||||
|
||||
void l2cap_block_new_credits(uint8_t blocked);
|
||||
int l2cap_can_send_packet_now(uint16_t local_cid); // non-blocking UART write
|
||||
|
||||
// get outgoing buffer and prepare data
|
||||
uint8_t *l2cap_get_outgoing_buffer(void);
|
||||
|
||||
int l2cap_send_prepared(uint16_t local_cid, uint16_t len);
|
||||
int l2cap_send_internal(uint16_t local_cid, uint8_t *data, uint16_t len);
|
||||
|
||||
int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len);
|
||||
int l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len);
|
||||
|
||||
void l2cap_close_connection(void *connection);
|
||||
|
||||
void l2cap_register_service_internal(void *connection, btstack_packet_handler_t packet_handler, uint16_t psm, uint16_t mtu);
|
||||
void l2cap_unregister_service_internal(void *connection, uint16_t psm);
|
||||
|
||||
void l2cap_accept_connection_internal(uint16_t local_cid);
|
||||
void l2cap_decline_connection_internal(uint16_t local_cid, uint8_t reason);
|
||||
|
||||
// Bluetooth 4.0 - allows to register handler for Attribute Protocol and Security Manager Protocol
|
||||
void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id);
|
||||
|
||||
|
||||
// private structs
|
||||
typedef enum {
|
||||
L2CAP_STATE_CLOSED = 1, // no baseband
|
||||
L2CAP_STATE_WILL_SEND_CREATE_CONNECTION,
|
||||
L2CAP_STATE_WAIT_CONNECTION_COMPLETE,
|
||||
L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT,
|
||||
L2CAP_STATE_WAIT_CONNECT_RSP, // from peer
|
||||
L2CAP_STATE_CONFIG,
|
||||
L2CAP_STATE_OPEN,
|
||||
L2CAP_STATE_WAIT_DISCONNECT, // from application
|
||||
L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST,
|
||||
L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE,
|
||||
L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT,
|
||||
L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST,
|
||||
L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE,
|
||||
} L2CAP_STATE;
|
||||
|
||||
typedef enum {
|
||||
L2CAP_CHANNEL_STATE_VAR_NONE = 0,
|
||||
L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ = 1 << 0,
|
||||
L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP = 1 << 1,
|
||||
L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ = 1 << 2,
|
||||
L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP = 1 << 3,
|
||||
L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ = 1 << 4,
|
||||
L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP = 1 << 5,
|
||||
} L2CAP_CHANNEL_STATE_VAR;
|
||||
|
||||
// info regarding an actual coneection
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
L2CAP_STATE state;
|
||||
L2CAP_CHANNEL_STATE_VAR state_var;
|
||||
|
||||
bd_addr_t address;
|
||||
hci_con_handle_t handle;
|
||||
|
||||
uint8_t remote_sig_id; // used by other side, needed for delayed response
|
||||
uint8_t local_sig_id; // own signaling identifier
|
||||
|
||||
uint16_t local_cid;
|
||||
uint16_t remote_cid;
|
||||
|
||||
uint16_t local_mtu;
|
||||
uint16_t remote_mtu;
|
||||
|
||||
uint16_t psm;
|
||||
|
||||
uint8_t packets_granted; // number of L2CAP/ACL packets client is allowed to send
|
||||
|
||||
uint8_t reason; // used in decline internal
|
||||
|
||||
// client connection
|
||||
void * connection;
|
||||
|
||||
// internal connection
|
||||
btstack_packet_handler_t packet_handler;
|
||||
|
||||
} l2cap_channel_t;
|
||||
|
||||
// info regarding potential connections
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
// service id
|
||||
uint16_t psm;
|
||||
|
||||
// incoming MTU
|
||||
uint16_t mtu;
|
||||
|
||||
// client connection
|
||||
void *connection;
|
||||
|
||||
// internal connection
|
||||
btstack_packet_handler_t packet_handler;
|
||||
|
||||
} l2cap_service_t;
|
||||
|
||||
|
||||
typedef struct l2cap_signaling_response {
|
||||
hci_con_handle_t handle;
|
||||
uint8_t sig_id;
|
||||
uint8_t code;
|
||||
uint16_t data; // infoType for INFORMATION REQUEST, result for CONNECTION request
|
||||
} l2cap_signaling_response_t;
|
||||
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
128
ios/RetroArch/input/BTStack/btstack/l2cap_signaling.c
Normal file
128
ios/RetroArch/input/BTStack/btstack/l2cap_signaling.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* l2cap_signaling.h
|
||||
*
|
||||
* Created by Matthias Ringwald on 7/23/09.
|
||||
*/
|
||||
|
||||
#include "l2cap_signaling.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static char *l2cap_signaling_commands_format[] = {
|
||||
"D", // 0x01 command reject: reason {cmd not understood (0), sig MTU exceeded (2:max sig MTU), invalid CID (4:req CID)}, data len, data
|
||||
"22", // 0x02 connection request: PSM, Source CID
|
||||
"2222", // 0x03 connection response: Dest CID, Source CID, Result, Status
|
||||
"22D", // 0x04 config request: Dest CID, Flags, Configuration options
|
||||
"222D", // 0x05 config response: Source CID, Flags, Result, Configuration options
|
||||
"22", // 0x06 disconection request: Dest CID, Source CID
|
||||
"22", // 0x07 disconection response: Dest CID, Source CID
|
||||
"D", // 0x08 echo request: Data
|
||||
"D", // 0x09 echo response: Data
|
||||
"2", // 0x0a information request: InfoType {1=Connectionless MTU, 2=Extended features supported}
|
||||
"22D", // 0x0b information response: InfoType, Result, Data
|
||||
};
|
||||
|
||||
uint8_t sig_seq_nr = 0xff;
|
||||
uint16_t source_cid = 0x40;
|
||||
|
||||
uint8_t l2cap_next_sig_id(void){
|
||||
if (sig_seq_nr == 0xff) {
|
||||
sig_seq_nr = 1;
|
||||
} else {
|
||||
sig_seq_nr++;
|
||||
}
|
||||
return sig_seq_nr;
|
||||
}
|
||||
|
||||
uint16_t l2cap_next_local_cid(void){
|
||||
return source_cid++;
|
||||
}
|
||||
|
||||
uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer, hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr){
|
||||
|
||||
// 0 - Connection handle : PB=10 : BC=00
|
||||
bt_store_16(acl_buffer, 0, handle | (2 << 12) | (0 << 14));
|
||||
// 6 - L2CAP channel = 1
|
||||
bt_store_16(acl_buffer, 6, 1);
|
||||
// 8 - Code
|
||||
acl_buffer[8] = cmd;
|
||||
// 9 - id (!= 0 sequentially)
|
||||
acl_buffer[9] = identifier;
|
||||
|
||||
// 12 - L2CAP signaling parameters
|
||||
uint16_t pos = 12;
|
||||
const char *format = l2cap_signaling_commands_format[cmd-1];
|
||||
uint16_t word;
|
||||
uint8_t * ptr;
|
||||
while (*format) {
|
||||
switch(*format) {
|
||||
case '1': // 8 bit value
|
||||
case '2': // 16 bit value
|
||||
word = va_arg(argptr, int);
|
||||
// minimal va_arg is int: 2 bytes on 8+16 bit CPUs
|
||||
acl_buffer[pos++] = word & 0xff;
|
||||
if (*format == '2') {
|
||||
acl_buffer[pos++] = word >> 8;
|
||||
}
|
||||
break;
|
||||
case 'D': // variable data. passed: len, ptr
|
||||
word = va_arg(argptr, int);
|
||||
ptr = va_arg(argptr, uint8_t *);
|
||||
memcpy(&acl_buffer[pos], ptr, word);
|
||||
pos += word;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
format++;
|
||||
};
|
||||
va_end(argptr);
|
||||
|
||||
// Fill in various length fields: it's the number of bytes following for ACL lenght and l2cap parameter length
|
||||
// - the l2cap payload length is counted after the following channel id (only payload)
|
||||
|
||||
// 2 - ACL length
|
||||
bt_store_16(acl_buffer, 2, pos - 4);
|
||||
// 4 - L2CAP packet length
|
||||
bt_store_16(acl_buffer, 4, pos - 6 - 2);
|
||||
// 10 - L2CAP signaling parameter length
|
||||
bt_store_16(acl_buffer, 10, pos - 12);
|
||||
|
||||
return pos;
|
||||
}
|
67
ios/RetroArch/input/BTStack/btstack/l2cap_signaling.h
Normal file
67
ios/RetroArch/input/BTStack/btstack/l2cap_signaling.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* l2cap_signaling.h
|
||||
*
|
||||
* Created by Matthias Ringwald on 7/23/09.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <btstack/utils.h>
|
||||
#include <btstack/hci_cmds.h>
|
||||
|
||||
typedef enum {
|
||||
COMMAND_REJECT = 1,
|
||||
CONNECTION_REQUEST,
|
||||
CONNECTION_RESPONSE,
|
||||
CONFIGURE_REQUEST,
|
||||
CONFIGURE_RESPONSE,
|
||||
DISCONNECTION_REQUEST,
|
||||
DISCONNECTION_RESPONSE,
|
||||
ECHO_REQUEST,
|
||||
ECHO_RESPONSE,
|
||||
INFORMATION_REQUEST,
|
||||
INFORMATION_RESPONSE
|
||||
} L2CAP_SIGNALING_COMMANDS;
|
||||
|
||||
uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer,hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr);
|
||||
uint8_t l2cap_next_sig_id(void);
|
||||
uint16_t l2cap_next_local_cid(void);
|
||||
|
118
ios/RetroArch/input/BTStack/btstack/linked_list.c
Normal file
118
ios/RetroArch/input/BTStack/btstack/linked_list.c
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* linked_list.c
|
||||
*
|
||||
* Created by Matthias Ringwald on 7/13/09.
|
||||
*/
|
||||
|
||||
#include <btstack/linked_list.h>
|
||||
#include <stdlib.h>
|
||||
/**
|
||||
* tests if list is empty
|
||||
*/
|
||||
int linked_list_empty(linked_list_t * list){
|
||||
return *list == (void *) 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* linked_list_get_last_item
|
||||
*/
|
||||
linked_item_t * linked_list_get_last_item(linked_list_t * list){ // <-- find the last item in the list
|
||||
linked_item_t *lastItem = NULL;
|
||||
linked_item_t *it;
|
||||
for (it = *list; it ; it = it->next){
|
||||
if (it) {
|
||||
lastItem = it;
|
||||
}
|
||||
}
|
||||
return lastItem;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* linked_list_add
|
||||
*/
|
||||
void linked_list_add(linked_list_t * list, linked_item_t *item){ // <-- add item to list
|
||||
// check if already in list
|
||||
linked_item_t *it;
|
||||
for (it = *list; it ; it = it->next){
|
||||
if (it == item) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// add first
|
||||
item->next = *list;
|
||||
*list = item;
|
||||
}
|
||||
|
||||
void linked_list_add_tail(linked_list_t * list, linked_item_t *item){ // <-- add item to list as last element
|
||||
// check if already in list
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) list; it->next ; it = it->next){
|
||||
if (it->next == item) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
item->next = (linked_item_t*) 0;
|
||||
it->next = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data_source from run loop
|
||||
*
|
||||
* @note: assumes that data_source_t.next is first element in data_source
|
||||
*/
|
||||
int linked_list_remove(linked_list_t * list, linked_item_t *item){ // <-- remove item from list
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) list; it ; it = it->next){
|
||||
if (it->next == item){
|
||||
it->next = item->next;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void linked_item_set_user(linked_item_t *item, void *user_data){
|
||||
item->next = (linked_item_t *) 0;
|
||||
item->user_data = user_data;
|
||||
}
|
||||
|
||||
void * linked_item_get_user(linked_item_t *item) {
|
||||
return item->user_data;
|
||||
}
|
84
ios/RetroArch/input/BTStack/btstack/memory_pool.c
Normal file
84
ios/RetroArch/input/BTStack/btstack/memory_pool.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* memory_pool.c
|
||||
*
|
||||
* Fixed-size block allocation
|
||||
*
|
||||
* Free blocks are kept in singly linked list
|
||||
*
|
||||
*/
|
||||
|
||||
#include <btstack/memory_pool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct node {
|
||||
struct node * next;
|
||||
} node_t;
|
||||
|
||||
void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size){
|
||||
node_t *free_blocks = (node_t*) pool;
|
||||
char *mem_ptr = (char *) storage;
|
||||
int i;
|
||||
|
||||
// create singly linked list of all available blocks
|
||||
free_blocks->next = NULL;
|
||||
for (i = 0 ; i < count ; i++){
|
||||
memory_pool_free(pool, mem_ptr);
|
||||
mem_ptr += block_size;
|
||||
}
|
||||
}
|
||||
|
||||
void * memory_pool_get(memory_pool_t *pool){
|
||||
node_t *free_blocks = (node_t*) pool;
|
||||
|
||||
if (!free_blocks->next) return NULL;
|
||||
|
||||
// remove first
|
||||
node_t *node = free_blocks->next;
|
||||
free_blocks->next = node->next;
|
||||
|
||||
return (void*) node;
|
||||
}
|
||||
|
||||
void memory_pool_free(memory_pool_t *pool, void * block){
|
||||
node_t *free_blocks = (node_t*) pool;
|
||||
node_t *node = (node_t*) block;
|
||||
// add block as node to list
|
||||
node->next = free_blocks->next;
|
||||
free_blocks->next = node;
|
||||
}
|
54
ios/RetroArch/input/BTStack/btstack/memory_pool.h
Normal file
54
ios/RetroArch/input/BTStack/btstack/memory_pool.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2011 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* memory_pool.h
|
||||
*
|
||||
* @Brief Fixed-size block allocation
|
||||
*
|
||||
* @Assumption block_size >= sizeof(void *)
|
||||
* @Assumption size of storage >= count * block_size
|
||||
*
|
||||
* @Note minimal implementation, no error checking/handling
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef void * memory_pool_t;
|
||||
|
||||
// initialize memory pool with with given storage, block size and count
|
||||
void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size);
|
||||
|
||||
// get free block from pool, @returns NULL or pointer to block
|
||||
void * memory_pool_get(memory_pool_t *pool);
|
||||
|
||||
// return previously reserved block to memory pool
|
||||
void memory_pool_free(memory_pool_t *pool, void * block);
|
53
ios/RetroArch/input/BTStack/btstack/platform_iphone.h
Normal file
53
ios/RetroArch/input/BTStack/btstack/platform_iphone.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// platform_iphone.h
|
||||
//
|
||||
// support for the iPhone platform
|
||||
//
|
||||
// Created by Matthias Ringwald on 8/15/09.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hci.h"
|
||||
|
||||
void platform_iphone_status_handler(BLUETOOTH_STATE state);
|
||||
void platform_iphone_register_window_manager_restart(void (*callback)());
|
||||
void platform_iphone_register_preferences_changed(void (*callback)());
|
||||
int platform_iphone_logging_enabled(void);
|
||||
|
137
ios/RetroArch/input/BTStack/btstack/platform_iphone.m
Normal file
137
ios/RetroArch/input/BTStack/btstack/platform_iphone.m
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
//
|
||||
// platform_iphone.c
|
||||
//
|
||||
// Created by Matthias Ringwald on 8/15/09.
|
||||
//
|
||||
|
||||
#include "platform_iphone.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "../SpringBoardAccess/SpringBoardAccess.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef USE_SPRINGBOARD
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
// update SpringBoard icons
|
||||
void platform_iphone_status_handler(BLUETOOTH_STATE state){
|
||||
switch (state) {
|
||||
case BLUETOOTH_OFF:
|
||||
SBA_removeStatusBarImage("BTstack");
|
||||
SBA_removeStatusBarImage("BTstackActive");
|
||||
NSLog(@"Bluetooth status: OFF");
|
||||
break;
|
||||
case BLUETOOTH_ON:
|
||||
SBA_removeStatusBarImage("BTstackActive");
|
||||
SBA_addStatusBarImage("BTstack");
|
||||
NSLog(@"Bluetooth status: ON");
|
||||
break;
|
||||
case BLUETOOTH_ACTIVE:
|
||||
SBA_removeStatusBarImage("BTstack");
|
||||
SBA_addStatusBarImage("BTstackActive");
|
||||
NSLog(@"Bluetooth status: ACTIVE");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void (*window_manager_restart_callback)() = NULL;
|
||||
static void springBoardDidLaunch(){
|
||||
NSLog(@"springBoardDidLaunch!\n");
|
||||
if (window_manager_restart_callback) {
|
||||
int timer;
|
||||
for (timer = 0 ; timer < 10 ; timer++){
|
||||
NSLog(@"ping SBA %u", timer);
|
||||
if (SBA_available()){
|
||||
NSLog(@"pong from SBA!");
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
(*window_manager_restart_callback)();
|
||||
}
|
||||
}
|
||||
|
||||
void platform_iphone_register_window_manager_restart(void (*callback)() ){
|
||||
static int registered = 0;
|
||||
if (!registered) {
|
||||
// register for launch notification
|
||||
CFNotificationCenterRef darwin = CFNotificationCenterGetDarwinNotifyCenter();
|
||||
CFNotificationCenterAddObserver(darwin, NULL, (CFNotificationCallback) springBoardDidLaunch,
|
||||
(CFStringRef) @"SBSpringBoardDidLaunchNotification", NULL, 0);
|
||||
}
|
||||
window_manager_restart_callback = callback;
|
||||
}
|
||||
|
||||
static void (*preferences_changed_callback)() = NULL;
|
||||
static void preferencesDidChange(){
|
||||
NSLog(@"ch.ringwald.btstack.preferences!\n");
|
||||
if (preferences_changed_callback) {
|
||||
(*preferences_changed_callback)();
|
||||
}
|
||||
}
|
||||
|
||||
void platform_iphone_register_preferences_changed(void (*callback)() ){
|
||||
static int registered = 0;
|
||||
if (!registered) {
|
||||
// register for launch notification
|
||||
CFNotificationCenterRef darwin = CFNotificationCenterGetDarwinNotifyCenter();
|
||||
CFNotificationCenterAddObserver(darwin, NULL, (CFNotificationCallback) preferencesDidChange,
|
||||
(CFStringRef) @"ch.ringwald.btstack.preferences", NULL, 0);
|
||||
}
|
||||
preferences_changed_callback = callback;
|
||||
}
|
||||
|
||||
int platform_iphone_logging_enabled(void){
|
||||
int result = 0;
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/ch.ringwald.btstack.plist"];
|
||||
NSNumber *loggingEnabled = [dict objectForKey:@"Logging"];
|
||||
if (loggingEnabled){
|
||||
result = [loggingEnabled boolValue];
|
||||
}
|
||||
[pool release];
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
95
ios/RetroArch/input/BTStack/btstack/remote_device_db.h
Normal file
95
ios/RetroArch/input/BTStack/btstack/remote_device_db.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* interface to provide link key and remote name storage
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <btstack/utils.h>
|
||||
|
||||
typedef struct {
|
||||
|
||||
// management
|
||||
void (*open)(void);
|
||||
void (*close)(void);
|
||||
|
||||
// link key
|
||||
int (*get_link_key)(bd_addr_t *bd_addr, link_key_t *link_key);
|
||||
void (*put_link_key)(bd_addr_t *bd_addr, link_key_t *key);
|
||||
void (*delete_link_key)(bd_addr_t *bd_addr);
|
||||
|
||||
// remote name
|
||||
int (*get_name)(bd_addr_t *bd_addr, device_name_t *device_name);
|
||||
void (*put_name)(bd_addr_t *bd_addr, device_name_t *device_name);
|
||||
void (*delete_name)(bd_addr_t *bd_addr);
|
||||
|
||||
// persistent rfcomm channel
|
||||
uint8_t (*persistent_rfcomm_channel)(char *servicename);
|
||||
|
||||
} remote_device_db_t;
|
||||
|
||||
extern remote_device_db_t remote_device_db_iphone;
|
||||
extern const remote_device_db_t remote_device_db_memory;
|
||||
|
||||
// MARK: non-persisten implementation
|
||||
#include <btstack/linked_list.h>
|
||||
#define MAX_NAME_LEN 32
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
bd_addr_t bd_addr;
|
||||
} db_mem_device_t;
|
||||
|
||||
typedef struct {
|
||||
db_mem_device_t device;
|
||||
link_key_t link_key;
|
||||
} db_mem_device_link_key_t;
|
||||
|
||||
typedef struct {
|
||||
db_mem_device_t device;
|
||||
char device_name[MAX_NAME_LEN];
|
||||
} db_mem_device_name_t;
|
||||
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
char service_name[MAX_NAME_LEN];
|
||||
uint8_t channel;
|
||||
} db_mem_service_t;
|
298
ios/RetroArch/input/BTStack/btstack/remote_device_db_iphone.m
Normal file
298
ios/RetroArch/input/BTStack/btstack/remote_device_db_iphone.m
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
#include "remote_device_db.h"
|
||||
#include "debug.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define BTdaemonID "ch.ringwald.btdaemon"
|
||||
#define BTDaemonPrefsPath "Library/Preferences/ch.ringwald.btdaemon.plist"
|
||||
|
||||
#define DEVICES_KEY "devices"
|
||||
#define PREFS_REMOTE_NAME @"RemoteName"
|
||||
#define PREFS_LINK_KEY @"LinkKey"
|
||||
|
||||
#define MAX_RFCOMM_CHANNEL_NR 30
|
||||
|
||||
#define RFCOMM_SERVICES_KEY "rfcommServices"
|
||||
#define PREFS_CHANNEL @"channel"
|
||||
#define PREFS_LAST_USED @"lastUsed"
|
||||
|
||||
static void put_name(bd_addr_t *bd_addr, device_name_t *device_name);
|
||||
|
||||
static NSMutableDictionary *remote_devices = nil;
|
||||
static NSMutableDictionary *rfcomm_services = nil;
|
||||
|
||||
// Device info
|
||||
static void db_open(void){
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
// NSUserDefaults didn't work
|
||||
//
|
||||
// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
// NSDictionary * dict = [defaults persistentDomainForName:BTdaemonID];
|
||||
|
||||
// NSDictionary * dict = (NSDictionary*) CFPreferencesCopyAppValue(CFSTR(DEVICES_KEY), CFSTR(BTdaemonID));
|
||||
NSDictionary * dict;
|
||||
dict = (NSDictionary*) CFPreferencesCopyAppValue(CFSTR(DEVICES_KEY), CFSTR(BTdaemonID));
|
||||
remote_devices = [[NSMutableDictionary alloc] initWithCapacity:([dict count]+5)];
|
||||
|
||||
// copy entries
|
||||
for (id key in dict) {
|
||||
NSDictionary *value = [dict objectForKey:key];
|
||||
NSMutableDictionary *deviceEntry = [NSMutableDictionary dictionaryWithCapacity:[value count]];
|
||||
[deviceEntry addEntriesFromDictionary:value];
|
||||
[remote_devices setObject:deviceEntry forKey:key];
|
||||
}
|
||||
|
||||
dict = (NSDictionary*) CFPreferencesCopyAppValue(CFSTR(RFCOMM_SERVICES_KEY), CFSTR(BTdaemonID));
|
||||
rfcomm_services = [[NSMutableDictionary alloc] initWithCapacity:([dict count]+5)];
|
||||
|
||||
// copy entries
|
||||
for (id key in dict) {
|
||||
NSDictionary *value = [dict objectForKey:key];
|
||||
NSMutableDictionary *serviceEntry = [NSMutableDictionary dictionaryWithCapacity:[value count]];
|
||||
[serviceEntry addEntriesFromDictionary:value];
|
||||
[rfcomm_services setObject:serviceEntry forKey:key];
|
||||
}
|
||||
|
||||
log_info("read prefs for %u devices\n", (unsigned int) [dict count]);
|
||||
|
||||
[pool release];
|
||||
}
|
||||
|
||||
static void db_synchronize(void){
|
||||
log_info("stored prefs for %u devices\n", (unsigned int) [remote_devices count]);
|
||||
|
||||
// 3 different ways
|
||||
|
||||
// Core Foundation
|
||||
CFPreferencesSetValue(CFSTR(DEVICES_KEY), (CFPropertyListRef) remote_devices, CFSTR(BTdaemonID), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
|
||||
CFPreferencesSetValue(CFSTR(RFCOMM_SERVICES_KEY), (CFPropertyListRef) rfcomm_services, CFSTR(BTdaemonID), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
|
||||
CFPreferencesSynchronize(CFSTR(BTdaemonID), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
|
||||
|
||||
// NSUserDefaults didn't work
|
||||
//
|
||||
// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
// [defaults setPersistentDomain:remote_devices forName:BTdaemonID];
|
||||
// [defaults synchronize];
|
||||
}
|
||||
|
||||
static void db_close(void){
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
// don't call db_synchronize();
|
||||
// a) we're calling db_synchronize() after each change already
|
||||
// b) db_close is called during the SIGINT handler which causes a corrupt prefs file
|
||||
|
||||
[remote_devices release];
|
||||
remote_devices = nil;
|
||||
[pool release];
|
||||
}
|
||||
|
||||
static NSString * stringForAddress(bd_addr_t* address) {
|
||||
uint8_t *addr = (uint8_t*) *address;
|
||||
return [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2],
|
||||
addr[3], addr[4], addr[5]];
|
||||
}
|
||||
|
||||
static void set_value(bd_addr_t *bd_addr, NSString *key, id value){
|
||||
NSString *devAddress = stringForAddress(bd_addr);
|
||||
NSMutableDictionary * deviceDict = [remote_devices objectForKey:devAddress];
|
||||
if (!deviceDict){
|
||||
deviceDict = [NSMutableDictionary dictionaryWithCapacity:3];
|
||||
[remote_devices setObject:deviceDict forKey:devAddress];
|
||||
}
|
||||
[deviceDict setObject:value forKey:key];
|
||||
db_synchronize();
|
||||
}
|
||||
|
||||
static void delete_value(bd_addr_t *bd_addr, NSString *key){
|
||||
NSString *devAddress = stringForAddress(bd_addr);
|
||||
NSMutableDictionary * deviceDict = [remote_devices objectForKey:devAddress];
|
||||
[deviceDict removeObjectForKey:key];
|
||||
db_synchronize();
|
||||
|
||||
}
|
||||
|
||||
static id get_value(bd_addr_t *bd_addr, NSString *key){
|
||||
NSString *devAddress = stringForAddress(bd_addr);
|
||||
NSMutableDictionary * deviceDict = [remote_devices objectForKey:devAddress];
|
||||
if (!deviceDict) return nil;
|
||||
return [deviceDict objectForKey:key];
|
||||
}
|
||||
|
||||
static int get_link_key(bd_addr_t *bd_addr, link_key_t *link_key) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSData *linkKey = get_value(bd_addr, PREFS_LINK_KEY);
|
||||
if ([linkKey length] == LINK_KEY_LEN){
|
||||
memcpy(link_key, [linkKey bytes], LINK_KEY_LEN);
|
||||
}
|
||||
[pool release];
|
||||
return (linkKey != nil);
|
||||
}
|
||||
|
||||
static void put_link_key(bd_addr_t *bd_addr, link_key_t *link_key){
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSData *linkKey = [NSData dataWithBytes:link_key length:16];
|
||||
set_value(bd_addr, PREFS_LINK_KEY, linkKey);
|
||||
[pool release];
|
||||
}
|
||||
|
||||
static void delete_link_key(bd_addr_t *bd_addr){
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
delete_value(bd_addr, PREFS_LINK_KEY);
|
||||
[pool release];
|
||||
}
|
||||
|
||||
static void put_name(bd_addr_t *bd_addr, device_name_t *device_name){
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString *remoteName = [NSString stringWithUTF8String:(char*)device_name];
|
||||
if (!remoteName){
|
||||
remoteName = [NSString stringWithCString:(char*)device_name encoding:NSISOLatin1StringEncoding];
|
||||
}
|
||||
if (remoteName) {
|
||||
set_value(bd_addr, PREFS_REMOTE_NAME, remoteName);
|
||||
}
|
||||
[pool release];
|
||||
}
|
||||
|
||||
static void delete_name(bd_addr_t *bd_addr){
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
delete_value(bd_addr, PREFS_REMOTE_NAME);
|
||||
[pool release];
|
||||
}
|
||||
|
||||
static int get_name(bd_addr_t *bd_addr, device_name_t *device_name) {
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
NSString *remoteName = get_value(bd_addr, PREFS_REMOTE_NAME);
|
||||
if (remoteName){
|
||||
memset(device_name, 0, sizeof(device_name_t));
|
||||
strncpy((char*) device_name, [remoteName UTF8String], sizeof(device_name_t)-1);
|
||||
}
|
||||
[pool release];
|
||||
return (remoteName != nil);
|
||||
}
|
||||
|
||||
// MARK: PERSISTENT RFCOMM CHANNEL ALLOCATION
|
||||
|
||||
static int firstFreeChannelNr(void){
|
||||
BOOL channelUsed[MAX_RFCOMM_CHANNEL_NR+1];
|
||||
int i;
|
||||
for (i=0; i<=MAX_RFCOMM_CHANNEL_NR ; i++) channelUsed[i] = NO;
|
||||
channelUsed[0] = YES;
|
||||
channelUsed[1] = YES; // preserve channel #1 for testing
|
||||
for (NSDictionary * serviceEntry in [rfcomm_services allValues]){
|
||||
int channel = [(NSNumber *) [serviceEntry objectForKey:PREFS_CHANNEL] intValue];
|
||||
channelUsed[channel] = YES;
|
||||
}
|
||||
for (i=0;i<=MAX_RFCOMM_CHANNEL_NR;i++) {
|
||||
if (channelUsed[i] == NO) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void deleteLeastUsed(void){
|
||||
NSString * leastUsedName = nil;
|
||||
NSDate * leastUsedDate = nil;
|
||||
for (NSString * serviceName in [rfcomm_services allKeys]){
|
||||
NSDictionary *service = [rfcomm_services objectForKey:serviceName];
|
||||
NSDate *serviceDate = [service objectForKey:PREFS_LAST_USED];
|
||||
if (leastUsedName == nil || [leastUsedDate compare:serviceDate] == NSOrderedDescending) {
|
||||
leastUsedName = serviceName;
|
||||
leastUsedDate = serviceDate;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (leastUsedName){
|
||||
// NSLog(@"removing %@", leastUsedName);
|
||||
[rfcomm_services removeObjectForKey:leastUsedName];
|
||||
}
|
||||
}
|
||||
|
||||
static void addService(NSString * serviceName, int channel){
|
||||
NSMutableDictionary * serviceEntry = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
[serviceEntry setObject:[NSNumber numberWithInt:channel] forKey:PREFS_CHANNEL];
|
||||
[serviceEntry setObject:[NSDate date] forKey:PREFS_LAST_USED];
|
||||
[rfcomm_services setObject:serviceEntry forKey:serviceName];
|
||||
}
|
||||
|
||||
static uint8_t persistent_rfcomm_channel(char *serviceName){
|
||||
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
NSLog(@"persistent_rfcomm_channel for %s", serviceName);
|
||||
|
||||
// find existing entry
|
||||
NSString *serviceString = [NSString stringWithUTF8String:serviceName];
|
||||
NSMutableDictionary *serviceEntry = [rfcomm_services objectForKey:serviceString];
|
||||
if (serviceEntry){
|
||||
// update timestamp
|
||||
[serviceEntry setObject:[NSDate date] forKey:PREFS_LAST_USED];
|
||||
|
||||
db_synchronize();
|
||||
|
||||
return [(NSNumber *) [serviceEntry objectForKey:PREFS_CHANNEL] intValue];
|
||||
}
|
||||
// free channel exist?
|
||||
int channel = firstFreeChannelNr();
|
||||
if (channel < 0){
|
||||
// free channel
|
||||
deleteLeastUsed();
|
||||
channel = firstFreeChannelNr();
|
||||
}
|
||||
addService(serviceString, channel);
|
||||
|
||||
db_synchronize();
|
||||
|
||||
[pool release];
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
remote_device_db_t remote_device_db_iphone = {
|
||||
db_open,
|
||||
db_close,
|
||||
get_link_key,
|
||||
put_link_key,
|
||||
delete_link_key,
|
||||
get_name,
|
||||
put_name,
|
||||
delete_name,
|
||||
persistent_rfcomm_channel
|
||||
};
|
||||
|
198
ios/RetroArch/input/BTStack/btstack/remote_device_db_memory.c
Normal file
198
ios/RetroArch/input/BTStack/btstack/remote_device_db_memory.c
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "remote_device_db.h"
|
||||
#include "btstack_memory.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <btstack/utils.h>
|
||||
#include <btstack/linked_list.h>
|
||||
|
||||
// This lists should be only accessed by tests.
|
||||
linked_list_t db_mem_link_keys = NULL;
|
||||
linked_list_t db_mem_names = NULL;
|
||||
static linked_list_t db_mem_services = NULL;
|
||||
|
||||
// Device info
|
||||
static void db_open(void){
|
||||
}
|
||||
|
||||
static void db_close(void){
|
||||
}
|
||||
|
||||
static db_mem_device_t * get_item(linked_list_t list, bd_addr_t *bd_addr) {
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) list; it ; it = it->next){
|
||||
db_mem_device_t * item = (db_mem_device_t *) it;
|
||||
if (BD_ADDR_CMP(item->bd_addr, *bd_addr) == 0) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int get_name(bd_addr_t *bd_addr, device_name_t *device_name) {
|
||||
db_mem_device_name_t * item = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr);
|
||||
|
||||
if (!item) return 0;
|
||||
|
||||
strncpy((char*)device_name, item->device_name, MAX_NAME_LEN);
|
||||
|
||||
linked_list_remove(&db_mem_names, (linked_item_t *) item);
|
||||
linked_list_add(&db_mem_names, (linked_item_t *) item);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_link_key(bd_addr_t *bd_addr, link_key_t *link_key) {
|
||||
db_mem_device_link_key_t * item = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr);
|
||||
|
||||
if (!item) return 0;
|
||||
|
||||
memcpy(link_key, item->link_key, LINK_KEY_LEN);
|
||||
|
||||
linked_list_remove(&db_mem_link_keys, (linked_item_t *) item);
|
||||
linked_list_add(&db_mem_link_keys, (linked_item_t *) item);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void delete_link_key(bd_addr_t *bd_addr){
|
||||
db_mem_device_t * item = get_item(db_mem_link_keys, bd_addr);
|
||||
|
||||
if (!item) return;
|
||||
|
||||
linked_list_remove(&db_mem_link_keys, (linked_item_t *) item);
|
||||
btstack_memory_db_mem_device_link_key_free(item);
|
||||
}
|
||||
|
||||
|
||||
static void put_link_key(bd_addr_t *bd_addr, link_key_t *link_key){
|
||||
db_mem_device_link_key_t * existingRecord = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr);
|
||||
|
||||
if (existingRecord){
|
||||
memcpy(existingRecord->link_key, link_key, LINK_KEY_LEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Record not found, create new one for this device
|
||||
db_mem_device_link_key_t * newItem = (db_mem_device_link_key_t*) btstack_memory_db_mem_device_link_key_get();
|
||||
if (!newItem){
|
||||
newItem = (db_mem_device_link_key_t*)linked_list_get_last_item(&db_mem_link_keys);
|
||||
}
|
||||
|
||||
if (!newItem) return;
|
||||
|
||||
memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t));
|
||||
memcpy(newItem->link_key, link_key, LINK_KEY_LEN);
|
||||
linked_list_add(&db_mem_link_keys, (linked_item_t *) newItem);
|
||||
}
|
||||
|
||||
static void delete_name(bd_addr_t *bd_addr){
|
||||
db_mem_device_t * item = get_item(db_mem_names, bd_addr);
|
||||
|
||||
if (!item) return;
|
||||
|
||||
linked_list_remove(&db_mem_names, (linked_item_t *) item);
|
||||
btstack_memory_db_mem_device_name_free(item);
|
||||
}
|
||||
|
||||
static void put_name(bd_addr_t *bd_addr, device_name_t *device_name){
|
||||
db_mem_device_name_t * existingRecord = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr);
|
||||
|
||||
if (existingRecord){
|
||||
strncpy(existingRecord->device_name, (const char*) device_name, MAX_NAME_LEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Record not found, create a new one for this device
|
||||
db_mem_device_name_t * newItem = (db_mem_device_name_t *) btstack_memory_db_mem_device_name_get();
|
||||
if (!newItem) {
|
||||
newItem = (db_mem_device_name_t*)linked_list_get_last_item(&db_mem_names);
|
||||
};
|
||||
|
||||
if (!newItem) return;
|
||||
|
||||
memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t));
|
||||
strncpy(newItem->device_name, (const char*) device_name, MAX_NAME_LEN);
|
||||
linked_list_add(&db_mem_names, (linked_item_t *) newItem);
|
||||
}
|
||||
|
||||
|
||||
// MARK: PERSISTENT RFCOMM CHANNEL ALLOCATION
|
||||
|
||||
static uint8_t persistent_rfcomm_channel(char *serviceName){
|
||||
linked_item_t *it;
|
||||
db_mem_service_t * item;
|
||||
uint8_t max_channel = 1;
|
||||
|
||||
for (it = (linked_item_t *) db_mem_services; it ; it = it->next){
|
||||
item = (db_mem_service_t *) it;
|
||||
if (strncmp(item->service_name, serviceName, MAX_NAME_LEN) == 0) {
|
||||
// Match found
|
||||
return item->channel;
|
||||
}
|
||||
|
||||
// TODO prevent overflow
|
||||
if (item->channel >= max_channel) max_channel = item->channel + 1;
|
||||
}
|
||||
|
||||
// Allocate new persistant channel
|
||||
db_mem_service_t * newItem = (db_mem_service_t *) btstack_memory_db_mem_service_get();
|
||||
|
||||
if (!newItem) return 0;
|
||||
|
||||
strncpy(newItem->service_name, serviceName, MAX_NAME_LEN);
|
||||
newItem->channel = max_channel;
|
||||
linked_list_add(&db_mem_services, (linked_item_t *) newItem);
|
||||
return max_channel;
|
||||
}
|
||||
|
||||
|
||||
const remote_device_db_t remote_device_db_memory = {
|
||||
db_open,
|
||||
db_close,
|
||||
get_link_key,
|
||||
put_link_key,
|
||||
delete_link_key,
|
||||
get_name,
|
||||
put_name,
|
||||
delete_name,
|
||||
persistent_rfcomm_channel
|
||||
};
|
1922
ios/RetroArch/input/BTStack/btstack/rfcomm.c
Normal file
1922
ios/RetroArch/input/BTStack/btstack/rfcomm.c
Normal file
File diff suppressed because it is too large
Load Diff
278
ios/RetroArch/input/BTStack/btstack/rfcomm.h
Normal file
278
ios/RetroArch/input/BTStack/btstack/rfcomm.h
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* RFCOMM.h
|
||||
*/
|
||||
|
||||
#include <btstack/btstack.h>
|
||||
#include <btstack/utils.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void rfcomm_init(void);
|
||||
|
||||
// register packet handler
|
||||
void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
|
||||
uint16_t channel, uint8_t *packet, uint16_t size));
|
||||
|
||||
// BTstack Internal RFCOMM API
|
||||
void rfcomm_create_channel_internal(void * connection, bd_addr_t *addr, uint8_t channel);
|
||||
void rfcomm_create_channel_with_initial_credits_internal(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t initial_credits);
|
||||
void rfcomm_disconnect_internal(uint16_t rfcomm_cid);
|
||||
void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size);
|
||||
void rfcomm_register_service_with_initial_credits_internal(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t initial_credits);
|
||||
void rfcomm_unregister_service_internal(uint8_t service_channel);
|
||||
void rfcomm_accept_connection_internal(uint16_t rfcomm_cid);
|
||||
void rfcomm_decline_connection_internal(uint16_t rfcomm_cid);
|
||||
void rfcomm_grant_credits(uint16_t rfcomm_cid, uint8_t credits);
|
||||
int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len);
|
||||
void rfcomm_close_connection(void *connection);
|
||||
|
||||
#define UNLIMITED_INCOMING_CREDITS 0xff
|
||||
|
||||
// private structs
|
||||
typedef enum {
|
||||
RFCOMM_MULTIPLEXER_CLOSED = 1,
|
||||
RFCOMM_MULTIPLEXER_W4_CONNECT, // outgoing
|
||||
RFCOMM_MULTIPLEXER_SEND_SABM_0, // "
|
||||
RFCOMM_MULTIPLEXER_W4_UA_0, // "
|
||||
RFCOMM_MULTIPLEXER_W4_SABM_0, // incoming
|
||||
RFCOMM_MULTIPLEXER_SEND_UA_0,
|
||||
RFCOMM_MULTIPLEXER_OPEN,
|
||||
RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC
|
||||
} RFCOMM_MULTIPLEXER_STATE;
|
||||
|
||||
typedef enum {
|
||||
MULT_EV_READY_TO_SEND = 1,
|
||||
|
||||
} RFCOMM_MULTIPLEXER_EVENT;
|
||||
|
||||
typedef enum {
|
||||
RFCOMM_CHANNEL_CLOSED = 1,
|
||||
RFCOMM_CHANNEL_W4_MULTIPLEXER,
|
||||
RFCOMM_CHANNEL_SEND_UIH_PN,
|
||||
RFCOMM_CHANNEL_W4_PN_RSP,
|
||||
RFCOMM_CHANNEL_SEND_SABM_W4_UA,
|
||||
RFCOMM_CHANNEL_W4_UA,
|
||||
RFCOMM_CHANNEL_INCOMING_SETUP,
|
||||
RFCOMM_CHANNEL_DLC_SETUP,
|
||||
RFCOMM_CHANNEL_OPEN,
|
||||
RFCOMM_CHANNEL_SEND_UA_AFTER_DISC,
|
||||
RFCOMM_CHANNEL_SEND_DISC,
|
||||
RFCOMM_CHANNEL_SEND_DM,
|
||||
|
||||
} RFCOMM_CHANNEL_STATE;
|
||||
|
||||
typedef enum {
|
||||
RFCOMM_CHANNEL_STATE_VAR_NONE = 0,
|
||||
RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED = 1 << 0,
|
||||
RFCOMM_CHANNEL_STATE_VAR_RCVD_PN = 1 << 1,
|
||||
RFCOMM_CHANNEL_STATE_VAR_RCVD_RPN = 1 << 2,
|
||||
RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM = 1 << 3,
|
||||
|
||||
RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD = 1 << 4,
|
||||
RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP = 1 << 5,
|
||||
RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP = 1 << 6,
|
||||
RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_INFO = 1 << 7,
|
||||
|
||||
RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP = 1 << 8,
|
||||
RFCOMM_CHANNEL_STATE_VAR_SEND_UA = 1 << 9,
|
||||
RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD = 1 << 10,
|
||||
RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP = 1 << 11,
|
||||
|
||||
RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS = 1 << 12,
|
||||
RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD = 1 << 13,
|
||||
RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP = 1 << 14,
|
||||
RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS = 1 << 15,
|
||||
} RFCOMM_CHANNEL_STATE_VAR;
|
||||
|
||||
typedef enum {
|
||||
CH_EVT_RCVD_SABM = 1,
|
||||
CH_EVT_RCVD_UA,
|
||||
CH_EVT_RCVD_PN,
|
||||
CH_EVT_RCVD_PN_RSP,
|
||||
CH_EVT_RCVD_DISC,
|
||||
CH_EVT_RCVD_DM,
|
||||
CH_EVT_RCVD_MSC_CMD,
|
||||
CH_EVT_RCVD_MSC_RSP,
|
||||
CH_EVT_RCVD_RPN_CMD,
|
||||
CH_EVT_RCVD_RPN_REQ,
|
||||
CH_EVT_RCVD_CREDITS,
|
||||
CH_EVT_MULTIPLEXER_READY,
|
||||
CH_EVT_READY_TO_SEND,
|
||||
} RFCOMM_CHANNEL_EVENT;
|
||||
|
||||
typedef struct rfcomm_channel_event {
|
||||
RFCOMM_CHANNEL_EVENT type;
|
||||
} rfcomm_channel_event_t;
|
||||
|
||||
typedef struct rfcomm_channel_event_pn {
|
||||
rfcomm_channel_event_t super;
|
||||
uint16_t max_frame_size;
|
||||
uint8_t priority;
|
||||
uint8_t credits_outgoing;
|
||||
} rfcomm_channel_event_pn_t;
|
||||
|
||||
typedef struct rfcomm_rpn_data {
|
||||
uint8_t baud_rate;
|
||||
uint8_t flags;
|
||||
uint8_t flow_control;
|
||||
uint8_t xon;
|
||||
uint8_t xoff;
|
||||
uint8_t parameter_mask_0; // first byte
|
||||
uint8_t parameter_mask_1; // second byte
|
||||
} rfcomm_rpn_data_t;
|
||||
|
||||
typedef struct rfcomm_channel_event_rpn {
|
||||
rfcomm_channel_event_t super;
|
||||
rfcomm_rpn_data_t data;
|
||||
} rfcomm_channel_event_rpn_t;
|
||||
|
||||
// info regarding potential connections
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
// server channel
|
||||
uint8_t server_channel;
|
||||
|
||||
// incoming max frame size
|
||||
uint16_t max_frame_size;
|
||||
|
||||
// use incoming flow control
|
||||
uint8_t incoming_flow_control;
|
||||
|
||||
// initial incoming credits
|
||||
uint8_t incoming_initial_credits;
|
||||
|
||||
// client connection
|
||||
void *connection;
|
||||
|
||||
// internal connection
|
||||
btstack_packet_handler_t packet_handler;
|
||||
|
||||
} rfcomm_service_t;
|
||||
|
||||
// info regarding multiplexer
|
||||
// note: spec mandates single multplexer per device combination
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
timer_source_t timer;
|
||||
int timer_active;
|
||||
|
||||
RFCOMM_MULTIPLEXER_STATE state;
|
||||
|
||||
uint16_t l2cap_cid;
|
||||
uint8_t l2cap_credits;
|
||||
|
||||
bd_addr_t remote_addr;
|
||||
hci_con_handle_t con_handle;
|
||||
|
||||
uint8_t outgoing;
|
||||
|
||||
// hack to deal with authentication failure only observed by remote side
|
||||
uint8_t at_least_one_connection;
|
||||
|
||||
uint16_t max_frame_size;
|
||||
|
||||
// send DM for DLCI != 0
|
||||
uint8_t send_dm_for_dlci;
|
||||
|
||||
} rfcomm_multiplexer_t;
|
||||
|
||||
// info regarding an actual coneection
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
rfcomm_multiplexer_t *multiplexer;
|
||||
uint16_t rfcomm_cid;
|
||||
uint8_t outgoing;
|
||||
uint8_t dlci;
|
||||
|
||||
// number of packets granted to client
|
||||
uint8_t packets_granted;
|
||||
|
||||
// credits for outgoing traffic
|
||||
uint8_t credits_outgoing;
|
||||
|
||||
// number of packets remote will be granted
|
||||
uint8_t new_credits_incoming;
|
||||
|
||||
// credits for incoming traffic
|
||||
uint8_t credits_incoming;
|
||||
|
||||
// use incoming flow control
|
||||
uint8_t incoming_flow_control;
|
||||
|
||||
// channel state
|
||||
RFCOMM_CHANNEL_STATE state;
|
||||
|
||||
// state variables used in RFCOMM_CHANNEL_INCOMING
|
||||
RFCOMM_CHANNEL_STATE_VAR state_var;
|
||||
|
||||
// priority set by incoming side in PN
|
||||
uint8_t pn_priority;
|
||||
|
||||
// negotiated frame size
|
||||
uint16_t max_frame_size;
|
||||
|
||||
// rpn data
|
||||
rfcomm_rpn_data_t rpn_data;
|
||||
|
||||
// server channel (see rfcomm_service_t) - NULL => outgoing channel
|
||||
rfcomm_service_t * service;
|
||||
|
||||
// internal connection
|
||||
btstack_packet_handler_t packet_handler;
|
||||
|
||||
// client connection
|
||||
void * connection;
|
||||
|
||||
} rfcomm_channel_t;
|
||||
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
153
ios/RetroArch/input/BTStack/btstack/run_loop.c
Normal file
153
ios/RetroArch/input/BTStack/btstack/run_loop.c
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* run_loop.c
|
||||
*
|
||||
* Created by Matthias Ringwald on 6/6/09.
|
||||
*/
|
||||
|
||||
#include <btstack/run_loop.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // exit()
|
||||
|
||||
#include "run_loop_private.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "config.h"
|
||||
|
||||
static run_loop_t * the_run_loop = NULL;
|
||||
|
||||
extern const run_loop_t run_loop_embedded;
|
||||
|
||||
#ifdef USE_POSIX_RUN_LOOP
|
||||
extern run_loop_t run_loop_posix;
|
||||
#endif
|
||||
|
||||
#ifdef USE_COCOA_RUN_LOOP
|
||||
extern run_loop_t run_loop_cocoa;
|
||||
#endif
|
||||
|
||||
// assert run loop initialized
|
||||
void run_loop_assert(void){
|
||||
#ifndef EMBEDDED
|
||||
if (!the_run_loop){
|
||||
log_error("ERROR: run_loop function called before run_loop_init!\n");
|
||||
exit(10);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data_source to run_loop
|
||||
*/
|
||||
void run_loop_add_data_source(data_source_t *ds){
|
||||
run_loop_assert();
|
||||
the_run_loop->add_data_source(ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data_source from run loop
|
||||
*/
|
||||
int run_loop_remove_data_source(data_source_t *ds){
|
||||
run_loop_assert();
|
||||
return the_run_loop->remove_data_source(ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add timer to run_loop (keep list sorted)
|
||||
*/
|
||||
void run_loop_add_timer(timer_source_t *ts){
|
||||
run_loop_assert();
|
||||
the_run_loop->add_timer(ts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove timer from run loop
|
||||
*/
|
||||
int run_loop_remove_timer(timer_source_t *ts){
|
||||
run_loop_assert();
|
||||
return the_run_loop->remove_timer(ts);
|
||||
}
|
||||
|
||||
void run_loop_timer_dump(){
|
||||
run_loop_assert();
|
||||
the_run_loop->dump_timer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute run_loop
|
||||
*/
|
||||
void run_loop_execute() {
|
||||
run_loop_assert();
|
||||
the_run_loop->execute();
|
||||
}
|
||||
|
||||
// init must be called before any other run_loop call
|
||||
void run_loop_init(RUN_LOOP_TYPE type){
|
||||
#ifndef EMBEDDED
|
||||
if (the_run_loop){
|
||||
log_error("ERROR: run loop initialized twice!\n");
|
||||
exit(10);
|
||||
}
|
||||
#endif
|
||||
switch (type) {
|
||||
#ifdef EMBEDDED
|
||||
case RUN_LOOP_EMBEDDED:
|
||||
the_run_loop = (run_loop_t*) &run_loop_embedded;
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_POSIX_RUN_LOOP
|
||||
case RUN_LOOP_POSIX:
|
||||
the_run_loop = &run_loop_posix;
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_COCOA_RUN_LOOP
|
||||
case RUN_LOOP_COCOA:
|
||||
the_run_loop = &run_loop_cocoa;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
#ifndef EMBEDDED
|
||||
log_error("ERROR: invalid run loop type %u selected!\n", type);
|
||||
exit(10);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
the_run_loop->init();
|
||||
}
|
||||
|
@ -37,10 +37,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include "config.h"
|
||||
#define HAVE_TIME
|
||||
#include "config.h"
|
||||
|
||||
#include "linked_list.h"
|
||||
#include <btstack/linked_list.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@ -76,44 +75,30 @@ typedef struct timer {
|
||||
} timer_source_t;
|
||||
|
||||
|
||||
// Set timer based on current time in milliseconds.
|
||||
// set timer based on current time
|
||||
void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms);
|
||||
|
||||
// Set callback that will be executed when timer expires.
|
||||
void run_loop_set_timer_handler(timer_source_t *ts, void (*process)(timer_source_t *_ts));
|
||||
|
||||
// Add/Remove timer source.
|
||||
// add/remove timer_source
|
||||
void run_loop_add_timer(timer_source_t *timer);
|
||||
int run_loop_remove_timer(timer_source_t *timer);
|
||||
|
||||
// Init must be called before any other run_loop call.
|
||||
// Use RUN_LOOP_EMBEDDED for embedded devices.
|
||||
// init must be called before any other run_loop call
|
||||
void run_loop_init(RUN_LOOP_TYPE type);
|
||||
|
||||
// Set data source callback.
|
||||
void run_loop_set_data_source_handler(data_source_t *ds, int (*process)(data_source_t *_ds));
|
||||
|
||||
|
||||
// Add/Remove data source.
|
||||
// add/remove data_source
|
||||
void run_loop_add_data_source(data_source_t *dataSource);
|
||||
int run_loop_remove_data_source(data_source_t *dataSource);
|
||||
|
||||
|
||||
// Execute configured run loop. This function does not return.
|
||||
// execute configured run_loop
|
||||
void run_loop_execute(void);
|
||||
|
||||
// hack to fix HCI timer handling
|
||||
#ifdef HAVE_TICK
|
||||
// Sets how many miliseconds has one tick.
|
||||
uint32_t embedded_ticks_for_ms(uint32_t time_in_ms);
|
||||
// Queries the current time in ticks.
|
||||
uint32_t embedded_get_ticks(void);
|
||||
uint32_t embedded_ticks_for_ms(uint32_t time_in_ms);
|
||||
#endif
|
||||
#ifdef EMBEDDED
|
||||
// Sets an internal flag that is checked in the critical section
|
||||
// just before entering sleep mode. Has to be called by the interupt
|
||||
// handler of a data source to signal the run loop that a new data
|
||||
// is available.
|
||||
void embedded_trigger(void);
|
||||
#endif
|
||||
#if defined __cplusplus
|
||||
|
163
ios/RetroArch/input/BTStack/btstack/run_loop_cocoa.m
Normal file
163
ios/RetroArch/input/BTStack/btstack/run_loop_cocoa.m
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* run_loop_cocoa.c
|
||||
*
|
||||
* Created by Matthias Ringwald on 8/2/09.
|
||||
*/
|
||||
|
||||
#include <btstack/btstack.h>
|
||||
|
||||
#include <btstack/run_loop.h>
|
||||
#include "run_loop_private.h"
|
||||
#include "debug.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void theCFRunLoopTimerCallBack (CFRunLoopTimerRef timer,void *info){
|
||||
timer_source_t * ts = (timer_source_t*)info;
|
||||
ts->process(ts);
|
||||
}
|
||||
|
||||
static void socketDataCallback (
|
||||
CFSocketRef s,
|
||||
CFSocketCallBackType callbackType,
|
||||
CFDataRef address,
|
||||
const void *data,
|
||||
void *info) {
|
||||
|
||||
if (callbackType == kCFSocketReadCallBack && info){
|
||||
data_source_t *dataSource = (data_source_t *) info;
|
||||
// printf("cocoa_data_source %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->fd, (int) s, (int) dataSource->item.next);
|
||||
dataSource->process(dataSource);
|
||||
}
|
||||
}
|
||||
|
||||
void cocoa_add_data_source(data_source_t *dataSource){
|
||||
|
||||
// add fd as CFSocket
|
||||
|
||||
// store our dataSource in socket context
|
||||
CFSocketContext socketContext;
|
||||
memset(&socketContext, 0, sizeof(CFSocketContext));
|
||||
socketContext.info = dataSource;
|
||||
|
||||
// create CFSocket from file descriptor
|
||||
CFSocketRef socket = CFSocketCreateWithNative (
|
||||
kCFAllocatorDefault,
|
||||
dataSource->fd,
|
||||
kCFSocketReadCallBack,
|
||||
socketDataCallback,
|
||||
&socketContext
|
||||
);
|
||||
|
||||
// don't close native fd on CFSocketInvalidate
|
||||
CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate);
|
||||
|
||||
// create run loop source
|
||||
CFRunLoopSourceRef socketRunLoop = CFSocketCreateRunLoopSource ( kCFAllocatorDefault, socket, 0);
|
||||
|
||||
// hack: store CFSocketRef in "next" and CFRunLoopSourceRef in "user_data" of linked_item_t
|
||||
dataSource->item.next = (void *) socket;
|
||||
dataSource->item.user_data = (void *) socketRunLoop;
|
||||
|
||||
// add to run loop
|
||||
CFRunLoopAddSource( CFRunLoopGetCurrent(), socketRunLoop, kCFRunLoopCommonModes);
|
||||
// printf("cocoa_add_data_source %x - fd %u - CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->fd, (int) socket, (int) socketRunLoop);
|
||||
|
||||
}
|
||||
|
||||
int cocoa_remove_data_source(data_source_t *dataSource){
|
||||
// printf("cocoa_remove_data_source %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->fd, (int) dataSource->item.next, (int) dataSource->item.user_data);
|
||||
CFRunLoopRemoveSource( CFRunLoopGetCurrent(), (CFRunLoopSourceRef) dataSource->item.user_data, kCFRunLoopCommonModes);
|
||||
CFRelease(dataSource->item.user_data);
|
||||
CFSocketInvalidate((CFSocketRef) dataSource->item.next);
|
||||
CFRelease(dataSource->item.next);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cocoa_add_timer(timer_source_t * ts)
|
||||
{
|
||||
// note: ts uses unix time: seconds since Jan 1st 1970, CF uses Jan 1st 2001 as reference date
|
||||
// printf("kCFAbsoluteTimeIntervalSince1970 = %f\n", kCFAbsoluteTimeIntervalSince1970);
|
||||
CFAbsoluteTime fireDate = ((double)ts->timeout.tv_sec) + (((double)ts->timeout.tv_usec)/1000000.0) - kCFAbsoluteTimeIntervalSince1970; // unix time - since Jan 1st 1970
|
||||
CFRunLoopTimerContext timerContext = {0, ts, NULL, NULL, NULL};
|
||||
CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate (kCFAllocatorDefault,fireDate,0,0,0,theCFRunLoopTimerCallBack,&timerContext);
|
||||
CFRetain(timerRef);
|
||||
|
||||
// hack: store CFRunLoopTimerRef in next pointer of linked_item
|
||||
ts->item.next = (void *)timerRef;
|
||||
// printf("cocoa_add_timer %x -> %x now %f, then %f\n", (int) ts, (int) ts->item.next, CFAbsoluteTimeGetCurrent(),fireDate);
|
||||
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerRef, kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
int cocoa_remove_timer(timer_source_t * ts){
|
||||
// printf("cocoa_remove_timer %x -> %x\n", (int) ts, (int) ts->item.next);
|
||||
if (ts->item.next != NULL) {
|
||||
CFRunLoopTimerInvalidate((CFRunLoopTimerRef) ts->item.next); // also removes timer from run loops + releases it
|
||||
CFRelease((CFRunLoopTimerRef) ts->item.next);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cocoa_init(void){
|
||||
}
|
||||
|
||||
void cocoa_execute(void)
|
||||
{
|
||||
CFRunLoopRun();
|
||||
}
|
||||
|
||||
void cocoa_dump_timer(void){
|
||||
log_error("WARNING: run_loop_dump_timer not implemented!");
|
||||
return;
|
||||
}
|
||||
|
||||
run_loop_t run_loop_cocoa = {
|
||||
&cocoa_init,
|
||||
&cocoa_add_data_source,
|
||||
&cocoa_remove_data_source,
|
||||
&cocoa_add_timer,
|
||||
&cocoa_remove_timer,
|
||||
&cocoa_execute,
|
||||
&cocoa_dump_timer
|
||||
};
|
||||
|
217
ios/RetroArch/input/BTStack/btstack/run_loop_embedded.c
Normal file
217
ios/RetroArch/input/BTStack/btstack/run_loop_embedded.c
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* run_loop_embedded.c
|
||||
*
|
||||
* For this run loop, we assume that there's no global way to wait for a list
|
||||
* of data sources to get ready. Instead, each data source has to queried
|
||||
* individually. Calling ds->isReady() before calling ds->process() doesn't
|
||||
* make sense, so we just poll each data source round robin.
|
||||
*
|
||||
* To support an idle state, where an MCU could go to sleep, the process function
|
||||
* has to return if it has to called again as soon as possible
|
||||
*
|
||||
* After calling process() on every data source and evaluating the pending timers,
|
||||
* the idle hook gets called if no data source did indicate that it needs to be
|
||||
* called right away.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <btstack/run_loop.h>
|
||||
#include <btstack/linked_list.h>
|
||||
#include <btstack/hal_tick.h>
|
||||
#include <btstack/hal_cpu.h>
|
||||
|
||||
#include "run_loop_private.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stddef.h> // NULL
|
||||
|
||||
// the run loop
|
||||
static linked_list_t data_sources;
|
||||
|
||||
static linked_list_t timers;
|
||||
|
||||
#ifdef HAVE_TICK
|
||||
static uint32_t system_ticks;
|
||||
#endif
|
||||
|
||||
static int trigger_event_received = 0;
|
||||
|
||||
/**
|
||||
* trigger run loop iteration
|
||||
*/
|
||||
void embedded_trigger(void){
|
||||
trigger_event_received = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data_source to run_loop
|
||||
*/
|
||||
void embedded_add_data_source(data_source_t *ds){
|
||||
linked_list_add(&data_sources, (linked_item_t *) ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data_source from run loop
|
||||
*/
|
||||
int embedded_remove_data_source(data_source_t *ds){
|
||||
return linked_list_remove(&data_sources, (linked_item_t *) ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add timer to run_loop (keep list sorted)
|
||||
*/
|
||||
void embedded_add_timer(timer_source_t *ts){
|
||||
#ifdef HAVE_TICK
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) &timers; it->next ; it = it->next){
|
||||
if (ts->timeout < ((timer_source_t *) it->next)->timeout) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ts->item.next = it->next;
|
||||
it->next = (linked_item_t *) ts;
|
||||
// log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
|
||||
// embedded_dump_timer();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove timer from run loop
|
||||
*/
|
||||
int embedded_remove_timer(timer_source_t *ts){
|
||||
#ifdef HAVE_TICK
|
||||
// log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
|
||||
return linked_list_remove(&timers, (linked_item_t *) ts);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void embedded_dump_timer(void){
|
||||
#ifdef HAVE_TICK
|
||||
#ifdef ENABLE_LOG_INFO
|
||||
linked_item_t *it;
|
||||
int i = 0;
|
||||
for (it = (linked_item_t *) timers; it ; it = it->next){
|
||||
timer_source_t *ts = (timer_source_t*) it;
|
||||
log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute run_loop
|
||||
*/
|
||||
void embedded_execute(void) {
|
||||
data_source_t *ds;
|
||||
|
||||
while (1) {
|
||||
|
||||
// process data sources
|
||||
data_source_t *next;
|
||||
for (ds = (data_source_t *) data_sources; ds != NULL ; ds = next){
|
||||
next = (data_source_t *) ds->item.next; // cache pointer to next data_source to allow data source to remove itself
|
||||
ds->process(ds);
|
||||
}
|
||||
|
||||
#ifdef HAVE_TICK
|
||||
// process timers
|
||||
while (timers) {
|
||||
timer_source_t *ts = (timer_source_t *) timers;
|
||||
if (ts->timeout > system_ticks) break;
|
||||
run_loop_remove_timer(ts);
|
||||
ts->process(ts);
|
||||
}
|
||||
#endif
|
||||
|
||||
// disable IRQs and check if run loop iteration has been requested. if not, go to sleep
|
||||
hal_cpu_disable_irqs();
|
||||
if (trigger_event_received){
|
||||
hal_cpu_enable_irqs_and_sleep();
|
||||
continue;
|
||||
}
|
||||
hal_cpu_enable_irqs();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_TICK
|
||||
static void embedded_tick_handler(void){
|
||||
system_ticks++;
|
||||
trigger_event_received = 1;
|
||||
}
|
||||
|
||||
uint32_t embedded_get_ticks(void){
|
||||
return system_ticks;
|
||||
}
|
||||
|
||||
uint32_t embedded_ticks_for_ms(uint32_t time_in_ms){
|
||||
return time_in_ms / hal_tick_get_tick_period_in_ms();
|
||||
}
|
||||
|
||||
// set timer
|
||||
void run_loop_set_timer(timer_source_t *ts, uint32_t timeout_in_ms){
|
||||
uint32_t ticks = embedded_ticks_for_ms(timeout_in_ms);
|
||||
if (ticks == 0) ticks++;
|
||||
ts->timeout = system_ticks + ticks;
|
||||
}
|
||||
#endif
|
||||
|
||||
void embedded_init(void){
|
||||
|
||||
data_sources = NULL;
|
||||
|
||||
#ifdef HAVE_TICK
|
||||
timers = NULL;
|
||||
system_ticks = 0;
|
||||
hal_tick_init();
|
||||
hal_tick_set_handler(&embedded_tick_handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
const run_loop_t run_loop_embedded = {
|
||||
&embedded_init,
|
||||
&embedded_add_data_source,
|
||||
&embedded_remove_data_source,
|
||||
&embedded_add_timer,
|
||||
&embedded_remove_timer,
|
||||
&embedded_execute,
|
||||
&embedded_dump_timer
|
||||
};
|
250
ios/RetroArch/input/BTStack/btstack/run_loop_posix.c
Normal file
250
ios/RetroArch/input/BTStack/btstack/run_loop_posix.c
Normal file
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* run_loop.c
|
||||
*
|
||||
* Created by Matthias Ringwald on 6/6/09.
|
||||
*/
|
||||
|
||||
#include <btstack/run_loop.h>
|
||||
#include <btstack/linked_list.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "run_loop_private.h"
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void posix_dump_timer(void);
|
||||
|
||||
// the run loop
|
||||
static linked_list_t data_sources;
|
||||
static int data_sources_modified;
|
||||
static linked_list_t timers;
|
||||
|
||||
/**
|
||||
* Add data_source to run_loop
|
||||
*/
|
||||
void posix_add_data_source(data_source_t *ds){
|
||||
data_sources_modified = 1;
|
||||
// log_info("posix_add_data_source %x with fd %u\n", (int) ds, ds->fd);
|
||||
linked_list_add(&data_sources, (linked_item_t *) ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data_source from run loop
|
||||
*/
|
||||
int posix_remove_data_source(data_source_t *ds){
|
||||
data_sources_modified = 1;
|
||||
// log_info("posix_remove_data_source %x\n", (int) ds);
|
||||
return linked_list_remove(&data_sources, (linked_item_t *) ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add timer to run_loop (keep list sorted)
|
||||
*/
|
||||
void posix_add_timer(timer_source_t *ts){
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) &timers; it->next ; it = it->next){
|
||||
if ((timer_source_t *) it->next == ts){
|
||||
log_error( "run_loop_timer_add error: timer to add already in list!\n");
|
||||
return;
|
||||
}
|
||||
if (run_loop_timer_compare( (timer_source_t *) it->next, ts) > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ts->item.next = it->next;
|
||||
it->next = (linked_item_t *) ts;
|
||||
// log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
|
||||
// posix_dump_timer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove timer from run loop
|
||||
*/
|
||||
int posix_remove_timer(timer_source_t *ts){
|
||||
// log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
|
||||
// posix_dump_timer();
|
||||
return linked_list_remove(&timers, (linked_item_t *) ts);
|
||||
}
|
||||
|
||||
void posix_dump_timer(void){
|
||||
linked_item_t *it;
|
||||
int i = 0;
|
||||
for (it = (linked_item_t *) timers; it ; it = it->next){
|
||||
timer_source_t *ts = (timer_source_t*) it;
|
||||
log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout.tv_sec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute run_loop
|
||||
*/
|
||||
void posix_execute(void) {
|
||||
fd_set descriptors;
|
||||
data_source_t *ds;
|
||||
timer_source_t *ts;
|
||||
struct timeval current_tv;
|
||||
struct timeval next_tv;
|
||||
struct timeval *timeout;
|
||||
|
||||
while (1) {
|
||||
// collect FDs
|
||||
FD_ZERO(&descriptors);
|
||||
int highest_fd = 0;
|
||||
for (ds = (data_source_t *) data_sources; ds ; ds = (data_source_t *) ds->item.next){
|
||||
if (ds->fd >= 0) {
|
||||
FD_SET(ds->fd, &descriptors);
|
||||
if (ds->fd > highest_fd) {
|
||||
highest_fd = ds->fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get next timeout
|
||||
// pre: 0 <= tv_usec < 1000000
|
||||
timeout = NULL;
|
||||
if (timers) {
|
||||
gettimeofday(¤t_tv, NULL);
|
||||
ts = (timer_source_t *) timers;
|
||||
next_tv.tv_usec = ts->timeout.tv_usec - current_tv.tv_usec;
|
||||
next_tv.tv_sec = ts->timeout.tv_sec - current_tv.tv_sec;
|
||||
while (next_tv.tv_usec < 0){
|
||||
next_tv.tv_usec += 1000000;
|
||||
next_tv.tv_sec--;
|
||||
}
|
||||
if (next_tv.tv_sec < 0 || (next_tv.tv_sec == 0 && next_tv.tv_usec < 0)){
|
||||
next_tv.tv_sec = 0;
|
||||
next_tv.tv_usec = 0;
|
||||
}
|
||||
timeout = &next_tv;
|
||||
}
|
||||
|
||||
// wait for ready FDs
|
||||
select( highest_fd+1 , &descriptors, NULL, NULL, timeout);
|
||||
|
||||
// process data sources very carefully
|
||||
// bt_control.close() triggered from a client can remove a different data source
|
||||
|
||||
// log_info("posix_execute: before ds check\n");
|
||||
data_sources_modified = 0;
|
||||
for (ds = (data_source_t *) data_sources; !data_sources_modified && ds != NULL; ds = (data_source_t *) ds->item.next){
|
||||
// log_info("posix_execute: check %x with fd %u\n", (int) ds, ds->fd);
|
||||
if (FD_ISSET(ds->fd, &descriptors)) {
|
||||
// log_info("posix_execute: process %x with fd %u\n", (int) ds, ds->fd);
|
||||
ds->process(ds);
|
||||
}
|
||||
}
|
||||
// log_info("posix_execute: after ds check\n");
|
||||
|
||||
// process timers
|
||||
// pre: 0 <= tv_usec < 1000000
|
||||
while (timers) {
|
||||
gettimeofday(¤t_tv, NULL);
|
||||
ts = (timer_source_t *) timers;
|
||||
if (ts->timeout.tv_sec > current_tv.tv_sec) break;
|
||||
if (ts->timeout.tv_sec == current_tv.tv_sec && ts->timeout.tv_usec > current_tv.tv_usec) break;
|
||||
// log_info("posix_execute: process times %x\n", (int) ts);
|
||||
|
||||
// remove timer before processing it to allow handler to re-register with run loop
|
||||
run_loop_remove_timer(ts);
|
||||
ts->process(ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set timer
|
||||
void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms){
|
||||
gettimeofday(&a->timeout, NULL);
|
||||
a->timeout.tv_sec += timeout_in_ms / 1000;
|
||||
a->timeout.tv_usec += (timeout_in_ms % 1000) * 1000;
|
||||
if (a->timeout.tv_usec > 1000000) {
|
||||
a->timeout.tv_usec -= 1000000;
|
||||
a->timeout.tv_sec++;
|
||||
}
|
||||
}
|
||||
|
||||
// compare timers - NULL is assumed to be before the Big Bang
|
||||
// pre: 0 <= tv_usec < 1000000
|
||||
int run_loop_timeval_compare(struct timeval *a, struct timeval *b){
|
||||
if (!a && !b) return 0;
|
||||
if (!a) return -1;
|
||||
if (!b) return 1;
|
||||
|
||||
if (a->tv_sec < b->tv_sec) {
|
||||
return -1;
|
||||
}
|
||||
if (a->tv_sec > b->tv_sec) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (a->tv_usec < b->tv_usec) {
|
||||
return -1;
|
||||
}
|
||||
if (a->tv_usec > b->tv_usec) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
// compare timers - NULL is assumed to be before the Big Bang
|
||||
// pre: 0 <= tv_usec < 1000000
|
||||
int run_loop_timer_compare(timer_source_t *a, timer_source_t *b){
|
||||
if (!a && !b) return 0;
|
||||
if (!a) return -1;
|
||||
if (!b) return 1;
|
||||
return run_loop_timeval_compare(&a->timeout, &b->timeout);
|
||||
}
|
||||
|
||||
void posix_init(void){
|
||||
data_sources = NULL;
|
||||
timers = NULL;
|
||||
}
|
||||
|
||||
run_loop_t run_loop_posix = {
|
||||
&posix_init,
|
||||
&posix_add_data_source,
|
||||
&posix_remove_data_source,
|
||||
&posix_add_timer,
|
||||
&posix_remove_timer,
|
||||
&posix_execute,
|
||||
&posix_dump_timer
|
||||
};
|
68
ios/RetroArch/input/BTStack/btstack/run_loop_private.h
Normal file
68
ios/RetroArch/input/BTStack/btstack/run_loop_private.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* run_loop_private.h
|
||||
*
|
||||
* Created by Matthias Ringwald on 6/6/09.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <btstack/run_loop.h>
|
||||
|
||||
#ifdef HAVE_TIME
|
||||
#include <sys/time.h>
|
||||
|
||||
// compare timeval or timers - NULL is assumed to be before the Big Bang
|
||||
int run_loop_timeval_compare(struct timeval *a, struct timeval *b);
|
||||
int run_loop_timer_compare(timer_source_t *a, timer_source_t *b);
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
void run_loop_timer_dump(void);
|
||||
|
||||
// internal use only
|
||||
typedef struct {
|
||||
void (*init)(void);
|
||||
void (*add_data_source)(data_source_t *dataSource);
|
||||
int (*remove_data_source)(data_source_t *dataSource);
|
||||
void (*add_timer)(timer_source_t *timer);
|
||||
int (*remove_timer)(timer_source_t *timer);
|
||||
void (*execute)(void);
|
||||
void (*dump_timer)(void);
|
||||
} run_loop_t;
|
662
ios/RetroArch/input/BTStack/btstack/sdp.c
Normal file
662
ios/RetroArch/input/BTStack/btstack/sdp.c
Normal file
@ -0,0 +1,662 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implementation of the Service Discovery Protocol Server
|
||||
*/
|
||||
|
||||
#include "sdp.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <btstack/sdp_util.h>
|
||||
|
||||
#include "hci_dump.h"
|
||||
#include "l2cap.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
// max reserved ServiceRecordHandle
|
||||
#define maxReservedServiceRecordHandle 0xffff
|
||||
|
||||
// max SDP response
|
||||
#define SDP_RESPONSE_BUFFER_SIZE (HCI_ACL_BUFFER_SIZE-HCI_ACL_HEADER_SIZE)
|
||||
|
||||
static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
|
||||
// registered service records
|
||||
static linked_list_t sdp_service_records = NULL;
|
||||
|
||||
// our handles start after the reserved range
|
||||
static uint32_t sdp_next_service_record_handle = maxReservedServiceRecordHandle + 2;
|
||||
|
||||
static uint8_t sdp_response_buffer[SDP_RESPONSE_BUFFER_SIZE];
|
||||
|
||||
static void (*app_packet_handler)(void * connection, uint8_t packet_type,
|
||||
uint16_t channel, uint8_t *packet, uint16_t size) = NULL;
|
||||
|
||||
static uint16_t l2cap_cid = 0;
|
||||
static uint16_t sdp_response_size = 0;
|
||||
|
||||
void sdp_init(){
|
||||
// register with l2cap psm sevices - max MTU
|
||||
l2cap_register_service_internal(NULL, sdp_packet_handler, PSM_SDP, 0xffff);
|
||||
}
|
||||
|
||||
// register packet handler
|
||||
void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
|
||||
uint16_t channel, uint8_t *packet, uint16_t size)){
|
||||
app_packet_handler = handler;
|
||||
l2cap_cid = 0;
|
||||
}
|
||||
|
||||
uint32_t sdp_get_service_record_handle(uint8_t * record){
|
||||
uint8_t * serviceRecordHandleAttribute = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle);
|
||||
if (!serviceRecordHandleAttribute) return 0;
|
||||
if (de_get_element_type(serviceRecordHandleAttribute) != DE_UINT) return 0;
|
||||
if (de_get_size_type(serviceRecordHandleAttribute) != DE_SIZE_32) return 0;
|
||||
return READ_NET_32(serviceRecordHandleAttribute, 1);
|
||||
}
|
||||
|
||||
// data: event(8), len(8), status(8), service_record_handle(32)
|
||||
static void sdp_emit_service_registered(void *connection, uint32_t handle, uint8_t status) {
|
||||
if (!app_packet_handler) return;
|
||||
uint8_t event[7];
|
||||
event[0] = SDP_SERVICE_REGISTERED;
|
||||
event[1] = sizeof(event) - 2;
|
||||
event[2] = status;
|
||||
bt_store_32(event, 3, handle);
|
||||
hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event));
|
||||
(*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event));
|
||||
}
|
||||
|
||||
service_record_item_t * sdp_get_record_for_handle(uint32_t handle){
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
|
||||
service_record_item_t * item = (service_record_item_t *) it;
|
||||
if (item->service_record_handle == handle){
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get next free, unregistered service record handle
|
||||
uint32_t sdp_create_service_record_handle(void){
|
||||
uint32_t handle = 0;
|
||||
do {
|
||||
handle = sdp_next_service_record_handle++;
|
||||
if (sdp_get_record_for_handle(handle)) handle = 0;
|
||||
} while (handle == 0);
|
||||
return handle;
|
||||
}
|
||||
|
||||
#ifdef EMBEDDED
|
||||
|
||||
// register service record internally - this special version doesn't copy the record, it should not be freeed
|
||||
// pre: AttributeIDs are in ascending order
|
||||
// pre: ServiceRecordHandle is first attribute and valid
|
||||
// pre: record
|
||||
// @returns ServiceRecordHandle or 0 if registration failed
|
||||
uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item){
|
||||
// get user record handle
|
||||
uint32_t record_handle = record_item->service_record_handle;
|
||||
// get actual record
|
||||
uint8_t *record = record_item->service_record;
|
||||
|
||||
// check for ServiceRecordHandle attribute, returns pointer or null
|
||||
uint8_t * req_record_handle = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle);
|
||||
if (!req_record_handle) {
|
||||
log_error("SDP Error - record does not contain ServiceRecordHandle attribute\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// validate service record handle is not in reserved range
|
||||
if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0;
|
||||
|
||||
// check if already in use
|
||||
if (record_handle) {
|
||||
if (sdp_get_record_for_handle(record_handle)) {
|
||||
record_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// create new handle if needed
|
||||
if (!record_handle){
|
||||
record_handle = sdp_create_service_record_handle();
|
||||
// Write the handle back into the record too
|
||||
record_item->service_record_handle = record_handle;
|
||||
sdp_set_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle, record_handle);
|
||||
}
|
||||
|
||||
// add to linked list
|
||||
linked_list_add(&sdp_service_records, (linked_item_t *) record_item);
|
||||
|
||||
sdp_emit_service_registered(connection, 0, record_item->service_record_handle);
|
||||
|
||||
return record_handle;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// AttributeIDList used to remove ServiceRecordHandle
|
||||
static const uint8_t removeServiceRecordHandleAttributeIDList[] = { 0x36, 0x00, 0x05, 0x0A, 0x00, 0x01, 0xFF, 0xFF };
|
||||
|
||||
// register service record internally - the normal version creates a copy of the record
|
||||
// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present
|
||||
// @returns ServiceRecordHandle or 0 if registration failed
|
||||
uint32_t sdp_register_service_internal(void *connection, uint8_t * record){
|
||||
|
||||
// dump for now
|
||||
// printf("Register service record\n");
|
||||
// de_dump_data_element(record);
|
||||
|
||||
// get user record handle
|
||||
uint32_t record_handle = sdp_get_service_record_handle(record);
|
||||
|
||||
// validate service record handle is not in reserved range
|
||||
if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0;
|
||||
|
||||
// check if already in use
|
||||
if (record_handle) {
|
||||
if (sdp_get_record_for_handle(record_handle)) {
|
||||
record_handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// create new handle if needed
|
||||
if (!record_handle){
|
||||
record_handle = sdp_create_service_record_handle();
|
||||
}
|
||||
|
||||
// calculate size of new service record: DES (2 byte len)
|
||||
// + ServiceRecordHandle attribute (UINT16 UINT32) + size of existing attributes
|
||||
uint16_t recordSize = 3 + (3 + 5) + de_get_data_size(record);
|
||||
|
||||
// alloc memory for new service_record_item
|
||||
service_record_item_t * newRecordItem = (service_record_item_t *) malloc(recordSize + sizeof(service_record_item_t));
|
||||
if (!newRecordItem) {
|
||||
sdp_emit_service_registered(connection, 0, BTSTACK_MEMORY_ALLOC_FAILED);
|
||||
return 0;
|
||||
}
|
||||
// link new service item to client connection
|
||||
newRecordItem->connection = connection;
|
||||
|
||||
// set new handle
|
||||
newRecordItem->service_record_handle = record_handle;
|
||||
|
||||
// create updated service record
|
||||
uint8_t * newRecord = (uint8_t *) &(newRecordItem->service_record);
|
||||
|
||||
// create DES for new record
|
||||
de_create_sequence(newRecord);
|
||||
|
||||
// set service record handle
|
||||
de_add_number(newRecord, DE_UINT, DE_SIZE_16, 0);
|
||||
de_add_number(newRecord, DE_UINT, DE_SIZE_32, record_handle);
|
||||
|
||||
// add other attributes
|
||||
sdp_append_attributes_in_attributeIDList(record, (uint8_t *) removeServiceRecordHandleAttributeIDList, 0, recordSize, newRecord);
|
||||
|
||||
// dump for now
|
||||
// de_dump_data_element(newRecord);
|
||||
// printf("reserved size %u, actual size %u\n", recordSize, de_get_len(newRecord));
|
||||
|
||||
// add to linked list
|
||||
linked_list_add(&sdp_service_records, (linked_item_t *) newRecordItem);
|
||||
|
||||
sdp_emit_service_registered(connection, 0, newRecordItem->service_record_handle);
|
||||
|
||||
return record_handle;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// unregister service record internally
|
||||
//
|
||||
// makes sure one client cannot remove service records of other clients
|
||||
//
|
||||
void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle){
|
||||
service_record_item_t * record_item = sdp_get_record_for_handle(service_record_handle);
|
||||
if (record_item && record_item->connection == connection) {
|
||||
linked_list_remove(&sdp_service_records, (linked_item_t *) record_item);
|
||||
}
|
||||
}
|
||||
|
||||
// remove all service record for a client connection
|
||||
void sdp_unregister_services_for_connection(void *connection){
|
||||
linked_item_t *it = (linked_item_t *) &sdp_service_records;
|
||||
while (it->next){
|
||||
service_record_item_t *record_item = (service_record_item_t *) it->next;
|
||||
if (record_item->connection == connection){
|
||||
it->next = it->next->next;
|
||||
#ifndef EMBEDDED
|
||||
free(record_item);
|
||||
#endif
|
||||
} else {
|
||||
it = it->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PDU
|
||||
// PDU ID (1), Transaction ID (2), Param Length (2), Param 1, Param 2, ..
|
||||
|
||||
int sdp_create_error_response(uint16_t transaction_id, uint16_t error_code){
|
||||
sdp_response_buffer[0] = SDP_ErrorResponse;
|
||||
net_store_16(sdp_response_buffer, 1, transaction_id);
|
||||
net_store_16(sdp_response_buffer, 3, 2);
|
||||
net_store_16(sdp_response_buffer, 5, error_code); // invalid syntax
|
||||
return 7;
|
||||
}
|
||||
|
||||
int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu){
|
||||
|
||||
// get request details
|
||||
uint16_t transaction_id = READ_NET_16(packet, 1);
|
||||
// not used yet - uint16_t param_len = READ_NET_16(packet, 3);
|
||||
uint8_t * serviceSearchPattern = &packet[5];
|
||||
uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
|
||||
uint16_t maximumServiceRecordCount = READ_NET_16(packet, 5 + serviceSearchPatternLen);
|
||||
uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2];
|
||||
|
||||
// calc maxumumServiceRecordCount based on remote MTU
|
||||
uint16_t maxNrServiceRecordsPerResponse = (remote_mtu - (9+3))/4;
|
||||
|
||||
// continuation state contains index of next service record to examine
|
||||
int continuation = 0;
|
||||
uint16_t continuation_index = 0;
|
||||
if (continuationState[0] == 2){
|
||||
continuation_index = READ_NET_16(continuationState, 1);
|
||||
}
|
||||
|
||||
// get and limit total count
|
||||
linked_item_t *it;
|
||||
uint16_t total_service_count = 0;
|
||||
for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
|
||||
service_record_item_t * item = (service_record_item_t *) it;
|
||||
if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
|
||||
total_service_count++;
|
||||
}
|
||||
if (total_service_count > maximumServiceRecordCount){
|
||||
total_service_count = maximumServiceRecordCount;
|
||||
}
|
||||
|
||||
// ServiceRecordHandleList at 9
|
||||
uint16_t pos = 9;
|
||||
uint16_t current_service_count = 0;
|
||||
uint16_t current_service_index = 0;
|
||||
uint16_t matching_service_count = 0;
|
||||
for (it = (linked_item_t *) sdp_service_records; it ; it = it->next, ++current_service_index){
|
||||
service_record_item_t * item = (service_record_item_t *) it;
|
||||
|
||||
if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
|
||||
matching_service_count++;
|
||||
|
||||
if (current_service_index < continuation_index) continue;
|
||||
|
||||
net_store_32(sdp_response_buffer, pos, item->service_record_handle);
|
||||
pos += 4;
|
||||
current_service_count++;
|
||||
|
||||
if (matching_service_count >= total_service_count) break;
|
||||
|
||||
if (current_service_count >= maxNrServiceRecordsPerResponse){
|
||||
continuation = 1;
|
||||
continuation_index = current_service_index + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Store continuation state
|
||||
if (continuation) {
|
||||
sdp_response_buffer[pos++] = 2;
|
||||
net_store_16(sdp_response_buffer, pos, continuation_index);
|
||||
pos += 2;
|
||||
} else {
|
||||
sdp_response_buffer[pos++] = 0;
|
||||
}
|
||||
|
||||
// header
|
||||
sdp_response_buffer[0] = SDP_ServiceSearchResponse;
|
||||
net_store_16(sdp_response_buffer, 1, transaction_id);
|
||||
net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
|
||||
net_store_16(sdp_response_buffer, 5, total_service_count);
|
||||
net_store_16(sdp_response_buffer, 7, current_service_count);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu){
|
||||
|
||||
// get request details
|
||||
uint16_t transaction_id = READ_NET_16(packet, 1);
|
||||
// not used yet - uint16_t param_len = READ_NET_16(packet, 3);
|
||||
uint32_t serviceRecordHandle = READ_NET_32(packet, 5);
|
||||
uint16_t maximumAttributeByteCount = READ_NET_16(packet, 9);
|
||||
uint8_t * attributeIDList = &packet[11];
|
||||
uint16_t attributeIDListLen = de_get_len(attributeIDList);
|
||||
uint8_t * continuationState = &packet[11+attributeIDListLen];
|
||||
|
||||
// calc maximumAttributeByteCount based on remote MTU
|
||||
uint16_t maximumAttributeByteCount2 = remote_mtu - (7+3);
|
||||
if (maximumAttributeByteCount2 < maximumAttributeByteCount) {
|
||||
maximumAttributeByteCount = maximumAttributeByteCount2;
|
||||
}
|
||||
|
||||
// continuation state contains the offset into the complete response
|
||||
uint16_t continuation_offset = 0;
|
||||
if (continuationState[0] == 2){
|
||||
continuation_offset = READ_NET_16(continuationState, 1);
|
||||
}
|
||||
|
||||
// get service record
|
||||
service_record_item_t * item = sdp_get_record_for_handle(serviceRecordHandle);
|
||||
if (!item){
|
||||
// service record handle doesn't exist
|
||||
return sdp_create_error_response(transaction_id, 0x0002); /// invalid Service Record Handle
|
||||
}
|
||||
|
||||
|
||||
// AttributeList - starts at offset 7
|
||||
uint16_t pos = 7;
|
||||
|
||||
if (continuation_offset == 0){
|
||||
|
||||
// get size of this record
|
||||
uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList);
|
||||
|
||||
// store DES
|
||||
de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size);
|
||||
maximumAttributeByteCount -= 3;
|
||||
pos += 3;
|
||||
}
|
||||
|
||||
// copy maximumAttributeByteCount from record
|
||||
uint16_t bytes_used;
|
||||
int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]);
|
||||
pos += bytes_used;
|
||||
|
||||
uint16_t attributeListByteCount = pos - 7;
|
||||
|
||||
if (complete) {
|
||||
sdp_response_buffer[pos++] = 0;
|
||||
} else {
|
||||
continuation_offset += bytes_used;
|
||||
sdp_response_buffer[pos++] = 2;
|
||||
net_store_16(sdp_response_buffer, pos, continuation_offset);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
// header
|
||||
sdp_response_buffer[0] = SDP_ServiceAttributeResponse;
|
||||
net_store_16(sdp_response_buffer, 1, transaction_id);
|
||||
net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
|
||||
net_store_16(sdp_response_buffer, 5, attributeListByteCount);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static uint16_t sdp_get_size_for_service_search_attribute_response(uint8_t * serviceSearchPattern, uint8_t * attributeIDList){
|
||||
uint16_t total_response_size = 0;
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
|
||||
service_record_item_t * item = (service_record_item_t *) it;
|
||||
|
||||
if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
|
||||
|
||||
// for all service records that match
|
||||
total_response_size += 3 + spd_get_filtered_size(item->service_record, attributeIDList);
|
||||
}
|
||||
return total_response_size;
|
||||
}
|
||||
|
||||
int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu){
|
||||
|
||||
// SDP header before attribute sevice list: 7
|
||||
// Continuation, worst case: 5
|
||||
|
||||
// get request details
|
||||
uint16_t transaction_id = READ_NET_16(packet, 1);
|
||||
// not used yet - uint16_t param_len = READ_NET_16(packet, 3);
|
||||
uint8_t * serviceSearchPattern = &packet[5];
|
||||
uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
|
||||
uint16_t maximumAttributeByteCount = READ_NET_16(packet, 5 + serviceSearchPatternLen);
|
||||
uint8_t * attributeIDList = &packet[5+serviceSearchPatternLen+2];
|
||||
uint16_t attributeIDListLen = de_get_len(attributeIDList);
|
||||
uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2+attributeIDListLen];
|
||||
|
||||
// calc maximumAttributeByteCount based on remote MTU, SDP header and reserved Continuation block
|
||||
uint16_t maximumAttributeByteCount2 = remote_mtu - 12;
|
||||
if (maximumAttributeByteCount2 < maximumAttributeByteCount) {
|
||||
maximumAttributeByteCount = maximumAttributeByteCount2;
|
||||
}
|
||||
|
||||
// continuation state contains: index of next service record to examine
|
||||
// continuation state contains: byte offset into this service record
|
||||
uint16_t continuation_service_index = 0;
|
||||
uint16_t continuation_offset = 0;
|
||||
if (continuationState[0] == 4){
|
||||
continuation_service_index = READ_NET_16(continuationState, 1);
|
||||
continuation_offset = READ_NET_16(continuationState, 3);
|
||||
}
|
||||
|
||||
// printf("--> sdp_handle_service_search_attribute_request, cont %u/%u, max %u\n", continuation_service_index, continuation_offset, maximumAttributeByteCount);
|
||||
|
||||
// AttributeLists - starts at offset 7
|
||||
uint16_t pos = 7;
|
||||
|
||||
// add DES with total size for first request
|
||||
if (continuation_service_index == 0 && continuation_offset == 0){
|
||||
uint16_t total_response_size = sdp_get_size_for_service_search_attribute_response(serviceSearchPattern, attributeIDList);
|
||||
de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, total_response_size);
|
||||
// log_info("total response size %u\n", total_response_size);
|
||||
pos += 3;
|
||||
maximumAttributeByteCount -= 3;
|
||||
}
|
||||
|
||||
// create attribute list
|
||||
int first_answer = 1;
|
||||
int continuation = 0;
|
||||
uint16_t current_service_index = 0;
|
||||
linked_item_t *it = (linked_item_t *) sdp_service_records;
|
||||
for ( ; it ; it = it->next, ++current_service_index){
|
||||
service_record_item_t * item = (service_record_item_t *) it;
|
||||
|
||||
if (current_service_index < continuation_service_index ) continue;
|
||||
if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
|
||||
|
||||
if (continuation_offset == 0){
|
||||
|
||||
// get size of this record
|
||||
uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList);
|
||||
|
||||
// stop if complete record doesn't fits into response but we already have a partial response
|
||||
if ((filtered_attributes_size + 3 > maximumAttributeByteCount) && !first_answer) {
|
||||
continuation = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// store DES
|
||||
de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size);
|
||||
pos += 3;
|
||||
maximumAttributeByteCount -= 3;
|
||||
}
|
||||
|
||||
first_answer = 0;
|
||||
|
||||
// copy maximumAttributeByteCount from record
|
||||
uint16_t bytes_used;
|
||||
int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]);
|
||||
pos += bytes_used;
|
||||
maximumAttributeByteCount -= bytes_used;
|
||||
|
||||
if (complete) {
|
||||
continuation_offset = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
continuation = 1;
|
||||
continuation_offset += bytes_used;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t attributeListsByteCount = pos - 7;
|
||||
|
||||
// Continuation State
|
||||
if (continuation){
|
||||
sdp_response_buffer[pos++] = 4;
|
||||
net_store_16(sdp_response_buffer, pos, (uint16_t) current_service_index);
|
||||
pos += 2;
|
||||
net_store_16(sdp_response_buffer, pos, continuation_offset);
|
||||
pos += 2;
|
||||
} else {
|
||||
// complete
|
||||
sdp_response_buffer[pos++] = 0;
|
||||
}
|
||||
|
||||
// create SDP header
|
||||
sdp_response_buffer[0] = SDP_ServiceSearchAttributeResponse;
|
||||
net_store_16(sdp_response_buffer, 1, transaction_id);
|
||||
net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
|
||||
net_store_16(sdp_response_buffer, 5, attributeListsByteCount);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void sdp_try_respond(void){
|
||||
if (!sdp_response_size ) return;
|
||||
if (!l2cap_cid) return;
|
||||
if (!l2cap_can_send_packet_now(l2cap_cid)) return;
|
||||
|
||||
// update state before sending packet (avoid getting called when new l2cap credit gets emitted)
|
||||
uint16_t size = sdp_response_size;
|
||||
sdp_response_size = 0;
|
||||
l2cap_send_internal(l2cap_cid, sdp_response_buffer, size);
|
||||
}
|
||||
|
||||
// we assume that we don't get two requests in a row
|
||||
static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
uint16_t transaction_id;
|
||||
SDP_PDU_ID_t pdu_id;
|
||||
uint16_t remote_mtu;
|
||||
// uint16_t param_len;
|
||||
|
||||
switch (packet_type) {
|
||||
|
||||
case L2CAP_DATA_PACKET:
|
||||
pdu_id = (SDP_PDU_ID_t) packet[0];
|
||||
transaction_id = READ_NET_16(packet, 1);
|
||||
// param_len = READ_NET_16(packet, 3);
|
||||
remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel);
|
||||
// account for our buffer
|
||||
if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE){
|
||||
remote_mtu = SDP_RESPONSE_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
// printf("SDP Request: type %u, transaction id %u, len %u, mtu %u\n", pdu_id, transaction_id, param_len, remote_mtu);
|
||||
switch (pdu_id){
|
||||
|
||||
case SDP_ServiceSearchRequest:
|
||||
sdp_response_size = sdp_handle_service_search_request(packet, remote_mtu);
|
||||
break;
|
||||
|
||||
case SDP_ServiceAttributeRequest:
|
||||
sdp_response_size = sdp_handle_service_attribute_request(packet, remote_mtu);
|
||||
break;
|
||||
|
||||
case SDP_ServiceSearchAttributeRequest:
|
||||
sdp_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu);
|
||||
break;
|
||||
|
||||
default:
|
||||
sdp_response_size = sdp_create_error_response(transaction_id, 0x0003); // invalid syntax
|
||||
break;
|
||||
}
|
||||
|
||||
sdp_try_respond();
|
||||
|
||||
break;
|
||||
|
||||
case HCI_EVENT_PACKET:
|
||||
|
||||
switch (packet[0]) {
|
||||
|
||||
case L2CAP_EVENT_INCOMING_CONNECTION:
|
||||
if (l2cap_cid) {
|
||||
// CONNECTION REJECTED DUE TO LIMITED RESOURCES
|
||||
l2cap_decline_connection_internal(channel, 0x0d);
|
||||
break;
|
||||
}
|
||||
// accept
|
||||
l2cap_cid = channel;
|
||||
sdp_response_size = 0;
|
||||
l2cap_accept_connection_internal(channel);
|
||||
break;
|
||||
|
||||
case L2CAP_EVENT_CHANNEL_OPENED:
|
||||
if (packet[2]) {
|
||||
// open failed -> reset
|
||||
l2cap_cid = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case L2CAP_EVENT_CREDITS:
|
||||
case DAEMON_EVENT_HCI_PACKET_SENT:
|
||||
sdp_try_respond();
|
||||
break;
|
||||
|
||||
case L2CAP_EVENT_CHANNEL_CLOSED:
|
||||
if (channel == l2cap_cid){
|
||||
// reset
|
||||
l2cap_cid = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// other event
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// other packet type
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
96
ios/RetroArch/input/BTStack/btstack/sdp.h
Normal file
96
ios/RetroArch/input/BTStack/btstack/sdp.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <btstack/linked_list.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
typedef enum {
|
||||
SDP_ErrorResponse = 1,
|
||||
SDP_ServiceSearchRequest,
|
||||
SDP_ServiceSearchResponse,
|
||||
SDP_ServiceAttributeRequest,
|
||||
SDP_ServiceAttributeResponse,
|
||||
SDP_ServiceSearchAttributeRequest,
|
||||
SDP_ServiceSearchAttributeResponse
|
||||
} SDP_PDU_ID_t;
|
||||
|
||||
// service record
|
||||
// -- uses user_data field for actual
|
||||
typedef struct {
|
||||
// linked list - assert: first field
|
||||
linked_item_t item;
|
||||
|
||||
// client connection
|
||||
void * connection;
|
||||
|
||||
// data is contained in same memory
|
||||
uint32_t service_record_handle;
|
||||
uint8_t service_record[0];
|
||||
} service_record_item_t;
|
||||
|
||||
|
||||
void sdp_init(void);
|
||||
|
||||
void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
|
||||
uint16_t channel, uint8_t *packet, uint16_t size));
|
||||
|
||||
#ifdef EMBEDDED
|
||||
// register service record internally - the normal version creates a copy of the record
|
||||
// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present
|
||||
// @returns ServiceRecordHandle or 0 if registration failed
|
||||
uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item);
|
||||
#else
|
||||
// register service record internally - this special version doesn't copy the record, it cannot be freeed
|
||||
// pre: AttributeIDs are in ascending order
|
||||
// pre: ServiceRecordHandle is first attribute and valid
|
||||
// pre: record
|
||||
// @returns ServiceRecordHandle or 0 if registration failed
|
||||
uint32_t sdp_register_service_internal(void *connection, uint8_t * service_record);
|
||||
#endif
|
||||
|
||||
// unregister service record internally
|
||||
void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle);
|
||||
|
||||
//
|
||||
void sdp_unregister_services_for_connection(void *connection);
|
||||
|
||||
//
|
||||
int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu);
|
||||
int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu);
|
||||
int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu);
|
698
ios/RetroArch/input/BTStack/btstack/sdp_util.c
Normal file
698
ios/RetroArch/input/BTStack/btstack/sdp_util.c
Normal file
@ -0,0 +1,698 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* sdp_util.c
|
||||
*/
|
||||
|
||||
#include <btstack/sdp_util.h>
|
||||
#include <btstack/utils.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h> // PRIx32
|
||||
|
||||
// workaround for missing PRIx32 on mspgcc (16-bit MCU)
|
||||
#ifndef PRIx32
|
||||
#warning Using own: #define PRIx32 "lx"
|
||||
#define PRIx32 "lx"
|
||||
#endif
|
||||
|
||||
// date element type names
|
||||
const char * const type_names[] = { "NIL", "UINT", "INT", "UUID", "STRING", "BOOL", "DES", "DEA", "URL"};
|
||||
|
||||
// Bluetooth Base UUID: 00000000-0000-1000-8000- 00805F9B34FB
|
||||
const uint8_t sdp_bluetooth_base_uuid[] = { 0x00, 0x00, 0x00, 0x00, /* - */ 0x00, 0x00, /* - */ 0x10, 0x00, /* - */
|
||||
0x80, 0x00, /* - */ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
|
||||
|
||||
void sdp_normalize_uuid(uint8_t *uuid, uint32_t shortUUID){
|
||||
memcpy(uuid, sdp_bluetooth_base_uuid, 16);
|
||||
net_store_32(uuid, 0, shortUUID);
|
||||
}
|
||||
|
||||
// MARK: DataElement getter
|
||||
de_size_t de_get_size_type(uint8_t *header){
|
||||
return (de_size_t) (header[0] & 7);
|
||||
}
|
||||
|
||||
de_type_t de_get_element_type(uint8_t *header){
|
||||
return (de_type_t) (header[0] >> 3);
|
||||
}
|
||||
|
||||
int de_get_header_size(uint8_t * header){
|
||||
de_size_t de_size = de_get_size_type(header);
|
||||
if (de_size <= DE_SIZE_128) {
|
||||
return 1;
|
||||
}
|
||||
return 1 + (1 << (de_size-DE_SIZE_VAR_8));
|
||||
}
|
||||
|
||||
int de_get_data_size(uint8_t * header){
|
||||
uint32_t result = 0;
|
||||
de_type_t de_type = de_get_element_type(header);
|
||||
de_size_t de_size = de_get_size_type(header);
|
||||
switch (de_size){
|
||||
case DE_SIZE_VAR_8:
|
||||
result = header[1];
|
||||
break;
|
||||
case DE_SIZE_VAR_16:
|
||||
result = READ_NET_16(header,1);
|
||||
break;
|
||||
case DE_SIZE_VAR_32:
|
||||
result = READ_NET_32(header,1);
|
||||
break;
|
||||
default:
|
||||
// case DE_SIZE_8:
|
||||
// case DE_SIZE_16:
|
||||
// case DE_SIZE_32:
|
||||
// case DE_SIZE_64:
|
||||
// case DE_SIZE_128:
|
||||
if (de_type == DE_NIL) return 0;
|
||||
return 1 << de_size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int de_get_len(uint8_t *header){
|
||||
return de_get_header_size(header) + de_get_data_size(header);
|
||||
}
|
||||
|
||||
// @returns: element is valid UUID
|
||||
int de_get_normalized_uuid(uint8_t *uuid128, uint8_t *element){
|
||||
de_type_t uuidType = de_get_element_type(element);
|
||||
de_size_t uuidSize = de_get_size_type(element);
|
||||
if (uuidType != DE_UUID) return 0;
|
||||
uint32_t shortUUID;
|
||||
switch (uuidSize){
|
||||
case DE_SIZE_16:
|
||||
shortUUID = READ_NET_16(element, 1);
|
||||
break;
|
||||
case DE_SIZE_32:
|
||||
shortUUID = READ_NET_32(element, 1);
|
||||
break;
|
||||
case DE_SIZE_128:
|
||||
memcpy(uuid128, element+1, 16);
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
sdp_normalize_uuid(uuid128, shortUUID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// functions to create record
|
||||
static void de_store_descriptor(uint8_t * header, de_type_t type, de_size_t size){
|
||||
header[0] = (type << 3) | size;
|
||||
}
|
||||
|
||||
void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len){
|
||||
header[0] = (type << 3) | size;
|
||||
switch (size){
|
||||
case DE_SIZE_VAR_8:
|
||||
header[1] = len;
|
||||
break;
|
||||
case DE_SIZE_VAR_16:
|
||||
net_store_16(header, 1, len);
|
||||
break;
|
||||
case DE_SIZE_VAR_32:
|
||||
net_store_32(header, 1, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: DataElement creation
|
||||
|
||||
/* starts a new sequence in empty buffer - first call */
|
||||
void de_create_sequence(uint8_t *header){
|
||||
de_store_descriptor_with_len( header, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length
|
||||
};
|
||||
|
||||
/* starts a sub-sequence, @returns handle for sub-sequence */
|
||||
uint8_t * de_push_sequence(uint8_t *header){
|
||||
int element_len = de_get_len(header);
|
||||
de_store_descriptor_with_len(header+element_len, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length
|
||||
return header + element_len;
|
||||
}
|
||||
|
||||
/* closes the current sequence and updates the parent sequence */
|
||||
void de_pop_sequence(uint8_t * parent, uint8_t * child){
|
||||
int child_len = de_get_len(child);
|
||||
int data_size_parent = READ_NET_16(parent,1);
|
||||
net_store_16(parent, 1, data_size_parent + child_len);
|
||||
}
|
||||
|
||||
/* adds a single number value and 16+32 bit UUID to the sequence */
|
||||
void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value){
|
||||
int data_size = READ_NET_16(seq,1);
|
||||
int element_size = 1; // e.g. for DE_TYPE_NIL
|
||||
de_store_descriptor(seq+3+data_size, type, size);
|
||||
switch (size){
|
||||
case DE_SIZE_8:
|
||||
if (type != DE_NIL){
|
||||
seq[4+data_size] = value;
|
||||
element_size = 2;
|
||||
}
|
||||
break;
|
||||
case DE_SIZE_16:
|
||||
net_store_16(seq, 4+data_size, value);
|
||||
element_size = 3;
|
||||
break;
|
||||
case DE_SIZE_32:
|
||||
net_store_32(seq, 4+data_size, value);
|
||||
element_size = 5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
net_store_16(seq, 1, data_size+element_size);
|
||||
}
|
||||
|
||||
/* add a single block of data, e.g. as DE_STRING, DE_URL */
|
||||
void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data){
|
||||
int data_size = READ_NET_16(seq,1);
|
||||
if (size > 0xff) {
|
||||
// use 16-bit lengh information (3 byte header)
|
||||
de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_16, size);
|
||||
data_size += 3;
|
||||
} else {
|
||||
// use 8-bit lengh information (2 byte header)
|
||||
de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_8, size);
|
||||
data_size += 2;
|
||||
}
|
||||
memcpy( seq + 3 + data_size, data, size);
|
||||
data_size += size;
|
||||
net_store_16(seq, 1, data_size);
|
||||
}
|
||||
|
||||
void de_add_uuid128(uint8_t * seq, uint8_t * uuid){
|
||||
int data_size = READ_NET_16(seq,1);
|
||||
de_store_descriptor(seq+3+data_size, DE_UUID, DE_SIZE_128);
|
||||
memcpy( seq + 4 + data_size, uuid, 16);
|
||||
net_store_16(seq, 1, data_size+1+16);
|
||||
}
|
||||
|
||||
void sdp_add_attribute(uint8_t *seq, uint16_t attributeID, uint8_t attributeValue){
|
||||
}
|
||||
|
||||
// MARK: DataElementSequence traversal
|
||||
typedef int (*de_traversal_callback_t)(uint8_t * element, de_type_t type, de_size_t size, void *context);
|
||||
static void de_traverse_sequence(uint8_t * element, de_traversal_callback_t handler, void *context){
|
||||
de_type_t type = de_get_element_type(element);
|
||||
if (type != DE_DES) return;
|
||||
int pos = de_get_header_size(element);
|
||||
int end_pos = de_get_len(element);
|
||||
while (pos < end_pos){
|
||||
de_type_t elemType = de_get_element_type(element + pos);
|
||||
de_size_t elemSize = de_get_size_type(element + pos);
|
||||
uint8_t done = (*handler)(element + pos, elemType, elemSize, context);
|
||||
if (done) break;
|
||||
pos += de_get_len(element + pos);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: AttributeList traversal
|
||||
typedef int (*sdp_attribute_list_traversal_callback_t)(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *context);
|
||||
static void sdp_attribute_list_traverse_sequence(uint8_t * element, sdp_attribute_list_traversal_callback_t handler, void *context){
|
||||
de_type_t type = de_get_element_type(element);
|
||||
if (type != DE_DES) return;
|
||||
int pos = de_get_header_size(element);
|
||||
int end_pos = de_get_len(element);
|
||||
while (pos < end_pos){
|
||||
de_type_t idType = de_get_element_type(element + pos);
|
||||
de_size_t idSize = de_get_size_type(element + pos);
|
||||
if (idType != DE_UINT || idSize != DE_SIZE_16) break; // wrong type
|
||||
uint16_t attribute_id = READ_NET_16(element, pos + 1);
|
||||
pos += 3;
|
||||
if (pos >= end_pos) break; // array out of bounds
|
||||
de_type_t valueType = de_get_element_type(element + pos);
|
||||
de_size_t valueSize = de_get_size_type(element + pos);
|
||||
uint8_t done = (*handler)(attribute_id, element + pos, valueType, valueSize, context);
|
||||
if (done) break;
|
||||
pos += de_get_len(element + pos);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: AttributeID in AttributeIDList
|
||||
// attribute ID in AttributeIDList
|
||||
// context { result, attributeID }
|
||||
struct sdp_context_attributeID_search {
|
||||
int result;
|
||||
uint16_t attributeID;
|
||||
};
|
||||
static int sdp_traversal_attributeID_search(uint8_t * element, de_type_t type, de_size_t size, void *my_context){
|
||||
struct sdp_context_attributeID_search * context = (struct sdp_context_attributeID_search *) my_context;
|
||||
if (type != DE_UINT) return 0;
|
||||
switch (size) {
|
||||
case DE_SIZE_16:
|
||||
if (READ_NET_16(element, 1) == context->attributeID) {
|
||||
context->result = 1;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case DE_SIZE_32:
|
||||
if (READ_NET_16(element, 1) <= context->attributeID
|
||||
&& context->attributeID <= READ_NET_16(element, 3)) {
|
||||
context->result = 1;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sdp_attribute_list_constains_id(uint8_t *attributeIDList, uint16_t attributeID){
|
||||
struct sdp_context_attributeID_search attributeID_search;
|
||||
attributeID_search.result = 0;
|
||||
attributeID_search.attributeID = attributeID;
|
||||
de_traverse_sequence(attributeIDList, sdp_traversal_attributeID_search, &attributeID_search);
|
||||
return attributeID_search.result;
|
||||
}
|
||||
|
||||
// MARK: Append Attributes for AttributeIDList
|
||||
// pre: buffer contains DES with 2 byte length field
|
||||
struct sdp_context_append_attributes {
|
||||
uint8_t * buffer;
|
||||
uint16_t startOffset; // offset of when to start copying
|
||||
uint16_t maxBytes;
|
||||
uint16_t usedBytes;
|
||||
uint8_t *attributeIDList;
|
||||
};
|
||||
|
||||
static int sdp_traversal_append_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
|
||||
struct sdp_context_append_attributes * context = (struct sdp_context_append_attributes *) my_context;
|
||||
if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) {
|
||||
// DES_HEADER(3) + DES_DATA + (UINT16(3) + attribute)
|
||||
uint16_t data_size = READ_NET_16(context->buffer, 1);
|
||||
int attribute_len = de_get_len(attributeValue);
|
||||
if (3 + data_size + (3 + attribute_len) <= context->maxBytes) {
|
||||
// copy Attribute
|
||||
de_add_number(context->buffer, DE_UINT, DE_SIZE_16, attributeID);
|
||||
data_size += 3; // 3 bytes
|
||||
memcpy(context->buffer + 3 + data_size, attributeValue, attribute_len);
|
||||
net_store_16(context->buffer,1,data_size+attribute_len);
|
||||
} else {
|
||||
// not enought space left -> continue with previous element
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// maxBytes: maximal size of data element sequence
|
||||
uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer){
|
||||
struct sdp_context_append_attributes context;
|
||||
context.buffer = buffer;
|
||||
context.maxBytes = maxBytes;
|
||||
context.usedBytes = 0;
|
||||
context.startOffset = startOffset;
|
||||
context.attributeIDList = attributeIDList;
|
||||
sdp_attribute_list_traverse_sequence(record, sdp_traversal_append_attributes, &context);
|
||||
return context.usedBytes;
|
||||
}
|
||||
|
||||
// MARK: Filter attributes that match attribute list from startOffset and a max nr bytes
|
||||
struct sdp_context_filter_attributes {
|
||||
uint8_t * buffer;
|
||||
uint16_t startOffset; // offset of when to start copying
|
||||
uint16_t maxBytes;
|
||||
uint16_t usedBytes;
|
||||
uint8_t *attributeIDList;
|
||||
int complete;
|
||||
};
|
||||
|
||||
// copy data with given start offset and max bytes, returns OK if all data has been copied
|
||||
static int spd_append_range(struct sdp_context_filter_attributes* context, uint16_t len, uint8_t *data){
|
||||
int ok = 1;
|
||||
uint16_t remainder_len = len - context->startOffset;
|
||||
if (context->maxBytes < remainder_len){
|
||||
remainder_len = context->maxBytes;
|
||||
ok = 0;
|
||||
}
|
||||
memcpy(context->buffer, &data[context->startOffset], remainder_len);
|
||||
context->usedBytes += remainder_len;
|
||||
context->buffer += remainder_len;
|
||||
context->maxBytes -= remainder_len;
|
||||
context->startOffset = 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int sdp_traversal_filter_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
|
||||
struct sdp_context_filter_attributes * context = (struct sdp_context_filter_attributes *) my_context;
|
||||
|
||||
if (!sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) return 0;
|
||||
|
||||
// { Attribute ID (Descriptor, big endian 16-bit ID), AttributeValue (data)}
|
||||
|
||||
// handle Attribute ID
|
||||
if (context->startOffset >= 3){
|
||||
context->startOffset -= 3;
|
||||
} else {
|
||||
uint8_t idBuffer[3];
|
||||
de_store_descriptor(idBuffer, DE_UINT, DE_SIZE_16);
|
||||
net_store_16(idBuffer,1,attributeID);
|
||||
|
||||
int ok = spd_append_range(context, 3, idBuffer);
|
||||
if (!ok) {
|
||||
context->complete = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// handle Attribute Value
|
||||
int attribute_len = de_get_len(attributeValue);
|
||||
if (context->startOffset >= attribute_len) {
|
||||
context->startOffset -= attribute_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ok = spd_append_range(context, attribute_len, attributeValue);
|
||||
if (!ok) {
|
||||
context->complete = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer){
|
||||
|
||||
struct sdp_context_filter_attributes context;
|
||||
context.buffer = buffer;
|
||||
context.maxBytes = maxBytes;
|
||||
context.usedBytes = 0;
|
||||
context.startOffset = startOffset;
|
||||
context.attributeIDList = attributeIDList;
|
||||
context.complete = 1;
|
||||
|
||||
sdp_attribute_list_traverse_sequence(record, sdp_traversal_filter_attributes, &context);
|
||||
|
||||
*usedBytes = context.usedBytes;
|
||||
return context.complete;
|
||||
}
|
||||
|
||||
// MARK: Get sum of attributes matching attribute list
|
||||
struct sdp_context_get_filtered_size {
|
||||
uint8_t *attributeIDList;
|
||||
uint16_t size;
|
||||
};
|
||||
|
||||
static int sdp_traversal_get_filtered_size(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
|
||||
struct sdp_context_get_filtered_size * context = (struct sdp_context_get_filtered_size *) my_context;
|
||||
if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) {
|
||||
context->size += 3 + de_get_len(attributeValue);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList){
|
||||
struct sdp_context_get_filtered_size context;
|
||||
context.size = 0;
|
||||
context.attributeIDList = attributeIDList;
|
||||
sdp_attribute_list_traverse_sequence(record, sdp_traversal_get_filtered_size, &context);
|
||||
return context.size;
|
||||
}
|
||||
|
||||
// MARK: Get AttributeValue for AttributeID
|
||||
// find attribute (ELEMENT) by ID
|
||||
struct sdp_context_attribute_by_id {
|
||||
uint16_t attributeID;
|
||||
uint8_t * attributeValue;
|
||||
};
|
||||
static int sdp_traversal_attribute_by_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){
|
||||
struct sdp_context_attribute_by_id * context = (struct sdp_context_attribute_by_id *) my_context;
|
||||
if (attributeID == context->attributeID) {
|
||||
context->attributeValue = attributeValue;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID){
|
||||
struct sdp_context_attribute_by_id context;
|
||||
context.attributeValue = NULL;
|
||||
context.attributeID = attributeID;
|
||||
sdp_attribute_list_traverse_sequence(record, sdp_traversal_attribute_by_id, &context);
|
||||
return context.attributeValue;
|
||||
}
|
||||
|
||||
// MARK: Set AttributeValue for AttributeID
|
||||
struct sdp_context_set_attribute_for_id {
|
||||
uint16_t attributeID;
|
||||
uint32_t attributeValue;
|
||||
uint8_t attributeFound;
|
||||
};
|
||||
static int sdp_traversal_set_attribute_for_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){
|
||||
struct sdp_context_set_attribute_for_id * context = (struct sdp_context_set_attribute_for_id *) my_context;
|
||||
if (attributeID == context->attributeID) {
|
||||
context->attributeFound = 1;
|
||||
switch (size){
|
||||
case DE_SIZE_8:
|
||||
if (attributeType != DE_NIL){
|
||||
attributeValue[1] = context->attributeValue;
|
||||
}
|
||||
break;
|
||||
case DE_SIZE_16:
|
||||
net_store_16(attributeValue, 1, context->attributeValue);
|
||||
break;
|
||||
case DE_SIZE_32:
|
||||
net_store_32(attributeValue, 1, context->attributeValue);
|
||||
break;
|
||||
// Might want to support STRINGS to, copy upto original length
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value){
|
||||
struct sdp_context_set_attribute_for_id context;
|
||||
context.attributeID = attributeID;
|
||||
context.attributeValue = value;
|
||||
context.attributeFound = 0;
|
||||
sdp_attribute_list_traverse_sequence(record, sdp_traversal_set_attribute_for_id, &context);
|
||||
return context.attributeFound;
|
||||
}
|
||||
|
||||
// MARK: ServiceRecord contains UUID
|
||||
// service record contains UUID
|
||||
// context { normalizedUUID }
|
||||
struct sdp_context_contains_uuid128 {
|
||||
uint8_t * uuid128;
|
||||
int result;
|
||||
};
|
||||
int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128);
|
||||
static int sdp_traversal_contains_UUID128(uint8_t * element, de_type_t type, de_size_t size, void *my_context){
|
||||
struct sdp_context_contains_uuid128 * context = (struct sdp_context_contains_uuid128 *) my_context;
|
||||
uint8_t normalizedUUID[16];
|
||||
if (type == DE_UUID){
|
||||
uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element);
|
||||
context->result = uuidOK && memcmp(context->uuid128, normalizedUUID, 16) == 0;
|
||||
}
|
||||
if (type == DE_DES){
|
||||
context->result = sdp_record_contains_UUID128(element, context->uuid128);
|
||||
}
|
||||
return context->result;
|
||||
}
|
||||
int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128){
|
||||
struct sdp_context_contains_uuid128 context;
|
||||
context.uuid128 = uuid128;
|
||||
context.result = 0;
|
||||
de_traverse_sequence(record, sdp_traversal_contains_UUID128, &context);
|
||||
return context.result;
|
||||
}
|
||||
|
||||
// MARK: ServiceRecord matches SearchServicePattern
|
||||
// if UUID in searchServicePattern is not found in record => false
|
||||
// context { result, record }
|
||||
struct sdp_context_match_pattern {
|
||||
uint8_t * record;
|
||||
int result;
|
||||
};
|
||||
int sdp_traversal_match_pattern(uint8_t * element, de_type_t attributeType, de_size_t size, void *my_context){
|
||||
struct sdp_context_match_pattern * context = (struct sdp_context_match_pattern *) my_context;
|
||||
uint8_t normalizedUUID[16];
|
||||
uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element);
|
||||
if (!uuidOK || !sdp_record_contains_UUID128(context->record, normalizedUUID)){
|
||||
context->result = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern){
|
||||
struct sdp_context_match_pattern context;
|
||||
context.record = record;
|
||||
context.result = 1;
|
||||
de_traverse_sequence(serviceSearchPattern, sdp_traversal_match_pattern, &context);
|
||||
return context.result;
|
||||
}
|
||||
|
||||
// MARK: Dump DataElement
|
||||
// context { indent }
|
||||
static int de_traversal_dump_data(uint8_t * element, de_type_t de_type, de_size_t de_size, void *my_context){
|
||||
int indent = *(int*) my_context;
|
||||
int i;
|
||||
for (i=0; i<indent;i++) printf(" ");
|
||||
int pos = de_get_header_size(element);
|
||||
int end_pos = de_get_len(element);
|
||||
printf("type %5s (%u), element len %2u ", type_names[de_type], de_type, end_pos);
|
||||
if (de_type == DE_DES) {
|
||||
printf("\n");
|
||||
indent++;
|
||||
de_traverse_sequence(element, de_traversal_dump_data, (void *)&indent);
|
||||
} else if (de_type == DE_UUID && de_size == DE_SIZE_128) {
|
||||
printf(", value: ");
|
||||
printUUID(element+1);
|
||||
printf("\n");
|
||||
} else if (de_type == DE_STRING) {
|
||||
int len = 0;
|
||||
switch (de_size){
|
||||
case DE_SIZE_VAR_8:
|
||||
len = element[1];
|
||||
break;
|
||||
case DE_SIZE_VAR_16:
|
||||
len = READ_NET_16(element, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf("len %u (0x%02x)\n", len, len);
|
||||
hexdump(&element[pos], len);
|
||||
} else {
|
||||
uint32_t value = 0;
|
||||
switch (de_size) {
|
||||
case DE_SIZE_8:
|
||||
if (de_type != DE_NIL){
|
||||
value = element[pos];
|
||||
}
|
||||
break;
|
||||
case DE_SIZE_16:
|
||||
value = READ_NET_16(element,pos);
|
||||
break;
|
||||
case DE_SIZE_32:
|
||||
value = READ_NET_32(element,pos);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
printf(", value: 0x%08" PRIx32 "\n", value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void de_dump_data_element(uint8_t * record){
|
||||
int indent = 0;
|
||||
// hack to get root DES, too.
|
||||
de_type_t type = de_get_element_type(record);
|
||||
de_size_t size = de_get_size_type(record);
|
||||
de_traversal_dump_data(record, type, size, (void*) &indent);
|
||||
}
|
||||
|
||||
void sdp_create_spp_service(uint8_t *service, int service_id, const char *name){
|
||||
|
||||
uint8_t* attribute;
|
||||
de_create_sequence(service);
|
||||
|
||||
// 0x0000 "Service Record Handle"
|
||||
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle);
|
||||
de_add_number(service, DE_UINT, DE_SIZE_32, 0x10001);
|
||||
|
||||
// 0x0001 "Service Class ID List"
|
||||
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList);
|
||||
attribute = de_push_sequence(service);
|
||||
{
|
||||
de_add_number(attribute, DE_UUID, DE_SIZE_16, 0x1101 );
|
||||
}
|
||||
de_pop_sequence(service, attribute);
|
||||
|
||||
// 0x0004 "Protocol Descriptor List"
|
||||
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList);
|
||||
attribute = de_push_sequence(service);
|
||||
{
|
||||
uint8_t* l2cpProtocol = de_push_sequence(attribute);
|
||||
{
|
||||
de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, 0x0100);
|
||||
}
|
||||
de_pop_sequence(attribute, l2cpProtocol);
|
||||
|
||||
uint8_t* rfcomm = de_push_sequence(attribute);
|
||||
{
|
||||
de_add_number(rfcomm, DE_UUID, DE_SIZE_16, 0x0003); // rfcomm_service
|
||||
de_add_number(rfcomm, DE_UINT, DE_SIZE_8, service_id); // rfcomm channel
|
||||
}
|
||||
de_pop_sequence(attribute, rfcomm);
|
||||
}
|
||||
de_pop_sequence(service, attribute);
|
||||
|
||||
// 0x0005 "Public Browse Group"
|
||||
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BrowseGroupList); // public browse group
|
||||
attribute = de_push_sequence(service);
|
||||
{
|
||||
de_add_number(attribute, DE_UUID, DE_SIZE_16, 0x1002 );
|
||||
}
|
||||
de_pop_sequence(service, attribute);
|
||||
|
||||
// 0x0006
|
||||
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_LanguageBaseAttributeIDList);
|
||||
attribute = de_push_sequence(service);
|
||||
{
|
||||
de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e);
|
||||
de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a);
|
||||
de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100);
|
||||
}
|
||||
de_pop_sequence(service, attribute);
|
||||
|
||||
// 0x0009 "Bluetooth Profile Descriptor List"
|
||||
de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList);
|
||||
attribute = de_push_sequence(service);
|
||||
{
|
||||
uint8_t *sppProfile = de_push_sequence(attribute);
|
||||
{
|
||||
de_add_number(sppProfile, DE_UUID, DE_SIZE_16, 0x1101);
|
||||
de_add_number(sppProfile, DE_UINT, DE_SIZE_16, 0x0100);
|
||||
}
|
||||
de_pop_sequence(attribute, sppProfile);
|
||||
}
|
||||
de_pop_sequence(service, attribute);
|
||||
|
||||
// 0x0100 "ServiceName"
|
||||
de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100);
|
||||
de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name);
|
||||
}
|
614
ios/RetroArch/input/BTStack/btstack/socket_connection.c
Normal file
614
ios/RetroArch/input/BTStack/btstack/socket_connection.c
Normal file
@ -0,0 +1,614 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* SocketServer.c
|
||||
*
|
||||
* Handles multiple connections to a single socket without blocking
|
||||
*
|
||||
* Created by Matthias Ringwald on 6/6/09.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "socket_connection.h"
|
||||
|
||||
#include "hci.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <btstack/btstack.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef USE_LAUNCHD
|
||||
#include "../3rdparty/launch.h"
|
||||
#endif
|
||||
|
||||
#define MAX_PENDING_CONNECTIONS 10
|
||||
|
||||
/** prototypes */
|
||||
static int socket_connection_hci_process(struct data_source *ds);
|
||||
static int socket_connection_dummy_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length);
|
||||
|
||||
/** globals */
|
||||
|
||||
/** packet header used over socket connections, in front of the HCI packet */
|
||||
typedef struct packet_header {
|
||||
uint16_t type;
|
||||
uint16_t channel;
|
||||
uint16_t length;
|
||||
uint8_t data[0];
|
||||
} packet_header_t; // 6
|
||||
|
||||
typedef enum {
|
||||
SOCKET_W4_HEADER,
|
||||
SOCKET_W4_DATA
|
||||
} SOCKET_STATE;
|
||||
|
||||
struct connection {
|
||||
data_source_t ds; // used for run loop
|
||||
linked_item_t item; // used for connection list, user_data points to connection_t base
|
||||
SOCKET_STATE state;
|
||||
uint16_t bytes_read;
|
||||
uint16_t bytes_to_read;
|
||||
uint8_t buffer[6+HCI_ACL_BUFFER_SIZE]; // packet_header(6) + max packet: 3-DH5 = header(6) + payload (1021)
|
||||
};
|
||||
|
||||
/** list of socket connections */
|
||||
static linked_list_t connections = NULL;
|
||||
static linked_list_t parked = NULL;
|
||||
|
||||
|
||||
/** client packet handler */
|
||||
|
||||
static int (*socket_connection_packet_callback)(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length) = socket_connection_dummy_handler;
|
||||
|
||||
static int socket_connection_dummy_handler(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length){
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int socket_connection_set_non_blocking(int fd) {
|
||||
int err;
|
||||
int flags;
|
||||
// According to the man page, F_GETFL can't error!
|
||||
flags = fcntl(fd, F_GETFL, NULL);
|
||||
err = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
return err;
|
||||
}
|
||||
|
||||
void socket_connection_set_no_sigpipe(int fd){
|
||||
#ifdef HAVE_SO_NOSIGPIPE
|
||||
int set = 1;
|
||||
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
|
||||
#endif
|
||||
}
|
||||
|
||||
void socket_connection_free_connection(connection_t *conn){
|
||||
// remove from run_loop
|
||||
run_loop_remove_data_source(&conn->ds);
|
||||
|
||||
// and from connection list
|
||||
linked_list_remove(&connections, &conn->item);
|
||||
|
||||
// destroy
|
||||
free(conn);
|
||||
}
|
||||
|
||||
void socket_connection_init_statemachine(connection_t *connection){
|
||||
// wait for next packet
|
||||
connection->state = SOCKET_W4_HEADER;
|
||||
connection->bytes_read = 0;
|
||||
connection->bytes_to_read = sizeof(packet_header_t);
|
||||
}
|
||||
|
||||
connection_t * socket_connection_register_new_connection(int fd){
|
||||
// create connection objec
|
||||
connection_t * conn = malloc( sizeof(connection_t));
|
||||
if (conn == NULL) return 0;
|
||||
linked_item_set_user( &conn->item, conn);
|
||||
conn->ds.fd = fd;
|
||||
conn->ds.process = socket_connection_hci_process;
|
||||
|
||||
// prepare state machine and
|
||||
socket_connection_init_statemachine(conn);
|
||||
|
||||
// add this socket to the run_loop
|
||||
run_loop_add_data_source( &conn->ds );
|
||||
|
||||
// and the connection list
|
||||
linked_list_add( &connections, &conn->item);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
void static socket_connection_emit_connection_opened(connection_t *connection){
|
||||
uint8_t event[1];
|
||||
event[0] = DAEMON_EVENT_CONNECTION_OPENED;
|
||||
(*socket_connection_packet_callback)(connection, DAEMON_EVENT_PACKET, 0, (uint8_t *) &event, 1);
|
||||
}
|
||||
|
||||
void static socket_connection_emit_connection_closed(connection_t *connection){
|
||||
uint8_t event[1];
|
||||
event[0] = DAEMON_EVENT_CONNECTION_CLOSED;
|
||||
(*socket_connection_packet_callback)(connection, DAEMON_EVENT_PACKET, 0, (uint8_t *) &event, 1);
|
||||
}
|
||||
|
||||
void static socket_connection_emit_nr_connections(void){
|
||||
linked_item_t *it;
|
||||
uint8_t nr_connections = 0;
|
||||
for (it = (linked_item_t *) connections; it != NULL ; it = it->next, nr_connections++);
|
||||
|
||||
uint8_t event[2];
|
||||
event[0] = DAEMON_NR_CONNECTIONS_CHANGED;
|
||||
event[1] = nr_connections;
|
||||
(*socket_connection_packet_callback)(NULL, DAEMON_EVENT_PACKET, 0, (uint8_t *) &event, 2);
|
||||
// log_info("Nr connections changed,.. new %u\n", nr_connections);
|
||||
}
|
||||
|
||||
int socket_connection_hci_process(struct data_source *ds) {
|
||||
connection_t *conn = (connection_t *) ds;
|
||||
|
||||
int maxToRead = 0;
|
||||
ioctl(ds->fd, FIONREAD, (char*)&maxToRead);
|
||||
int bytesToRead = maxToRead;
|
||||
|
||||
do
|
||||
{
|
||||
int bytes_read = read(ds->fd, &conn->buffer[conn->bytes_read], conn->bytes_to_read);
|
||||
if (bytes_read <= 0){
|
||||
// connection broken (no particular channel, no date yet)
|
||||
socket_connection_emit_connection_closed(conn);
|
||||
|
||||
// free connection
|
||||
socket_connection_free_connection(linked_item_get_user(&conn->item));
|
||||
|
||||
socket_connection_emit_nr_connections();
|
||||
return 0;
|
||||
}
|
||||
conn->bytes_read += bytes_read;
|
||||
conn->bytes_to_read -= bytes_read;
|
||||
// hexdump( conn->buffer, conn->bytes_read);
|
||||
if (conn->bytes_to_read > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispatch = 0;
|
||||
switch (conn->state){
|
||||
case SOCKET_W4_HEADER:
|
||||
conn->state = SOCKET_W4_DATA;
|
||||
conn->bytes_to_read = READ_BT_16( conn->buffer, 4);
|
||||
if (conn->bytes_to_read == 0){
|
||||
dispatch = 1;
|
||||
}
|
||||
break;
|
||||
case SOCKET_W4_DATA:
|
||||
dispatch = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (dispatch){
|
||||
// dispatch packet !!! connection, type, channel, data, size
|
||||
int dispatch_err = (*socket_connection_packet_callback)(conn, READ_BT_16( conn->buffer, 0), READ_BT_16( conn->buffer, 2),
|
||||
&conn->buffer[sizeof(packet_header_t)], READ_BT_16( conn->buffer, 4));
|
||||
|
||||
// reset state machine
|
||||
socket_connection_init_statemachine(conn);
|
||||
|
||||
// "park" if dispatch failed
|
||||
if (dispatch_err) {
|
||||
log_info("socket_connection_hci_process dispatch failed -> park connection\n");
|
||||
run_loop_remove_data_source(ds);
|
||||
linked_list_add_tail(&parked, (linked_item_t *) ds);
|
||||
}
|
||||
}
|
||||
|
||||
// update bytes read from socket
|
||||
bytesToRead -= bytes_read;
|
||||
} while(bytesToRead > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* try to dispatch packet for all "parked" connections.
|
||||
* if dispatch is successful, a connection is added again to run loop
|
||||
* pre: connections get parked iff packet was dispatched but could not be sent
|
||||
*/
|
||||
void socket_connection_retry_parked(){
|
||||
// log_info("socket_connection_hci_process retry parked\n");
|
||||
linked_item_t *it = (linked_item_t *) &parked;
|
||||
while (it->next) {
|
||||
connection_t * conn = (connection_t *) it->next;
|
||||
|
||||
// dispatch packet !!! connection, type, channel, data, size
|
||||
log_info("socket_connection_hci_process retry parked %p\n", conn);
|
||||
int dispatch_err = (*socket_connection_packet_callback)(conn, READ_BT_16( conn->buffer, 0), READ_BT_16( conn->buffer, 2),
|
||||
&conn->buffer[sizeof(packet_header_t)], READ_BT_16( conn->buffer, 4));
|
||||
// "un-park" if successful
|
||||
if (!dispatch_err) {
|
||||
log_info("socket_connection_hci_process dispatch succeeded -> un-park connection %p\n", conn);
|
||||
it->next = it->next->next;
|
||||
run_loop_add_data_source( (data_source_t *) conn);
|
||||
} else {
|
||||
it = it->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int socket_connection_has_parked_connections(void){
|
||||
return parked != NULL;
|
||||
}
|
||||
|
||||
static int socket_connection_accept(struct data_source *socket_ds) {
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen = sizeof(ss);
|
||||
|
||||
/* New connection coming in! */
|
||||
int fd = accept(socket_ds->fd, (struct sockaddr *)&ss, &slen);
|
||||
if (fd < 0) {
|
||||
perror("accept");
|
||||
return 0;
|
||||
}
|
||||
// non-blocking ?
|
||||
// socket_connection_set_non_blocking(ds->fd);
|
||||
|
||||
// no sigpipe
|
||||
socket_connection_set_no_sigpipe(fd);
|
||||
|
||||
log_info("socket_connection_accept new connection %u\n", fd);
|
||||
|
||||
connection_t * connection = socket_connection_register_new_connection(fd);
|
||||
socket_connection_emit_connection_opened(connection);
|
||||
socket_connection_emit_nr_connections();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* create socket data_source for tcp socket
|
||||
*
|
||||
* @return data_source object. If null, check errno
|
||||
*/
|
||||
int socket_connection_create_tcp(int port){
|
||||
|
||||
// create data_source_t
|
||||
data_source_t *ds = malloc( sizeof(data_source_t));
|
||||
if (ds == NULL) return -1;
|
||||
ds->fd = 0;
|
||||
ds->process = socket_connection_accept;
|
||||
|
||||
// create tcp socket
|
||||
if ((ds->fd = socket (PF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
log_error("Error creating socket ...(%s)\n", strerror(errno));
|
||||
free(ds);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info ("Socket created for port %u\n", port);
|
||||
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons (port);
|
||||
memset (&addr.sin_addr, 0, sizeof (addr.sin_addr));
|
||||
|
||||
const int y = 1;
|
||||
setsockopt(ds->fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
|
||||
|
||||
if (bind ( ds->fd, (struct sockaddr *) &addr, sizeof (addr) ) ) {
|
||||
log_error("Error on bind() ...(%s)\n", strerror(errno));
|
||||
free(ds);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen (ds->fd, MAX_PENDING_CONNECTIONS)) {
|
||||
log_error("Error on listen() ...(%s)\n", strerror(errno));
|
||||
free(ds);
|
||||
return -1;
|
||||
}
|
||||
|
||||
run_loop_add_data_source(ds);
|
||||
|
||||
log_info ("Server up and running ...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef USE_LAUNCHD
|
||||
|
||||
/*
|
||||
* Register listening sockets with our run loop
|
||||
*/
|
||||
void socket_connection_launchd_register_fd_array(launch_data_t listening_fd_array){
|
||||
int i;
|
||||
for (i = 0; i < launch_data_array_get_count(listening_fd_array); i++) {
|
||||
// get fd
|
||||
launch_data_t tempi = launch_data_array_get_index (listening_fd_array, i);
|
||||
int listening_fd = launch_data_get_fd(tempi);
|
||||
launch_data_free (tempi);
|
||||
log_info("file descriptor = %u\n", listening_fd);
|
||||
|
||||
// create data_source_t for fd
|
||||
data_source_t *ds = malloc( sizeof(data_source_t));
|
||||
if (ds == NULL) return;
|
||||
ds->process = socket_connection_accept;
|
||||
ds->fd = listening_fd;
|
||||
run_loop_add_data_source(ds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create socket data_source for socket specified by launchd configuration
|
||||
*/
|
||||
int socket_connection_create_launchd(){
|
||||
|
||||
launch_data_t sockets_dict, checkin_response;
|
||||
launch_data_t checkin_request;
|
||||
launch_data_t listening_fd_array;
|
||||
|
||||
/*
|
||||
* Register ourselves with launchd.
|
||||
*
|
||||
*/
|
||||
if ((checkin_request = launch_data_new_string(LAUNCH_KEY_CHECKIN)) == NULL) {
|
||||
log_error( "launch_data_new_string(\"" LAUNCH_KEY_CHECKIN "\") Unable to create string.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((checkin_response = launch_msg(checkin_request)) == NULL) {
|
||||
log_error( "launch_msg(\"" LAUNCH_KEY_CHECKIN "\") IPC failure: %u", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LAUNCH_DATA_ERRNO == launch_data_get_type(checkin_response)) {
|
||||
errno = launch_data_get_errno(checkin_response);
|
||||
log_error( "Check-in failed: %u", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
launch_data_t the_label = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_LABEL);
|
||||
if (NULL == the_label) {
|
||||
log_error( "No label found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the dictionary of Socket entries in the config file
|
||||
*/
|
||||
sockets_dict = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_SOCKETS);
|
||||
if (NULL == sockets_dict) {
|
||||
log_error("No sockets found to answer requests on!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if (launch_data_dict_get_count(sockets_dict) > 1) {
|
||||
// log_error("Some sockets will be ignored!");
|
||||
// }
|
||||
|
||||
/*
|
||||
* Get the dictionary value from the key "Listeners"
|
||||
*/
|
||||
listening_fd_array = launch_data_dict_lookup(sockets_dict, "Listeners");
|
||||
if (listening_fd_array) {
|
||||
// log_error("Listeners...\n");
|
||||
socket_connection_launchd_register_fd_array( listening_fd_array );
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the dictionary value from the key "Listeners"
|
||||
*/
|
||||
listening_fd_array = launch_data_dict_lookup(sockets_dict, "Listeners2");
|
||||
if (listening_fd_array) {
|
||||
// log_error("Listeners2...\n");
|
||||
socket_connection_launchd_register_fd_array( listening_fd_array );
|
||||
}
|
||||
|
||||
// although used in Apple examples, it creates a malloc warning
|
||||
// launch_data_free(checkin_response);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* create socket data_source for unix domain socket
|
||||
*/
|
||||
int socket_connection_create_unix(char *path){
|
||||
|
||||
// create data_source_t
|
||||
data_source_t *ds = malloc( sizeof(data_source_t));
|
||||
if (ds == NULL) return -1;
|
||||
ds->fd = 0;
|
||||
ds->process = socket_connection_accept;
|
||||
|
||||
// create unix socket
|
||||
if ((ds->fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||||
log_error( "Error creating socket ...(%s)\n", strerror(errno));
|
||||
free(ds);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info ("Socket created at %s\n", path);
|
||||
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, path);
|
||||
unlink(path);
|
||||
|
||||
const int y = 1;
|
||||
setsockopt(ds->fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(int));
|
||||
|
||||
if (bind ( ds->fd, (struct sockaddr *) &addr, sizeof (addr) ) ) {
|
||||
log_error( "Error on bind() ...(%s)\n", strerror(errno));
|
||||
free(ds);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen (ds->fd, MAX_PENDING_CONNECTIONS)) {
|
||||
log_error( "Error on listen() ...(%s)\n", strerror(errno));
|
||||
free(ds);
|
||||
return -1;
|
||||
}
|
||||
|
||||
run_loop_add_data_source(ds);
|
||||
|
||||
log_info ("Server up and running ...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* set packet handler for all auto-accepted connections
|
||||
*/
|
||||
void socket_connection_register_packet_callback( int (*packet_callback)(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length) ){
|
||||
socket_connection_packet_callback = packet_callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* send HCI packet to single connection
|
||||
*/
|
||||
void socket_connection_send_packet(connection_t *conn, uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
uint8_t header[sizeof(packet_header_t)];
|
||||
bt_store_16(header, 0, type);
|
||||
bt_store_16(header, 2, channel);
|
||||
bt_store_16(header, 4, size);
|
||||
#ifdef HAVE_SO_NOSIGPIPE
|
||||
// BSD Variants like Darwin and iOS
|
||||
write(conn->ds.fd, header, 6);
|
||||
write(conn->ds.fd, packet, size);
|
||||
#else
|
||||
// Linux
|
||||
send(conn->ds.fd, header, 6, MSG_NOSIGNAL);
|
||||
send(conn->ds.fd, packet, size, MSG_NOSIGNAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* send HCI packet to all connections
|
||||
*/
|
||||
void socket_connection_send_packet_all(uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
linked_item_t *next;
|
||||
linked_item_t *it;
|
||||
for (it = (linked_item_t *) connections; it ; it = next){
|
||||
next = it->next; // cache pointer to next connection_t to allow for removal
|
||||
socket_connection_send_packet( (connection_t *) linked_item_get_user(it), type, channel, packet, size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create socket connection to BTdaemon
|
||||
*/
|
||||
connection_t * socket_connection_open_tcp(const char *address, uint16_t port){
|
||||
// TCP
|
||||
struct protoent* tcp = getprotobyname("tcp");
|
||||
|
||||
int btsocket = socket(PF_INET, SOCK_STREAM, tcp->p_proto);
|
||||
if(btsocket == -1){
|
||||
return NULL;
|
||||
}
|
||||
// localhost
|
||||
struct sockaddr_in btdaemon_address;
|
||||
btdaemon_address.sin_family = AF_INET;
|
||||
btdaemon_address.sin_port = htons(port);
|
||||
struct hostent* localhost = gethostbyname(address);
|
||||
if(!localhost){
|
||||
return NULL;
|
||||
}
|
||||
// connect
|
||||
char* addr = localhost->h_addr_list[0];
|
||||
memcpy(&btdaemon_address.sin_addr.s_addr, addr, sizeof addr);
|
||||
if(connect(btsocket, (struct sockaddr*)&btdaemon_address, sizeof btdaemon_address) == -1){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return socket_connection_register_new_connection(btsocket);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* close socket connection to BTdaemon
|
||||
*/
|
||||
int socket_connection_close_tcp(connection_t * connection){
|
||||
if (!connection) return -1;
|
||||
shutdown(connection->ds.fd, SHUT_RDWR);
|
||||
socket_connection_free_connection(connection);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create socket connection to BTdaemon
|
||||
*/
|
||||
connection_t * socket_connection_open_unix(){
|
||||
|
||||
int btsocket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if(btsocket == -1){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sockaddr_un server;
|
||||
memset(&server, 0, sizeof(server));
|
||||
server.sun_family = AF_UNIX;
|
||||
strcpy(server.sun_path, BTSTACK_UNIX);
|
||||
if (connect(btsocket, (struct sockaddr *)&server, sizeof (server)) == -1){
|
||||
return NULL;
|
||||
};
|
||||
|
||||
return socket_connection_register_new_connection(btsocket);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* close socket connection to BTdaemon
|
||||
*/
|
||||
int socket_connection_close_unix(connection_t * connection){
|
||||
if (!connection) return -1;
|
||||
shutdown(connection->ds.fd, SHUT_RDWR);
|
||||
socket_connection_free_connection(connection);
|
||||
return 0;
|
||||
}
|
||||
|
120
ios/RetroArch/input/BTStack/btstack/socket_connection.h
Normal file
120
ios/RetroArch/input/BTStack/btstack/socket_connection.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* socket_connection.h
|
||||
*
|
||||
* Created by Matthias Ringwald on 6/6/09.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <btstack/run_loop.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/** opaque connection type */
|
||||
typedef struct connection connection_t;
|
||||
|
||||
/**
|
||||
* create socket data_source for socket specified by launchd configuration
|
||||
*/
|
||||
int socket_connection_create_launchd(void);
|
||||
|
||||
/**
|
||||
* create socket for incoming tcp connections
|
||||
*/
|
||||
int socket_connection_create_tcp(int port);
|
||||
|
||||
/**
|
||||
* create socket for incoming unix domain connections
|
||||
*/
|
||||
int socket_connection_create_unix(char *path);
|
||||
|
||||
/**
|
||||
* close socket connection to BTdaemon
|
||||
*/
|
||||
int socket_connection_close_tcp(connection_t *connection);
|
||||
|
||||
/**
|
||||
* create TCP socket connection to BTdaemon
|
||||
*/
|
||||
connection_t * socket_connection_open_tcp(const char *address, uint16_t port);
|
||||
|
||||
/**
|
||||
* close TCP socket connection to BTdaemon
|
||||
*/
|
||||
int socket_connection_close_tcp(connection_t *connection);
|
||||
|
||||
/**
|
||||
* create unix socket connection to BTdaemon
|
||||
*/
|
||||
connection_t * socket_connection_open_unix(void);
|
||||
|
||||
/**
|
||||
* close unix connection to BTdaemon
|
||||
*/
|
||||
int socket_connection_close_unix(connection_t *connection);
|
||||
|
||||
/**
|
||||
* set packet handler for all auto-accepted connections
|
||||
* -- packet_callback @return: 0 == OK/NO ERROR
|
||||
*/
|
||||
void socket_connection_register_packet_callback( int (*packet_callback)(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t length) );
|
||||
|
||||
/**
|
||||
* send HCI packet to single connection
|
||||
*/
|
||||
void socket_connection_send_packet(connection_t *connection, uint16_t packet_type, uint16_t channel, uint8_t *data, uint16_t size);
|
||||
|
||||
/**
|
||||
* send event data to all clients
|
||||
*/
|
||||
void socket_connection_send_packet_all(uint16_t type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
|
||||
/**
|
||||
* try to dispatch packet for all "parked" connections.
|
||||
* if dispatch is successful, a connection is added again to run loop
|
||||
*/
|
||||
void socket_connection_retry_parked(void);
|
||||
|
||||
|
||||
/**
|
||||
* query if at least one connection had to be parked
|
||||
*/
|
||||
int socket_connection_has_parked_connections(void);
|
||||
|
184
ios/RetroArch/input/BTStack/btstack/utils.c
Normal file
184
ios/RetroArch/input/BTStack/btstack/utils.c
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 by Matthias Ringwald
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at btstack@ringwald.ch
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* utils.c
|
||||
*
|
||||
* General utility functions
|
||||
*
|
||||
* Created by Matthias Ringwald on 7/23/09.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <btstack/utils.h>
|
||||
#include <stdio.h>
|
||||
#include "debug.h"
|
||||
|
||||
void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
|
||||
buffer[pos++] = value;
|
||||
buffer[pos++] = value >> 8;
|
||||
}
|
||||
|
||||
void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){
|
||||
buffer[pos++] = value;
|
||||
buffer[pos++] = value >> 8;
|
||||
buffer[pos++] = value >> 16;
|
||||
buffer[pos++] = value >> 24;
|
||||
}
|
||||
|
||||
void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
|
||||
buffer[pos++] = value >> 8;
|
||||
buffer[pos++] = value;
|
||||
}
|
||||
|
||||
void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){
|
||||
buffer[pos++] = value >> 24;
|
||||
buffer[pos++] = value >> 16;
|
||||
buffer[pos++] = value >> 8;
|
||||
buffer[pos++] = value;
|
||||
}
|
||||
|
||||
void bt_flip_addr(bd_addr_t dest, bd_addr_t src){
|
||||
dest[0] = src[5];
|
||||
dest[1] = src[4];
|
||||
dest[2] = src[3];
|
||||
dest[3] = src[2];
|
||||
dest[4] = src[1];
|
||||
dest[5] = src[0];
|
||||
}
|
||||
|
||||
void hexdump(void *data, int size){
|
||||
int i;
|
||||
for (i=0; i<size;i++){
|
||||
log_info("%02X ", ((uint8_t *)data)[i]);
|
||||
}
|
||||
log_info("\n");
|
||||
}
|
||||
|
||||
void printUUID(uint8_t *uuid) {
|
||||
log_info("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
|
||||
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
|
||||
}
|
||||
|
||||
static char bd_addr_to_str_buffer[6*3]; // 12:45:78:01:34:67\0
|
||||
char * bd_addr_to_str(bd_addr_t addr){
|
||||
sprintf(bd_addr_to_str_buffer, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
|
||||
return (char *) bd_addr_to_str_buffer;
|
||||
}
|
||||
|
||||
void print_bd_addr( bd_addr_t addr){
|
||||
log_info("%s", bd_addr_to_str(addr));
|
||||
}
|
||||
|
||||
#ifndef EMBEDDED
|
||||
int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr){
|
||||
unsigned int bd_addr_buffer[BD_ADDR_LEN]; //for sscanf, integer needed
|
||||
// reset result buffer
|
||||
int i;
|
||||
for (i = 0; i < BD_ADDR_LEN; i++) {
|
||||
bd_addr_buffer[i] = 0;
|
||||
}
|
||||
|
||||
// parse
|
||||
int result = sscanf( (char *) addr_string, "%2x:%2x:%2x:%2x:%2x:%2x", &bd_addr_buffer[0], &bd_addr_buffer[1], &bd_addr_buffer[2],
|
||||
&bd_addr_buffer[3], &bd_addr_buffer[4], &bd_addr_buffer[5]);
|
||||
// store
|
||||
if (result == 6){
|
||||
for (i = 0; i < BD_ADDR_LEN; i++) {
|
||||
addr[i] = (uint8_t) bd_addr_buffer[i];
|
||||
}
|
||||
}
|
||||
return (result == 6);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
|
||||
*/
|
||||
static const uint8_t crc8table[256] = { /* reversed, 8-bit, poly=0x07 */
|
||||
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
|
||||
0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
|
||||
0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
|
||||
0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
|
||||
0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
|
||||
0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
|
||||
0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
|
||||
0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
|
||||
0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
|
||||
0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
|
||||
0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
|
||||
0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
|
||||
0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
|
||||
0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
|
||||
0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
|
||||
0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
|
||||
};
|
||||
|
||||
#define CRC8_INIT 0xFF // Initial FCS value
|
||||
#define CRC8_OK 0xCF // Good final FCS value
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
uint8_t crc8(uint8_t *data, uint16_t len)
|
||||
{
|
||||
uint16_t count;
|
||||
uint8_t crc = CRC8_INIT;
|
||||
for (count = 0; count < len; count++)
|
||||
crc = crc8table[crc ^ data[count]];
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum)
|
||||
{
|
||||
uint8_t crc;
|
||||
|
||||
crc = crc8(data, len);
|
||||
|
||||
crc = crc8table[crc ^ check_sum];
|
||||
if (crc == CRC8_OK)
|
||||
return 0; /* Valid */
|
||||
else
|
||||
return 1; /* Failed */
|
||||
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
uint8_t crc8_calc(uint8_t *data, uint16_t len)
|
||||
{
|
||||
/* Ones complement */
|
||||
return 0xFF - crc8(data, len);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user