1#include <hardware/bluetooth.h> 2#include <netinet/in.h> 3#include <stdio.h> 4#include <string.h> 5#include <sys/socket.h> 6#include <sys/types.h> 7#include <unistd.h> 8 9#include "osi/include/osi.h" 10 11typedef int (*handler_t)(int argc, char **argv); 12 13typedef enum { 14 HCI_PACKET_COMMAND = 1, 15 HCI_PACKET_ACL_DATA = 2, 16 HCI_PACKET_SCO_DATA = 3, 17 HCI_PACKET_EVENT = 4, 18} hci_packet_t; 19 20typedef struct { 21 const char *name; 22 const char *help; 23 handler_t handler; 24} command_t; 25 26static int help(int argc, char **argv); 27static int set_discoverable(int argc, char **argv); 28static int set_name(int argc, char **argv); 29static int set_pcm_loopback(int argc, char **argv); 30static int set_sco_route(int argc, char **argv); 31 32static bool write_hci_command(hci_packet_t type, const void *packet, size_t length); 33static const command_t *find_command(const char *name); 34static void usage(const char *name); 35 36static const command_t commands[] = { 37 { "help", "<command> - shows help text for <command>.", help }, 38 { "setDiscoverable", "(true|false) - whether the controller should be discoverable.", set_discoverable }, 39 { "setName", "<name> - sets the device's Bluetooth name to <name>.", set_name }, 40 { "setPcmLoopback", "(true|false) - enables or disables PCM loopback on the controller.", set_pcm_loopback }, 41 { "setScoRoute", "(pcm|i2s|uart) - sets the SCO packet route to one of the specified buses.", set_sco_route }, 42}; 43 44static int help(int argc, char **argv) { 45 if (!argc) { 46 printf("No help command specified.\n"); 47 return 1; 48 } 49 50 const command_t *command = find_command(argv[0]); 51 if (!command) { 52 printf("No command named '%s'.\n", argv[0]); 53 return 2; 54 } 55 56 printf("%s %s\n", argv[0], command->help); 57 return 0; 58} 59 60static int set_discoverable(int argc, char **argv) { 61 if (argc != 1) { 62 printf("Discoverable mode not specified.\n"); 63 return 1; 64 } 65 66 if (strcmp(argv[0], "true") && strcmp(argv[0], "false")) { 67 printf("Invalid discoverable mode '%s'.\n", argv[0]); 68 return 2; 69 } 70 71 uint8_t packet[] = { 0x1A, 0x0C, 0x01, 0x00 }; 72 if (argv[0][0] == 't') 73 packet[ARRAY_SIZE(packet) - 1] = 0x03; 74 75 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet)); 76} 77 78static int set_name(int argc, char **argv) { 79 if (argc != 1) { 80 printf("Device name not specified.\n"); 81 return 1; 82 } 83 84 size_t len = strlen(argv[0]); 85 if (len > 247) { 86 printf("Device name cannot exceed 247 bytes.\n"); 87 return 2; 88 } 89 90 uint8_t packet[251] = { 0x13, 0x0C, 248 }; 91 memcpy(&packet[3], argv[0], len + 1); 92 93 if (!write_hci_command(HCI_PACKET_COMMAND, packet, sizeof(packet))) 94 return 1; 95 96 memset(&packet[0], 0, sizeof(packet)); 97 packet[0] = 0x52; 98 packet[1] = 0x0C; 99 packet[2] = 0xF1; // HCI command packet length. 100 packet[3] = 0x01; // FEC required. 101 packet[4] = len + 1; 102 packet[5] = 0x09; // Device name field tag. 103 memcpy(&packet[6], argv[0], len); 104 return !write_hci_command(HCI_PACKET_COMMAND, packet, 0xF4); 105} 106 107static int set_pcm_loopback(int argc, char **argv) { 108 if (argc != 1) { 109 printf("PCM loopback mode not specified.\n"); 110 return 1; 111 } 112 113 if (strcmp(argv[0], "true") && strcmp(argv[0], "false")) { 114 printf("Invalid PCM mode '%s'.\n", argv[0]); 115 return 2; 116 } 117 118 uint8_t packet[] = { 0x24, 0xFC, 0x01, 0x00 }; 119 if (argv[0][0] == 't') 120 packet[ARRAY_SIZE(packet) - 1] = 0x01; 121 122 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet)); 123} 124 125static int set_sco_route(int argc, char **argv) { 126 if (argc != 1) { 127 printf("SCO route parameter must be specified.\n"); 128 return 1; 129 } 130 131 uint8_t route = 0xFF; 132 if (!strcmp(argv[0], "pcm")) 133 route = 0; 134 else if (!strcmp(argv[0], "i2s")) 135 route = 3; 136 else if (!strcmp(argv[0], "uart")) 137 route = 1; 138 139 if (route == 0xFF) { 140 printf("Invalid SCO route specified: %s\n", argv[0]); 141 return 2; 142 } 143 144 uint8_t packet[] = { 0x1C, 0xFC, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00 }; 145 packet[3] = route; 146 147 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet)); 148} 149 150int main(int argc, char **argv) { 151 if (argc < 2) { 152 usage(argv[0]); 153 return -1; 154 } 155 156 const command_t *command = find_command(argv[1]); 157 if (!command) { 158 printf("Unrecognized command '%s'.\n", argv[1]); 159 return -2; 160 } 161 162 if (!command->handler) { 163 printf("Unhandled command '%s'.\n", argv[1]); 164 return -3; 165 } 166 167 return command->handler(argc - 2, &argv[2]); 168} 169 170static bool write_hci_command(hci_packet_t type, const void *packet, size_t length) { 171 int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 172 if (sock == INVALID_FD) 173 goto error; 174 175 struct sockaddr_in addr; 176 addr.sin_family = AF_INET; 177 addr.sin_addr.s_addr = htonl(0x7F000001); 178 addr.sin_port = htons(8873); 179 int ret; 180 OSI_NO_INTR(ret = connect(sock, (const struct sockaddr *)&addr, 181 sizeof(addr))); 182 if (ret == -1) 183 goto error; 184 185 if (send(sock, &type, 1, 0) != 1) 186 goto error; 187 188 if (send(sock, &length, 2, 0) != 2) 189 goto error; 190 191 if (send(sock, packet, length, 0) != (ssize_t)length) 192 goto error; 193 194 close(sock); 195 return true; 196 197error:; 198 close(sock); 199 return false; 200} 201 202static const command_t *find_command(const char *name) { 203 for (size_t i = 0; i < ARRAY_SIZE(commands); ++i) 204 if (!strcmp(commands[i].name, name)) 205 return &commands[i]; 206 return NULL; 207} 208 209static void usage(const char *name) { 210 printf("Usage: %s <command> [options]\n", name); 211 printf("Commands:\n"); 212 for (size_t i = 0; i < ARRAY_SIZE(commands); ++i) 213 printf(" %s\n", commands[i].name); 214 printf("For detailed help on a command, run '%s help <command>'.\n", name); 215} 216