?? headset.c
字號:
/* * * 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 <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#define AG_FEATURE_EXTENDED_ERROR_RESULT_CODES 0x0100static uint32_t ag_features = 0;static gboolean sco_hci = TRUE;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 ring_timer; char buf[BUF_SIZE]; int data_start; int data_length; gboolean hfp_active; gboolean search_hfp; gboolean cli_active; char *ph_number; int type; headset_state_t state; struct pending_connect *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, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id);static int get_handles(struct device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id);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->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; if (strlen(buf) < 9) return -EINVAL; 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(hs, "\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(hs, "\r\nOK\r\n");}static void pending_connect_complete(struct connect_cb *cb, struct 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 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 gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, struct device *device){ struct headset *hs; int ret, sk; socklen_t len; struct pending_connect *p; if (cond & G_IO_NVAL) return FALSE; hs = device->headset; p = hs->pending; sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { p->err = errno; error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), p->err); goto failed; } if (ret != 0) { p->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; p->io = NULL; pending_connect_finalize(device); fcntl(sk, F_SETFL, 0); headset_set_state(device, HEADSET_STATE_PLAYING); return FALSE;failed: pending_connect_finalize(device); if (hs->rfcomm) headset_set_state(device, HEADSET_STATE_CONNECTED); else headset_set_state(device, HEADSET_STATE_DISCONNECTED); return FALSE;}static int sco_connect(struct device *dev, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id){ struct headset *hs = dev->headset; struct sockaddr_sco addr; GIOChannel *io; int sk, err; if (hs->state != HEADSET_STATE_CONNECTED) return -EINVAL; sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sk < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); return -err; } io = g_io_channel_unix_new(sk); if (!io) { close(sk); return -ENOMEM; } 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); goto failed; } if (set_nonblocking(sk) < 0) { err = errno; goto failed; } memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, &dev->dst); err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { err = errno; error("connect: %s (%d)", strerror(errno), errno); goto failed; } 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; } g_io_add_watch(io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, (GIOFunc) sco_connect_cb, dev); hs->pending->io = io; return 0;failed: g_io_channel_close(io); g_io_channel_unref(io); return -err;}static void hfp_slc_complete(struct 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->msg) { DBusMessage *reply = dbus_message_new_method_return(p->msg); send_message_and_unref(dev->conn, reply); } if (p->target_state == HEADSET_STATE_CONNECTED) { pending_connect_finalize(dev); return; } p->err = sco_connect(dev, NULL, NULL, NULL); if (p->err < 0) pending_connect_finalize(dev);}static int event_reporting(struct device *dev, const char *buf){ struct headset *hs = dev->headset; int ret; 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(dev); return 0;}static int call_hold(struct device *dev, const char *buf){ struct headset *hs = dev->headset; int err; err = headset_send(hs, "\r\n+CHLD:(0,1,1x,2,2x,3,4)\r\n"); if (err < 0) return err; err = headset_send(hs, "\r\nOK\r\n"); if (err < 0) return err; if (hs->state != HEADSET_STATE_CONNECT_IN_PROGRESS) return 0; hfp_slc_complete(dev); return 0;}static int answer_call(struct device *device, const char *buf){ struct headset *hs = device->headset; int err; dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "AnswerRequested", DBUS_TYPE_INVALID); if (hs->ring_timer) { g_source_remove(hs->ring_timer); hs->ring_timer = 0; } if (!hs->hfp_active) return headset_send(hs, "\r\nOK\r\n"); if (hs->ph_number) { g_free(hs->ph_number); hs->ph_number = NULL; } err = headset_send(hs, "\r\nOK\r\n"); if (err < 0) return err; /*+CIEV: (call = 1)*/ err = headset_send(hs, "\r\n+CIEV:2,1\r\n"); if (err < 0) return err; /*+CIEV: (callsetup = 0)*/ return headset_send(hs, "\r\n+CIEV:3,0\r\n");}static int terminate_call(struct device *device, const char *buf){ struct headset *hs = device->headset; int err; dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "CallTerminated", DBUS_TYPE_INVALID); err = headset_send(hs, "\r\nOK\r\n"); if (err < 0) return err; if (hs->ph_number) { g_free(hs->ph_number); hs->ph_number = NULL; } if (hs->ring_timer) { g_source_remove(hs->ring_timer); hs->ring_timer = 0; /*+CIEV: (callsetup = 0)*/ return headset_send(hs, "\r\n+CIEV:3,0\r\n"); } /*+CIEV: (call = 0)*/ return headset_send(hs, "\r\n+CIEV:2,0\r\n");
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -