| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2014 Google Inc. |
| * |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <stdbool.h> |
| #include <glib.h> |
| |
| #include "src/plugin.h" |
| #include "src/adapter.h" |
| #include "src/device.h" |
| #include "src/log.h" |
| |
| #define BD_ADDR_FIFO "/tmp/bd-addr-fifo" |
| |
| struct gfrm_device { |
| char *name; |
| uint8_t bdaddr_type; |
| uint16_t vid; |
| uint16_t pid; |
| }; |
| |
| static struct gfrm_device gfrm_devs[] = { |
| { "GFRM100", BDADDR_BREDR, 0x58, 0x2000 }, |
| { "GFRM200", BDADDR_LE_PUBLIC, 0x471, 0x2210 }, |
| { "HID AdvRemote", BDADDR_LE_PUBLIC, 0xD, 0x0 }, |
| }; |
| |
| static struct gfrm_device *gfrm_find_dev_by_bdaddr_type(uint8_t bdaddr_type) |
| { |
| uint16_t i; |
| |
| for (i = 0; i < G_N_ELEMENTS(gfrm_devs); ++i) { |
| if (bdaddr_type == gfrm_devs[i].bdaddr_type) { |
| return &gfrm_devs[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static struct gfrm_device *gfrm_find_dev_by_vid_pid(uint16_t vid, uint16_t pid) |
| { |
| uint16_t i; |
| |
| for (i = 0; i < G_N_ELEMENTS(gfrm_devs); ++i) { |
| if (vid == gfrm_devs[i].vid && pid == gfrm_devs[i].pid) { |
| return &gfrm_devs[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static ssize_t gfrm_pincb(struct btd_adapter *adapter, struct btd_device *device, |
| char *pinbuf, bool *display, unsigned int attempt) |
| { |
| uint16_t vid, pid; |
| char addr[18], name[25]; |
| |
| /* |
| * Only try the pin code once per device. |
| * If it's not correct then it's an unknown device. |
| */ |
| if (attempt > 1) |
| return 0; |
| |
| ba2str(device_get_address(device), addr); |
| |
| vid = btd_device_get_vendor(device); |
| pid = btd_device_get_product(device); |
| DBG("vendor 0x%x product 0x%x", vid, pid); |
| |
| device_get_name(device, name, sizeof(name)); |
| name[sizeof(name) - 1] = 0; |
| |
| if (gfrm_find_dev_by_vid_pid(vid, pid)) { |
| DBG("Forcing PIN 0000 on %s at %s", name, addr); |
| memcpy(pinbuf, "0000", 4); |
| return 4; |
| } |
| |
| return 0; |
| } |
| |
| static void gfrm_start_bd_addr_fifo_watch( |
| gboolean (*callback)(GIOChannel *, GIOCondition, gpointer)) |
| { |
| int fd; |
| GIOChannel *io; |
| |
| if ((fd = open(BD_ADDR_FIFO, O_RDONLY | O_NONBLOCK)) < 0) { |
| DBG("open failed"); |
| return; |
| } |
| |
| if ((io = g_io_channel_unix_new(fd)) == NULL) { |
| DBG("g_io_channel_unix_new failed"); |
| close(fd); |
| return; |
| } |
| |
| if (!g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, |
| callback, NULL)) { |
| DBG("g_io_add_watch failed"); |
| } |
| |
| g_io_channel_set_close_on_unref(io, TRUE); |
| g_io_channel_unref(io); |
| } |
| |
| static gboolean gfrm_add_device(GIOChannel *io, GIOCondition condition, |
| gpointer data) |
| { |
| int fd; |
| uint8_t bdaddr_and_type[7] = { 0 }; |
| uint8_t bdaddr_type; |
| bdaddr_t bdaddr; |
| char bdaddr_str[18]; |
| struct gfrm_device *gfrm_dev; |
| struct btd_adapter *adapter; |
| struct btd_device *device; |
| |
| if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { |
| DBG("io error: condition %u", condition); |
| } |
| |
| if (!(condition & (G_IO_IN))) { |
| DBG("pipe has no data to read"); |
| goto reopen; |
| } |
| |
| /* Read BD address from FIFO */ |
| fd = g_io_channel_unix_get_fd(io); |
| |
| if (read(fd, &bdaddr_and_type, sizeof(bdaddr_and_type)) != 7) { |
| DBG("read failed"); |
| goto reopen; |
| } |
| |
| bdaddr_type = bdaddr_and_type[6]; |
| baswap(&bdaddr, (bdaddr_t*) bdaddr_and_type); |
| ba2str(&bdaddr, bdaddr_str); |
| |
| gfrm_dev = gfrm_find_dev_by_bdaddr_type(bdaddr_type); |
| if (!gfrm_dev) |
| goto reopen; |
| |
| DBG("Discovered %s at %s", gfrm_dev->name, bdaddr_str); |
| |
| /* Add device to BlueZ stack */ |
| adapter = btd_adapter_get_default(); |
| if (!adapter) { |
| DBG("btd_adapter_get_default failed"); |
| goto reopen; |
| } |
| |
| device = btd_adapter_find_device(adapter, &bdaddr, bdaddr_type); |
| if (device) { |
| btd_device_set_temporary(device, TRUE); |
| btd_adapter_remove_device(adapter, device); |
| } |
| |
| device = btd_adapter_get_device(adapter, &bdaddr, bdaddr_type); |
| btd_device_device_set_name(device, gfrm_dev->name); |
| btd_device_set_pnpid(device, 0x1, gfrm_dev->vid, gfrm_dev->pid, 0x0); |
| btd_device_set_temporary(device, FALSE); |
| |
| /* |
| * Pairing the remote is handled in Python script: |
| * test/gfiber-agent |
| */ |
| |
| reopen: |
| /* Reopen the FIFO (new fd, io channel, and watch) */ |
| gfrm_start_bd_addr_fifo_watch(gfrm_add_device); |
| |
| /* |
| * Return FALSE, so that the watch on the old io channel is removed. |
| * That, in turn, triggers closing of io channel and file descriptor. |
| */ |
| return FALSE; |
| } |
| |
| static int gfrm_probe(struct btd_adapter *adapter) |
| { |
| btd_adapter_register_pin_cb(adapter, gfrm_pincb); |
| } |
| |
| static void gfrm_remove(struct btd_adapter *adapter) |
| { |
| btd_adapter_unregister_pin_cb(adapter, gfrm_pincb); |
| } |
| |
| static struct btd_adapter_driver gfrm_driver = { |
| .name = "gfrm", |
| .probe = gfrm_probe, |
| .remove = gfrm_remove, |
| }; |
| |
| static int gfrm_init(void) |
| { |
| btd_register_adapter_driver(&gfrm_driver); |
| |
| /* Create FIFO for IR-assisted pairing */ |
| if (mkfifo(BD_ADDR_FIFO, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | |
| S_IROTH | S_IWOTH) < 0 && errno != EEXIST) { |
| DBG("mkfifo failed"); |
| return 0; |
| } |
| |
| /* Start watching the FIFO */ |
| gfrm_start_bd_addr_fifo_watch(gfrm_add_device); |
| |
| return 0; |
| } |
| |
| static void gfrm_exit(void) |
| { |
| btd_unregister_adapter_driver(&gfrm_driver); |
| } |
| |
| BLUETOOTH_PLUGIN_DEFINE(gfrm, VERSION, |
| BLUETOOTH_PLUGIN_PRIORITY_HIGH, gfrm_init, gfrm_exit) |