Пример #1
0
static void stream_state_changed(struct avdtp_stream *stream,
					avdtp_state_t old_state,
					avdtp_state_t new_state,
					struct avdtp_error *err,
					void *user_data)
{
	struct a2dp_sep *sep = user_data;

	if (new_state != AVDTP_STATE_IDLE)
		return;

	if (sep->suspend_timer) {
		g_source_remove(sep->suspend_timer);
		sep->suspend_timer = 0;
	}

	if (sep->session) {
		avdtp_unref(sep->session);
		sep->session = NULL;
	}

	sep->stream = NULL;

}
static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
								void *data)
{
	struct audio_device *dev = data;
	struct dev_priv *priv = dev->priv;

	if (priv->state == AUDIO_STATE_CONNECTING)
		return btd_error_in_progress(msg);
	else if (priv->state == AUDIO_STATE_CONNECTED)
		return btd_error_already_connected(msg);

	dev->auto_connect = TRUE;

	if (dev->headset)
		headset_config_stream(dev, FALSE, NULL, NULL);

	if (priv->state != AUDIO_STATE_CONNECTING && dev->sink) {
		struct avdtp *session = avdtp_get(&dev->src, &dev->dst);

		if (!session)
			return btd_error_failed(msg,
					"Failed to get AVDTP session");

		sink_setup_stream(dev->sink, session);
		avdtp_unref(session);
	}

	/* The previous calls should cause a call to the state callback to
	 * indicate AUDIO_STATE_CONNECTING */
	if (priv->state != AUDIO_STATE_CONNECTING)
		return btd_error_failed(msg, "Connect Failed");

	priv->conn_req = dbus_message_ref(msg);

	return NULL;
}
Пример #3
0
static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
			GSList *caps, void *user_data)
{
	struct source *source = user_data;
	int id;

	source->connect_id = 0;

	if (caps == NULL)
		goto failed;

	id = a2dp_config(session, sep, stream_setup_complete, caps, source);
	if (id == 0)
		goto failed;

	source->connect_id = id;
	return;

failed:
	audio_source_connected(source->dev->btd_dev, -EIO);

	avdtp_unref(source->session);
	source->session = NULL;
}
Пример #4
0
static void stream_state_changed(struct avdtp_stream *stream,
					avdtp_state_t old_state,
					avdtp_state_t new_state,
					struct avdtp_error *err,
					void *user_data)
{
	struct audio_device *dev = user_data;
	struct sink *sink = dev->sink;
	gboolean value;

	if (err)
		return;

	switch (new_state) {
	case AVDTP_STATE_IDLE:
		if (sink->disconnect) {
			DBusMessage *reply;
			struct pending_request *p;

			p = sink->disconnect;
			sink->disconnect = NULL;

			reply = dbus_message_new_method_return(p->msg);
			g_dbus_send_message(p->conn, reply);
			pending_request_free(dev, p);
		}

		if (sink->session) {
			avdtp_unref(sink->session);
			sink->session = NULL;
		}
		sink->stream = NULL;
		sink->cb_id = 0;
		break;
	case AVDTP_STATE_OPEN:
		if (old_state == AVDTP_STATE_CONFIGURED &&
				sink->state == SINK_STATE_CONNECTING) {
			value = TRUE;
			g_dbus_emit_signal(dev->conn, dev->path,
						AUDIO_SINK_INTERFACE,
						"Connected",
						DBUS_TYPE_INVALID);
			emit_property_changed(dev->conn, dev->path,
						AUDIO_SINK_INTERFACE,
						"Connected",
						DBUS_TYPE_BOOLEAN, &value);
		} else if (old_state == AVDTP_STATE_STREAMING) {
			value = FALSE;
			g_dbus_emit_signal(dev->conn, dev->path,
						AUDIO_SINK_INTERFACE,
						"Stopped",
						DBUS_TYPE_INVALID);
			emit_property_changed(dev->conn, dev->path,
						AUDIO_SINK_INTERFACE,
						"Playing",
						DBUS_TYPE_BOOLEAN, &value);
		}
		sink_set_state(dev, SINK_STATE_CONNECTED);
		break;
	case AVDTP_STATE_STREAMING:
		value = TRUE;
		g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE,
					"Playing", DBUS_TYPE_INVALID);
		emit_property_changed(dev->conn, dev->path,
					AUDIO_SINK_INTERFACE, "Playing",
					DBUS_TYPE_BOOLEAN, &value);
		sink_set_state(dev, SINK_STATE_PLAYING);
		break;
	case AVDTP_STATE_CONFIGURED:
	case AVDTP_STATE_CLOSING:
	case AVDTP_STATE_ABORTING:
	default:
		break;
	}

	sink->stream_state = new_state;
}
Пример #5
0
static void start_open(struct audio_device *dev, struct unix_client *client)
{
	struct a2dp_data *a2dp;
	struct headset_data *hs;
	struct avdtp_remote_sep *rsep;
	gboolean unref_avdtp_on_fail = FALSE;

	switch (client->type) {
	case TYPE_SINK:
	case TYPE_SOURCE:
		a2dp = &client->d.a2dp;

		if (!a2dp->session) {
			a2dp->session = avdtp_get(&dev->src, &dev->dst);
			unref_avdtp_on_fail = TRUE;
		}

		if (!a2dp->session) {
			error("Unable to get a session");
			goto failed;
		}

		if (a2dp->sep) {
			error("Client already has an opened session");
			goto failed;
		}

		rsep = avdtp_get_remote_sep(a2dp->session, client->seid);
		if (!rsep) {
			error("Invalid seid %d", client->seid);
			goto failed;
		}

		a2dp->sep = a2dp_get(a2dp->session, rsep);
		if (!a2dp->sep) {
			error("seid %d not available or locked", client->seid);
			goto failed;
		}

		if (!a2dp_sep_lock(a2dp->sep, a2dp->session)) {
			error("Unable to open seid %d", client->seid);
			a2dp->sep = NULL;
			goto failed;
		}

		break;

	case TYPE_HEADSET:
		hs = &client->d.hs;

		if (hs->locked) {
			error("Client already has an opened session");
			goto failed;
		}

		hs->locked = headset_lock(dev, client->lock);
		if (!hs->locked) {
			error("Unable to open seid %d", client->seid);
			goto failed;
		}
		break;

        case TYPE_GATEWAY:
                break;
	default:
		error("No known services for device");
		goto failed;
	}

	client->dev = dev;

	open_complete(dev, client);

	return;

failed:
	if (unref_avdtp_on_fail && a2dp->session) {
		avdtp_unref(a2dp->session);
		a2dp->session = NULL;
	}
	unix_ipc_error(client, BT_OPEN, EINVAL);
}
Пример #6
0
static void a2dp_discovery_complete(struct avdtp *session, GSList *seps,
					struct avdtp_error *err,
					void *user_data)
{
	struct unix_client *client = user_data;
	char buf[BT_SUGGESTED_BUFFER_SIZE];
	struct bt_get_capabilities_rsp *rsp = (void *) buf;
	struct a2dp_data *a2dp = &client->d.a2dp;
	GSList *l;

	if (!g_slist_find(clients, client)) {
		DBG("Client disconnected during discovery");
		return;
	}

	if (err)
		goto failed;

	memset(buf, 0, sizeof(buf));
	client->req_id = 0;

	rsp->h.type = BT_RESPONSE;
	rsp->h.name = BT_GET_CAPABILITIES;
	rsp->h.length = sizeof(*rsp);
	ba2str(&client->dev->src, rsp->source);
	ba2str(&client->dev->dst, rsp->destination);
	strncpy(rsp->object, client->dev->path, sizeof(rsp->object));

	for (l = seps; l; l = g_slist_next(l)) {
		struct avdtp_remote_sep *rsep = l->data;
		struct a2dp_sep *sep;
		struct avdtp_service_capability *cap;
		struct avdtp_stream *stream;
		uint8_t type, seid, configured = 0, lock = 0;
		GSList *cl;

		type = avdtp_get_type(rsep);

		if (type != AVDTP_SEP_TYPE_SINK &&
						type != AVDTP_SEP_TYPE_SOURCE)
			continue;

		cap = avdtp_get_codec(rsep);

		if (cap->category != AVDTP_MEDIA_CODEC)
			continue;

		seid = avdtp_get_seid(rsep);

		if (client->seid != 0 && client->seid != seid)
			continue;

		stream = avdtp_get_stream(rsep);
		if (stream) {
			configured = 1;
			if (client->seid == seid)
				cap = avdtp_stream_get_codec(stream);
		}

		for (cl = clients; cl; cl = cl->next) {
			struct unix_client *c = cl->data;
			struct a2dp_data *ca2dp = &c->d.a2dp;

			if (ca2dp->session == session && c->seid == seid) {
				lock = c->lock;
				break;
			}
		}

		sep = a2dp_get_sep(session, stream);
		if (sep && a2dp_sep_get_lock(sep))
			lock = BT_WRITE_LOCK;

		a2dp_append_codec(rsp, cap, seid, type, configured, lock);
	}

	unix_ipc_sendmsg(client, &rsp->h);

	return;

failed:
	error("discovery failed");
	unix_ipc_error(client, BT_GET_CAPABILITIES, EIO);

	if (a2dp->sep) {
		a2dp_sep_unlock(a2dp->sep, a2dp->session);
		a2dp->sep = NULL;
	}

	avdtp_unref(a2dp->session);
	a2dp->session = NULL;
	a2dp->stream = NULL;
}
Пример #7
0
static void start_suspend(struct audio_device *dev, struct unix_client *client)
{
	struct a2dp_data *a2dp;
	struct headset_data *hs;
	unsigned int id;
	gboolean unref_avdtp_on_fail = FALSE;

	switch (client->type) {
	case TYPE_SINK:
	case TYPE_SOURCE:
		a2dp = &client->d.a2dp;

		if (!a2dp->session) {
			a2dp->session = avdtp_get(&dev->src, &dev->dst);
			unref_avdtp_on_fail = TRUE;
		}

		if (!a2dp->session) {
			error("Unable to get a session");
			goto failed;
		}

		if (!a2dp->sep) {
			error("Unable to get a sep");
			goto failed;
		}

		id = a2dp_suspend(a2dp->session, a2dp->sep,
					a2dp_suspend_complete, client);
		client->cancel = a2dp_cancel;
		break;

	case TYPE_HEADSET:
		hs = &client->d.hs;

		if (!hs->locked) {
			error("seid not opened");
			goto failed;
		}

		id = headset_suspend_stream(dev, headset_suspend_complete,
						client);
		client->cancel = headset_cancel_stream;
		break;

	case TYPE_GATEWAY:
		gateway_suspend_stream(dev);
		client->cancel = gateway_cancel_stream;
		headset_suspend_complete(dev, client);
		id = 1;
		break;

	default:
		error("No known services for device");
		goto failed;
	}

	if (id == 0) {
		error("suspend failed");
		goto failed;
	}

	return;

failed:
	if (unref_avdtp_on_fail && a2dp->session) {
		avdtp_unref(a2dp->session);
		a2dp->session = NULL;
	}
	unix_ipc_error(client, BT_STOP_STREAM, EIO);
}
Пример #8
0
static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep,
					struct avdtp_stream *stream,
					struct avdtp_error *err,
					void *user_data)
{
	struct unix_client *client = user_data;
	char buf[BT_SUGGESTED_BUFFER_SIZE];
	struct bt_set_configuration_rsp *rsp = (void *) buf;
	struct a2dp_data *a2dp = &client->d.a2dp;
	uint16_t imtu, omtu;
	GSList *caps;
	struct avdtp_service_capability *protection;

	client->req_id = 0;

	if (err)
		goto failed;

	memset(buf, 0, sizeof(buf));

	if (!stream)
		goto failed;

	if (client->cb_id > 0)
		avdtp_stream_remove_cb(a2dp->session, a2dp->stream,
								client->cb_id);

	a2dp->sep = sep;
	a2dp->stream = stream;

	if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu,
					&caps)) {
		error("Unable to get stream transport");
		goto failed;
	}

	rsp->h.type = BT_RESPONSE;
	rsp->h.name = BT_SET_CONFIGURATION;
	rsp->h.length = sizeof(*rsp);

	/* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */
	rsp->link_mtu = omtu;

	protection = avdtp_get_protection(stream);

	/* Initialize to Zero */
	rsp->content_protection = 0;

	if (protection != NULL) {
		if (protection->length >= 2) {
			struct avdtp_content_protection_capability *prot = (void *)protection->data;

			rsp->content_protection = (prot->cp_type_msb << 8) | prot->cp_type_lsb;
		}
	}

	unix_ipc_sendmsg(client, &rsp->h);

	client->cb_id = avdtp_stream_add_cb(session, stream,
						stream_state_changed, client);

	return;

failed:
	error("config failed");

	unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);

	avdtp_unref(a2dp->session);

	a2dp->session = NULL;
	a2dp->stream = NULL;
	a2dp->sep = NULL;
}
Пример #9
0
int main(int argc, char *argv[])
{
	GError *err = NULL;
	int opt;

	bacpy(&src, BDADDR_ANY);
	bacpy(&dst, BDADDR_ANY);

	mainloop = g_main_loop_new(NULL, FALSE);
	if (!mainloop) {
		printf("Failed to create main loop\n");

		exit(1);
	}

	while ((opt = getopt_long(argc, argv, "d:hi:s:c:v:lrfp",
						main_options, NULL)) != EOF) {
		switch (opt) {
		case 'i':
			if (!strncmp(optarg, "hci", 3))
				hci_devba(atoi(optarg + 3), &src);
			else
				str2ba(optarg, &src);
			break;
		case 'd':
			if (!strncasecmp(optarg, "SRC", sizeof("SRC"))) {
				dev_role = AVDTP_SEP_TYPE_SOURCE;
			} else if (!strncasecmp(optarg, "SINK",
							sizeof("SINK"))) {
				dev_role = AVDTP_SEP_TYPE_SINK;
			} else {
				usage();
				exit(0);
			}
			break;
		case 'c':
			if (str2ba(optarg, &dst) < 0) {
				usage();
				exit(0);
			}
			break;
		case 'l':
			bacpy(&dst, BDADDR_ANY);
			break;
		case 'r':
			reject = true;
			break;
		case 'f':
			fragment = true;
			break;
		case 'p':
			preconf = true;
			break;
		case 's':
			parse_command(optarg);
			break;
		case 'v':
			version = strtol(optarg, NULL, 0);
			if (version != 0x0100 && version != 0x0102 &&
							version != 0x0103) {
				printf("invalid version\n");
				exit(0);
			}

			break;
		case 'h':
		default:
			usage();
			exit(0);
		}
	}

	local_sep = avdtp_register_sep(dev_role, AVDTP_MEDIA_TYPE_AUDIO,
					0x00, TRUE, &sep_ind, &sep_cfm, NULL);
	if (!local_sep) {
		printf("Failed to register sep\n");
		exit(0);
	}

	if (!bacmp(&dst, BDADDR_ANY)) {
		printf("Listening...\n");
		io = do_listen(&err);
	} else {
		printf("Connecting...\n");
		io = do_connect(&err);
	}

	if (!io) {
		printf("Failed: %s\n", err->message);
		g_error_free(err);
		exit(0);
	}

	g_main_loop_run(mainloop);

	printf("Done\n");

	avdtp_unref(avdtp);
	avdtp = NULL;

	g_main_loop_unref(mainloop);
	mainloop = NULL;

	return 0;
}
Пример #10
0
static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
				void *user_data)
{
	struct sink *sink = user_data;
	struct pending_request *pending;
	struct avdtp_local_sep *lsep;
	struct avdtp_remote_sep *rsep;
	struct a2dp_sep *sep;
	GSList *caps = NULL;
	int id;

	pending = sink->connect;

	if (err) {
		avdtp_unref(sink->session);
		sink->session = NULL;
		if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO
				&& avdtp_error_posix_errno(err) != EHOSTDOWN) {
			DBG("connect:connect XCASE detected");
			sink->retry_id =
				g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
							stream_setup_retry,
							sink);
		} else
			goto failed;
		return;
	}

	DBG("Discovery complete");

	if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
				A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
		error("No matching ACP and INT SEPs found");
		goto failed;
	}

	if (!select_capabilities(session, rsep, &caps)) {
		error("Unable to select remote SEP capabilities");
		goto failed;
	}

	sep = a2dp_get(session, rsep);
	if (!sep) {
		error("Unable to get a local source SEP");
		goto failed;
	}

	id = a2dp_config(sink->session, sep, stream_setup_complete, caps, sink);
	if (id == 0)
		goto failed;

	pending->id = id;
	return;

failed:
	if (pending->msg)
		error_failed(pending->conn, pending->msg, "Stream setup failed");
	pending_request_free(sink->dev, pending);
	sink->connect = NULL;
	avdtp_unref(sink->session);
	sink->session = NULL;
}
Пример #11
0
static void start_resume(struct audio_device *dev, struct unix_client *client)
{
	struct a2dp_data *a2dp = NULL;
	struct headset_data *hs;
	unsigned int id;
	struct avdtp *session = NULL;

	switch (client->type) {
	case TYPE_SINK:
	case TYPE_SOURCE:
		a2dp = &client->d.a2dp;

		if (!a2dp->sep) {
			error("seid not opened");
			goto failed;
		}

		if (!a2dp->session) {
			session = avdtp_get(&dev->src, &dev->dst);
			if (!session) {
				error("Unable to get a session");
				goto failed;
			}
			a2dp->session = session;
		}

		id = a2dp_resume(a2dp->session, a2dp->sep, a2dp_resume_complete,
					client);
		client->cancel = a2dp_cancel;

		break;

	case TYPE_HEADSET:
		hs = &client->d.hs;

		if (!hs->locked) {
			error("seid not opened");
			goto failed;
		}

		id = headset_request_stream(dev, headset_resume_complete,
						client);
		client->cancel = headset_cancel_stream;
		break;

	case TYPE_GATEWAY:
		id = gateway_request_stream(dev, gateway_resume_complete,
						client);
		client->cancel = gateway_cancel_stream;
		break;

	default:
		error("No known services for device");
		goto failed;
	}

	if (id == 0) {
		error("start_resume: resume failed");
		goto failed;
	}

	client->req_id = id;

	return;

failed:
	if (session) {
		avdtp_unref(session);
		a2dp->session = NULL;
	}

	unix_ipc_error(client, BT_START_STREAM, EIO);
}
Пример #12
0
static void stream_state_changed(struct avdtp_stream *stream,
					avdtp_state_t old_state,
					avdtp_state_t new_state,
					struct avdtp_error *err,
					void *user_data)
{
	struct audio_device *dev = user_data;
	struct sink *sink = dev->sink;

	if (err)
		return;

	switch (new_state) {
	case AVDTP_STATE_IDLE:
		g_dbus_emit_signal(dev->conn, dev->path,
						AUDIO_SINK_INTERFACE,
						"Disconnected",
						DBUS_TYPE_INVALID);
		if (sink->disconnect) {
			DBusMessage *reply;
			struct pending_request *p;

			p = sink->disconnect;
			sink->disconnect = NULL;

			reply = dbus_message_new_method_return(p->msg);
			dbus_connection_send(p->conn, reply, NULL);
			dbus_message_unref(reply);
			pending_request_free(p);
		}

		if (sink->session) {
			avdtp_unref(sink->session);
			sink->session = NULL;
		}
		sink->stream = NULL;
		sink->cb_id = 0;
		break;
	case AVDTP_STATE_OPEN:
		if (old_state == AVDTP_STATE_CONFIGURED)
			g_dbus_emit_signal(dev->conn, dev->path,
							AUDIO_SINK_INTERFACE,
							"Connected",
							DBUS_TYPE_INVALID);
		else if (old_state == AVDTP_STATE_STREAMING)
			g_dbus_emit_signal(dev->conn, dev->path,
							AUDIO_SINK_INTERFACE,
							"Stopped",
							DBUS_TYPE_INVALID);
		break;
	case AVDTP_STATE_STREAMING:
		g_dbus_emit_signal(dev->conn, dev->path,
						AUDIO_SINK_INTERFACE,
						"Playing",
						DBUS_TYPE_INVALID);
		break;
	case AVDTP_STATE_CONFIGURED:
	case AVDTP_STATE_CLOSING:
	case AVDTP_STATE_ABORTING:
	default:
		break;
	}

	sink->state = new_state;
}