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