[RFC 1/2] audio: Move tel drivers to DBus interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 Makefile.am       |   13 +-
 audio/headset.c   |  614 ++---------------------------------------------------
 audio/headset.h   |    2 +
 audio/manager.c   |    4 +-
 audio/telephony.c |  494 ++++++++++++++++++++++++++++++++++++++++++
 audio/telephony.h |   25 +--
 doc/audio-api.txt |   99 +++++++++
 7 files changed, 620 insertions(+), 631 deletions(-)
 create mode 100644 audio/telephony.c

diff --git a/Makefile.am b/Makefile.am
index 07b8626..31c1083 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -154,14 +154,8 @@ builtin_sources += audio/main.c \
 			audio/unix.h audio/unix.c \
 			audio/media.h audio/media.c \
 			audio/transport.h audio/transport.c \
-			audio/telephony.h audio/a2dp-codecs.h
-builtin_nodist += audio/telephony.c
-
-noinst_LIBRARIES += audio/libtelephony.a
-
-audio_libtelephony_a_SOURCES = audio/telephony.h audio/telephony-dummy.c \
-				audio/telephony-maemo5.c audio/telephony-ofono.c \
-				audio/telephony-maemo6.c
+			audio/telephony.h audio/telephony.c \
+			audio/a2dp-codecs.h
 endif
 
 if SAPPLUGIN
@@ -476,9 +470,6 @@ MAINTAINERCLEANFILES = Makefile.in \
 src/builtin.h: src/genbuiltin $(builtin_sources)
 	$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
 
-audio/telephony.c: audio/@TELEPHONY_DRIVER@
-	$(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
-
 sap/sap.c: sap/@SAP_DRIVER@
 	$(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
 
diff --git a/audio/headset.c b/audio/headset.c
index 6aef6a8..74eb7a4 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -70,8 +70,6 @@
 #define HEADSET_GAIN_MICROPHONE 'M'
 
 static struct {
-	gboolean telephony_ready;	/* Telephony plugin initialized */
-	uint32_t features;		/* HFP AG features */
 	const struct indicator *indicators;	/* Available HFP indicators */
 	int er_mode;			/* Event reporting mode */
 	int er_ind;			/* Event reporting for indicators */
@@ -81,8 +79,6 @@ static struct {
 	guint ring_timer;		/* For incoming call indication */
 	const char *chld;		/* Response to AT+CHLD=? */
 } ag = {
-	.telephony_ready = FALSE,
-	.features = 0,
 	.er_mode = 3,
 	.er_ind = 0,
 	.rh = BTRH_NOT_SUPPORTED,
@@ -236,40 +232,6 @@ static void print_ag_features(uint32_t features)
 	g_free(str);
 }
 
-static void print_hf_features(uint32_t features)
-{
-	GString *gstr;
-	char *str;
-
-	if (features == 0) {
-		DBG("HFP HF features: (none)");
-		return;
-	}
-
-	gstr = g_string_new("HFP HF features: ");
-
-	if (features & HF_FEATURE_EC_ANDOR_NR)
-		g_string_append(gstr, "\"EC and/or NR function\" ");
-	if (features & HF_FEATURE_CALL_WAITING_AND_3WAY)
-		g_string_append(gstr, "\"Call waiting and 3-way calling\" ");
-	if (features & HF_FEATURE_CLI_PRESENTATION)
-		g_string_append(gstr, "\"CLI presentation capability\" ");
-	if (features & HF_FEATURE_VOICE_RECOGNITION)
-		g_string_append(gstr, "\"Voice recognition activation\" ");
-	if (features & HF_FEATURE_REMOTE_VOLUME_CONTROL)
-		g_string_append(gstr, "\"Remote volume control\" ");
-	if (features & HF_FEATURE_ENHANCED_CALL_STATUS)
-		g_string_append(gstr, "\"Enhanced call status\" ");
-	if (features & HF_FEATURE_ENHANCED_CALL_CONTROL)
-		g_string_append(gstr, "\"Enhanced call control\" ");
-
-	str = g_string_free(gstr, FALSE);
-
-	DBG("%s", str);
-
-	g_free(str);
-}
-
 static const char *state2str(headset_state_t state)
 {
 	switch (state) {
@@ -333,97 +295,6 @@ static int __attribute__((format(printf, 2, 3)))
 	return ret;
 }
 
-static int supported_features(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-	struct headset_slc *slc = hs->slc;
-	int err;
-
-	if (strlen(buf) < 9)
-		return -EINVAL;
-
-	slc->hf_features = strtoul(&buf[8], NULL, 10);
-
-	print_hf_features(slc->hf_features);
-
-	err = headset_send(hs, "\r\n+BRSF: %u\r\n", ag.features);
-	if (err < 0)
-		return err;
-
-	return headset_send(hs, "\r\nOK\r\n");
-}
-
-static char *indicator_ranges(const struct indicator *indicators)
-{
-	int i;
-	GString *gstr;
-
-	gstr = g_string_new("\r\n+CIND: ");
-
-	for (i = 0; indicators[i].desc != NULL; i++) {
-		if (i == 0)
-			g_string_append_printf(gstr, "(\"%s\",(%s))",
-						indicators[i].desc,
-						indicators[i].range);
-		else
-			g_string_append_printf(gstr, ",(\"%s\",(%s))",
-						indicators[i].desc,
-						indicators[i].range);
-	}
-
-	g_string_append(gstr, "\r\n");
-
-	return g_string_free(gstr, FALSE);
-}
-
-static char *indicator_values(const struct indicator *indicators)
-{
-	int i;
-	GString *gstr;
-
-	gstr = g_string_new("\r\n+CIND: ");
-
-	for (i = 0; indicators[i].desc != NULL; i++) {
-		if (i == 0)
-			g_string_append_printf(gstr, "%d", indicators[i].val);
-		else
-			g_string_append_printf(gstr, ",%d", indicators[i].val);
-	}
-
-	g_string_append(gstr, "\r\n");
-
-	return g_string_free(gstr, FALSE);
-}
-
-static int report_indicators(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-	int err;
-	char *str;
-
-	if (strlen(buf) < 8)
-		return -EINVAL;
-
-	if (ag.indicators == NULL) {
-		error("HFP AG indicators not initialized");
-		return headset_send(hs, "\r\nERROR\r\n");
-	}
-
-	if (buf[7] == '=')
-		str = indicator_ranges(ag.indicators);
-	else
-		str = indicator_values(ag.indicators);
-
-	err = headset_send(hs, "%s", str);
-
-	g_free(str);
-
-	if (err < 0)
-		return err;
-
-	return headset_send(hs, "\r\nOK\r\n");
-}
-
 static void pending_connect_complete(struct connect_cb *cb, struct audio_device *dev)
 {
 	struct headset *hs = dev->headset;
@@ -656,7 +527,7 @@ static int hfp_cmp(struct headset *hs)
 		return -1;
 }
 
-static void hfp_slc_complete(struct audio_device *dev)
+void headset_slc_complete(struct audio_device *dev)
 {
 	struct headset *hs = dev->headset;
 	struct pending_connect *p = hs->pending;
@@ -721,73 +592,10 @@ int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err)
 		return 0;
 
 	if (slc->hf_features & HF_FEATURE_CALL_WAITING_AND_3WAY &&
-			ag.features & AG_FEATURE_THREE_WAY_CALLING)
-		return 0;
-
-	hfp_slc_complete(device);
-
-	return 0;
-}
-
-static int event_reporting(struct audio_device *dev, const char *buf)
-{
-	char **tokens; /* <mode>, <keyp>, <disp>, <ind>, <bfr> */
-
-	if (strlen(buf) < 13)
-		return -EINVAL;
-
-	tokens = g_strsplit(&buf[8], ",", 5);
-	if (g_strv_length(tokens) < 4) {
-		g_strfreev(tokens);
-		return -EINVAL;
-	}
-
-	ag.er_mode = atoi(tokens[0]);
-	ag.er_ind = atoi(tokens[3]);
-
-	g_strfreev(tokens);
-	tokens = NULL;
-
-	DBG("Event reporting (CMER): mode=%d, ind=%d",
-			ag.er_mode, ag.er_ind);
-
-	switch (ag.er_ind) {
-	case 0:
-	case 1:
-		telephony_event_reporting_req(dev, ag.er_ind);
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int call_hold(struct audio_device *dev, const char *buf)
-{
-	struct headset *hs = dev->headset;
-	int err;
-
-	if (strlen(buf) < 9)
-		return -EINVAL;
-
-	if (buf[8] != '?') {
-		telephony_call_hold_req(dev, &buf[8]);
+			telephony_get_ag_features() & AG_FEATURE_THREE_WAY_CALLING)
 		return 0;
-	}
 
-	err = headset_send(hs, "\r\n+CHLD: (%s)\r\n", ag.chld);
-	if (err < 0)
-		return err;
-
-	err = headset_send(hs, "\r\nOK\r\n");
-	if (err < 0)
-		return err;
-
-	if (hs->state != HEADSET_STATE_CONNECTING)
-		return 0;
-
-	hfp_slc_complete(dev);
+	headset_slc_complete(device);
 
 	return 0;
 }
@@ -797,47 +605,11 @@ int telephony_key_press_rsp(void *telephony_device, cme_error_t err)
 	return telephony_generic_rsp(telephony_device, err);
 }
 
-static int key_press(struct audio_device *device, const char *buf)
-{
-	if (strlen(buf) < 9)
-		return -EINVAL;
-
-	g_dbus_emit_signal(device->conn, device->path,
-			AUDIO_HEADSET_INTERFACE, "AnswerRequested",
-			DBUS_TYPE_INVALID);
-
-	if (ag.ring_timer) {
-		g_source_remove(ag.ring_timer);
-		ag.ring_timer = 0;
-	}
-
-	telephony_key_press_req(device, &buf[8]);
-
-	return 0;
-}
-
 int telephony_answer_call_rsp(void *telephony_device, cme_error_t err)
 {
 	return telephony_generic_rsp(telephony_device, err);
 }
 
-static int answer_call(struct audio_device *device, const char *buf)
-{
-	if (ag.ring_timer) {
-		g_source_remove(ag.ring_timer);
-		ag.ring_timer = 0;
-	}
-
-	if (ag.number) {
-		g_free(ag.number);
-		ag.number = NULL;
-	}
-
-	telephony_answer_call_req(device);
-
-	return 0;
-}
-
 int telephony_terminate_call_rsp(void *telephony_device,
 					cme_error_t err)
 {
@@ -854,99 +626,21 @@ int telephony_terminate_call_rsp(void *telephony_device,
 	return headset_send(hs, "\r\nOK\r\n");
 }
 
-static int terminate_call(struct audio_device *device, const char *buf)
-{
-	if (ag.number) {
-		g_free(ag.number);
-		ag.number = NULL;
-	}
-
-	if (ag.ring_timer) {
-		g_source_remove(ag.ring_timer);
-		ag.ring_timer = 0;
-	}
-
-	telephony_terminate_call_req(device);
-
-	return 0;
-}
-
-static int cli_notification(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-	struct headset_slc *slc = hs->slc;
-
-	if (strlen(buf) < 9)
-		return -EINVAL;
-
-	slc->cli_active = buf[8] == '1' ? TRUE : FALSE;
-
-	return headset_send(hs, "\r\nOK\r\n");
-}
-
 int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err)
 {
 	return telephony_generic_rsp(telephony_device, err);
 }
 
-static int response_and_hold(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-
-	if (strlen(buf) < 8)
-		return -EINVAL;
-
-	if (ag.rh == BTRH_NOT_SUPPORTED)
-		return telephony_generic_rsp(device, CME_ERROR_NOT_SUPPORTED);
-
-	if (buf[7] == '=') {
-		telephony_response_and_hold_req(device, atoi(&buf[8]) < 0);
-		return 0;
-	}
-
-	if (ag.rh >= 0)
-		headset_send(hs, "\r\n+BTRH: %d\r\n", ag.rh);
-
-	return headset_send(hs, "\r\nOK\r\n");
-}
-
 int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err)
 {
 	return telephony_generic_rsp(telephony_device, err);
 }
 
-static int last_dialed_number(struct audio_device *device, const char *buf)
-{
-	telephony_last_dialed_number_req(device);
-
-	return 0;
-}
-
 int telephony_dial_number_rsp(void *telephony_device, cme_error_t err)
 {
 	return telephony_generic_rsp(telephony_device, err);
 }
 
-static int dial_number(struct audio_device *device, const char *buf)
-{
-	char number[BUF_SIZE];
-	size_t buf_len;
-
-	buf_len = strlen(buf);
-
-	if (buf[buf_len - 1] != ';') {
-		DBG("Rejecting non-voice call dial request");
-		return -EINVAL;
-	}
-
-	memset(number, 0, sizeof(number));
-	strncpy(number, &buf[3], buf_len - 4);
-
-	telephony_dial_number_req(device, number);
-
-	return 0;
-}
-
 static int headset_set_gain(struct audio_device *device, uint16_t gain, char type)
 {
 	struct headset *hs = device->headset;
@@ -994,111 +688,21 @@ static int headset_set_gain(struct audio_device *device, uint16_t gain, char typ
 	return 0;
 }
 
-static int signal_gain_setting(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-	dbus_uint16_t gain;
-	int err;
-
-	if (strlen(buf) < 8) {
-		error("Too short string for Gain setting");
-		return -EINVAL;
-	}
-
-	gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10);
-
-	err = headset_set_gain(device, gain, buf[5]);
-	if (err < 0 && err != -EALREADY)
-		return err;
-
-	return headset_send(hs, "\r\nOK\r\n");
-}
-
 int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err)
 {
 	return telephony_generic_rsp(telephony_device, err);
 }
 
-static int dtmf_tone(struct audio_device *device, const char *buf)
-{
-	char tone;
-
-	if (strlen(buf) < 8) {
-		error("Too short string for DTMF tone");
-		return -EINVAL;
-	}
-
-	tone = buf[7];
-	if (tone >= '#' && tone <= 'D')
-		telephony_transmit_dtmf_req(device, tone);
-	else
-		return -EINVAL;
-
-	return 0;
-}
-
 int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err)
 {
 	return telephony_generic_rsp(telephony_device, err);
 }
 
-static int subscriber_number(struct audio_device *device, const char *buf)
-{
-	telephony_subscriber_number_req(device);
-
-	return 0;
-}
-
 int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err)
 {
 	return telephony_generic_rsp(telephony_device, err);
 }
 
-static int list_current_calls(struct audio_device *device, const char *buf)
-{
-	telephony_list_current_calls_req(device);
-
-	return 0;
-}
-
-static int extended_errors(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-	struct headset_slc *slc = hs->slc;
-
-	if (strlen(buf) < 9)
-		return -EINVAL;
-
-	if (buf[8] == '1') {
-		slc->cme_enabled = TRUE;
-		DBG("CME errors enabled for headset %p", hs);
-	} else {
-		slc->cme_enabled = FALSE;
-		DBG("CME errors disabled for headset %p", hs);
-	}
-
-	return headset_send(hs, "\r\nOK\r\n");
-}
-
-static int call_waiting_notify(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-	struct headset_slc *slc = hs->slc;
-
-	if (strlen(buf) < 9)
-		return -EINVAL;
-
-	if (buf[8] == '1') {
-		slc->cwa_enabled = TRUE;
-		DBG("Call waiting notification enabled for headset %p", hs);
-	} else {
-		slc->cwa_enabled = FALSE;
-		DBG("Call waiting notification disabled for headset %p", hs);
-	}
-
-	return headset_send(hs, "\r\nOK\r\n");
-}
-
 int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err)
 {
 	return telephony_generic_rsp(telephony_device, err);
@@ -1146,108 +750,6 @@ int telephony_operator_selection_ind(int mode, const char *oper)
 	return 0;
 }
 
-static int operator_selection(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-
-	if (strlen(buf) < 8)
-		return -EINVAL;
-
-	switch (buf[7]) {
-	case '?':
-		telephony_operator_selection_req(device);
-		break;
-	case '=':
-		return headset_send(hs, "\r\nOK\r\n");
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int nr_and_ec(struct audio_device *device, const char *buf)
-{
-	struct headset *hs = device->headset;
-	struct headset_slc *slc = hs->slc;
-
-	if (strlen(buf) < 9)
-		return -EINVAL;
-
-	if (buf[8] == '0')
-		slc->nrec_req = FALSE;
-	else
-		slc->nrec_req = TRUE;
-
-	telephony_nr_and_ec_req(device, slc->nrec_req);
-
-	return 0;
-}
-
-static int voice_dial(struct audio_device *device, const char *buf)
-{
-	gboolean enable;
-
-	if (strlen(buf) < 9)
-		return -EINVAL;
-
-	if (buf[8] == '0')
-		enable = FALSE;
-	else
-		enable = TRUE;
-
-	telephony_voice_dial_req(device, enable);
-
-	return 0;
-}
-
-static int apple_command(struct audio_device *device, const char *buf)
-{
-	DBG("Got Apple command: %s", buf);
-
-	return telephony_generic_rsp(device, CME_ERROR_NONE);
-}
-
-static struct event event_callbacks[] = {
-	{ "ATA", answer_call },
-	{ "ATD", dial_number },
-	{ "AT+VG", signal_gain_setting },
-	{ "AT+BRSF", supported_features },
-	{ "AT+CIND", report_indicators },
-	{ "AT+CMER", event_reporting },
-	{ "AT+CHLD", call_hold },
-	{ "AT+CHUP", terminate_call },
-	{ "AT+CKPD", key_press },
-	{ "AT+CLIP", cli_notification },
-	{ "AT+BTRH", response_and_hold },
-	{ "AT+BLDN", last_dialed_number },
-	{ "AT+VTS", dtmf_tone },
-	{ "AT+CNUM", subscriber_number },
-	{ "AT+CLCC", list_current_calls },
-	{ "AT+CMEE", extended_errors },
-	{ "AT+CCWA", call_waiting_notify },
-	{ "AT+COPS", operator_selection },
-	{ "AT+NREC", nr_and_ec },
-	{ "AT+BVRA", voice_dial },
-	{ "AT+XAPL", apple_command },
-	{ "AT+IPHONEACCEV", apple_command },
-	{ 0 }
-};
-
-static int handle_event(struct audio_device *device, const char *buf)
-{
-	struct event *ev;
-
-	DBG("Received %s", buf);
-
-	for (ev = event_callbacks; ev->cmd; ev++) {
-		if (!strncmp(buf, ev->cmd, strlen(ev->cmd)))
-			return ev->callback(device, buf);
-	}
-
-	return -EINVAL;
-}
-
 static void close_sco(struct audio_device *device)
 {
 	struct headset *hs = device->headset;
@@ -1266,94 +768,6 @@ static void close_sco(struct audio_device *device)
 	}
 }
 
-static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond,
-				struct audio_device *device)
-{
-	struct headset *hs;
-	struct headset_slc *slc;
-	unsigned char buf[BUF_SIZE];
-	ssize_t bytes_read;
-	size_t free_space;
-	int fd;
-
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	hs = device->headset;
-	slc = hs->slc;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		DBG("ERR or HUP on RFCOMM socket");
-		goto failed;
-	}
-
-	fd = g_io_channel_unix_get_fd(chan);
-
-	bytes_read = read(fd, buf, sizeof(buf) - 1);
-	if (bytes_read < 0)
-		return TRUE;
-
-	free_space = sizeof(slc->buf) - slc->data_start -
-			slc->data_length - 1;
-
-	if (free_space < (size_t) bytes_read) {
-		/* Very likely that the HS is sending us garbage so
-		 * just ignore the data and disconnect */
-		error("Too much data to fit incomming buffer");
-		goto failed;
-	}
-
-	memcpy(&slc->buf[slc->data_start], buf, bytes_read);
-	slc->data_length += bytes_read;
-
-	/* Make sure the data is null terminated so we can use string
-	 * functions */
-	slc->buf[slc->data_start + slc->data_length] = '\0';
-
-	while (slc->data_length > 0) {
-		char *cr;
-		int err;
-		off_t cmd_len;
-
-		cr = strchr(&slc->buf[slc->data_start], '\r');
-		if (!cr)
-			break;
-
-		cmd_len = 1 + (off_t) cr - (off_t) &slc->buf[slc->data_start];
-		*cr = '\0';
-
-		if (cmd_len > 1)
-			err = handle_event(device, &slc->buf[slc->data_start]);
-		else
-			/* Silently skip empty commands */
-			err = 0;
-
-		if (err == -EINVAL) {
-			error("Badly formated or unrecognized command: %s",
-					&slc->buf[slc->data_start]);
-			err = headset_send(hs, "\r\nERROR\r\n");
-			if (err < 0)
-				goto failed;
-		} else if (err < 0)
-			error("Error handling command %s: %s (%d)",
-						&slc->buf[slc->data_start],
-						strerror(-err), -err);
-
-		slc->data_start += cmd_len;
-		slc->data_length -= cmd_len;
-
-		if (!slc->data_length)
-			slc->data_start = 0;
-	}
-
-	return TRUE;
-
-failed:
-	headset_set_state(device, HEADSET_STATE_DISCONNECTED);
-
-	return FALSE;
-}
-
 static gboolean sco_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *device)
 {
@@ -1381,7 +795,7 @@ void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 	}
 
 	/* For HFP telephony isn't ready just disconnect */
-	if (hs->hfp_active && !ag.telephony_ready) {
+	if (hs->hfp_active && !telephony_get_ready_state()) {
 		error("Unable to accept HFP connection since the telephony "
 				"subsystem isn't initialized");
 		goto failed;
@@ -1397,8 +811,7 @@ void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 	else
 		hs->auto_dc = FALSE;
 
-	g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,
-			(GIOFunc) rfcomm_io_cb, dev);
+	hs->slc = telephony_device_connecting(chan, dev);
 
 	DBG("%s: Connected to %s", dev->path, hs_address);
 
@@ -1740,7 +1153,7 @@ static DBusMessage *hs_connect(DBusConnection *conn, DBusMessage *msg,
 	else if (hs->state > HEADSET_STATE_CONNECTING)
 		return btd_error_already_connected(msg);
 
-	if (hs->hfp_handle && !ag.telephony_ready)
+	if (hs->hfp_handle && !telephony_get_ready_state())
 		return btd_error_not_ready(msg);
 
 	device->auto_connect = FALSE;
@@ -2245,7 +1658,7 @@ uint32_t headset_config_init(GKeyFile *config)
 
 	/* Use the default values if there is no config file */
 	if (config == NULL)
-		return ag.features;
+		return telephony_get_ag_features();
 
 	str = g_key_file_get_string(config, "General", "SCORouting",
 					&err);
@@ -2275,7 +1688,7 @@ uint32_t headset_config_init(GKeyFile *config)
 		g_free(str);
 	}
 
-	return ag.features;
+	return telephony_get_ag_features();
 }
 
 static gboolean hs_dc_timeout(struct audio_device *dev)
@@ -2518,6 +1931,10 @@ void headset_set_state(struct audio_device *dev, headset_state_t state)
 	case HEADSET_STATE_DISCONNECTED:
 		value = FALSE;
 		close_sco(dev);
+
+		if (dev->headset->slc)
+			telephony_device_disconnect(dev->headset->slc);
+
 		headset_close_rfcomm(dev);
 		emit_property_changed(dev->conn, dev->path,
 					AUDIO_HEADSET_INTERFACE, "State",
@@ -2546,7 +1963,8 @@ void headset_set_state(struct audio_device *dev, headset_state_t state)
 					AUDIO_HEADSET_INTERFACE, "State",
 					DBUS_TYPE_STRING, &state_str);
 		if (hs->state < state) {
-			if (ag.features & AG_FEATURE_INBAND_RINGTONE)
+			if (telephony_get_ag_features() &
+					AG_FEATURE_INBAND_RINGTONE)
 				slc->inband_ring = TRUE;
 			else
 				slc->inband_ring = FALSE;
@@ -2880,15 +2298,13 @@ int telephony_ready_ind(uint32_t features,
 			const struct indicator *indicators, int rh,
 			const char *chld)
 {
-	ag.telephony_ready = TRUE;
-	ag.features = features;
 	ag.indicators = indicators;
 	ag.rh = rh;
 	ag.chld = chld;
 
 	DBG("Telephony plugin initialized");
 
-	print_ag_features(ag.features);
+	print_ag_features(telephony_get_ag_features());
 
 	return 0;
 }
diff --git a/audio/headset.h b/audio/headset.h
index 99eeca8..d43952f 100644
--- a/audio/headset.h
+++ b/audio/headset.h
@@ -111,3 +111,5 @@ gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock);
 gboolean headset_suspend(struct audio_device *dev, void *data);
 gboolean headset_play(struct audio_device *dev, void *data);
 void headset_shutdown(struct audio_device *dev);
+
+void headset_slc_complete(struct audio_device *dev);
diff --git a/audio/manager.c b/audio/manager.c
index 8de5515..4624552 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -880,7 +880,7 @@ static void state_changed(struct btd_adapter *adapter, gboolean powered)
 		/* telephony driver already initialized*/
 		if (telephony == TRUE)
 			return;
-		telephony_init();
+		telephony_init(adapter);
 		telephony = TRUE;
 		return;
 	}
@@ -896,7 +896,7 @@ static void state_changed(struct btd_adapter *adapter, gboolean powered)
 			return;
 	}
 
-	telephony_exit();
+	telephony_exit(adapter);
 	telephony = FALSE;
 }
 
