📄 unix.c
字号:
/* * * BlueZ - Bluetooth protocol stack for Linux * * 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 <sys/socket.h>#include <sys/un.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <stdint.h>#include <bluetooth/bluetooth.h>#include <bluetooth/sdp.h>#include <dbus/dbus.h>#include <glib.h>#include "logging.h"#include "dbus.h"#include "ipc.h"#include "device.h"#include "manager.h"#include "avdtp.h"#include "a2dp.h"#include "headset.h"#include "sink.h"#include "unix.h"typedef enum { TYPE_NONE, TYPE_HEADSET, TYPE_SINK, TYPE_SOURCE} service_type_t;typedef void (*notify_cb_t) (struct device *dev, void *data);struct a2dp_data { struct avdtp *session; struct avdtp_stream *stream; struct a2dp_sep *sep;};struct headset_data { headset_lock_t lock;};struct unix_client { struct device *dev; struct avdtp_service_capability *media_codec; service_type_t type; char *interface; union { struct a2dp_data a2dp; struct headset_data hs; } d; int sock; int fd_opt; unsigned int req_id; unsigned int cb_id; gboolean (*cancel_stream) (struct device *dev, unsigned int id);};static GSList *clients = NULL;static int unix_sock = -1;static void client_free(struct unix_client *client){ struct a2dp_data *a2dp; switch (client->type) { case TYPE_SINK: case TYPE_SOURCE: a2dp = &client->d.a2dp; if (client->cb_id > 0) avdtp_stream_remove_cb(a2dp->session, a2dp->stream, client->cb_id); if (a2dp->sep) a2dp_sep_unlock(a2dp->sep, a2dp->session); if (a2dp->session) avdtp_unref(a2dp->session); break; default: break; } if (client->sock >= 0) close(client->sock); if (client->media_codec) g_free(client->media_codec); g_free(client->interface); g_free(client);}/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly AF_UNIX)and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" setto SCM_RIGHTS and the data being an integer value equal to the handle of the file descriptor to be passed.*/static int unix_sendmsg_fd(int sock, int fd){ char cmsg_b[CMSG_SPACE(sizeof(int))], m = 'm'; struct cmsghdr *cmsg; struct iovec iov = { &m, sizeof(m) }; struct msghdr msgh; memset(&msgh, 0, sizeof(msgh)); msgh.msg_iov = &iov; msgh.msg_iovlen = 1; msgh.msg_control = &cmsg_b; msgh.msg_controllen = CMSG_LEN(sizeof(int)); cmsg = CMSG_FIRSTHDR(&msgh); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); /* Initialize the payload */ (*(int *) CMSG_DATA(cmsg)) = fd; return sendmsg(sock, &msgh, MSG_NOSIGNAL);}static service_type_t select_service(struct device *dev, const char *interface){ if (!interface) { if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst)) return TYPE_SINK; else if (dev->headset && headset_is_active(dev)) return TYPE_HEADSET; else if (dev->sink) return TYPE_SINK; else if (dev->headset) return TYPE_HEADSET; } else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink) return TYPE_SINK; else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset) return TYPE_HEADSET; return TYPE_NONE;}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 unix_client *client = user_data; struct a2dp_data *a2dp = &client->d.a2dp; switch (new_state) { case AVDTP_STATE_IDLE: if (a2dp->sep) { a2dp_sep_unlock(a2dp->sep, a2dp->session); a2dp->sep = NULL; } client->dev = NULL; if (a2dp->session) { avdtp_unref(a2dp->session); a2dp->session = NULL; } a2dp->stream = NULL; client->cb_id = 0; break; default: break; }}static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd){ char buf[IPC_MTU]; struct ipc_packet *pkt = (void *) buf; int len, codec_len; memset(buf, 0, sizeof(buf)); pkt->type = PKT_TYPE_CFG_RSP; if (!cfg) { pkt->error = EINVAL; len = send(sock, pkt, sizeof(struct ipc_packet), 0); if (len < 0) error("send: %s (%d)", strerror(errno), errno); return len; } debug("fd=%d, fd_opt=%u, pkt_len=%u, sample_size=%u, rate=%u", fd, cfg->fd_opt, cfg->pkt_len, cfg->sample_size, cfg->rate); if (cfg->codec == CFG_CODEC_SBC) codec_len = sizeof(struct ipc_codec_sbc); else codec_len = 0; pkt->error = PKT_ERROR_NONE; pkt->length = sizeof(struct ipc_data_cfg) + codec_len; memcpy(pkt->data, cfg, pkt->length); len = sizeof(struct ipc_packet) + pkt->length; len = send(sock, pkt, len, 0); if (len < 0) error("Error %s(%d)", strerror(errno), errno); debug("%d bytes sent", len); if (fd != -1) { len = unix_sendmsg_fd(sock, fd); if (len < 0) error("Error %s(%d)", strerror(errno), errno); debug("%d bytes sent", len); } return 0;}static void headset_setup_complete(struct device *dev, void *user_data){ struct unix_client *client = user_data; struct ipc_data_cfg cfg; struct headset_data *hs = &client->d.hs; int fd; client->req_id = 0; if (!dev) { unix_send_cfg(client->sock, NULL, -1); client->dev = NULL; return; } switch (client->fd_opt) { case CFG_FD_OPT_READ: hs->lock = HEADSET_LOCK_READ; break; case CFG_FD_OPT_WRITE: hs->lock = HEADSET_LOCK_WRITE; break; case CFG_FD_OPT_READWRITE: hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE; break; default: hs->lock = 0; break; } if (!headset_lock(dev, hs->lock)) { error("Unable to lock headset"); unix_send_cfg(client->sock, NULL, -1); client->dev = NULL; return; } memset(&cfg, 0, sizeof(cfg)); cfg.fd_opt = client->fd_opt; cfg.codec = CFG_CODEC_SCO; cfg.mode = CFG_MODE_MONO; cfg.pkt_len = 48; cfg.sample_size = 2; cfg.rate = 8000; fd = headset_get_sco_fd(dev); unix_send_cfg(client->sock, &cfg, fd);}static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, void *user_data){ struct unix_client *client = user_data; char buf[sizeof(struct ipc_data_cfg) + sizeof(struct ipc_codec_sbc)]; struct ipc_data_cfg *cfg = (void *) buf; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_cap; struct sbc_codec_cap *sbc_cap; struct ipc_codec_sbc *sbc = (void *) cfg->data; struct a2dp_data *a2dp = &client->d.a2dp; int fd; GSList *caps; client->req_id = 0; if (!stream) goto failed; if (!a2dp_sep_lock(sep, session)) { error("Unable to lock A2DP source SEP"); goto failed; } a2dp->sep = sep; a2dp->stream = stream; if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) { error("Unable to get stream transport"); goto failed; } for (codec_cap = NULL; caps; caps = g_slist_next(caps)) { cap = caps->data; if (cap->category == AVDTP_MEDIA_CODEC) { codec_cap = (void *) cap->data; break; } } if (codec_cap == NULL || codec_cap->media_codec_type != A2DP_CODEC_SBC) { error("Unable to find matching codec capability"); goto failed; } cfg->fd_opt = CFG_FD_OPT_WRITE; sbc_cap = (void *) codec_cap; cfg->sample_size = 2; switch (sbc_cap->channel_mode) { case A2DP_CHANNEL_MODE_MONO: cfg->mode = CFG_MODE_MONO; break; case A2DP_CHANNEL_MODE_DUAL_CHANNEL: cfg->mode = CFG_MODE_DUAL_CHANNEL; break; case A2DP_CHANNEL_MODE_STEREO: cfg->mode = CFG_MODE_STEREO; break; case A2DP_CHANNEL_MODE_JOINT_STEREO: cfg->mode = CFG_MODE_JOINT_STEREO; break; } switch (sbc_cap->frequency) { case A2DP_SAMPLING_FREQ_16000: cfg->rate = 16000; break; case A2DP_SAMPLING_FREQ_32000: cfg->rate = 32000; break; case A2DP_SAMPLING_FREQ_44100: cfg->rate = 44100; break; case A2DP_SAMPLING_FREQ_48000: cfg->rate = 48000; break; } cfg->codec = CFG_CODEC_SBC; sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ? 0x01 : 0x00; sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; switch (sbc_cap->block_length) { case A2DP_BLOCK_LENGTH_4: sbc->blocks = 4; break; case A2DP_BLOCK_LENGTH_8: sbc->blocks = 8; break; case A2DP_BLOCK_LENGTH_12: sbc->blocks = 12; break; case A2DP_BLOCK_LENGTH_16: sbc->blocks = 16; break; } sbc->bitpool = sbc_cap->max_bitpool; unix_send_cfg(client->sock, cfg, fd); client->cb_id = avdtp_stream_add_cb(session, stream, stream_state_changed, client); return;failed: error("stream setup failed"); if (a2dp->sep) { a2dp_sep_unlock(a2dp->sep, a2dp->session); a2dp->sep = NULL; } unix_send_cfg(client->sock, NULL, -1); avdtp_unref(a2dp->session); a2dp->session = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -