1 /* RetroArch - A frontend for libretro.
2 *
3 * RetroArch is free software: you can redistribute it and/or modify it under the terms
4 * of the GNU General Public License as published by the Free Software Found-
5 * ation, either version 3 of the License, or (at your option) any later version.
6 *
7 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9 * PURPOSE. See the GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License along with RetroArch.
12 * If not, see <http://www.gnu.org/licenses/>.
13 */
14
15 #include <compat/strl.h>
16 #include <configuration.h>
17
18 #include "../bluetooth_driver.h"
19 #include "../../retroarch.h"
20
21 typedef struct
22 {
23 bool bluetoothctl_cache[256];
24 unsigned bluetoothctl_counter[256];
25 struct string_list* lines;
26 char command[256];
27 } bluetoothctl_t;
28
bluetoothctl_init(void)29 static void *bluetoothctl_init(void)
30 {
31 return calloc(1, sizeof(bluetoothctl_t));
32 }
33
bluetoothctl_free(void * data)34 static void bluetoothctl_free(void *data)
35 {
36 if (data)
37 free(data);
38 }
39
bluetoothctl_scan(void * data)40 static void bluetoothctl_scan(void *data)
41 {
42 char line[512];
43 union string_list_elem_attr attr;
44 FILE *dev_file = NULL;
45 bluetoothctl_t *btctl = (bluetoothctl_t*) data;
46
47 attr.i = 0;
48 if (btctl->lines)
49 free(btctl->lines);
50 btctl->lines = string_list_new();
51
52 pclose(popen("bluetoothctl -- power on", "r"));
53
54 pclose(popen("bluetoothctl --timeout 10 scan on", "r"));
55
56 runloop_msg_queue_push(msg_hash_to_str(MSG_BLUETOOTH_SCAN_COMPLETE),
57 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
58 MESSAGE_QUEUE_CATEGORY_INFO);
59
60 dev_file = popen("bluetoothctl -- devices", "r");
61
62 while (fgets(line, 512, dev_file))
63 {
64 size_t len = strlen(line);
65 if (len > 0 && line[len-1] == '\n')
66 line[--len] = '\0';
67
68 string_list_append(btctl->lines, line, attr);
69 }
70
71 pclose(dev_file);
72 }
73
bluetoothctl_get_devices(void * data,struct string_list * devices)74 static void bluetoothctl_get_devices(void *data, struct string_list* devices)
75 {
76 unsigned i;
77 union string_list_elem_attr attr;
78 bluetoothctl_t *btctl = (bluetoothctl_t*) data;
79
80 attr.i = 0;
81
82 if (!btctl->lines)
83 return;
84
85 for (i = 0; i < btctl->lines->size; i++)
86 {
87 char device[64];
88 const char *line = btctl->lines->elems[i].data;
89
90 /* bluetoothctl devices outputs lines of the format:
91 * $ bluetoothctl devices
92 * 'Device (mac address) (device name)'
93 */
94 strlcpy(device, line+24, sizeof(device));
95 string_list_append(devices, device, attr);
96 }
97 }
98
bluetoothctl_device_is_connected(void * data,unsigned i)99 static bool bluetoothctl_device_is_connected(void *data, unsigned i)
100 {
101 bluetoothctl_t *btctl = (bluetoothctl_t*) data;
102 char ln[512] = {0};
103 char device[18] = {0};
104 const char *line = btctl->lines->elems[i].data;
105 FILE *command_file = NULL;
106
107 if (btctl->bluetoothctl_counter[i] == 60)
108 {
109 static struct string_list* list = NULL;
110 btctl->bluetoothctl_counter[i] = 0;
111 list = string_split(line, " ");
112 if (!list)
113 return false;
114
115 if (list->size == 0)
116 {
117 string_list_free(list);
118 return false;
119 }
120
121 strlcpy(device, list->elems[1].data, sizeof(device));
122 string_list_free(list);
123
124 snprintf(btctl->command, sizeof(btctl->command), "\
125 bluetoothctl -- info %s | grep 'Connected: yes'",
126 device);
127
128 command_file = popen(btctl->command, "r");
129
130 while (fgets(ln, 512, command_file))
131 {
132 btctl->bluetoothctl_cache[i] = true;
133 return true;
134 }
135 pclose(command_file);
136 btctl->bluetoothctl_cache[i] = false;
137 }
138 else
139 {
140 btctl->bluetoothctl_counter[i]++;
141 return btctl->bluetoothctl_cache[i];
142 }
143
144 return false;
145 }
146
bluetoothctl_connect_device(void * data,unsigned idx)147 static bool bluetoothctl_connect_device(void *data, unsigned idx)
148 {
149 unsigned i;
150 bluetoothctl_t *btctl = (bluetoothctl_t*) data;
151 char device[18] = {0};
152 const char *line = btctl->lines->elems[idx].data;
153 static struct string_list* list = NULL;
154
155 /* bluetoothctl devices outputs lines of the format:
156 * $ bluetoothctl devices
157 * 'Device (mac address) (device name)'
158 */
159 list = string_split(line, " ");
160 if (!list)
161 return false;
162
163 if (list->size == 0)
164 {
165 string_list_free(list);
166 return false;
167 }
168
169 strlcpy(device, list->elems[1].data, sizeof(device));
170 string_list_free(list);
171
172 snprintf(btctl->command, sizeof(btctl->command), "\
173 bluetoothctl -- trust %s",
174 device);
175
176 pclose(popen(btctl->command, "r"));
177
178 snprintf(btctl->command, sizeof(btctl->command), "\
179 bluetoothctl -- pair %s",
180 device);
181
182 pclose(popen(btctl->command, "r"));
183
184 snprintf(btctl->command, sizeof(btctl->command), "\
185 bluetoothctl -- connect %s",
186 device);
187
188 pclose(popen(btctl->command, "r"));
189
190 btctl->bluetoothctl_counter[idx] = 0;
191 return true;
192 }
193
bluetoothctl_device_get_sublabel(void * data,char * s,unsigned i,size_t len)194 static void bluetoothctl_device_get_sublabel(
195 void *data, char *s, unsigned i, size_t len)
196 {
197 bluetoothctl_t *btctl = (bluetoothctl_t*) data;
198 /* bluetoothctl devices outputs lines of the format:
199 * $ bluetoothctl devices
200 * 'Device (mac address) (device name)'
201 */
202 const char *line = btctl->lines->elems[i].data;
203 strlcpy(s, line+7, 18);
204 }
205
206 bluetooth_driver_t bluetooth_bluetoothctl = {
207 bluetoothctl_init,
208 bluetoothctl_free,
209 bluetoothctl_scan,
210 bluetoothctl_get_devices,
211 bluetoothctl_device_is_connected,
212 bluetoothctl_device_get_sublabel,
213 bluetoothctl_connect_device,
214 "bluetoothctl",
215 };
216