📄 control.c
字号:
avctp->cr ? "response" : "command", avrcp->code, avrcp->subunit_type, avrcp->subunit_id, avrcp->opcode, operand_count); if (avctp->packet_type == AVCTP_PACKET_SINGLE && avctp->cr == AVCTP_COMMAND && avctp->pid == htons(AV_REMOTE_SVCLASS_ID) && avrcp->code == CTYPE_CONTROL && avrcp->subunit_type == SUBUNIT_PANEL && avrcp->opcode == OP_PASSTHROUGH) { handle_panel_passthrough(session, operands, operand_count); avctp->cr = AVCTP_RESPONSE; avrcp->code = CTYPE_ACCEPTED; ret = write(session->sock, buf, packet_size); } if (avctp->packet_type == AVCTP_PACKET_SINGLE && avctp->cr == AVCTP_COMMAND && avctp->pid == htons(AV_REMOTE_SVCLASS_ID) && avrcp->code == CTYPE_STATUS && (avrcp->opcode == OP_UNITINFO || avrcp->opcode == OP_SUBUNITINFO)) { avctp->cr = AVCTP_RESPONSE; avrcp->code = CTYPE_STABLE; debug("reply to %s", avrcp->opcode == OP_UNITINFO ? "OP_UNITINFO" : "OP_SUBUNITINFO"); ret = write(session->sock, buf, packet_size); } return TRUE;failed: debug("AVCTP session %p got disconnected", session); avctp_unref(session); return FALSE;}static int uinput_create(char *name){ struct uinput_dev dev; int fd, err; fd = open("/dev/uinput", O_RDWR); if (fd < 0) { fd = open("/dev/input/uinput", O_RDWR); if (fd < 0) { fd = open("/dev/misc/uinput", O_RDWR); if (fd < 0) { err = errno; error("Can't open input device: %s (%d)", strerror(err), err); return -err; } } } memset(&dev, 0, sizeof(dev)); if (name) strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE); dev.id.bustype = BUS_BLUETOOTH; dev.id.vendor = 0x0000; dev.id.product = 0x0000; dev.id.version = 0x0000; if (write(fd, &dev, sizeof(dev)) < 0) { err = errno; error("Can't write device information: %s (%d)", strerror(err), err); close(fd); errno = err; return -err; } ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_REL); ioctl(fd, UI_SET_EVBIT, EV_REP); ioctl(fd, UI_SET_EVBIT, EV_SYN); ioctl(fd, UI_SET_KEYBIT, KEY_PLAYPAUSE); ioctl(fd, UI_SET_KEYBIT, KEY_STOP); ioctl(fd, UI_SET_KEYBIT, KEY_NEXTSONG); ioctl(fd, UI_SET_KEYBIT, KEY_PREVIOUSSONG); ioctl(fd, UI_SET_KEYBIT, KEY_REWIND); ioctl(fd, UI_SET_KEYBIT, KEY_FORWARD); if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { err = errno; error("Can't create uinput device: %s (%d)", strerror(err), err); close(fd); errno = err; return -err; } return fd;}static void init_uinput(struct avctp *session){ char address[18]; ba2str(&session->dst, address); session->uinput = uinput_create(address); if (session->uinput < 0) error("AVRCP: failed to init uinput for %s", address); else debug("AVRCP: uinput initialized for %s", address);}static void avctp_connect_session(struct avctp *session){ GIOChannel *io; session->state = AVCTP_STATE_CONNECTED; session->dev = manager_find_device(&session->dst, NULL, FALSE); if (!session->dev) return; session->dev->control->session = session; init_uinput(session); g_dbus_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); if (session->io) g_source_remove(session->io); io = g_io_channel_unix_new(session->sock); session->io = g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); g_io_channel_unref(io);}static void auth_cb(DBusError *derr, void *user_data){ struct avctp *session = user_data; if (derr && dbus_error_is_set(derr)) { error("Access denied: %s", derr->message); if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); service_cancel_auth(&session->src, &session->dst); } avctp_unref(session); return; } avctp_connect_session(session);}static void avctp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer data){ socklen_t size; struct l2cap_options l2o; struct avctp *session; GIOCondition flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; char address[18]; if (err < 0) { error("AVCTP server socket: %s (%d)", strerror(-err), -err); return; } session = avctp_get(src, dst); if (!session) { error("Unable to create new AVCTP session"); goto drop; } if (session->sock >= 0) { error("Refusing unexpected connect from %s", address); goto drop; } session->state = AVCTP_STATE_CONNECTING; session->sock = g_io_channel_unix_get_fd(chan); memset(&l2o, 0, sizeof(l2o)); size = sizeof(l2o); if (getsockopt(session->sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &size) < 0) { err = errno; error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(err), err); avctp_unref(session); goto drop; } session->mtu = l2o.imtu; session->io = g_io_add_watch(chan, flags, (GIOFunc) session_cb, session); g_io_channel_unref(chan); if (avdtp_is_connected(src, dst)) goto proceed; if (service_req_auth(src, dst, AVRCP_TARGET_UUID, auth_cb, session) < 0) goto drop; return;proceed: avctp_connect_session(session); return;drop: close(session->sock);}static GIOChannel *avctp_server_socket(gboolean master){ int lm; GIOChannel *io; lm = L2CAP_LM_SECURE; if (master) lm |= L2CAP_LM_MASTER; io = bt_l2cap_listen(BDADDR_ANY, AVCTP_PSM, 0, lm, avctp_server_cb, NULL); if (!io) { error("Unable to allocate new io channel"); return NULL; } return io;}static void avctp_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer data){ struct avctp *session = data; struct l2cap_options l2o; socklen_t len; int sk; char address[18]; if (err < 0) { avctp_unref(session); error("AVCTP connect(%s): %s (%d)", address, strerror(-err), -err); return; } ba2str(&session->dst, address); debug("AVCTP: connected to %s", address); g_io_channel_set_close_on_unref(chan, FALSE); sk = g_io_channel_unix_get_fd(chan); session->sock = sk; memset(&l2o, 0, sizeof(l2o)); len = sizeof(l2o); if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { err = errno; avctp_unref(session); error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(err), err); return; } init_uinput(session); g_dbus_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); session->state = AVCTP_STATE_CONNECTED; session->mtu = l2o.imtu; session->io = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session);}gboolean avrcp_connect(struct audio_device *dev){ struct control *control = dev->control; struct avctp *session; int err; if (control->session) return TRUE; session = avctp_get(&dev->src, &dev->dst); if (!session) { error("Unable to create new AVCTP session"); return FALSE; } session->dev = dev; session->state = AVCTP_STATE_CONNECTING; err = bt_l2cap_connect(&dev->src, &dev->dst, AVCTP_PSM, 0, avctp_connect_cb, session); if (err < 0) { avctp_unref(session); error("Connect failed. %s(%d)", strerror(-err), -err); return FALSE; } control->session = session; return TRUE;}void avrcp_disconnect(struct audio_device *dev){ struct control *control = dev->control; struct avctp *session = control->session; if (!session) return; avctp_unref(session); control->session = NULL;}int avrcp_init(DBusConnection *conn, GKeyFile *config){ sdp_record_t *record; gboolean tmp, master = TRUE; GError *err = NULL; if (avctp_server) return 0; if (config) { tmp = g_key_file_get_boolean(config, "General", "Master", &err); if (err) { debug("audio.conf: %s", err->message); g_error_free(err); } else master = tmp; } connection = dbus_connection_ref(conn); record = avrcp_tg_record(); if (!record) { error("Unable to allocate new service record"); return -1; } if (add_record_to_server(BDADDR_ANY, record) < 0) { error("Unable to register AVRCP target service record"); sdp_record_free(record); return -1; } tg_record_id = record->handle; record = avrcp_ct_record(); if (!record) { error("Unable to allocate new service record"); return -1; } if (add_record_to_server(BDADDR_ANY, record) < 0) { error("Unable to register AVRCP controller service record"); sdp_record_free(record); return -1; } ct_record_id = record->handle; avctp_server = avctp_server_socket(master); if (!avctp_server) return -1; return 0;}void avrcp_exit(void){ if (!avctp_server) return; g_io_channel_close(avctp_server); g_io_channel_unref(avctp_server); avctp_server = NULL; remove_record_from_server(ct_record_id); ct_record_id = 0; remove_record_from_server(tg_record_id); tg_record_id = 0; dbus_connection_unref(connection); connection = NULL;}static DBusMessage *control_is_connected(DBusConnection *conn, DBusMessage *msg, void *data){ struct audio_device *device = data; struct control *control = device->control; DBusMessage *reply; dbus_bool_t connected; reply = dbus_message_new_method_return(msg); if (!reply) return NULL; connected = (control->session != NULL); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); return reply;}static GDBusMethodTable control_methods[] = { { "IsConnected", "", "b", control_is_connected }, { NULL, NULL, NULL, NULL }};static GDBusSignalTable control_signals[] = { { "Connected", "" }, { "Disconnected", "" }, { NULL, NULL }};static void control_free(struct audio_device *dev){ struct control *control = dev->control; if (control->session) avctp_unref(control->session); g_free(control); dev->control = NULL;}static void path_unregister(void *data){ struct audio_device *dev = data; info("Unregistered interface %s on path %s", AUDIO_CONTROL_INTERFACE, dev->path); control_free(dev);}void control_unregister(struct audio_device *dev){ g_dbus_unregister_interface(dev->conn, dev->path, AUDIO_CONTROL_INTERFACE);}struct control *control_init(struct audio_device *dev){ if (!g_dbus_register_interface(dev->conn, dev->path, AUDIO_CONTROL_INTERFACE, control_methods, control_signals, NULL, dev, path_unregister)) return NULL; info("Registered interface %s on path %s", AUDIO_CONTROL_INTERFACE, dev->path); return g_new0(struct control, 1);}gboolean control_is_active(struct audio_device *dev){ struct control *control = dev->control; if (control->session && control->session->state != AVCTP_STATE_DISCONNECTED) return TRUE; return FALSE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -