📄 adapter.c
字号:
/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> * * * 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#define _GNU_SOURCE#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <time.h>#include <sys/param.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include <bluetooth/l2cap.h>#include <bluetooth/sdp.h>#include <bluetooth/sdp_lib.h>#include <glib.h>#include <dbus/dbus.h>#include <gdbus.h>#include "hcid.h"#include "sdpd.h"#include "adapter.h"#include "device.h"#include "textfile.h"#include "oui.h"#include "dbus-common.h"#include "dbus-hci.h"#include "dbus-database.h"#include "error.h"#include "glib-helper.h"#include "logging.h"#include "agent.h"#define NUM_ELEMENTS(table) (sizeof(table)/sizeof(const char *))#define IO_CAPABILITY_DISPLAYONLY 0x00#define IO_CAPABILITY_DISPLAYYESNO 0x01#define IO_CAPABILITY_KEYBOARDONLY 0x02#define IO_CAPABILITY_NOINPUTOUTPUT 0x03#define IO_CAPABILITY_INVALID 0xFFstatic DBusConnection *connection = NULL;struct record_list { sdp_list_t *recs; const gchar *addr;};struct mode_req { struct adapter *adapter; DBusConnection *conn; /* Connection reference */ DBusMessage *msg; /* Message reference */ uint8_t mode; /* Requested mode */ guint id; /* Listener id */};static inline DBusMessage *invalid_args(DBusMessage *msg){ return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call");}static inline DBusMessage *not_available(DBusMessage *msg){ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", "Not Available");}static inline DBusMessage *adapter_not_ready(DBusMessage *msg){ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady", "Adapter is not ready");}static inline DBusMessage *no_such_adapter(DBusMessage *msg){ return g_dbus_create_error(msg, ERROR_INTERFACE ".NoSuchAdapter", "No such adapter");}static inline DBusMessage *failed_strerror(DBusMessage *msg, int err){ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", strerror(err));}static inline DBusMessage *in_progress(DBusMessage *msg, const char *str){ return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", str);}static inline DBusMessage *not_in_progress(DBusMessage *msg, const char *str){ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotInProgress", str);}static inline DBusMessage *not_authorized(DBusMessage *msg){ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAuthorized", "Not authorized");}static inline DBusMessage *unsupported_major_class(DBusMessage *msg){ return g_dbus_create_error(msg, ERROR_INTERFACE ".UnsupportedMajorClass", "Unsupported Major Class");}static DBusHandlerResult error_failed(DBusConnection *conn, DBusMessage *msg, const char * desc){ return error_common_reply(conn, msg, ERROR_INTERFACE ".Failed", desc);}static DBusHandlerResult error_failed_errno(DBusConnection *conn, DBusMessage *msg, int err){ const char *desc = strerror(err); return error_failed(conn, msg, desc);}static DBusHandlerResult error_connection_attempt_failed(DBusConnection *conn, DBusMessage *msg, int err){ return error_common_reply(conn, msg, ERROR_INTERFACE ".ConnectionAttemptFailed", err > 0 ? strerror(err) : "Connection attempt failed");}static int auth_req_cmp(const void *p1, const void *p2){ const struct pending_auth_info *pb1 = p1; const bdaddr_t *bda = p2; return bda ? bacmp(&pb1->bdaddr, bda) : -1;}void adapter_auth_request_replied(struct adapter *adapter, bdaddr_t *dba){ GSList *l; struct pending_auth_info *auth; l = g_slist_find_custom(adapter->auth_reqs, dba, auth_req_cmp); if (!l) return; auth = l->data; auth->replied = TRUE;}struct pending_auth_info *adapter_find_auth_request(struct adapter *adapter, bdaddr_t *dba){ GSList *l; l = g_slist_find_custom(adapter->auth_reqs, dba, auth_req_cmp); if (l) return l->data; return NULL;}void adapter_remove_auth_request(struct adapter *adapter, bdaddr_t *dba){ GSList *l; struct pending_auth_info *auth; l = g_slist_find_custom(adapter->auth_reqs, dba, auth_req_cmp); if (!l) return; auth = l->data; adapter->auth_reqs = g_slist_remove(adapter->auth_reqs, auth); g_free(auth);}struct pending_auth_info *adapter_new_auth_request(struct adapter *adapter, bdaddr_t *dba, auth_type_t type){ struct pending_auth_info *info; debug("hcid_dbus_new_auth_request"); info = g_new0(struct pending_auth_info, 1); bacpy(&info->bdaddr, dba); info->type = type; adapter->auth_reqs = g_slist_append(adapter->auth_reqs, info); if (adapter->bonding && !bacmp(dba, &adapter->bonding->bdaddr)) adapter->bonding->auth_active = 1; return info;}int pending_remote_name_cancel(struct adapter *adapter){ struct remote_dev_info *dev, match; GSList *l; int dd, err = 0; /* find the pending remote name request */ memset(&match, 0, sizeof(struct remote_dev_info)); bacpy(&match.bdaddr, BDADDR_ANY); match.name_status = NAME_REQUESTED; l = g_slist_find_custom(adapter->found_devices, &match, (GCompareFunc) found_device_cmp); if (!l) /* no pending request */ return 0; dd = hci_open_dev(adapter->dev_id); if (dd < 0) return -ENODEV; dev = l->data; if (hci_read_remote_name_cancel(dd, &dev->bdaddr, 1000) < 0) { error("Remote name cancel failed: %s(%d)", strerror(errno), errno); err = -errno; } /* free discovered devices list */ g_slist_foreach(adapter->found_devices, (GFunc) g_free, NULL); g_slist_free(adapter->found_devices); adapter->found_devices = NULL; hci_close_dev(dd); return err;}static int auth_info_agent_cmp(const void *a, const void *b){ const struct pending_auth_info *auth = a; const struct agent *agent = b; if (auth->agent == agent) return 0; return -1;}static void device_agent_removed(struct agent *agent, void *user_data){ struct btd_device *device = user_data; struct pending_auth_info *auth; GSList *l; struct adapter *adapter; adapter = device_get_adapter(device); device_set_agent(device, NULL); l = g_slist_find_custom(adapter->auth_reqs, agent, auth_info_agent_cmp); if (!l) return; auth = l->data; auth->agent = NULL;}static struct bonding_request_info *bonding_request_new(DBusConnection *conn, DBusMessage *msg, struct adapter *adapter, const char *address, const char *agent_path, uint8_t capability){ struct bonding_request_info *bonding; struct btd_device *device; const char *name = dbus_message_get_sender(msg); const gchar *destination; struct agent *agent; debug("bonding_request_new(%s)", address); device = adapter_get_device(conn, adapter, address); if (!device) return NULL; destination = device_get_address(device); agent = agent_create(adapter, name, agent_path, capability, device_agent_removed, device); device_set_agent(device, agent); debug("Temporary agent registered for hci%d/%s at %s:%s", adapter->dev_id, destination, name, agent_path); bonding = g_new0(struct bonding_request_info, 1); bonding->conn = dbus_connection_ref(conn); bonding->msg = dbus_message_ref(msg); bonding->adapter = adapter; str2ba(address, &bonding->bdaddr); return bonding;}const char *mode2str(uint8_t mode){ switch(mode) { case MODE_OFF: return "off"; case MODE_CONNECTABLE: return "connectable"; case MODE_DISCOVERABLE: return "discoverable"; case MODE_LIMITED: return "limited"; default: return "unknown"; }}static uint8_t on_mode(const char *addr){ char mode[14]; bdaddr_t sba; str2ba(addr, &sba); if (read_on_mode(&sba, mode, sizeof(mode)) < 0) return MODE_CONNECTABLE; return str2mode(addr, mode);}uint8_t str2mode(const char *addr, const char *mode){ if (strcasecmp("off", mode) == 0) return MODE_OFF; else if (strcasecmp("connectable", mode) == 0) return MODE_CONNECTABLE; else if (strcasecmp("discoverable", mode) == 0) return MODE_DISCOVERABLE; else if (strcasecmp("limited", mode) == 0) return MODE_LIMITED; else if (strcasecmp("on", mode) == 0) return on_mode(addr); else return MODE_UNKNOWN;}static DBusMessage *set_mode(DBusConnection *conn, DBusMessage *msg, uint8_t new_mode, void *data){ struct adapter *adapter = data; uint8_t scan_enable; uint8_t current_scan = adapter->scan_mode; bdaddr_t local; gboolean limited; int err, dd; const char *mode; switch(new_mode) { case MODE_OFF: scan_enable = SCAN_DISABLED; break; case MODE_CONNECTABLE: scan_enable = SCAN_PAGE; break; case MODE_DISCOVERABLE: case MODE_LIMITED: scan_enable = (SCAN_PAGE | SCAN_INQUIRY); break; default: return invalid_args(msg); } /* Do reverse resolution in case of "on" mode */ mode = mode2str(new_mode); dd = hci_open_dev(adapter->dev_id); if (dd < 0) return no_such_adapter(msg); if (!adapter->up && (hcid.offmode == HCID_OFFMODE_NOSCAN || (hcid.offmode == HCID_OFFMODE_DEVDOWN && scan_enable != SCAN_DISABLED))) { /* Start HCI device */ if (ioctl(dd, HCIDEVUP, adapter->dev_id) == 0) goto done; /* on success */ if (errno != EALREADY) { err = errno; error("Can't init device hci%d: %s (%d)\n", adapter->dev_id, strerror(errno), errno); hci_close_dev(dd); return failed_strerror(msg, err); } } if (adapter->up && scan_enable == SCAN_DISABLED && hcid.offmode == HCID_OFFMODE_DEVDOWN) { if (ioctl(dd, HCIDEVDOWN, adapter->dev_id) < 0) { hci_close_dev(dd); return failed_strerror(msg, errno); } goto done; } limited = (new_mode == MODE_LIMITED ? TRUE : FALSE); err = set_limited_discoverable(dd, adapter->dev.class, limited); if (err < 0) { hci_close_dev(dd); return failed_strerror(msg, -err); } if (current_scan != scan_enable) { struct hci_request rq; uint8_t status = 0; memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; rq.ocf = OCF_WRITE_SCAN_ENABLE; rq.cparam = &scan_enable; rq.clen = sizeof(scan_enable); rq.rparam = &status; rq.rlen = sizeof(status); rq.event = EVT_CMD_COMPLETE; if (hci_send_req(dd, &rq, 1000) < 0) { err = errno; error("Sending write scan enable command failed: %s (%d)", strerror(errno), errno); hci_close_dev(dd); return failed_strerror(msg, err); } if (status) { error("Setting scan enable failed with status 0x%02x", status); hci_close_dev(dd); return failed_strerror(msg, bt_error(status)); } } else { /* discoverable or limited */ if ((scan_enable & SCAN_INQUIRY) && (new_mode != adapter->mode)) { if (adapter->discov_timeout_id) g_source_remove(adapter->discov_timeout_id); if (!adapter->sessions && !adapter->discov_timeout) adapter->discov_timeout_id = g_timeout_add(adapter->discov_timeout * 1000, discov_timeout_handler, adapter); } }done: str2ba(adapter->address, &local); write_device_mode(&local, mode); hci_close_dev(dd); adapter->mode = new_mode; return dbus_message_new_method_return(msg);}gint find_session(struct mode_req *req, DBusMessage *msg){ const char *name = dbus_message_get_sender(req->msg); const char *sender = dbus_message_get_sender(msg); return strcmp(name, sender);}static void confirm_mode_cb(struct agent *agent, DBusError *err, void *data){ struct mode_req *req = data; DBusMessage *reply; if (err && dbus_error_is_set(err)) { reply = dbus_message_new_error(req->msg, err->name, err->message); dbus_connection_send(req->conn, reply, NULL); dbus_message_unref(reply); goto cleanup; } reply = set_mode(req->conn, req->msg, req->mode, req->adapter); dbus_connection_send(req->conn, reply, NULL); dbus_message_unref(reply); if (!g_slist_find_custom(req->adapter->sessions, req->msg, (GCompareFunc) find_session)) goto cleanup; return;cleanup: dbus_message_unref(req->msg); if (req->id) g_dbus_remove_watch(req->conn, req->id); dbus_connection_unref(req->conn); g_free(req);}static DBusMessage *confirm_mode(DBusConnection *conn, DBusMessage *msg,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -