blob: 093af6bb3a4771a0ad3d29e29a8500eb707dc8b1 [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,
Jakub Pawlowskide6c0132019-11-12 16:14:32 +010018 HCI_PACKET_ISO = 5,
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070019} hci_packet_t;
20
21typedef struct {
Myles Watson911d1ae2016-11-28 16:44:40 -080022 const char* name;
23 const char* help;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070024 handler_t handler;
25} command_t;
26
Myles Watson911d1ae2016-11-28 16:44:40 -080027static int help(int argc, char** argv);
28static int set_discoverable(int argc, char** argv);
29static int set_name(int argc, char** argv);
30static int set_pcm_loopback(int argc, char** argv);
31static int set_sco_route(int argc, char** argv);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070032
Myles Watson911d1ae2016-11-28 16:44:40 -080033static bool write_hci_command(hci_packet_t type, const void* packet,
34 size_t length);
35static const command_t* find_command(const char* name);
36static void usage(const char* name);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070037
38static const command_t commands[] = {
Myles Watson911d1ae2016-11-28 16:44:40 -080039 {"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},
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070052};
53
Myles Watson911d1ae2016-11-28 16:44:40 -080054static int help(int argc, char** argv) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070055 if (!argc) {
56 printf("No help command specified.\n");
57 return 1;
58 }
59
Myles Watson911d1ae2016-11-28 16:44:40 -080060 const command_t* command = find_command(argv[0]);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070061 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
Myles Watson911d1ae2016-11-28 16:44:40 -080070static int set_discoverable(int argc, char** argv) {
Sharvil Nanavati52743022014-11-05 14:48:58 -080071 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
Myles Watson911d1ae2016-11-28 16:44:40 -080081 uint8_t packet[] = {0x1A, 0x0C, 0x01, 0x00};
82 if (argv[0][0] == 't') packet[ARRAY_SIZE(packet) - 1] = 0x03;
Sharvil Nanavati52743022014-11-05 14:48:58 -080083
84 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet));
85}
86
Myles Watson911d1ae2016-11-28 16:44:40 -080087static int set_name(int argc, char** argv) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -070088 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
Myles Watson911d1ae2016-11-28 16:44:40 -080099 uint8_t packet[251] = {0x13, 0x0C, 248};
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700100 memcpy(&packet[3], argv[0], len + 1);
101
Myles Watson911d1ae2016-11-28 16:44:40 -0800102 if (!write_hci_command(HCI_PACKET_COMMAND, packet, sizeof(packet))) return 1;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700103
Edward Savage-Jones2e908122016-02-25 00:44:51 +0100104 memset(&packet[0], 0, sizeof(packet));
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700105 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
Myles Watson911d1ae2016-11-28 16:44:40 -0800115static int set_pcm_loopback(int argc, char** argv) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700116 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
Myles Watson911d1ae2016-11-28 16:44:40 -0800126 uint8_t packet[] = {0x24, 0xFC, 0x01, 0x00};
127 if (argv[0][0] == 't') packet[ARRAY_SIZE(packet) - 1] = 0x01;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700128
129 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet));
130}
131
Myles Watson911d1ae2016-11-28 16:44:40 -0800132static int set_sco_route(int argc, char** argv) {
Sharvil Nanavatif0603652015-07-16 02:55:27 -0700133 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
Myles Watson911d1ae2016-11-28 16:44:40 -0800151 uint8_t packet[] = {0x1C, 0xFC, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00};
Sharvil Nanavatif0603652015-07-16 02:55:27 -0700152 packet[3] = route;
153
154 return !write_hci_command(HCI_PACKET_COMMAND, packet, ARRAY_SIZE(packet));
155}
156
Myles Watson911d1ae2016-11-28 16:44:40 -0800157int main(int argc, char** argv) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700158 if (argc < 2) {
159 usage(argv[0]);
160 return -1;
161 }
162
Myles Watson911d1ae2016-11-28 16:44:40 -0800163 const command_t* command = find_command(argv[1]);
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700164 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
Myles Watson911d1ae2016-11-28 16:44:40 -0800177static bool write_hci_command(hci_packet_t type, const void* packet,
178 size_t length) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700179 int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Myles Watson911d1ae2016-11-28 16:44:40 -0800180 if (sock == INVALID_FD) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700181
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);
Pavlin Radoslavovd6121a32016-05-12 11:36:44 -0700186 int ret;
Myles Watson911d1ae2016-11-28 16:44:40 -0800187 OSI_NO_INTR(ret = connect(sock, (const struct sockaddr*)&addr, sizeof(addr)));
188 if (ret == -1) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700189
Myles Watson911d1ae2016-11-28 16:44:40 -0800190 if (send(sock, &type, 1, 0) != 1) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700191
Myles Watson911d1ae2016-11-28 16:44:40 -0800192 if (send(sock, &length, 2, 0) != 2) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700193
Myles Watson911d1ae2016-11-28 16:44:40 -0800194 if (send(sock, packet, length, 0) != (ssize_t)length) goto error;
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700195
196 close(sock);
197 return true;
198
199error:;
200 close(sock);
201 return false;
202}
203
Myles Watson911d1ae2016-11-28 16:44:40 -0800204static const command_t* find_command(const char* name) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700205 for (size_t i = 0; i < ARRAY_SIZE(commands); ++i)
Myles Watson911d1ae2016-11-28 16:44:40 -0800206 if (!strcmp(commands[i].name, name)) return &commands[i];
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700207 return NULL;
208}
209
Myles Watson911d1ae2016-11-28 16:44:40 -0800210static void usage(const char* name) {
Sharvil Nanavati1f8c2352014-08-13 01:14:49 -0700211 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}