diff --git a/audio/telephony.c b/audio/telephony.c
new file mode 100644
index 0000000..0f2e066
--- /dev/null
+++ b/audio/telephony.c
@@ -0,0 +1,494 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@xxxxxxxxxxxx>
+ *  Copyright (C) 2011  Frederic Danis <frederic.danis@xxxxxxxxx>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "btio.h"
+#include "log.h"
+#include "device.h"
+#include "error.h"
+#include "glib-helper.h"
+#include "sdp-client.h"
+#include "headset.h"
+#include "telephony.h"
+#include "dbus-common.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#define AUDIO_TELEPHONY_INTERFACE "org.bluez.Telephony"
+
+struct telsrv {
+	GSList *servers;	/* server list */
+};
+
+struct tel_device {
+	struct tel_agent	*agent;
+	struct audio_device	*au_dev;
+	GIOChannel		*rfcomm;
+	uint16_t		version;
+	uint16_t		features;
+};
+
+struct tel_agent {
+	char		*name;		/* agent DBus bus id */
+	char		*path;		/* agent object path */
+	const char	*uuid;		/* agent property UUID */
+	uint16_t	version;
+	uint8_t		features;
+	uint16_t	r_class;
+	uint16_t	r_profile;
+};
+
+static DBusConnection *connection = NULL;
+
+struct telsrv telsrv;
+
+static struct tel_agent *find_agent(const char *sender, const char *path,
+						const char *uuid)
+{
+	GSList *l;
+
+	for (l = telsrv.servers; l; l = l->next) {
+		struct tel_agent *agent = l->data;
+
+		if (sender && g_strcmp0(agent->name, sender) != 0)
+			continue;
+
+		if (path && g_strcmp0(agent->path, path) != 0)
+			continue;
+
+		if (uuid && g_strcmp0(agent->uuid, uuid) != 0)
+			continue;
+
+		return agent;
+	}
+
+	return NULL;
+}
+
+static int parse_properties(DBusMessageIter *props, const char **uuid,
+				uint16_t *version, uint8_t *features)
+{
+	gboolean has_uuid = FALSE;
+
+	while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+		const char *key;
+		DBusMessageIter value, entry;
+		int var;
+
+		dbus_message_iter_recurse(props, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		var = dbus_message_iter_get_arg_type(&value);
+		if (strcasecmp(key, "UUID") == 0) {
+			if (var != DBUS_TYPE_STRING)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, uuid);
+			has_uuid = TRUE;
+		} else if (strcasecmp(key, "Version") == 0) {
+			if (var != DBUS_TYPE_UINT16)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, version);
+		} else if (strcasecmp(key, "Features") == 0) {
+			if (var != DBUS_TYPE_BYTE)
+				return -EINVAL;
+			dbus_message_iter_get_basic(&value, features);
+		}
+
+		dbus_message_iter_next(props);
+	}
+
+	return (has_uuid) ? 0 : -EINVAL;
+}
+
+static int dev_close(struct tel_device *dev)
+{
+	int sock;
+
+	if (dev->rfcomm) {
+		sock = g_io_channel_unix_get_fd(dev->rfcomm);
+		shutdown(sock, SHUT_RDWR);
+	}
+
+	return 0;
+}
+
+static gboolean agent_sendfd(struct tel_device *dev, int fd,
+				DBusPendingCallNotifyFunction notify)
+{
+	struct tel_agent *agent = dev->agent;
+	DBusMessage *msg;
+	DBusMessageIter iter, dict;
+	char *str;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.TelephonyAgent", "NewConnection");
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	str = g_strdup(agent->uuid);
+	dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &str);
+	g_free(str);
+
+	dict_append_entry(&dict, "Version", DBUS_TYPE_UINT16, &dev->version);
+
+	if (dev->features != 0xFFFF)
+		dict_append_entry(&dict, "Features", DBUS_TYPE_BYTE,
+							&dev->features);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	if (dbus_connection_send_with_reply(connection, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, dev, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return TRUE;
+}
+
+static gboolean agent_disconnect_cb(GIOChannel *chan, GIOCondition cond,
+						struct tel_device *dev)
+{
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	headset_set_state(dev->au_dev, HEADSET_STATE_DISCONNECTED);
+
+	return FALSE;
+}
+
+static void newconnection_reply(DBusPendingCall *call, void *user_data)
+{
+	struct tel_device *dev = user_data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!dev->rfcomm) {
+		DBG("RFCOMM disconnected from server before agent reply");
+		goto done;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		DBG("Agent reply: file descriptor passed successfully");
+		g_io_add_watch(dev->rfcomm, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+					(GIOFunc) agent_disconnect_cb, dev);
+		headset_slc_complete(dev->au_dev);
+		goto done;
+	}
+
+	DBG("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	dev_close(dev);
+	headset_set_state(dev->au_dev, HEADSET_STATE_DISCONNECTED);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+	struct tel_device *dev = user_data;
+	sdp_data_t *sdpdata;
+	uuid_t uuid;
+	sdp_list_t *profiles;
+	sdp_profile_desc_t *desc;
+	int sk, ret;
+
+	if (err < 0) {
+		error("Unable to get service record: %s (%d)", strerror(-err),
+					-err);
+		goto failed;
+	}
+
+	if (!recs || !recs->data) {
+		error("No records found");
+		goto failed;
+	}
+
+	sdpdata = sdp_data_get(recs->data, SDP_ATTR_SUPPORTED_FEATURES);
+	if (sdpdata && sdpdata->dtd == SDP_UINT16)
+		dev->features = sdpdata->val.uint16;
+
+	sdp_uuid16_create(&uuid, dev->agent->r_profile);
+
+	sdp_get_profile_descs(recs->data, &profiles);
+	if (profiles == NULL)
+		goto failed;
+
+	desc = profiles->data;
+
+	if (sdp_uuid16_cmp(&desc->uuid, &uuid) == 0)
+		dev->version = desc->version;
+
+	sdp_list_free(profiles, free);
+
+	sk = g_io_channel_unix_get_fd(dev->rfcomm);
+
+	ret = agent_sendfd(dev, sk, newconnection_reply);
+
+	return;
+
+failed:
+	headset_set_state(dev->au_dev, HEADSET_STATE_DISCONNECTED);
+}
+
+void *telephony_device_connecting(GIOChannel *io, void *telephony_device)
+{
+	struct audio_device *device = telephony_device;
+	struct tel_device *dev;
+	const char *agent_uuid;
+	struct tel_agent *agent;
+	uuid_t uuid;
+	int err;
+
+	/*TODO: check for HS roles */
+	if (headset_get_hfp_active(device))
+		agent_uuid = HFP_AG_UUID;
+	else
+		agent_uuid = HSP_AG_UUID;
+
+	agent = find_agent(NULL, NULL, agent_uuid);
+	if (agent == NULL) {
+		error("No agent registered for %s", agent_uuid);
+		return NULL;
+	}
+
+	dev = g_new0(struct tel_device, 1);
+	dev->agent = agent;
+	dev->au_dev = telephony_device;
+	dev->rfcomm = io;
+	dev->features = 0xFFFF;
+
+	sdp_uuid16_create(&uuid, agent->r_class);
+
+	err = bt_search_service(&device->src, &device->dst, &uuid,
+						get_record_cb, dev, NULL);
+	if (err < 0) {
+		g_free(dev);
+		return NULL;
+	}
+
+	return dev;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+	DBG("telephony-dbus: device %p connected", telephony_device);
+}
+
+void telephony_device_disconnect(void *slc)
+{
+	struct tel_device *dev = slc;
+
+	dev_close(dev);
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+	DBG("telephony-dbus: device %p disconnected", telephony_device);
+}
+
+gboolean telephony_get_ready_state(void)
+{
+	return find_agent(NULL, NULL, HFP_AG_UUID) ? TRUE : FALSE;
+}
+
+uint32_t telephony_get_ag_features(void)
+{
+	return 0;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	DBusMessageIter args, props;
+	const char *sender, *path, *uuid;
+	uint16_t version = 0;
+	uint8_t features = 0;
+	uint16_t r_class, r_profile;
+	struct tel_agent *agent;
+
+	sender = dbus_message_get_sender(msg);
+
+	dbus_message_iter_init(msg, &args);
+
+	dbus_message_iter_get_basic(&args, &path);
+	dbus_message_iter_next(&args);
+
+	if (find_agent(sender, path, NULL) != NULL)
+		return btd_error_already_exists(msg);
+
+	dbus_message_iter_recurse(&args, &props);
+	if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+		return btd_error_invalid_args(msg);
+
+	if (parse_properties(&props, &uuid, &version, &features) < 0)
+		return btd_error_invalid_args(msg);
+
+	if (strcasecmp(uuid, HSP_HS_UUID) == 0) {
+		r_class = HEADSET_AGW_SVCLASS_ID;
+		r_profile = HEADSET_PROFILE_ID;
+	} else if (strcasecmp(uuid, HSP_AG_UUID) == 0) {
+		r_class = HEADSET_SVCLASS_ID;
+		r_profile = HEADSET_PROFILE_ID;
+	} else if (strcasecmp(uuid, HFP_HS_UUID) == 0) {
+		r_class = HANDSFREE_AGW_SVCLASS_ID;
+		r_profile = HANDSFREE_PROFILE_ID;
+	} else if (strcasecmp(uuid, HFP_AG_UUID) == 0) {
+		r_class = HANDSFREE_SVCLASS_ID;
+		r_profile = HANDSFREE_PROFILE_ID;
+	} else
+		return btd_error_invalid_args(msg);
+
+	if (find_agent(NULL, NULL, uuid) != NULL)
+		return btd_error_already_exists(msg);
+
+	DBG("Register agent : %s%s for %s version 0x%04X with features 0x%02X",
+					sender, path, uuid, version, features);
+
+	agent = g_new0(struct tel_agent, 1);
+	agent->name = g_strdup(sender);
+	agent->path = g_strdup(path);
+	agent->uuid = g_strdup(uuid);
+	agent->version = version;
+	agent->features = features;
+	agent->r_class = r_class;
+	agent->r_profile = r_profile;
+
+	telsrv.servers = g_slist_append(telsrv.servers, agent);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	const char *sender, *path;
+	struct tel_agent *agent;
+
+	if (!dbus_message_get_args(msg, NULL,
+				DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID))
+		return NULL;
+
+	sender = dbus_message_get_sender(msg);
+
+	agent = find_agent(sender, path, NULL);
+	if (agent == NULL)
+		return btd_error_does_not_exist(msg);
+
+	telsrv.servers = g_slist_remove(telsrv.servers, agent);
+
+	DBG("Unregister agent : %s%s", sender, path);
+
+	g_free(agent);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable telsrv_methods[] = {
+	{ "RegisterAgent", "oa{sv}", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
+	{ NULL, NULL, NULL, NULL }
+};
+
+static void path_unregister(void *data)
+{
+	DBG("Unregistered interface %s", AUDIO_TELEPHONY_INTERFACE);
+}
+
+static int register_interface(void *adapter)
+{
+	const char *path;
+
+	if (DBUS_TYPE_UNIX_FD < 0)
+		return -1;
+
+	path = adapter_get_path(adapter);
+
+	if (!g_dbus_register_interface(connection, path,
+					AUDIO_TELEPHONY_INTERFACE,
+					telsrv_methods, NULL,
+					NULL, NULL, path_unregister)) {
+		error("D-Bus failed to register %s interface",
+				AUDIO_TELEPHONY_INTERFACE);
+		return -1;
+	}
+
+	DBG("Registered interface %s", AUDIO_TELEPHONY_INTERFACE);
+
+	return 0;
+}
+
+static void unregister_interface(void *adapter)
+{
+	g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+			AUDIO_TELEPHONY_INTERFACE);
+}
+
+int telephony_init(void *adapter)
+{
+	DBG("");
+
+	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+	return register_interface(adapter);
+}
+
+void telephony_exit(void *adapter)
+{
+	DBG("");
+
+	unregister_interface(adapter);
+
+	dbus_connection_unref(connection);
+	connection = NULL;
+}
diff --git a/audio/telephony.h b/audio/telephony.h
index 73b390c..7d1d337 100644
--- a/audio/telephony.h
+++ b/audio/telephony.h
@@ -144,26 +144,13 @@ struct indicator {
 /* Notify telephony-*.c of connected/disconnected devices. Implemented by
  * telephony-*.c
  */
+void *telephony_device_connecting(GIOChannel *io, void *telephony_device);
 void telephony_device_connected(void *telephony_device);
+void telephony_device_disconnect(void *slc);
 void telephony_device_disconnected(void *telephony_device);
 
-/* HF requests (sent by the handsfree device). These are implemented by
- * telephony-*.c
- */
-void telephony_event_reporting_req(void *telephony_device, int ind);
-void telephony_response_and_hold_req(void *telephony_device, int rh);
-void telephony_last_dialed_number_req(void *telephony_device);
-void telephony_terminate_call_req(void *telephony_device);
-void telephony_answer_call_req(void *telephony_device);
-void telephony_dial_number_req(void *telephony_device, const char *number);
-void telephony_transmit_dtmf_req(void *telephony_device, char tone);
-void telephony_subscriber_number_req(void *telephony_device);
-void telephony_list_current_calls_req(void *telephony_device);
-void telephony_operator_selection_req(void *telephony_device);
-void telephony_call_hold_req(void *telephony_device, const char *cmd);
-void telephony_nr_and_ec_req(void *telephony_device, gboolean enable);
-void telephony_voice_dial_req(void *telephony_device, gboolean enable);
-void telephony_key_press_req(void *telephony_device, const char *keys);
+gboolean telephony_get_ready_state(void);
+uint32_t telephony_get_ag_features(void);
 
 /* AG responses to HF requests. These are implemented by headset.c */
 int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err);
@@ -240,5 +227,5 @@ static inline int telephony_get_indicator(const struct indicator *indicators,
 	return -ENOENT;
 }
 
-int telephony_init(void);
-void telephony_exit(void);
+int telephony_init(void *adapter);
+void telephony_exit(void *adapter);
diff --git a/doc/audio-api.txt b/doc/audio-api.txt
index b85400b..d32b51d 100644
--- a/doc/audio-api.txt
+++ b/doc/audio-api.txt
@@ -456,3 +456,102 @@ properties	boolean Connected [readonly]
 		uint16 MicrophoneGain  [readonly]
 
 			The speaker gain when available.
+
+
+Telephony hierarchy [experiemental]
+===================
+
+Service		org.bluez
+Interface	org.bluez.Telephony
+Object path	[variable prefix]/{hci0,hci1,...}
+
+Methods		void RegisterAgent(object path, dict properties)
+
+			Register a TelephonyAgent to sender, the sender can
+			register as many agents as it likes.
+
+			Note: If the sender disconnects its agents are
+			automatically unregistered.
+
+			possible properties:
+
+				string UUID:
+
+					UUID of the profile which the agent is
+					for.
+
+				uint16 Version:
+
+					Version of the profile which the agent
+					implements.
+
+				byte Features:
+
+					Agent supported features as defined in
+					profile spec e.g. HFP.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+
+
+		void UnregisterAgent(object path)
+
+			Unregister sender agent.
+
+TelephonyAgent hierarchy
+========================
+
+Service		unique name
+Interface	org.bluez.TelephonyAgent
+Object path	freely definable
+
+ethods		void NewConnection(filedescriptor fd, dict properties)
+
+			This method gets called whenever a new connection
+			has been established. This method assumes that DBus
+			daemon with file descriptor passing capability is
+			being used.
+
+			The agent should only return successfully once the
+			establishment of the service level connection (SLC)
+			has been completed.  In the case of Handsfree this
+			means that BRSF exchange has been performed and
+			necessary initialization has been done.
+
+			If Endpoint is set the agent is responsible to
+			create an object implementing org.bluez.MediaTransport
+			and notify the Endpoint using org.bluez.MediaEndpoint.
+
+			possible properties:
+
+				strict Device:
+
+					BlueZ remote device object.
+
+				string UUID:
+
+					Profile UUID of the connection.
+
+				uint16 Version:
+
+					Remote profile version.
+
+				byte Features:
+
+					Remote profile features.
+
+				string Endpoint:
+
+					Optional. Endpoint bus id.
+
+				string EndpointPath:
+
+					Optional. Endpoint object path.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+		void Release()
+
+			This method gets called whenever the service daemon
+			unregisters the agent or whenever the Adapter where
+			the TelephonyAgent registers itself is removed.
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux