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; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20 #include "libaltos_private.h"
21 #include "libaltos_posix.h"
25 #include <bluetooth/bluetooth.h>
26 #include <bluetooth/hci.h>
27 #include <bluetooth/hci_lib.h>
28 #include <bluetooth/rfcomm.h>
29 #include <bluetooth/sdp.h>
30 #include <bluetooth/sdp_lib.h>
33 cc_fullname (char *dir, char *file)
36 int dlen = strlen (dir);
37 int flen = strlen (file);
40 if (dir[dlen-1] != '/')
42 new = malloc (dlen + slen + flen + 1);
53 cc_basename(char *file)
57 b = strrchr(file, '/');
64 load_string(char *dir, char *file)
66 char *full = cc_fullname(dir, file);
76 r = fgets(line, sizeof (line), f);
81 if (r[rlen-1] == '\n')
87 load_hex(char *dir, char *file)
93 line = load_string(dir, file);
96 i = strtol(line, &end, 16);
104 load_dec(char *dir, char *file)
110 line = load_string(dir, file);
113 i = strtol(line, &end, 10);
121 dir_filter_tty_colon(const struct dirent *d)
123 return strncmp(d->d_name, "tty:", 4) == 0;
127 dir_filter_tty(const struct dirent *d)
129 return strncmp(d->d_name, "tty", 3) == 0;
132 struct altos_usbdev {
137 int serial; /* AltOS always uses simple integer serial numbers */
148 struct dirent **namelist;
151 char endpoint_base[20];
157 base = cc_basename(sys);
158 num_configs = load_hex(sys, "bNumConfigurations");
159 num_interfaces = load_hex(sys, "bNumInterfaces");
160 for (config = 1; config <= num_configs; config++) {
161 for (interface = 0; interface < num_interfaces; interface++) {
162 sprintf(endpoint_base, "%s:%d.%d",
163 base, config, interface);
164 endpoint_full = cc_fullname(sys, endpoint_base);
167 /* Check for tty:ttyACMx style names
169 ntty = scandir(endpoint_full, &namelist,
170 dir_filter_tty_colon,
174 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
179 /* Check for tty/ttyACMx style names
181 tty_dir = cc_fullname(endpoint_full, "tty");
182 ntty = scandir(tty_dir, &namelist,
187 tty = cc_fullname("/dev", namelist[0]->d_name);
193 /* Check for ttyACMx style names
195 ntty = scandir(endpoint_full, &namelist,
200 tty = cc_fullname("/dev", namelist[0]->d_name);
210 static struct altos_usbdev *
211 usb_scan_device(char *sys)
213 struct altos_usbdev *usbdev;
219 usbdev = calloc(1, sizeof (struct altos_usbdev));
222 usbdev->sys = strdup(sys);
223 usbdev->manufacturer = load_string(sys, "manufacturer");
224 usbdev->product_name = load_string(sys, "product");
225 usbdev->serial = load_dec(sys, "serial");
226 usbdev->idProduct = load_hex(sys, "idProduct");
227 usbdev->idVendor = load_hex(sys, "idVendor");
233 usbdev_free(struct altos_usbdev *usbdev)
236 free(usbdev->manufacturer);
237 free(usbdev->product_name);
238 /* this can get used as a return value */
244 #define USB_DEVICES "/sys/bus/usb/devices"
247 dir_filter_dev(const struct dirent *d)
249 const char *n = d->d_name;
257 if (c == '.' && n != d->d_name + 1)
265 struct altos_usbdev **dev;
271 altos_list_start(void)
274 struct dirent **ents;
276 struct altos_usbdev *dev;
277 struct altos_list *devs;
280 devs = calloc(1, sizeof (struct altos_list));
284 n = scandir (USB_DEVICES, &ents,
289 for (e = 0; e < n; e++) {
290 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
291 dev = usb_scan_device(dir);
296 devs->dev = realloc(devs->dev,
297 (devs->ndev + 1) * sizeof (struct usbdev *));
299 devs->dev = malloc (sizeof (struct usbdev *));
300 devs->dev[devs->ndev++] = dev;
307 PUBLIC struct altos_list *
308 altos_ftdi_list_start(void)
310 return altos_list_start();
314 altos_list_next(struct altos_list *list, struct altos_device *device)
316 struct altos_usbdev *dev;
317 if (list->current >= list->ndev) {
320 dev = list->dev[list->current];
321 strcpy(device->name, dev->product_name);
322 device->vendor = dev->idVendor;
323 device->product = dev->idProduct;
324 strcpy(device->path, dev->tty);
325 device->serial = dev->serial;
331 altos_list_finish(struct altos_list *usbdevs)
337 for (i = 0; i < usbdevs->ndev; i++)
338 usbdev_free(usbdevs->dev[i]);
345 static int bt_initialized;
347 static int init_bt(void) {
348 if (!bt_initialized) {
350 libbt = dlopen("libbluetooth.so.3", RTLD_LAZY);
352 printf("failed to find bluetooth library\n");
354 return libbt != NULL;
357 #define join(a,b) a ## b
358 #define bt_func(name, ret, fail, formals, actuals) \
359 static ret join(altos_, name) formals { \
360 static ret (*name) formals; \
361 if (!init_bt()) return fail; \
362 name = dlsym(libbt, #name); \
363 if (!name) return fail; \
364 return name actuals; \
367 bt_func(ba2str, int, -1, (const bdaddr_t *ba, char *str), (ba, str))
368 #define ba2str altos_ba2str
370 bt_func(str2ba, int, -1, (const char *str, bdaddr_t *ba), (str, ba))
371 #define str2ba altos_str2ba
373 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))
374 #define hci_read_remote_name altos_hci_read_remote_name
376 bt_func(hci_open_dev, int, -1, (int dev_id), (dev_id))
377 #define hci_open_dev altos_hci_open_dev
379 bt_func(hci_get_route, int, -1, (bdaddr_t *bdaddr), (bdaddr))
380 #define hci_get_route altos_hci_get_route
382 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))
383 #define hci_inquiry altos_hci_inquiry
385 bt_func(sdp_connect, sdp_session_t *, 0, (const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags), (src, dst, flags))
386 #define sdp_connect altos_sdp_connect
388 bt_func(sdp_uuid16_create, uuid_t *, 0, (uuid_t *uuid, uint16_t data), (uuid, data))
389 #define sdp_uuid16_create altos_sdp_uuid16_create
391 bt_func(sdp_list_append, sdp_list_t *, 0, (sdp_list_t *list, void *d), (list, d))
392 #define sdp_list_append altos_sdp_list_append
394 bt_func(sdp_service_search_attr_req, int, -1, (sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list), (session, search, reqtype, attrid_list, rsp_list))
395 #define sdp_service_search_attr_req altos_sdp_service_search_attr_req
397 bt_func(sdp_uuid_to_proto, int, 0, (uuid_t *uuid), (uuid))
398 #define sdp_uuid_to_proto altos_sdp_uuid_to_proto
400 bt_func(sdp_get_access_protos, int, 0, (const sdp_record_t *rec, sdp_list_t **protos), (rec, protos))
401 #define sdp_get_access_protos altos_sdp_get_access_protos
403 bt_func(sdp_get_proto_port, int, 0, (const sdp_list_t *list, int proto), (list, proto))
404 #define sdp_get_proto_port altos_sdp_get_proto_port
406 bt_func(sdp_close, int, 0, (sdp_session_t *session), (session))
407 #define sdp_close altos_sdp_close
409 struct altos_bt_list {
417 #define INQUIRY_MAX_RSP 255
419 struct altos_bt_list *
420 altos_bt_list_start(int inquiry_time)
422 struct altos_bt_list *bt_list;
424 bt_list = calloc(1, sizeof (struct altos_bt_list));
428 bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
431 bt_list->dev_id = hci_get_route(NULL);
432 if (bt_list->dev_id < 0)
435 bt_list->sock = hci_open_dev(bt_list->dev_id);
436 if (bt_list->sock < 0)
439 bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
445 if (bt_list->num_rsp < 0)
452 close(bt_list->sock);
463 altos_bt_list_next(struct altos_bt_list *bt_list,
464 struct altos_bt_device *device)
468 if (bt_list->rsp >= bt_list->num_rsp)
471 ii = &bt_list->ii[bt_list->rsp];
472 if (ba2str(&ii->bdaddr, device->addr) < 0)
474 memset(&device->name, '\0', sizeof (device->name));
475 if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
476 sizeof (device->name),
477 device->name, 0) < 0) {
478 strcpy(device->name, "[unknown]");
485 altos_bt_list_finish(struct altos_bt_list *bt_list)
487 close(bt_list->sock);
493 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
495 strncpy(device->name, name, sizeof (device->name));
496 device->name[sizeof(device->name)-1] = '\0';
497 strncpy(device->addr, addr, sizeof (device->addr));
498 device->addr[sizeof(device->addr)-1] = '\0';
502 altos_bt_open(struct altos_bt_device *device)
504 struct sockaddr_rc addr = { 0 };
506 struct altos_file_posix *file;
507 sdp_session_t *session = NULL;
510 if (str2ba(device->addr, &addr.rc_bdaddr) < 0) {
511 altos_set_last_posix_error();
517 * Search for the RFCOMM service to get the right channel
519 session = sdp_connect(BDADDR_ANY, &addr.rc_bdaddr, SDP_RETRY_IF_BUSY);
522 static const uint8_t svc_uuid_int[] = {
523 0, 0, 0, 0, 0, 0, 0, 0,
524 0, 0, 0, 0, 0, 0, 0x11, 0x01
529 sdp_list_t *search_list, *attrid_list;
530 sdp_list_t *response_list = NULL, *r;
531 sdp_uuid16_create(&svc_uuid, PUBLIC_BROWSE_GROUP);
532 search_list = sdp_list_append(NULL, &svc_uuid);
535 attrid_list = sdp_list_append(NULL, &range);
537 err = sdp_service_search_attr_req(session, search_list,
538 SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
541 for (r = response_list; r; r = r->next) {
542 sdp_record_t *rec = (sdp_record_t*) r->data;
543 sdp_list_t *proto_list;
544 sdp_list_t *access = NULL;
547 proto = sdp_uuid_to_proto(&rec->svclass);
549 if (proto == SERIAL_PORT_SVCLASS_ID) {
550 sdp_get_access_protos(rec, &access);
552 int this_chan = sdp_get_proto_port(access, RFCOMM_UUID);
560 /* Leave the session open so we don't disconnect from the device before opening
566 channel = altos_bt_port(device);
568 /* Connect to the channel */
569 file = calloc(1, sizeof (struct altos_file_posix));
572 altos_set_last_posix_error();
575 addr.rc_family = AF_BLUETOOTH;
576 addr.rc_channel = channel;
578 for (i = 0; i < 5; i++) {
579 file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
581 altos_set_last_posix_error();
585 status = connect(file->fd,
586 (struct sockaddr *)&addr,
588 if (status >= 0 || errno != EBUSY)
596 altos_set_last_posix_error();
608 file->out_fd = dup(file->fd);