2 * Copyright © 2016 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 #include "libaltos_private.h"
20 #include "libaltos_posix.h"
24 #include <bluetooth/bluetooth.h>
25 #include <bluetooth/hci.h>
26 #include <bluetooth/hci_lib.h>
27 #include <bluetooth/rfcomm.h>
30 cc_fullname (char *dir, char *file)
33 int dlen = strlen (dir);
34 int flen = strlen (file);
37 if (dir[dlen-1] != '/')
39 new = malloc (dlen + slen + flen + 1);
50 cc_basename(char *file)
54 b = strrchr(file, '/');
61 load_string(char *dir, char *file)
63 char *full = cc_fullname(dir, file);
73 r = fgets(line, sizeof (line), f);
78 if (r[rlen-1] == '\n')
84 load_hex(char *dir, char *file)
90 line = load_string(dir, file);
93 i = strtol(line, &end, 16);
101 load_dec(char *dir, char *file)
107 line = load_string(dir, file);
110 i = strtol(line, &end, 10);
118 dir_filter_tty_colon(const struct dirent *d)
120 return strncmp(d->d_name, "tty:", 4) == 0;
124 dir_filter_tty(const struct dirent *d)
126 return strncmp(d->d_name, "tty", 3) == 0;
129 struct altos_usbdev {
134 int serial; /* AltOS always uses simple integer serial numbers */
145 struct dirent **namelist;
148 char endpoint_base[20];
154 base = cc_basename(sys);
155 num_configs = load_hex(sys, "bNumConfigurations");
156 num_interfaces = load_hex(sys, "bNumInterfaces");
157 for (config = 1; config <= num_configs; config++) {
158 for (interface = 0; interface < num_interfaces; interface++) {
159 sprintf(endpoint_base, "%s:%d.%d",
160 base, config, interface);
161 endpoint_full = cc_fullname(sys, endpoint_base);
164 /* Check for tty:ttyACMx style names
166 ntty = scandir(endpoint_full, &namelist,
167 dir_filter_tty_colon,
171 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
176 /* Check for tty/ttyACMx style names
178 tty_dir = cc_fullname(endpoint_full, "tty");
179 ntty = scandir(tty_dir, &namelist,
184 tty = cc_fullname("/dev", namelist[0]->d_name);
190 /* Check for ttyACMx style names
192 ntty = scandir(endpoint_full, &namelist,
197 tty = cc_fullname("/dev", namelist[0]->d_name);
207 static struct altos_usbdev *
208 usb_scan_device(char *sys)
210 struct altos_usbdev *usbdev;
216 usbdev = calloc(1, sizeof (struct altos_usbdev));
219 usbdev->sys = strdup(sys);
220 usbdev->manufacturer = load_string(sys, "manufacturer");
221 usbdev->product_name = load_string(sys, "product");
222 usbdev->serial = load_dec(sys, "serial");
223 usbdev->idProduct = load_hex(sys, "idProduct");
224 usbdev->idVendor = load_hex(sys, "idVendor");
230 usbdev_free(struct altos_usbdev *usbdev)
233 free(usbdev->manufacturer);
234 free(usbdev->product_name);
235 /* this can get used as a return value */
241 #define USB_DEVICES "/sys/bus/usb/devices"
244 dir_filter_dev(const struct dirent *d)
246 const char *n = d->d_name;
254 if (c == '.' && n != d->d_name + 1)
262 struct altos_usbdev **dev;
268 altos_list_start(void)
271 struct dirent **ents;
273 struct altos_usbdev *dev;
274 struct altos_list *devs;
277 devs = calloc(1, sizeof (struct altos_list));
281 n = scandir (USB_DEVICES, &ents,
286 for (e = 0; e < n; e++) {
287 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
288 dev = usb_scan_device(dir);
293 devs->dev = realloc(devs->dev,
294 (devs->ndev + 1) * sizeof (struct usbdev *));
296 devs->dev = malloc (sizeof (struct usbdev *));
297 devs->dev[devs->ndev++] = dev;
304 PUBLIC struct altos_list *
305 altos_ftdi_list_start(void)
307 return altos_list_start();
311 altos_list_next(struct altos_list *list, struct altos_device *device)
313 struct altos_usbdev *dev;
314 if (list->current >= list->ndev) {
317 dev = list->dev[list->current];
318 strcpy(device->name, dev->product_name);
319 device->vendor = dev->idVendor;
320 device->product = dev->idProduct;
321 strcpy(device->path, dev->tty);
322 device->serial = dev->serial;
328 altos_list_finish(struct altos_list *usbdevs)
334 for (i = 0; i < usbdevs->ndev; i++)
335 usbdev_free(usbdevs->dev[i]);
342 static int bt_initialized;
344 static int init_bt(void) {
345 if (!bt_initialized) {
347 libbt = dlopen("libbluetooth.so.3", RTLD_LAZY);
349 printf("failed to find bluetooth library\n");
351 return libbt != NULL;
354 #define join(a,b) a ## b
355 #define bt_func(name, ret, fail, formals, actuals) \
356 static ret join(altos_, name) formals { \
357 static ret (*name) formals; \
358 if (!init_bt()) return fail; \
359 name = dlsym(libbt, #name); \
360 if (!name) return fail; \
361 return name actuals; \
364 bt_func(ba2str, int, -1, (const bdaddr_t *ba, char *str), (ba, str))
365 #define ba2str altos_ba2str
367 bt_func(str2ba, int, -1, (const char *str, bdaddr_t *ba), (str, ba))
368 #define str2ba altos_str2ba
370 bt_func(hci_read_remote_name, int, -1, (int sock, const bdaddr_t *ba, int len, char *name, int timeout), (sock, ba, len, name, timeout))
371 #define hci_read_remote_name altos_hci_read_remote_name
373 bt_func(hci_open_dev, int, -1, (int dev_id), (dev_id))
374 #define hci_open_dev altos_hci_open_dev
376 bt_func(hci_get_route, int, -1, (bdaddr_t *bdaddr), (bdaddr))
377 #define hci_get_route altos_hci_get_route
379 bt_func(hci_inquiry, int, -1, (int adapter_id, int len, int max_rsp, const uint8_t *lap, inquiry_info **devs, long flags), (adapter_id, len, max_rsp, lap, devs, flags))
380 #define hci_inquiry altos_hci_inquiry
382 struct altos_bt_list {
390 #define INQUIRY_MAX_RSP 255
392 struct altos_bt_list *
393 altos_bt_list_start(int inquiry_time)
395 struct altos_bt_list *bt_list;
397 bt_list = calloc(1, sizeof (struct altos_bt_list));
401 bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
404 bt_list->dev_id = hci_get_route(NULL);
405 if (bt_list->dev_id < 0)
408 bt_list->sock = hci_open_dev(bt_list->dev_id);
409 if (bt_list->sock < 0)
412 bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
418 if (bt_list->num_rsp < 0)
425 close(bt_list->sock);
436 altos_bt_list_next(struct altos_bt_list *bt_list,
437 struct altos_bt_device *device)
441 if (bt_list->rsp >= bt_list->num_rsp)
444 ii = &bt_list->ii[bt_list->rsp];
445 if (ba2str(&ii->bdaddr, device->addr) < 0)
447 memset(&device->name, '\0', sizeof (device->name));
448 if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
449 sizeof (device->name),
450 device->name, 0) < 0) {
451 strcpy(device->name, "[unknown]");
458 altos_bt_list_finish(struct altos_bt_list *bt_list)
460 close(bt_list->sock);
466 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
468 strncpy(device->name, name, sizeof (device->name));
469 device->name[sizeof(device->name)-1] = '\0';
470 strncpy(device->addr, addr, sizeof (device->addr));
471 device->addr[sizeof(device->addr)-1] = '\0';
475 altos_bt_open(struct altos_bt_device *device)
477 struct sockaddr_rc addr = { 0 };
479 struct altos_file_posix *file;
481 file = calloc(1, sizeof (struct altos_file_posix));
484 altos_set_last_posix_error();
487 addr.rc_family = AF_BLUETOOTH;
489 if (str2ba(device->addr, &addr.rc_bdaddr) < 0) {
490 altos_set_last_posix_error();
494 for (i = 0; i < 5; i++) {
495 file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
497 altos_set_last_posix_error();
501 status = connect(file->fd,
502 (struct sockaddr *)&addr,
504 if (status >= 0 || errno != EBUSY)
510 altos_set_last_posix_error();
518 file->out_fd = dup(file->fd);