📄 device.c
字号:
iconn->intr_sk = g_io_channel_unix_get_fd(chan); err = hidp_connadd(&idev->src, &idev->dst, iconn->ctrl_sk, iconn->intr_sk, iconn->timeout, idev->name); if (err < 0) goto failed; iconn->intr_watch = create_watch(iconn->intr_sk, intr_watch_cb, iconn); iconn->ctrl_watch = create_watch(iconn->ctrl_sk, ctrl_watch_cb, iconn); g_dbus_emit_signal(idev->conn, idev->path, INPUT_DEVICE_INTERFACE, "Connected", DBUS_TYPE_STRING, &iconn->uuid, DBUS_TYPE_INVALID); /* Replying to the requestor */ g_dbus_send_reply(idev->conn, iconn->pending_connect, DBUS_TYPE_INVALID); goto cleanup;failed: reply = connection_attempt_failed(iconn->pending_connect, -err); g_dbus_send_message(idev->conn, reply); iconn->intr_sk = -1; iconn->ctrl_sk = -1;cleanup: dbus_message_unref(iconn->pending_connect); iconn->pending_connect = NULL;}static void control_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer user_data){ struct input_conn *iconn = user_data; struct input_device *idev = iconn->idev; if (err < 0) { error("connect(): %s (%d)", strerror(-err), -err); goto failed; } /* Set HID control channel */ iconn->ctrl_sk = g_io_channel_unix_get_fd(chan); /* Connect to the HID interrupt channel */ err = bt_l2cap_connect(&idev->src, &idev->dst, L2CAP_PSM_HIDP_INTR, 0, interrupt_connect_cb, iconn); if (err < 0) { error("L2CAP connect failed:%s (%d)", strerror(-err), -err); goto failed; } return;failed: iconn->ctrl_sk = -1; connection_attempt_failed(iconn->pending_connect, -err); dbus_message_unref(iconn->pending_connect); iconn->pending_connect = NULL;}static int fake_disconnect(struct input_conn *iconn){ struct fake_input *fake = iconn->fake; if (!fake->io) return -ENOTCONN; g_io_channel_close(fake->io); g_io_channel_unref(fake->io); fake->io = NULL; if (fake->uinput >= 0) { ioctl(fake->uinput, UI_DEV_DESTROY); close(fake->uinput); fake->uinput = -1; } return 0;}static int is_connected(struct input_conn *iconn){ struct input_device *idev = iconn->idev; struct fake_input *fake = iconn->fake; struct hidp_conninfo ci; int ctl; /* Fake input */ if (fake) return fake->flags & FI_FLAG_CONNECTED; /* Standard HID */ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); if (ctl < 0) return 0; memset(&ci, 0, sizeof(ci)); bacpy(&ci.bdaddr, &idev->dst); if (ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) { close(ctl); return 0; } close(ctl); if (ci.state != BT_CONNECTED) return 0; else return 1;}static int connection_disconnect(struct input_conn *iconn, uint32_t flags){ struct input_device *idev = iconn->idev; struct fake_input *fake = iconn->fake; struct hidp_conndel_req req; struct hidp_conninfo ci; int ctl, err; /* Fake input disconnect */ if (fake) { err = fake->disconnect(iconn); if (err == 0) fake->flags &= ~FI_FLAG_CONNECTED; return err; } /* Standard HID disconnect */ if (iconn->ctrl_sk >= 0) { close(iconn->ctrl_sk); iconn->ctrl_sk = -1; } if (iconn->intr_sk >= 0) { close(iconn->intr_sk); iconn->intr_sk = -1; } ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); if (ctl < 0) { error("Can't open HIDP control socket"); return -errno; } memset(&ci, 0, sizeof(ci)); bacpy(&ci.bdaddr, &idev->dst); if ((ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) || (ci.state != BT_CONNECTED)) { errno = ENOTCONN; goto fail; } memset(&req, 0, sizeof(req)); bacpy(&req.bdaddr, &idev->dst); req.flags = flags; if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { error("Can't delete the HID device: %s(%d)", strerror(errno), errno); goto fail; } close(ctl); return 0;fail: err = errno; close(ctl); errno = err; return -err;}static int disconnect(struct input_device *idev, uint32_t flags){ struct input_conn *iconn = NULL; GSList *l; for (l = idev->connections; l; l = l->next) { iconn = l->data; if (is_connected(iconn)) break; } if (!iconn) return ENOTCONN; return connection_disconnect(iconn, flags);}/* * Input Device methods */static DBusMessage *device_connect(DBusConnection *conn, DBusMessage *msg, void *data){ struct input_device *idev = data; struct input_conn *iconn; struct fake_input *fake; const char *svc; int err; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc, DBUS_TYPE_INVALID) == FALSE) return NULL; iconn = find_connection(idev->connections, svc); if (!iconn) return not_supported(msg); if (iconn->pending_connect) return in_progress(msg); if (is_connected(iconn)) return already_connected(msg); iconn->pending_connect = dbus_message_ref(msg); fake = iconn->fake; /* Fake input device */ if (fake) { if (fake->connect(iconn) < 0) { int err = errno; const char *str = strerror(err); error("Connect failed: %s(%d)", str, err); dbus_message_unref(iconn->pending_connect); iconn->pending_connect = NULL; return connection_attempt_failed(msg, err); } fake->flags |= FI_FLAG_CONNECTED; return NULL; } /* HID devices */ err = bt_l2cap_connect(&idev->src, &idev->dst, L2CAP_PSM_HIDP_CTRL, 0, control_connect_cb, iconn); if (err < 0) { error("L2CAP connect failed: %s(%d)", strerror(-err), -err); dbus_message_unref(iconn->pending_connect); iconn->pending_connect = NULL; return connection_attempt_failed(msg, -err); } return NULL;}static DBusMessage *create_errno_message(DBusMessage *msg, int err){ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", strerror(err));}static DBusMessage *device_disconnect(DBusConnection *conn, DBusMessage *msg, void *data){ struct input_device *idev = data; int err; err = disconnect(idev, 0); if (err < 0) return create_errno_message(msg, -err); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);}static DBusMessage *device_is_connected(DBusConnection *conn, DBusMessage *msg, void *data){ struct input_device *idev = data; dbus_bool_t connected = FALSE; GSList *l; for (l = idev->connections; l; l = l->next) { struct input_conn *iconn = l->data; if (!is_connected(iconn)) continue; connected = TRUE; break; } return g_dbus_create_reply(msg, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID);}static void device_unregister(void *data){ struct input_device *idev = data; info("Unregistered interface %s on path %s", INPUT_DEVICE_INTERFACE, idev->path); /* Disconnect if applied */ disconnect(idev, (1 << HIDP_VIRTUAL_CABLE_UNPLUG)); devices = g_slist_remove(devices, idev); input_device_free(idev);}static GDBusMethodTable device_methods[] = { { "Connect", "s", "", device_connect, G_DBUS_METHOD_FLAG_ASYNC }, { "Disconnect", "", "", device_disconnect }, { "IsConnected", "", "b", device_is_connected }, { }};static GDBusSignalTable device_signals[] = { { "Connected", "ss" }, { "Disconnected", "s" }, { }};static struct input_device *input_device_new(DBusConnection *conn, const char *path, bdaddr_t *src, bdaddr_t *dst){ struct input_device *idev; idev = g_new0(struct input_device, 1); bacpy(&idev->src, src); bacpy(&idev->dst, dst); idev->path = g_strdup(path); read_device_name(src, dst, &idev->name); idev->conn = dbus_connection_ref(conn); if (g_dbus_register_interface(conn, idev->path, INPUT_DEVICE_INTERFACE, device_methods, device_signals, NULL, idev, device_unregister) == FALSE) { error("Failed to register interface %s on path %s", INPUT_DEVICE_INTERFACE, path); input_device_free(idev); return NULL; } info("Registered interface %s on path %s", INPUT_DEVICE_INTERFACE, idev->path); return idev;}static struct input_conn *input_conn_new(struct input_device *idev, const char *uuid, const char *alias, int timeout){ struct input_conn *iconn; iconn = g_new0(struct input_conn, 1); iconn->ctrl_sk = -1; iconn->intr_sk = -1; iconn->timeout = timeout; iconn->uuid = g_strdup(uuid); iconn->alias = g_strdup(alias); iconn->idev = idev; return iconn;}int input_device_register(DBusConnection *conn, const char *path, bdaddr_t *src, bdaddr_t *dst, const char *uuid, int timeout){ struct input_device *idev; struct input_conn *iconn; idev = find_device_by_path(devices, path); if (!idev) { idev = input_device_new(conn, path, src, dst); if (!idev) return -EINVAL; devices = g_slist_append(devices, idev); } iconn = input_conn_new(idev, uuid, "hid", timeout); if (!iconn) return -EINVAL; idev->connections = g_slist_append(idev->connections, iconn); return 0;}int fake_input_register(DBusConnection *conn, const char *path, bdaddr_t *src, bdaddr_t *dst, const char *uuid, uint8_t channel){ struct input_device *idev; struct input_conn *iconn; idev = find_device_by_path(devices, path); if (!idev) { idev = input_device_new(conn, path, src, dst); if (!idev) return -EINVAL; devices = g_slist_append(devices, idev); } iconn = input_conn_new(idev, uuid, "hsp", 0); if (!iconn) return -EINVAL; iconn->fake = g_new0(struct fake_input, 1); iconn->fake->ch = channel; iconn->fake->connect = rfcomm_connect; iconn->fake->disconnect = fake_disconnect; idev->connections = g_slist_append(idev->connections, iconn); return 0;}static struct input_device *find_device(const bdaddr_t *src, const bdaddr_t *dst){ GSList *list; for (list = devices; list != NULL; list = list->next) { struct input_device *idev = list->data; if (!bacmp(&idev->src, src) && !bacmp(&idev->dst, dst)) return idev; } return NULL;}int input_device_unregister(const char *path, const char *uuid){ struct input_device *idev; struct input_conn *iconn; idev = find_device_by_path(devices, path); if (idev == NULL) return -EINVAL; iconn = find_connection(idev->connections, uuid); if (iconn == NULL) return -EINVAL; if (iconn->pending_connect) { /* Pending connection running */ return -EBUSY; } del_stored_device_info(&idev->src, &idev->dst); idev->connections = g_slist_remove(idev->connections, iconn); input_conn_free(iconn); if (idev->connections) return 0; g_dbus_unregister_interface(idev->conn, path, INPUT_DEVICE_INTERFACE); return 0;}gboolean input_device_is_registered(bdaddr_t *src, bdaddr_t *dst){ struct input_device *idev = find_device(src, dst); return idev ? TRUE : FALSE;}int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, int nsk){ struct input_device *idev = find_device(src, dst); struct input_conn *iconn; if (!idev) return -ENOENT; iconn = find_connection(idev->connections, "hid"); if (!iconn) return -ENOENT; switch (psm) { case L2CAP_PSM_HIDP_CTRL: iconn->ctrl_sk = nsk; break; case L2CAP_PSM_HIDP_INTR: iconn->intr_sk = nsk; break; } return 0;}int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst){ struct input_device *idev = find_device(src, dst); struct input_conn *iconn; if (!idev) return -ENOENT; iconn = find_connection(idev->connections, "hid"); if (!iconn) return -ENOENT; if (iconn->ctrl_sk >= 0) { close(iconn->ctrl_sk); iconn->ctrl_sk = -1; } if (iconn->intr_sk >= 0) { close(iconn->intr_sk); iconn->intr_sk = -1; } return 0;}int input_device_connadd(bdaddr_t *src, bdaddr_t *dst){ struct input_device *idev; struct input_conn *iconn; int err; idev = find_device(src, dst); if (!idev) return -ENOENT; iconn = find_connection(idev->connections, "hid"); if (!iconn) return -ENOENT; err = hidp_connadd(src, dst, iconn->ctrl_sk, iconn->intr_sk, iconn->timeout, idev->name); if (err < 0) goto error; iconn->intr_watch = create_watch(iconn->intr_sk, intr_watch_cb, iconn); iconn->ctrl_watch = create_watch(iconn->ctrl_sk, ctrl_watch_cb, iconn); g_dbus_emit_signal(idev->conn, idev->path, INPUT_DEVICE_INTERFACE, "Connected", DBUS_TYPE_STRING, &iconn->uuid, DBUS_TYPE_INVALID); return 0;error: close(iconn->ctrl_sk); close(iconn->intr_sk); iconn->ctrl_sk = -1; iconn->intr_sk = -1; return err;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -