static gboolean control_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct control *control = data; unsigned char buf[1024], *operands; struct avctp_header *avctp; struct avrcp_header *avrcp; int ret, packet_size, operand_count, sock; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; sock = g_io_channel_unix_get_fd(control->io); ret = read(sock, buf, sizeof(buf)); if (ret <= 0) goto failed; DBG("Got %d bytes of data for AVCTP session %p", ret, control); if ((unsigned int) ret < sizeof(struct avctp_header)) { error("Too small AVCTP packet"); goto failed; } packet_size = ret; avctp = (struct avctp_header *) buf; DBG("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 ((unsigned int) 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; DBG("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_RESPONSE; avrcp->code = CTYPE_NOT_IMPLEMENTED; } else if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) { avctp->ipid = 1; avctp->cr = AVCTP_RESPONSE; avrcp->code = CTYPE_REJECTED; } else if (avctp->cr == AVCTP_COMMAND && avrcp->code == CTYPE_CONTROL && avrcp->subunit_type == SUBUNIT_PANEL && avrcp->opcode == OP_PASSTHROUGH) { handle_panel_passthrough(control, operands, operand_count); avctp->cr = AVCTP_RESPONSE; avrcp->code = CTYPE_ACCEPTED; } else if (avctp->cr == AVCTP_COMMAND && avrcp->code == CTYPE_STATUS && (avrcp->opcode == OP_UNITINFO || avrcp->opcode == OP_SUBUNITINFO)) { avctp->cr = AVCTP_RESPONSE; avrcp->code = CTYPE_STABLE; /* The first operand should be 0x07 for the UNITINFO response. * Neither AVRCP (section 22.1, page 117) nor AVC Digital * Interface Command Set (section 9.2.1, page 45) specs * explain this value but both use it */ if (operand_count >= 1 && avrcp->opcode == OP_UNITINFO) operands[0] = 0x07; if (operand_count >= 2) operands[1] = SUBUNIT_PANEL << 3; DBG("reply to %s", avrcp->opcode == OP_UNITINFO ? "OP_UNITINFO" : "OP_SUBUNITINFO"); } else { avctp->cr = AVCTP_RESPONSE; avrcp->code = CTYPE_REJECTED; } ret = write(sock, buf, packet_size); return TRUE; failed: DBG("AVCTP session %p got disconnected", control); avctp_set_state(control, AVCTP_STATE_DISCONNECTED); return FALSE; }
static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; struct avctp_channel *control = session->control; uint8_t *buf = control->buffer; uint8_t *operands, code, subunit; struct avctp_header *avctp; struct avc_header *avc; int ret, packet_size, operand_count, sock; struct avctp_pdu_handler *handler; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; sock = g_io_channel_unix_get_fd(chan); ret = read(sock, buf, control->imtu); if (ret <= 0) goto failed; if ((unsigned int) ret < sizeof(struct avctp_header)) { error("Too small AVCTP packet"); goto failed; } avctp = (struct avctp_header *) buf; ret -= sizeof(struct avctp_header); if ((unsigned int) ret < sizeof(struct avc_header)) { error("Too small AVCTP packet"); goto failed; } avc = (struct avc_header *) (buf + sizeof(struct avctp_header)); ret -= sizeof(struct avc_header); operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header); operand_count = ret; if (avctp->cr == AVCTP_RESPONSE) { control_response(control, avctp, avc, operands, operand_count); return TRUE; } packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH; avctp->cr = AVCTP_RESPONSE; if (avctp->packet_type != AVCTP_PACKET_SINGLE) { avc->code = AVC_CTYPE_NOT_IMPLEMENTED; goto done; } if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) { avctp->ipid = 1; packet_size = AVCTP_HEADER_LENGTH; goto done; } handler = find_handler(control->handlers, avc->opcode); if (!handler) { DBG("handler not found for 0x%02x", avc->opcode); packet_size += avrcp_handle_vendor_reject(&code, operands); avc->code = code; goto done; } code = avc->code; subunit = avc->subunit_type; packet_size += handler->cb(session, avctp->transaction, &code, &subunit, operands, operand_count, handler->user_data); avc->code = code; avc->subunit_type = subunit; done: ret = write(sock, buf, packet_size); if (ret != packet_size) goto failed; return TRUE; failed: DBG("AVCTP session %p got disconnected", session); avctp_set_state(session, AVCTP_STATE_DISCONNECTED); return FALSE; }
static void avctp_confirm_cb(GIOChannel *chan, gpointer data) { struct avctp *session; struct audio_device *dev; char address[18]; bdaddr_t src, dst; GError *err = NULL; bt_io_get(chan, BT_IO_L2CAP, &err, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); if (err) { error("%s", err->message); g_error_free(err); g_io_channel_shutdown(chan, TRUE, NULL); return; } DBG("AVCTP: incoming connect from %s", address); session = avctp_get_internal(&src, &dst); if (!session) goto drop; dev = manager_get_device(&src, &dst, FALSE); if (!dev) { dev = manager_get_device(&src, &dst, TRUE); if (!dev) { error("Unable to get audio device object for %s", address); goto drop; } } if (dev->control == NULL) { btd_device_add_uuid(dev->btd_dev, AVRCP_REMOTE_UUID); if (dev->control == NULL) goto drop; } if (session->io) { error("Refusing unexpected connect from %s", address); goto drop; } avctp_set_state(session, AVCTP_STATE_CONNECTING); session->io = g_io_channel_ref(chan); if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID, auth_cb, session) < 0) goto drop; session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, session_cb, session); return; drop: if (!session || !session->io) g_io_channel_shutdown(chan, TRUE, NULL); if (session) avctp_set_state(session, AVCTP_STATE_DISCONNECTED); }
static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; struct avctp_channel *browsing = session->browsing; uint8_t *buf = browsing->buffer; uint8_t *operands; struct avctp_header *avctp; int sock, ret, packet_size, operand_count; struct avctp_browsing_pdu_handler *handler; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; sock = g_io_channel_unix_get_fd(chan); ret = read(sock, buf, browsing->imtu); if (ret <= 0) goto failed; avctp = (struct avctp_header *) buf; if (avctp->packet_type != AVCTP_PACKET_SINGLE) goto failed; operands = buf + AVCTP_HEADER_LENGTH; ret -= AVCTP_HEADER_LENGTH; operand_count = ret; if (avctp->cr == AVCTP_RESPONSE) { browsing_response(browsing, avctp, operands, operand_count); return TRUE; } packet_size = AVCTP_HEADER_LENGTH; avctp->cr = AVCTP_RESPONSE; handler = g_slist_nth_data(browsing->handlers, 0); if (handler == NULL) { DBG("handler not found"); packet_size += avrcp_browsing_general_reject(operands); goto send; } packet_size += handler->cb(session, avctp->transaction, operands, operand_count, handler->user_data); send: if (packet_size != 0) { ret = write(sock, buf, packet_size); if (ret != packet_size) goto failed; } return TRUE; failed: DBG("AVCTP Browsing: disconnected"); avctp_set_state(session, AVCTP_STATE_CONNECTED); if (session->browsing) { avctp_channel_destroy(session->browsing); session->browsing = NULL; } return FALSE; }
static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avctp *session = data; uint8_t buf[1024], *operands, code, subunit; struct avctp_header *avctp; struct avc_header *avc; int ret, packet_size, operand_count, sock; struct avctp_pdu_handler *handler; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) goto failed; sock = g_io_channel_unix_get_fd(session->io); ret = read(sock, buf, sizeof(buf)); if (ret <= 0) goto failed; DBG("Got %d bytes of data for AVCTP session %p", ret, session); if ((unsigned int) ret < sizeof(struct avctp_header)) { error("Too small AVCTP packet"); goto failed; } avctp = (struct avctp_header *) buf; DBG("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 ((unsigned int) ret < sizeof(struct avc_header)) { error("Too small AVCTP packet"); goto failed; } avc = (struct avc_header *) (buf + sizeof(struct avctp_header)); ret -= sizeof(struct avc_header); operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header); operand_count = ret; DBG("AV/C %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, " "opcode 0x%02X, %d operands", avctp->cr ? "response" : "command", avc->code, avc->subunit_type, avc->subunit_id, avc->opcode, operand_count); if (avctp->cr == AVCTP_RESPONSE) { handle_response(session, avctp, avc, operands, operand_count); return TRUE; } packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH; avctp->cr = AVCTP_RESPONSE; if (avctp->packet_type != AVCTP_PACKET_SINGLE) { avc->code = AVC_CTYPE_NOT_IMPLEMENTED; goto done; } if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) { avctp->ipid = 1; avc->code = AVC_CTYPE_REJECTED; goto done; } handler = find_handler(handlers, avc->opcode); if (!handler) { DBG("handler not found for 0x%02x", avc->opcode); packet_size += avrcp_handle_vendor_reject(&code, operands); avc->code = code; goto done; } code = avc->code; subunit = avc->subunit_type; packet_size += handler->cb(session, avctp->transaction, &code, &subunit, operands, operand_count, handler->user_data); avc->code = code; avc->subunit_type = subunit; done: ret = write(sock, buf, packet_size); if (ret != packet_size) goto failed; return TRUE; failed: DBG("AVCTP session %p got disconnected", session); avctp_set_state(session, AVCTP_STATE_DISCONNECTED); return FALSE; }