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