📄 control.c
字号:
switch (operands[0] & 0x7F) { case PLAY_OP: debug("AVRCP: PLAY %s", status); send_key(session->uinput, KEY_PLAYPAUSE, pressed); break; case STOP_OP: debug("AVRCP: STOP %s", status); send_key(session->uinput, KEY_STOP, pressed); break; case PAUSE_OP: debug("AVRCP: PAUSE %s", status); send_key(session->uinput, KEY_PLAYPAUSE, pressed); break; case NEXT_OP: debug("AVRCP: NEXT %s", status); send_key(session->uinput, KEY_NEXTSONG, pressed); break; case PREV_OP: debug("AVRCP: PREV %s", status); send_key(session->uinput, KEY_PREVIOUSSONG, pressed); break; case REWIND_OP: debug("AVRCP: REWIND %s", status); send_key(session->uinput, KEY_REWIND, pressed); break; case FAST_FORWARD_OP: debug("AVRCP: FAST FORWARD %s", status); send_key(session->uinput, KEY_FORWARD, pressed); break; default: debug("AVRCP: unknown button 0x%02X %s", operands[0] & 0x7F, status); break; }}static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data){ struct avctp *session = data; unsigned char buf[1024], *operands; struct avctp_header *avctp; struct avrcp_header *avrcp; int ret, packet_size, operand_count; if (!(cond | G_IO_IN)) goto failed; ret = read(session->sock, buf, sizeof(buf)); if (ret <= 0) goto failed; debug("Got %d bytes of data for AVCTP session %p", ret, session); if (ret < sizeof(struct avctp_header)) { error("Too small AVCTP packet"); goto failed; } packet_size = ret; avctp = (struct avctp_header *) buf; debug("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, " "PID 0x%04X", avctp->transaction, avctp->packet_type, avctp->cr, avctp->ipid, ntohs(avctp->pid)); ret -= sizeof(struct avctp_header); if (ret < sizeof(struct avrcp_header)) { error("Too small AVRCP packet"); goto failed; } avrcp = (struct avrcp_header *) (buf + sizeof(struct avctp_header)); ret -= sizeof(struct avrcp_header); operands = buf + sizeof(struct avctp_header) + sizeof(struct avrcp_header); operand_count = ret; debug("AVRCP %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, " "opcode 0x%02X, %d operands", 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); } return TRUE;failed: debug("AVCTP session %p got disconnected", session); avctp_unref(session); return FALSE;}static void auth_cb(DBusPendingCall *call, void *data){ GIOChannel *io; struct avctp *session = data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; dbus_pending_call_unref(session->pending_auth); session->pending_auth = NULL; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) { error("Access denied: %s", err.message); if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); manager_cancel_authorize(&session->dst, AVRCP_TARGET_UUID, NULL); } avctp_unref(session); dbus_message_unref(reply); return; } session->state = AVCTP_STATE_CONNECTED; session->dev = manager_device_connected(&session->dst, AVRCP_TARGET_UUID); session->dev->control->session = session; init_uinput(session); dbus_connection_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); 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 gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data){ int srv_sk, cli_sk; socklen_t size; struct sockaddr_l2 addr; struct l2cap_options l2o; bdaddr_t src, dst; struct avctp *session; GIOChannel *io; GIOCondition flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; char address[18]; if (cond & G_IO_NVAL) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR)) { error("Hangup or error on AVCTP server socket"); g_io_channel_close(chan); raise(SIGTERM); return FALSE; } srv_sk = g_io_channel_unix_get_fd(chan); size = sizeof(struct sockaddr_l2); cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); if (cli_sk < 0) { error("AVCTP accept: %s (%d)", strerror(errno), errno); return TRUE; } bacpy(&dst, &addr.l2_bdaddr); ba2str(&dst, address); debug("AVCTP: incoming connect from %s", address); size = sizeof(struct sockaddr_l2); if (getsockname(cli_sk, (struct sockaddr *) &addr, &size) < 0) { error("getsockname: %s (%d)", strerror(errno), errno); close(cli_sk); return TRUE; } bacpy(&src, &addr.l2_bdaddr); memset(&l2o, 0, sizeof(l2o)); size = sizeof(l2o); if (getsockopt(cli_sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &size) < 0) { error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(errno), errno); close(cli_sk); return TRUE; } session = avctp_get(&src, &dst); if (!session) { error("Unable to create new AVCTP session"); close(cli_sk); return TRUE; } if (session->sock >= 0) { error("Refusing unexpected connect from %s", address); close(cli_sk); return TRUE; } session->state = AVCTP_STATE_CONNECTING; if (avdtp_is_connected(&src, &dst)) goto proceed; if (!manager_authorize(&dst, AVRCP_TARGET_UUID, auth_cb, session, &session->pending_auth)) { close(cli_sk); avctp_unref(session); return TRUE; }proceed: session->mtu = l2o.imtu; session->sock = cli_sk; io = g_io_channel_unix_new(session->sock); if (!session->pending_auth) { session->state = AVCTP_STATE_CONNECTED; session->dev = manager_device_connected(&dst, AVRCP_TARGET_UUID); session->dev->control->session = session; init_uinput(session); flags |= G_IO_IN; dbus_connection_emit_signal(session->dev->conn, session->dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); } session->io = g_io_add_watch(io, flags, (GIOFunc) session_cb, session); g_io_channel_unref(io); return TRUE;}static gboolean avctp_connect_cb(GIOChannel *chan, GIOCondition cond, gpointer data){ struct avctp *session = data; struct l2cap_options l2o; socklen_t len; int ret, err, sk; char address[18]; if (cond & G_IO_NVAL) return FALSE; sk = g_io_channel_unix_get_fd(chan); ba2str(&session->dst, address); len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { err = errno; error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err); goto failed; } if (ret != 0) { err = ret; error("AVCTP connect(%s): %s (%d)", address, strerror(err), err); goto failed; } if (cond & G_IO_HUP) { err = EIO; goto failed; } debug("AVCTP: connected to %s", address); memset(&l2o, 0, sizeof(l2o)); len = sizeof(l2o); if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { err = errno; error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(err), err); goto failed; } init_uinput(session); dbus_connection_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); return FALSE;failed: close(sk); avctp_unref(session); return FALSE;}gboolean avrcp_connect(struct device *dev){ struct control *control = dev->control; struct avctp *session; struct sockaddr_l2 l2a; GIOChannel *io; int sk; 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; memset(&l2a, 0, sizeof(l2a)); l2a.l2_family = AF_BLUETOOTH; bacpy(&l2a.l2_bdaddr, &dev->src); sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sk < 0) { error("Cannot create L2CAP socket. %s(%d)", strerror(errno), errno); goto failed; } if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { error("Bind failed. %s (%d)", strerror(errno), errno); goto failed; } memset(&l2a, 0, sizeof(l2a)); l2a.l2_family = AF_BLUETOOTH; bacpy(&l2a.l2_bdaddr, &dev->dst); l2a.l2_psm = htobs(AVCTP_PSM); if (set_nonblocking(sk) < 0) { error("Set non blocking: %s (%d)", strerror(errno), errno); goto failed; } io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, FALSE); session->sock = sk; if (connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { if (!(errno == EAGAIN || errno == EINPROGRESS)) { error("Connect failed. %s(%d)", strerror(errno), errno); g_io_channel_close(io); g_io_channel_unref(io); goto failed; } session->state = AVCTP_STATE_CONNECTING; g_io_add_watch(io, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) avctp_connect_cb, session); } else avctp_connect_cb(io, G_IO_OUT, session); g_io_channel_unref(io); control->session = session; return TRUE;failed: avctp_unref(session); return FALSE;}void avrcp_disconnect(struct 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){ sdp_buf_t buf; if (avctp_server) return 0; connection = dbus_connection_ref(conn); if (avrcp_tg_record(&buf) < 0) { error("Unable to allocate new service record"); return -1; } tg_record_id = add_service_record(conn, &buf); free(buf.data); if (!tg_record_id) { error("Unable to register AVRCP target service record"); return -1; } if (avrcp_ct_record(&buf) < 0) { error("Unable to allocate new service record"); return -1; } ct_record_id = add_service_record(conn, &buf); free(buf.data); if (!ct_record_id) { error("Unable to register AVRCP controller service record"); return -1; } avctp_server = avctp_server_socket(); if (!avctp_server) return -1; g_io_add_watch(avctp_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) avctp_server_cb, NULL); 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_service_record(connection, ct_record_id); ct_record_id = 0; remove_service_record(connection, ct_record_id); ct_record_id = 0; dbus_connection_unref(connection); connection = NULL;}static DBusHandlerResult control_is_connected(DBusConnection *conn, DBusMessage *msg, void *data){ struct device *device = data; struct control *control = device->control; DBusMessage *reply; dbus_bool_t connected; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; connected = (control->session != NULL); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED;}static DBusMethodVTable control_methods[] = { { "IsConnected", control_is_connected, "", "b" }, { NULL, NULL, NULL, NULL }};static DBusSignalVTable control_signals[] = { { "Connected", "" }, { "Disconnected", "" }, { NULL, NULL }};struct control *control_init(struct device *dev){ if (!dbus_connection_register_interface(dev->conn, dev->path, AUDIO_CONTROL_INTERFACE, control_methods, control_signals, NULL)) return NULL; return g_new0(struct control, 1);}void control_free(struct device *dev){ struct control *control = dev->control; if (control->session) avctp_unref(control->session); g_free(control); dev->control = NULL;}gboolean control_is_active(struct 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 + -