include $(BUILD_SHARED_LIBRARY)
-# liba2sp
+#
+# liba2dp
+# This is linked to Audioflinger so **LGPL only**
include $(CLEAR_VARS)
../sbc/sbc.c.arm \
../sbc/sbc_primitives.c
-# to improve SBC performance
+# to improve SBC performance
LOCAL_CFLAGS:= -funroll-loops
LOCAL_C_INCLUDES:= \
- $(call include-path-for, bluez-libs) \
- $(call include-path-for, bluez-utils)/common \
- $(call include-path-for, bluez-utils)/hcid \
- $(call include-path-for, bluez-utils)/sdpd \
- $(call include-path-for, bluez-utils)/eglib \
- $(call include-path-for, bluez-utils)/gdbus \
$(call include-path-for, bluez-utils)/sbc \
- $(call include-path-for, dbus)
LOCAL_SHARED_LIBRARIES := \
- libbluetooth \
- libhcid \
- libdbus \
libcutils
LOCAL_MODULE := liba2dp
Returns TRUE if AVRCP is connected.
+ void Connect()
+
+ Connect to AVRCP on the remote device.
+
+ NOTE: Most remote devices do not allow AVRCP without
+ A2DP, so this is intended for testing purposes.
+
+ void Disconnect()
+
+ Disconnect from the remote device.
+
+ NOTE: Most remote devices do not allow AVRCP without
+ A2DP, so this is intended for testing purposes.
+
Signals void Connected()
Sent when a successful AVRCP connection has been made.
#define AVDTP_PKT_TYPE_END 0x03
#define AVDTP_MSG_TYPE_COMMAND 0x00
+#define AVDTP_MSG_TYPE_GENERAL_REJECT 0x01
#define AVDTP_MSG_TYPE_ACCEPT 0x02
#define AVDTP_MSG_TYPE_REJECT 0x03
static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
void *buf, int size)
{
- return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
- 0, NULL, 0);
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GENERAL_REJECT,
+ session->in.signal_id, NULL, 0);
}
static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
uint8_t err, buf[1024], *ptr = buf;
if (size < sizeof(struct seid_req)) {
- error("Too short getcap request");
- return FALSE;
+ err = AVDTP_BAD_LENGTH;
+ goto failed;
}
sep = find_local_sep_by_seid(req->acp_seid);
struct avdtp_local_sep *sep;
struct avdtp_stream *stream;
uint8_t err, category = 0x00;
+ GSList *l;
if (size < sizeof(struct setconf_req)) {
error("Too short getcap request");
&stream->codec);
stream->sock = -1;
+ /* Verify that the Media Transport capability's length = 0. Reject otherwise */
+ for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if ((cap->category == AVDTP_MEDIA_TRANSPORT) && (cap->length != 0)) {
+ err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+ goto failed;
+ }
+ }
+
if (sep->ind && sep->ind->set_configuration) {
if (!sep->ind->set_configuration(session, sep, stream,
stream->caps, &err,
static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
struct seid_req *req, int size)
{
- return avdtp_unknown_cmd(session, transaction, (void *) req, size);
+ GSList *l;
+ struct avdtp_local_sep *sep = NULL;
+ int rsp_size;
+ uint8_t err;
+ uint8_t buf[1024];
+ uint8_t *ptr = buf;
+
+ if (size < sizeof(struct seid_req)) {
+ error("Too short getconf request");
+ return FALSE;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ sep = find_local_sep_by_seid(req->acp_seid);
+ if (!sep) {
+ err = AVDTP_BAD_ACP_SEID;
+ goto failed;
+ }
+ if (!sep->stream || !sep->stream->caps) {
+ err = AVDTP_UNSUPPORTED_CONFIGURATION;
+ goto failed;
+ }
+
+ for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (rsp_size + cap->length + 2 > sizeof(buf))
+ break;
+
+ memcpy(ptr, cap, cap->length + 2);
+ rsp_size += cap->length + 2;
+ ptr += cap->length + 2;
+ }
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+ AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_GET_CONFIGURATION, &err, sizeof(err));
}
static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
/* ctype entries */
#define CTYPE_CONTROL 0x0
#define CTYPE_STATUS 0x1
+#define CTYPE_NOT_IMPLEMENTED 0x8
#define CTYPE_ACCEPTED 0x9
+#define CTYPE_REJECTED 0xA
#define CTYPE_STABLE 0xC
/* opcodes */
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) &&
+ 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(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) &&
+ } else if (avctp->cr == AVCTP_COMMAND &&
avrcp->code == CTYPE_STATUS &&
(avrcp->opcode == OP_UNITINFO
|| avrcp->opcode == OP_SUBUNITINFO)) {
avrcp->code = CTYPE_STABLE;
debug("reply to %s", avrcp->opcode == OP_UNITINFO ?
"OP_UNITINFO" : "OP_SUBUNITINFO");
- ret = write(session->sock, buf, packet_size);
+ } else {
+ avctp->cr = AVCTP_RESPONSE;
+ avrcp->code = CTYPE_REJECTED;
}
+ ret = write(session->sock, buf, packet_size);
return TRUE;
connection = NULL;
}
+static DBusMessage *control_connect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ DBusMessage *reply;
+ dbus_bool_t result;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ result = avrcp_connect(device);
+
+ dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &result,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *control_disconnect(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ avrcp_disconnect(device);
+
+ return reply;
+}
+
static DBusMessage *control_is_connected(DBusConnection *conn,
DBusMessage *msg,
void *data)
}
static GDBusMethodTable control_methods[] = {
+ { "Connect", "", "b", control_connect},
+ { "Disconnect", "", "", control_disconnect},
{ "IsConnected", "", "b", control_is_connected },
{ NULL, NULL, NULL, NULL }
};
else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset &&
headset_is_active(dev))
return TRUE;
- else if (!strcmp(interface, AUDIO_CONTROL_INTERFACE) && dev->headset &&
+ else if (!strcmp(interface, AUDIO_CONTROL_INTERFACE) && dev->control &&
control_is_active(dev))
return TRUE;
data->server.fd = sk;
data->server.events = POLLIN;
- return 0;
+ return 0;
}
static int bluetooth_configure(struct bluetooth_data *data)
{
struct bluetooth_data* data = (struct bluetooth_data*)d;
uint8_t* src = (uint8_t *)buffer;
- int codesize = data->codesize;
+ int codesize;
int err, ret = 0;
long frames_left = count;
int encoded, written;
configure:
if (!data->configured) {
- err = bluetooth_configure(data);
- if (err < 0)
+ err = bluetooth_configure(data);
+ if (err < 0)
return err;
did_configure = 1;
}
}
}
+ codesize = data->codesize;
+
while (frames_left >= codesize) {
/* Enough data to encode (sbc wants 512 byte blocks) */
encoded = sbc_encode(&(data->sbc), src, codesize,