int a2dp_source_stream_send_media_payload(uint16_t a2dp_cid, uint8_t local_seid, uint8_t * storage, int num_bytes_to_copy, uint8_t num_frames, uint8_t marker){ avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, &a2dp_source_context); if (!stream_endpoint) { log_error("A2DP source: no stream_endpoint with seid %d", local_seid); return 0; } if (a2dp_source_context.avdtp_cid != a2dp_cid){ log_error("A2DP source: a2dp cid 0x%02x not known, expected 0x%02x", a2dp_cid, a2dp_source_context.avdtp_cid); return 0; } if (stream_endpoint->l2cap_media_cid == 0){ log_error("A2DP source: no media connection for seid %d", local_seid); return 0; } int size = l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid); int offset = 0; l2cap_reserve_packet_buffer(); uint8_t * media_packet = l2cap_get_outgoing_buffer(); //int size = l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid); a2dp_source_setup_media_header(media_packet, size, &offset, marker, stream_endpoint->sequence_number); a2dp_source_copy_media_payload(media_packet, size, &offset, storage, num_bytes_to_copy, num_frames); stream_endpoint->sequence_number++; l2cap_send_prepared(stream_endpoint->l2cap_media_cid, offset); return size; }
static int avdtp_source_stream_send_media_payload(uint8_t local_seid, uint8_t * storage, int num_bytes_to_copy, uint8_t num_frames, uint8_t marker){ avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, &a2dp_source_context); if (!stream_endpoint) { log_error("no stream_endpoint found for seid %d", local_seid); return 0; } if (stream_endpoint->l2cap_media_cid == 0){ log_error("no media cid found for seid %d", local_seid); return 0; } int size = l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid); int offset = 0; // HACK / PTS requests ERTM although we did not offer it in L2CAP Information Request. Withouth ERTM support, default MTU of 48 will be used, but that's to // small for a 44.1kHz/16/8/Stereo/bitpool 53 sbc frame of 119 bytes (+ media info) size = 0x290; l2cap_reserve_packet_buffer(); uint8_t * media_packet = l2cap_get_outgoing_buffer(); //int size = l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid); a2dp_source_setup_media_header(media_packet, size, &offset, marker, stream_endpoint->sequence_number); a2dp_source_copy_media_payload(media_packet, size, &offset, storage, num_bytes_to_copy, num_frames); stream_endpoint->sequence_number++; l2cap_send_prepared(stream_endpoint->l2cap_media_cid, offset); return size; }
int a2dp_max_media_payload_size(uint16_t a2dp_cid, uint8_t local_seid){ avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, &a2dp_source_context); if (!stream_endpoint) { log_error("A2DP source: no stream_endpoint with seid %d", local_seid); return 0; } if (a2dp_source_context.avdtp_cid != a2dp_cid){ log_error("A2DP source: a2dp cid 0x%02x not known, expected 0x%02x", a2dp_cid, a2dp_source_context.avdtp_cid); return 0; } if (stream_endpoint->l2cap_media_cid == 0){ log_error("A2DP source: no media connection for seid %d", local_seid); return 0; } return l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid) - AVDTP_MEDIA_PAYLOAD_HEADER_SIZE; }
// we assume that we don't get two requests in a row static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { uint16_t transaction_id; SDP_PDU_ID_t pdu_id; uint16_t remote_mtu; // uint16_t param_len; switch (packet_type) { case L2CAP_DATA_PACKET: pdu_id = (SDP_PDU_ID_t) packet[0]; transaction_id = READ_NET_16(packet, 1); // param_len = READ_NET_16(packet, 3); remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel); // account for our buffer if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE) { remote_mtu = SDP_RESPONSE_BUFFER_SIZE; } // log_info("SDP Request: type %u, transaction id %u, len %u, mtu %u", pdu_id, transaction_id, param_len, remote_mtu); switch (pdu_id) { case SDP_ServiceSearchRequest: sdp_response_size = sdp_handle_service_search_request(packet, remote_mtu); break; case SDP_ServiceAttributeRequest: sdp_response_size = sdp_handle_service_attribute_request(packet, remote_mtu); break; case SDP_ServiceSearchAttributeRequest: sdp_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu); break; default: sdp_response_size = sdp_create_error_response(transaction_id, 0x0003); // invalid syntax break; } sdp_try_respond(); break; case HCI_EVENT_PACKET: switch (packet[0]) { case L2CAP_EVENT_INCOMING_CONNECTION: if (l2cap_cid) { // CONNECTION REJECTED DUE TO LIMITED RESOURCES l2cap_decline_connection_internal(channel, 0x04); break; } // accept l2cap_cid = channel; sdp_response_size = 0; l2cap_accept_connection_internal(channel); break; case L2CAP_EVENT_CHANNEL_OPENED: if (packet[2]) { // open failed -> reset l2cap_cid = 0; } break; case L2CAP_EVENT_CREDITS: case DAEMON_EVENT_HCI_PACKET_SENT: sdp_try_respond(); break; case L2CAP_EVENT_CHANNEL_CLOSED: if (channel == l2cap_cid) { // reset l2cap_cid = 0; } break; default: // other event break; } break; default: // other packet type break; } }