main.c revision 2e90812743309f71ce779a356c0e0ec8284b9867
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  if (connect(sock, (const struct sockaddr *)&addr, sizeof(addr)) == -1)
180    goto error;
181
182  if (send(sock, &type, 1, 0) != 1)
183    goto error;
184
185  if (send(sock, &length, 2, 0) != 2)
186    goto error;
187
188  if (send(sock, packet, length, 0) != (ssize_t)length)
189    goto error;
190
191  close(sock);
192  return true;
193
194error:;
195  close(sock);
196  return false;
197}
198
199static const command_t *find_command(const char *name) {
200  for (size_t i = 0; i < ARRAY_SIZE(commands); ++i)
201    if (!strcmp(commands[i].name, name))
202      return &commands[i];
203  return NULL;
204}
205
206static void usage(const char *name) {
207  printf("Usage: %s <command> [options]\n", name);
208  printf("Commands:\n");
209  for (size_t i = 0; i < ARRAY_SIZE(commands); ++i)
210    printf("  %s\n", commands[i].name);
211  printf("For detailed help on a command, run '%s help <command>'.\n", name);
212}
213