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

📄 headset.c

📁 Linux的蓝牙操作工具。配合bluez-lib使用
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * *  BlueZ - Bluetooth protocol stack for Linux * *  Copyright (C) 2006-2007  Nokia Corporation *  Copyright (C) 2004-2007  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 <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 "dbus.h"#include "dbus-helper.h"#include "logging.h"#include "device.h"#include "manager.h"#include "error.h"#include "headset.h"#define RING_INTERVAL 3000#define BUF_SIZE 1024#define HEADSET_GAIN_SPEAKER 'S'#define HEADSET_GAIN_MICROPHONE 'M'#define AG_FEATURE_THREE_WAY_CALLING             0x0001#define AG_FEATURE_EC_ANDOR_NR                   0x0002#define AG_FEATURE_VOICE_RECOGNITION             0x0004#define AG_FEATURE_INBAND_RINGTONE               0x0008#define AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG     0x0010#define AG_FEATURE_REJECT_A_CALL                 0x0020#define AG_FEATURE_ENHANCES_CALL_STATUS          0x0040#define AG_FEATURE_ENHANCES_CALL_CONTROL         0x0080/*Audio Gateway features.Default is In-band Ringtone*/static unsigned int ag_features = AG_FEATURE_INBAND_RINGTONE;static char *str_state[] = {	"HEADSET_STATE_DISCONNECTED",	"HEADSET_STATE_CONNECT_IN_PROGRESS",	"HEADSET_STATE_CONNECTED",	"HEADSET_STATE_PLAY_IN_PROGRESS",	"HEADSET_STATE_PLAYING",	};struct pending_connect {	DBusMessage *msg;	DBusPendingCall *call;	GIOChannel *io;	int sock;	int err;	unsigned int id;	headset_stream_cb_t cb;	void *cb_data;};struct headset {	uint32_t hsp_handle;	uint32_t hfp_handle;	int rfcomm_ch;	GIOChannel *rfcomm;	GIOChannel *sco;	guint sco_id;	guint ring_timer;	char buf[BUF_SIZE];	int data_start;	int data_length;	int enable_hfp;	headset_type_t type;	headset_state_t state;	GSList *pending;	int sp_gain;	int mic_gain;	unsigned int hfp_features;	headset_lock_t lock;};struct event {	const char *cmd;	int (*callback) (struct device *device, const char *buf);};static int rfcomm_connect(struct device *device, struct pending_connect *c);static int get_handles(struct device *device, struct pending_connect *c);static int headset_send(struct headset *hs, char *format, ...){	char rsp[BUF_SIZE];	va_list ap;	ssize_t total_written, written, count;	int fd;	va_start(ap, format);	count = vsnprintf(rsp, sizeof(rsp), format, ap);	va_end(ap);	if (count < 0)		return -EINVAL;	if (hs->state < HEADSET_STATE_CONNECTED || !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 supported_features(struct device *device, const char *buf){	struct headset *hs = device->headset;	int err;	hs->hfp_features = strtoul(&buf[8], NULL, 10);	err = headset_send(hs, "\r\n+BRSF=%u\r\n", ag_features);	if (err < 0)		return err;	return headset_send(device->headset, "\r\nOK\r\n");}static int report_indicators(struct device *device, const char *buf){	struct headset *hs = device->headset;	int err;	if (buf[7] == '=')		err = headset_send(hs, "\r\n+CIND:(\"service\",(0,1)),"				"(\"call\",(0,1)),(\"callsetup\",(0-3))\r\n");	else		err = headset_send(hs, "\r\n+CIND:1, 0, 0\r\n");	if (err < 0)		return err;	return headset_send(device->headset, "\r\nOK\r\n");}static int event_reporting(struct device *device, const char *buf){	return headset_send(device->headset, "\r\nOK\r\n");}static int call_hold(struct device *device, const char *buf){	struct headset *hs = device->headset;	int err;	err = headset_send(hs, "\r\n+CHLD:(0,1,1x,2,2x,3,4)\r\n");	if (err < 0)		return err;	return headset_send(device->headset, "\r\nOK\r\n");}static int answer_call(struct device *device, const char *buf){	dbus_connection_emit_signal(device->conn, device->path,			AUDIO_HEADSET_INTERFACE, "AnswerRequested",			DBUS_TYPE_INVALID);	return headset_send(device->headset, "\r\nOK\r\n");}static int terminate_call(struct device *device, const char *buf){	return headset_send(device->headset, "\r\nOK\r\n");}static int signal_gain_setting(struct device *device, const char *buf){	const char *name;	dbus_uint16_t gain;	if (strlen(buf) < 8) {		error("Too short string for Gain setting");		return -1;	}	gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10);	if (gain > 15) {		error("Invalid gain value received: %u", gain);		return -1;	}	switch (buf[5]) {	case HEADSET_GAIN_SPEAKER:		if (device->headset->sp_gain == gain)			goto ok;		name = "SpeakerGainChanged";		device->headset->sp_gain = gain;		break;	case HEADSET_GAIN_MICROPHONE:		if (device->headset->mic_gain == gain)			goto ok;		name = "MicrophoneGainChanged";		device->headset->mic_gain = gain;		break;	default:		error("Unknown gain setting");		return G_IO_ERROR_INVAL;	}	dbus_connection_emit_signal(device->conn, device->path,				    AUDIO_HEADSET_INTERFACE, name,				    DBUS_TYPE_UINT16, &gain,				    DBUS_TYPE_INVALID);ok:	return headset_send(device->headset, "\r\nOK\r\n");}static struct event event_callbacks[] = {	{"ATA", answer_call},	{"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", answer_call},	{0}};static GIOError handle_event(struct device *device, const char *buf){	struct event *pt;	debug("Received %s", buf);	for (pt = event_callbacks; pt->cmd; pt++) {		if (!strncmp(buf, pt->cmd, strlen(pt->cmd)))			return pt->callback(device, buf);	}	return -EINVAL;}static void pending_connect_free(struct pending_connect *c){	if (c->io) {		g_io_channel_close(c->io);		g_io_channel_unref(c->io);	}	if (c->msg)		dbus_message_unref(c->msg);	if (c->call) {		dbus_pending_call_cancel(c->call);		dbus_pending_call_unref(c->call);	}	g_free(c);}static void close_sco(struct device *device){	struct headset *hs = device->headset;	if (hs->sco) {		g_source_remove(hs->sco_id);		hs->sco_id = 0;		g_io_channel_close(hs->sco);		g_io_channel_unref(hs->sco);		hs->sco = NULL;	}}static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond,				struct device *device){	struct headset *hs;	unsigned char buf[BUF_SIZE];	char *cr;	gsize bytes_read = 0;	gsize free_space;	int err;	off_t cmd_len;	if (cond & G_IO_NVAL)		return FALSE;	hs = device->headset;	if (cond & (G_IO_ERR | G_IO_HUP))		goto failed;	err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1,				&bytes_read);	if (err != G_IO_ERROR_NONE)		return TRUE;	free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1;	if (free_space < 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(&hs->buf[hs->data_start], buf, bytes_read);	hs->data_length += bytes_read;	/* Make sure the data is null terminated so we can use string	 * functions */	hs->buf[hs->data_start + hs->data_length] = '\0';	cr = strchr(&hs->buf[hs->data_start], '\r');	if (!cr)		return TRUE;	cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start];	*cr = '\0';	err = handle_event(device, &hs->buf[hs->data_start]);	if (err < 0)		error("Error handling command %s: %s (%d)", &hs->buf[hs->data_start],		      strerror(-err), -err);	hs->data_start += cmd_len;	hs->data_length -= cmd_len;	if (!hs->data_length)		hs->data_start = 0;	return TRUE;failed:	headset_set_state(device, HEADSET_STATE_DISCONNECTED);	return FALSE;}static gboolean sco_cb(GIOChannel *chan, GIOCondition cond,			struct device *device){	struct headset *hs;	if (cond & G_IO_NVAL)		return FALSE;	hs = device->headset;	error("Audio connection got disconnected");	headset_set_state(device, HEADSET_STATE_CONNECTED);	return FALSE;}static void pending_connect_ok(struct pending_connect *c, struct device *dev){	struct headset *hs = dev->headset;	if (c->msg) {		DBusMessage *reply = dbus_message_new_method_return(c->msg);		if (reply)			send_message_and_unref(dev->conn, reply);	}	if (c->cb) {		if (hs->rfcomm && hs->sco)			c->cb(dev, c->cb_data);		else			c->cb(NULL, c->cb_data);	}	pending_connect_free(c);}static gboolean finalize_stream_setup(struct device *dev){	struct headset *hs = dev->headset;	g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, dev);	g_slist_free(hs->pending);	hs->pending = NULL;	return FALSE;}static void pending_connect_failed(struct pending_connect *c, struct device *dev){	if (c->msg)		error_connection_attempt_failed(dev->conn, c->msg, c->err);	if (c->cb)		c->cb(NULL, c->cb_data);	pending_connect_free(c);}static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond,				struct device *device){	struct headset *hs;	struct pending_connect *c;	int ret, sk;	socklen_t len;	if (cond & G_IO_NVAL)		return FALSE;	hs = device->headset;	c = hs->pending->data;	sk = g_io_channel_unix_get_fd(chan);	len = sizeof(ret);	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {		c->err = errno;		error("getsockopt(SO_ERROR): %s (%d)", strerror(c->err),				c->err);		goto failed;	}	if (ret != 0) {		c->err = ret;		error("connect(): %s (%d)", strerror(ret), ret);		goto failed;	}	debug("SCO socket opened for headset %s", device->path);	info("SCO fd=%d", sk);	hs->sco = chan;	c->io = NULL;	g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, device);	g_slist_free(hs->pending);	hs->pending = NULL;	fcntl(sk, F_SETFL, 0);	headset_set_state(device, HEADSET_STATE_PLAYING);	return FALSE;failed:	g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device);	g_slist_free(hs->pending);	hs->pending = NULL;	headset_set_state(device, HEADSET_STATE_CONNECTED);	return FALSE;}static int sco_connect(struct device *device, struct pending_connect *c){	struct headset *hs = device->headset;	struct sockaddr_sco addr;	gboolean do_callback = FALSE;	int sk, err;	if (!g_slist_find(hs->pending, c))		hs->pending = g_slist_append(hs->pending, c);	if (hs->state != HEADSET_STATE_CONNECTED)		return 0;	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);	if (sk < 0) {		err = errno;		error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err);		return -err;	}	c->io = g_io_channel_unix_new(sk);	if (!c->io) {		close(sk);		return -EINVAL;	}	memset(&addr, 0, sizeof(addr));	addr.sco_family = AF_BLUETOOTH;	bacpy(&addr.sco_bdaddr, BDADDR_ANY);	if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {		err = errno;		error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err);		return -err;	}	if (set_nonblocking(sk) < 0) {		err = errno;		return -err;	}	memset(&addr, 0, sizeof(addr));	addr.sco_family = AF_BLUETOOTH;	bacpy(&addr.sco_bdaddr, &device->dst);	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {		if (!(errno == EAGAIN || errno == EINPROGRESS)) {			err = errno;			error("connect: %s (%d)", strerror(errno), errno);			return -err;		}		g_io_add_watch(c->io,				G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP,				(GIOFunc) sco_connect_cb, device);	} else		do_callback = TRUE;	headset_set_state(device, HEADSET_STATE_PLAY_IN_PROGRESS);	if (!g_slist_find(hs->pending, c))		hs->pending = g_slist_append(hs->pending, c);	if (do_callback)		sco_connect_cb(c->io, G_IO_OUT, device);	return 0;}static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,					struct device *device){	struct headset *hs;	struct pending_connect *c;	char hs_address[18];	int sk, ret;

⌨️ 快捷键说明

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