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

📄 unix.c

📁 实现bluez蓝牙profile需要的库
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * *  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 + -