⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 headset.c

📁 BlueZ源码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * *  BlueZ - Bluetooth protocol stack for Linux * *  Copyright (C) 2006-2007  Nokia Corporation *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org> * * *  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 <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <stdarg.h>#include <signal.h>#include <string.h>#include <getopt.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <assert.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include <bluetooth/sco.h>#include <bluetooth/rfcomm.h>#include <bluetooth/sdp.h>#include <bluetooth/sdp_lib.h>#include <glib.h>#include <dbus/dbus.h>#include <gdbus.h>#include "logging.h"#include "device.h"#include "manager.h"#include "error.h"#include "telephony.h"#include "headset.h"#include "glib-helper.h"#include "dbus-common.h"#include "../src/adapter.h"#include "../src/device.h"#define DC_TIMEOUT 3#define RING_INTERVAL 3#define BUF_SIZE 1024#define HEADSET_GAIN_SPEAKER 'S'#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 */	int rh;				/* Response and Hold state */	char *number;			/* Incoming phone number */	int number_type;		/* Incoming number type */	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 = -1,	.number = NULL,	.number_type = 0,	.ring_timer = 0,};static gboolean sco_hci = TRUE;static GSList *active_devices = NULL;static char *str_state[] = {	"HEADSET_STATE_DISCONNECTED",	"HEADSET_STATE_CONNECT_IN_PROGRESS",	"HEADSET_STATE_CONNECTED",	"HEADSET_STATE_PLAY_IN_PROGRESS",	"HEADSET_STATE_PLAYING",};struct connect_cb {	unsigned int id;	headset_stream_cb_t cb;	void *cb_data;};struct pending_connect {	DBusMessage *msg;	DBusPendingCall *call;	GIOChannel *io;	int err;	headset_state_t target_state;	GSList *callbacks;};struct headset {	uint32_t hsp_handle;	uint32_t hfp_handle;	int rfcomm_ch;	GIOChannel *rfcomm;	GIOChannel *tmp_rfcomm;	GIOChannel *sco;	guint sco_id;	gboolean auto_dc;	guint dc_timer;	char buf[BUF_SIZE];	int data_start;	int data_length;	gboolean hfp_active;	gboolean search_hfp;	gboolean cli_active;	gboolean cme_enabled;	gboolean cwa_enabled;	gboolean pending_ring;	headset_state_t state;	struct pending_connect *pending;	int sp_gain;	int mic_gain;	unsigned int hf_features;	headset_lock_t lock;};struct event {	const char *cmd;	int (*callback) (struct audio_device *device, const char *buf);};static inline DBusMessage *invalid_args(DBusMessage *msg){	return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",			"Invalid arguments in method call");}static DBusHandlerResult error_not_supported(DBusConnection *conn,							DBusMessage *msg){	return error_common_reply(conn, msg, ERROR_INTERFACE ".NotSupported",							"Not supported");}static DBusHandlerResult error_connection_attempt_failed(DBusConnection *conn,						DBusMessage *msg, int err){	return error_common_reply(conn, msg,			ERROR_INTERFACE ".ConnectionAttemptFailed",			err > 0 ? strerror(err) : "Connection attempt failed");}static int rfcomm_connect(struct audio_device *device, headset_stream_cb_t cb,				void *user_data, unsigned int *cb_id);static int get_records(struct audio_device *device, headset_stream_cb_t cb,			void *user_data, unsigned int *cb_id);static void print_ag_features(uint32_t features){	GString *gstr;	char *str;	if (features == 0) {		debug("HFP AG features: (none)");		return;	}	gstr = g_string_new("HFP AG features: ");	if (features & AG_FEATURE_THREE_WAY_CALLING)		g_string_append(gstr, "\"Three-way calling\" ");	if (features & AG_FEATURE_EC_ANDOR_NR)		g_string_append(gstr, "\"EC and/or NR function\" ");	if (features & AG_FEATURE_VOICE_RECOGNITION)		g_string_append(gstr, "\"Voice recognition function\" ");	if (features & AG_FEATURE_INBAND_RINGTONE)		g_string_append(gstr, "\"In-band ring tone capability\" ");	if (features & AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG)		g_string_append(gstr, "\"Attach a number to a voice tag\" ");	if (features & AG_FEATURE_REJECT_A_CALL)		g_string_append(gstr, "\"Ability to reject a call\" ");	if (features & AG_FEATURE_ENHANCED_CALL_STATUS)		g_string_append(gstr, "\"Enhanced call status\" ");	if (features & AG_FEATURE_ENHANCED_CALL_CONTROL)		g_string_append(gstr, "\"Enhanced call control\" ");	if (features & AG_FEATURE_EXTENDED_ERROR_RESULT_CODES)		g_string_append(gstr, "\"Extended Error Result Codes\" ");	str = g_string_free(gstr, FALSE);	debug("%s", str);	g_free(str);}static void print_hf_features(uint32_t features){	GString *gstr;	char *str;	if (features == 0) {		debug("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);	debug("%s", str);	g_free(str);}static int headset_send_valist(struct headset *hs, char *format, va_list ap){	char rsp[BUF_SIZE];	ssize_t total_written, written, count;	int fd;	count = vsnprintf(rsp, sizeof(rsp), format, ap);	if (count < 0)		return -EINVAL;	if (!hs->rfcomm) {		error("headset_send: the headset is not connected");		return -EIO;	}	written = total_written = 0;	fd = g_io_channel_unix_get_fd(hs->rfcomm);	while (total_written < count) {		written = write(fd, rsp + total_written, count - total_written);		if (written < 0)			return -errno;		total_written += written;	}	return 0;}static int headset_send(struct headset *hs, char *format, ...){	va_list ap;	int ret;	va_start(ap, format);	ret = headset_send_valist(hs, format, ap);	va_end(ap);	return ret;}static int supported_features(struct audio_device *device, const char *buf){	struct headset *hs = device->headset;	int err;	if (strlen(buf) < 9)		return -EINVAL;	hs->hf_features = strtoul(&buf[8], NULL, 10);	print_hf_features(hs->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 (buf[7] == '=')		str = indicator_ranges(ag.indicators);	else		str = indicator_values(ag.indicators);	err = headset_send(hs, 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;	if (hs->pending->err)		cb->cb(NULL, cb->cb_data);	else		cb->cb(dev, cb->cb_data);}static void pending_connect_finalize(struct audio_device *dev){	struct headset *hs = dev->headset;	struct pending_connect *p = hs->pending;	g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev);	g_slist_foreach(p->callbacks, (GFunc) g_free, NULL);	g_slist_free(p->callbacks);	if (p->io) {		g_io_channel_close(p->io);		g_io_channel_unref(p->io);	}	if (p->msg)		dbus_message_unref(p->msg);	if (p->call) {		dbus_pending_call_cancel(p->call);		dbus_pending_call_unref(p->call);	}	g_free(p);	hs->pending = NULL;}static void pending_connect_init(struct headset *hs, headset_state_t target_state){	if (hs->pending) {		if (hs->pending->target_state < target_state)			hs->pending->target_state = target_state;		return;	}	hs->pending = g_new0(struct pending_connect, 1);	hs->pending->target_state = target_state;}static unsigned int connect_cb_new(struct headset *hs,					headset_state_t target_state,					headset_stream_cb_t func,					void *user_data){	struct connect_cb *cb;	unsigned int free_cb_id = 1;	pending_connect_init(hs, target_state);	cb = g_new(struct connect_cb, 1);	cb->cb = func;	cb->cb_data = user_data;	cb->id = free_cb_id++;	hs->pending->callbacks = g_slist_append(hs->pending->callbacks,						cb);	return cb->id;}static void send_foreach_headset(GSList *devices,					int (*cmp) (struct headset *hs),					char *format, ...){	GSList *l;	va_list ap;	for (l = devices; l != NULL; l = l->next) {		struct audio_device *device = l->data;		struct headset *hs = device->headset;		int ret;		assert(hs != NULL);		if (cmp && cmp(hs) != 0)			continue;		va_start(ap, format);		ret = headset_send_valist(hs, format, ap);		if (ret < 0)			error("Failed to send to headset: %s (%d)",					strerror(-ret), -ret);		va_end(ap);	}}static int cli_cmp(struct headset *hs){	if (!hs->hfp_active)		return -1;	if (hs->cli_active)		return 0;	else		return -1;}static gboolean ring_timer_cb(gpointer data){	send_foreach_headset(active_devices, NULL, "\r\nRING\r\n");	if (ag.number)		send_foreach_headset(active_devices, cli_cmp,					"\r\n+CLIP: \"%s\",%d\r\n",					ag.number, ag.number_type);	return TRUE;}static void sco_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src,			const bdaddr_t *dst, gpointer user_data){	int sk;	struct audio_device *dev = user_data;	struct headset *hs = dev->headset;	struct pending_connect *p = hs->pending;	if (err < 0) {		error("connect(): %s (%d)", strerror(-err), -err);		if (p->msg)			error_connection_attempt_failed(dev->conn, p->msg, p->err);		pending_connect_finalize(dev);		if (hs->rfcomm)			headset_set_state(dev, HEADSET_STATE_CONNECTED);		else			headset_set_state(dev, HEADSET_STATE_DISCONNECTED);		return;	}	debug("SCO socket opened for headset %s", dev->path);	sk = g_io_channel_unix_get_fd(chan);	debug("SCO fd=%d", sk);	hs->sco = chan;	p->io = NULL;	if (p->msg) {		DBusMessage *reply = dbus_message_new_method_return(p->msg);		g_dbus_send_message(dev->conn, reply);	}	pending_connect_finalize(dev);	fcntl(sk, F_SETFL, 0);	headset_set_state(dev, HEADSET_STATE_PLAYING);	if (hs->pending_ring) {		ring_timer_cb(NULL);		ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL,						ring_timer_cb,						NULL);		hs->pending_ring = FALSE;	}}static int sco_connect(struct audio_device *dev, headset_stream_cb_t cb,			void *user_data, unsigned int *cb_id){	struct headset *hs = dev->headset;	int err;	if (hs->state != HEADSET_STATE_CONNECTED)		return -EINVAL;	err = bt_sco_connect(&dev->src, &dev->dst, sco_connect_cb, dev);	if (err < 0) {		error("connect: %s (%d)", strerror(-err), -err);		return err;	}	headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS);	pending_connect_init(hs, HEADSET_STATE_PLAYING);	if (cb) {		unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING,							cb, user_data);		if (cb_id)			*cb_id = id;	}	return 0;}static int hfp_cmp(struct headset *hs){	if (hs->hfp_active)		return 0;	else		return -1;}static void hfp_slc_complete(struct audio_device *dev){	struct headset *hs = dev->headset;	struct pending_connect *p = hs->pending;	debug("HFP Service Level Connection established");	headset_set_state(dev, HEADSET_STATE_CONNECTED);	if (p == NULL)		return;	if (p->target_state == HEADSET_STATE_CONNECTED) {		if (p->msg) {			DBusMessage *reply = dbus_message_new_method_return(p->msg);			g_dbus_send_message(dev->conn, reply);		}		pending_connect_finalize(dev);		return;	}	p->err = sco_connect(dev, NULL, NULL, NULL);	if (p->err < 0) {		if (p->msg)			error_connection_attempt_failed(dev->conn, p->msg, p->err);		pending_connect_finalize(dev);	}}static int telephony_generic_rsp(struct audio_device *device, cme_error_t err){	struct headset *hs = device->headset;	if (err != CME_ERROR_NONE) {		if (hs->cme_enabled)			return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);		else			return headset_send(hs, "\r\nERROR\r\n");	}	return headset_send(hs, "\r\nOK\r\n");}int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err){	struct audio_device *device = telephony_device;	struct headset *hs = device->headset;	int ret;	if (err != CME_ERROR_NONE)		return telephony_generic_rsp(telephony_device, err);	ret = headset_send(hs, "\r\nOK\r\n");	if (ret < 0)		return ret;	if (hs->state != HEADSET_STATE_CONNECT_IN_PROGRESS)		return 0;	if (ag.features & AG_FEATURE_THREE_WAY_CALLING)		return 0;	hfp_slc_complete(device);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -