blob: c1678867ce1d02e6e3f72ea2837e09e7e93ec796 [file] [log] [blame]
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -07001#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
Sharvil Nanavati0f9b91e2015-03-12 15:42:50 -07009#include "osi/include/osi.h"
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070010
Myles Watson911d1ae2016-11-28 16:44:40 -080011typedef int (*handler_t)(int argc, char** argv);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070012
13typedef enum {
Myles Watson911d1ae2016-11-28 16:44:40 -080014 HCI_PACKET_COMMAND = 1,
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070015 HCI_PACKET_ACL_DATA = 2,
16 HCI_PACKET_SCO_DATA = 3,
Myles Watson911d1ae2016-11-28 16:44:40 -080017 HCI_PACKET_EVENT = 4,
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070018} hci_packet_t;
19
20typedef struct {
Myles Watson911d1ae2016-11-28 16:44:40 -080021 const char* name;
22 const char* help;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070023 handler_t handler;
24} command_t;
25
Myles Watson911d1ae2016-11-28 16:44:40 -080026static 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);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070031
Myles Watson911d1ae2016-11-28 16:44:40 -080032static bool write_hci_command(hci_packet_t type, const void* packet,
33 size_t length);
34static const command_t* find_command(const char* name);
35static void usage(const char* name);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070036
37static const command_t commands[] = {
Myles Watson911d1ae2016-11-28 16:44:40 -080038 {"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},
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070051};
52
Myles Watson911d1ae2016-11-28 16:44:40 -080053static int help(int argc, char** argv) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070054 if (!argc) {
55 printf("No help command specified.\n");
56 return 1;
57 }
58
Myles Watson911d1ae2016-11-28 16:44:40 -080059 const command_t* command = find_command(argv[0]);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070060 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
Myles Watson911d1ae2016-11-28 16:44:40 -080069static int set_discoverable(int argc, char** argv) {
Sharvil Nanavati52743022014-11-05 14:48:58 -080070 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
Myles Watson911d1ae2016-11-28 16:44:40 -080080 uint8_t packet[] = {0x1A, 0x0C, 0x01, 0x00};
81 if (argv[0][0] == 't') packet[ARRAY_SIZE(packet) - 1] = 0x03;
Sharvil Nanavati52743022014-11-05 14:48:58 -080082
83 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet));
84}
85
Myles Watson911d1ae2016-11-28 16:44:40 -080086static int set_name(int argc, char** argv) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070087 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
Myles Watson911d1ae2016-11-28 16:44:40 -080098 uint8_t packet[251] = {0x13, 0x0C, 248};
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070099 memcpy(&packet[3], argv[0], len + 1);
100
Myles Watson911d1ae2016-11-28 16:44:40 -0800101 if (!write_hci_command(HCI_PACKET_COMMAND, packet, sizeof(packet))) return 1;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700102
Edward Savage-Jones2e908122016-02-25 00:44:51 +0100103 memset(&packet[0], 0, sizeof(packet));
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700104 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
Myles Watson911d1ae2016-11-28 16:44:40 -0800114static int set_pcm_loopback(int argc, char** argv) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700115 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
Myles Watson911d1ae2016-11-28 16:44:40 -0800125 uint8_t packet[] = {0x24, 0xFC, 0x01, 0x00};
126 if (argv[0][0] == 't') packet[ARRAY_SIZE(packet) - 1] = 0x01;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700127
128 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet));
129}
130
Myles Watson911d1ae2016-11-28 16:44:40 -0800131static int set_sco_route(int argc, char** argv) {
Sharvil Nanavatif0603652015-07-16 02:55:27 -0700132 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
Myles Watson911d1ae2016-11-28 16:44:40 -0800150 uint8_t packet[] = {0x1C, 0xFC, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00};
Sharvil Nanavatif0603652015-07-16 02:55:27 -0700151 packet[3] = route;
152
153 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet));
154}
155
Myles Watson911d1ae2016-11-28 16:44:40 -0800156int main(int argc, char** argv) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700157 if (argc < 2) {
158 usage(argv[0]);
159 return -1;
160 }
161
Myles Watson911d1ae2016-11-28 16:44:40 -0800162 const command_t* command = find_command(argv[1]);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700163 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
Myles Watson911d1ae2016-11-28 16:44:40 -0800176static bool write_hci_command(hci_packet_t type, const void* packet,
177 size_t length) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700178 int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Myles Watson911d1ae2016-11-28 16:44:40 -0800179 if (sock == INVALID_FD) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700180
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);
Pavlin Radoslavovd6121a32016-05-12 11:36:44 -0700185 int ret;
Myles Watson911d1ae2016-11-28 16:44:40 -0800186 OSI_NO_INTR(ret = connect(sock, (const struct sockaddr*)&addr, sizeof(addr)));
187 if (ret == -1) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700188
Myles Watson911d1ae2016-11-28 16:44:40 -0800189 if (send(sock, &type, 1, 0) != 1) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700190
Myles Watson911d1ae2016-11-28 16:44:40 -0800191 if (send(sock, &length, 2, 0) != 2) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700192
Myles Watson911d1ae2016-11-28 16:44:40 -0800193 if (send(sock, packet, length, 0) != (ssize_t)length) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700194
195 close(sock);
196 return true;
197
198error:;
199 close(sock);
200 return false;
201}
202
Myles Watson911d1ae2016-11-28 16:44:40 -0800203static const command_t* find_command(const char* name) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700204 for (size_t i = 0; i < ARRAY_SIZE(commands); ++i)
Myles Watson911d1ae2016-11-28 16:44:40 -0800205 if (!strcmp(commands[i].name, name)) return &commands[i];
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700206 return NULL;
207}
208
Myles Watson911d1ae2016-11-28 16:44:40 -0800209static void usage(const char* name) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700210 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}