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