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

📄 manager.c

📁 Linux的蓝牙操作工具。配合bluez-lib使用
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * *  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 <ctype.h>#include <dirent.h>#include <errno.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/un.h>#include <glib.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include <bluetooth/sdp.h>#include <bluetooth/sdp_lib.h>#include <bluetooth/rfcomm.h>#include "dbus.h"#include "dbus-helper.h"#include "logging.h"#include "textfile.h"#include "error.h"#include "port.h"#include "storage.h"#include "manager.h"#define BASE_UUID			"00000000-0000-1000-8000-00805F9B34FB"#define SERIAL_PROXY_INTERFACE		"org.bluez.serial.Proxy"#define BUF_SIZE			1024/* Waiting for udev to create the device node */#define MAX_OPEN_TRIES 		5#define OPEN_WAIT		300	/* ms */struct pending_connect {	DBusConnection	*conn;	DBusMessage	*msg;	char		*bda;		/* Destination address  */	char		*adapter_path;	/* Adapter D-Bus path   */	char		*pattern;	/* Connection request pattern */	bdaddr_t	src;	uint8_t		channel;	guint		io_id;		/* GIOChannel watch id */	GIOChannel	*io;		/* GIOChannel for RFCOMM connect */	char		*dev;		/* tty device name */	int		id;		/* RFCOMM device id */	int		ntries;		/* Open attempts */	int 		canceled;	/* Operation canceled */};/* FIXME: Common file required */static struct {	const char	*name;	uint16_t	class;} serial_services[] = {	{ "vcp",	VIDEO_CONF_SVCLASS_ID		},	{ "pbap",	PBAP_SVCLASS_ID			},	{ "sap",	SAP_SVCLASS_ID			},	{ "ftp",	OBEX_FILETRANS_SVCLASS_ID	},	{ "bpp",	BASIC_PRINTING_SVCLASS_ID	},	{ "bip",	IMAGING_SVCLASS_ID		},	{ "synch",	IRMC_SYNC_SVCLASS_ID		},	{ "dun",	DIALUP_NET_SVCLASS_ID		},	{ "opp",	OBEX_OBJPUSH_SVCLASS_ID		},	{ "fax",	FAX_SVCLASS_ID			},	{ "spp",	SERIAL_PORT_SVCLASS_ID		},	{ NULL }};typedef enum {	TTY_PROXY,	UNIX_SOCKET_PROXY,	TCP_SOCKET_PROXY,	UNKNOWN_PROXY_TYPE = 0xFF} proxy_type_t;struct proxy {	bdaddr_t	src;	bdaddr_t	dst;	char		*uuid128;	/* UUID 128 */	char		*address;	/* TTY or Unix socket name */	short int	port;		/* TCP port */	proxy_type_t	type;		/* TTY or Unix socket */	struct termios  sys_ti;		/* Default TTY setting */	struct termios  proxy_ti;	/* Proxy TTY settings */	uint8_t		channel;	/* RFCOMM channel */	uint32_t	record_id;	/* Service record id */	guint		listen_watch;	/* Server listen watch */	guint		rfcomm_watch;	/* RFCOMM watch: Remote */	guint		local_watch;	/* Local watch: TTY or Unix socket */};static DBusConnection *connection = NULL;static GSList *pending_connects = NULL;static GSList *ports_paths = NULL;static GSList *proxies_paths = NULL;static int rfcomm_ctl = -1;static int sk_counter = 0;static void proxy_free(struct proxy *prx){	g_free(prx->address);	g_free(prx->uuid128);	g_free(prx);}static void pending_connect_free(struct pending_connect *pc){	if (pc->conn)		dbus_connection_unref(pc->conn);	if (pc->msg)		dbus_message_unref(pc->msg);	if (pc->bda)		g_free(pc->bda);	if (pc->pattern)		g_free(pc->pattern);	if (pc->adapter_path)		g_free(pc->adapter_path);	if (pc->dev)		g_free(pc->dev);	if (pc->io_id > 0)		g_source_remove(pc->io_id);	if (pc->io) {		g_io_channel_close(pc->io);		g_io_channel_unref(pc->io);	}	g_free(pc);}static struct pending_connect *find_pending_connect_by_pattern(const char *bda,							const char *pattern){	GSList *l;	/* Pattern can be friendly name, uuid128, record handle or channel */	for (l = pending_connects; l != NULL; l = l->next) {		struct pending_connect *pending = l->data;		if (!strcasecmp(pending->bda, bda) &&				!strcasecmp(pending->pattern, pattern))			return pending;	}	return NULL;}static void transaction_owner_exited(const char *name, void *data){	GSList *l, *tmp = NULL;	debug("transaction owner %s exited", name);	/* Remove all pending calls that belongs to this owner */	for (l = pending_connects; l != NULL; l = l->next) {		struct pending_connect *pc = l->data;		if (strcmp(name, dbus_message_get_sender(pc->msg)) != 0) {			tmp = g_slist_append(tmp, pc);			continue;		}		if (pc->id >= 0)			rfcomm_release(pc->id);		pending_connect_free(pc);	}	g_slist_free(pending_connects);	pending_connects = tmp;}static void pending_connect_remove(struct pending_connect *pc){	/* Remove the connection request owner */	name_listener_remove(pc->conn, dbus_message_get_sender(pc->msg),				(name_cb_t) transaction_owner_exited, NULL);	pending_connects = g_slist_remove(pending_connects, pc);	pending_connect_free(pc);}static void open_notify(int fd, int err, struct pending_connect *pc){	DBusMessage *reply;	bdaddr_t dst;	if (err) {		/* Max tries exceeded */		rfcomm_release(pc->id);		error_connection_attempt_failed(pc->conn, pc->msg, err);		return;	}	if (pc->canceled) {		rfcomm_release(pc->id);		error_canceled(pc->conn, pc->msg, "Connection canceled");		return;	}	/* Reply to the requestor */	reply = dbus_message_new_method_return(pc->msg);	dbus_message_append_args(reply,			DBUS_TYPE_STRING, &pc->dev,			DBUS_TYPE_INVALID);	send_message_and_unref(pc->conn, reply);	/* Send the D-Bus signal */	dbus_connection_emit_signal(pc->conn, SERIAL_MANAGER_PATH,			SERIAL_MANAGER_INTERFACE, "ServiceConnected" ,			DBUS_TYPE_STRING, &pc->dev,			DBUS_TYPE_INVALID);	str2ba(pc->bda, &dst);	/* Add the RFCOMM connection listener */	port_add_listener(pc->conn, pc->id, &dst, fd,			pc->dev, dbus_message_get_sender(pc->msg));}static gboolean open_continue(struct pending_connect *pc){	int fd;	if (!g_slist_find(pending_connects, pc))		return FALSE; /* Owner exited */	fd = open(pc->dev, O_RDONLY | O_NOCTTY);	if (fd < 0) {		int err = errno;		error("Could not open %s: %s (%d)",				pc->dev, strerror(err), err);		if (++pc->ntries >= MAX_OPEN_TRIES) {			/* Reporting error */			open_notify(fd, err, pc);			pending_connect_remove(pc);			return FALSE;		}		return TRUE;	}	/* Connection succeeded */	open_notify(fd, 0, pc);	pending_connect_remove(pc);	return FALSE;}int port_open(struct pending_connect *pc){	int fd;	fd = open(pc->dev, O_RDONLY | O_NOCTTY);	if (fd < 0) {		g_timeout_add(OPEN_WAIT, (GSourceFunc) open_continue, pc);		return -EINPROGRESS;	}	return fd;}static uint16_t str2class(const char *pattern){	int i;	for (i = 0; serial_services[i].name; i++) {		if (strcasecmp(serial_services[i].name, pattern) == 0)			return serial_services[i].class;	}	return 0;}int rfcomm_release(int16_t id){	struct rfcomm_dev_req req;	memset(&req, 0, sizeof(req));	req.dev_id = id;	/*	 * We are hitting a kernel bug inside RFCOMM code when	 * RFCOMM_HANGUP_NOW bit is set on request's flags passed to	 * ioctl(RFCOMMRELEASEDEV)!	 */	req.flags = (1 << RFCOMM_HANGUP_NOW);	if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) {		int err = errno;		error("Can't release device %d: %s (%d)",				id, strerror(err), err);		return -err;	}	return 0;}static int rfcomm_bind(bdaddr_t *src, bdaddr_t *dst, int16_t dev_id, uint8_t ch){	struct rfcomm_dev_req req;	int id;	memset(&req, 0, sizeof(req));	req.dev_id = dev_id;	req.flags = 0;	bacpy(&req.src, src);	bacpy(&req.dst, dst);	req.channel = ch;	id = ioctl(rfcomm_ctl, RFCOMMCREATEDEV, &req);	if (id < 0) {		int err = errno;		error("RFCOMMCREATEDEV failed: %s (%d)", strerror(err), err);		return -err;	}	return id;}static gboolean rfcomm_connect_cb(GIOChannel *chan,		GIOCondition cond, struct pending_connect *pc){	struct rfcomm_dev_req req;	int sk, err, fd, ret;	socklen_t len;	if (pc->canceled) {		error_canceled(pc->conn, pc->msg, "Connection canceled");		goto fail;	}	if (cond & G_IO_NVAL) {		/* Avoid close invalid file descriptor */		g_io_channel_unref(pc->io);		pc->io = NULL;		error_canceled(pc->conn, pc->msg, "Connection canceled");		goto fail;	}	sk = g_io_channel_unix_get_fd(chan);	len = sizeof(ret);	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) {		err = errno;		error("getsockopt(SO_ERROR): %s (%d)",				strerror(err), err);		error_connection_attempt_failed(pc->conn, pc->msg, err);		goto fail;	}	if (ret != 0) {		error("connect(): %s (%d)", strerror(ret), ret);		error_connection_attempt_failed(pc->conn, pc->msg, ret);		goto fail;	}	debug("rfcomm_connect_cb: connected");	memset(&req, 0, sizeof(req));	req.dev_id = -1;	req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);	bacpy(&req.src, &pc->src);	str2ba(pc->bda, &req.dst);	req.channel = pc->channel;	pc->id = ioctl(sk, RFCOMMCREATEDEV, &req);	if (pc->id < 0) {		err = errno;		error("ioctl(RFCOMMCREATEDEV): %s (%d)", strerror(err), err);		error_connection_attempt_failed(pc->conn, pc->msg, err);		goto fail;	}	pc->dev	= g_new0(char, 16);	snprintf(pc->dev, 16, "/dev/rfcomm%d", pc->id);	/* Addressing connect port */	fd = port_open(pc);	if (fd < 0)		/* Open in progress: Wait the callback */		return FALSE;	open_notify(fd, 0, pc);fail:	pending_connect_remove(pc);	return FALSE;}static int rfcomm_connect(struct pending_connect *pc){	struct sockaddr_rc addr;	int sk, err;	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);	if (sk < 0)		return -errno;	memset(&addr, 0, sizeof(addr));	addr.rc_family	= AF_BLUETOOTH;	bacpy(&addr.rc_bdaddr, &pc->src);	addr.rc_channel	= 0;	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0)		goto fail;	if (set_nonblocking(sk) < 0)		goto fail;	pc->io = g_io_channel_unix_new(sk);	addr.rc_family	= AF_BLUETOOTH;	str2ba(pc->bda, &addr.rc_bdaddr);	addr.rc_channel	= pc->channel;	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {		/* BlueZ returns EAGAIN eventhough it should return EINPROGRESS */		if (!(errno == EAGAIN || errno == EINPROGRESS)) {			error("connect() failed: %s (%d)",					strerror(errno), errno);			g_io_channel_unref(pc->io);			pc->io = NULL;			goto fail;		}		debug("Connect in progress");		pc->io_id = g_io_add_watch(pc->io,				G_IO_OUT | G_IO_ERR | G_IO_NVAL | G_IO_HUP,				(GIOFunc) rfcomm_connect_cb, pc);	} else {		debug("Connect succeeded with first try");		(void) rfcomm_connect_cb(pc->io, G_IO_OUT, pc);	}	return 0;fail:	err = errno;	close(sk);	errno = err;	return -err;}static void record_reply(DBusPendingCall *call, void *data){	struct pending_connect *pc;	DBusMessage *reply = dbus_pending_call_steal_reply(call);	sdp_record_t *rec = NULL;	const uint8_t *rec_bin;	sdp_list_t *protos;	DBusError derr;	int len, scanned, ch, err;	/* Owner exited? */	if (!g_slist_find(pending_connects, data)) {		dbus_message_unref(reply);		return;	}	pc = data;	if (pc->canceled) {		error_canceled(pc->conn, pc->msg, "Connection canceled");		goto fail;	}	dbus_error_init(&derr);	if (dbus_set_error_from_message(&derr, reply)) {		/* FIXME : forward error as is */		if (dbus_error_has_name(&derr,				"org.bluez.Error.ConnectionAttemptFailed"))			error_connection_attempt_failed(pc->conn, pc->msg,					EIO);		else			error_not_supported(pc->conn, pc->msg);		error("GetRemoteServiceRecord: %s(%s)",					derr.name, derr.message);		dbus_error_free(&derr);		goto fail;	}	if (!dbus_message_get_args(reply, &derr,				DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &rec_bin, &len,				DBUS_TYPE_INVALID)) {		error_not_supported(pc->conn, pc->msg);		error("%s: %s", derr.name, derr.message);		dbus_error_free(&derr);		goto fail;	}	if (len == 0) {		error_not_supported(pc->conn, pc->msg);		error("Invalid service record length");		goto fail;	}	rec = sdp_extract_pdu(rec_bin, &scanned);	if (!rec) {		error("Can't extract SDP record.");		error_not_supported(pc->conn, pc->msg);		goto fail;	}	if (len != scanned || (sdp_get_access_protos(rec, &protos) < 0)) {		error_not_supported(pc->conn, pc->msg);		goto fail;	}	ch = sdp_get_proto_port(protos, RFCOMM_UUID);	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);	sdp_list_free(protos, NULL);	if (ch < 1 || ch > 30) {		error("Channel out of range: %d", ch);		error_not_supported(pc->conn, pc->msg);		goto fail;	}	if (dbus_message_has_member(pc->msg, "CreatePort")) {		char path[MAX_PATH_LENGTH], port_name[16];		const char *ppath = path;		sdp_data_t *d;		char *svcname = NULL;		DBusMessage *reply;		bdaddr_t dst;		str2ba(pc->bda, &dst);		err = rfcomm_bind(&pc->src, &dst, -1, ch);		if (err < 0) {			error_failed_errno(pc->conn, pc->msg, -err);			goto fail;		}		snprintf(port_name, sizeof(port_name), "/dev/rfcomm%d", err);		d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);		if (d) {			svcname = g_new0(char, d->unitSize);			snprintf(svcname, d->unitSize, "%.*s",					d->unitSize, d->val.str);		}		port_store(&pc->src, &dst, err, ch, svcname);		port_register(pc->conn, err, &pc->src, &dst, port_name,			      path, svcname);		if (svcname)			g_free(svcname);		ports_paths = g_slist_append(ports_paths, g_strdup(path));		reply = dbus_message_new_method_return(pc->msg);		dbus_message_append_args(reply,				DBUS_TYPE_STRING, &ppath,				DBUS_TYPE_INVALID);		send_message_and_unref(pc->conn, reply);		dbus_connection_emit_signal(pc->conn, SERIAL_MANAGER_PATH,				SERIAL_MANAGER_INTERFACE, "PortCreated" ,				DBUS_TYPE_STRING, &ppath,				DBUS_TYPE_INVALID);	} else {		/* ConnectService */		pc->channel = ch;		err = rfcomm_connect(pc);		if (err < 0) {			error("RFCOMM connection failed");			error_connection_attempt_failed(pc->conn,					pc->msg, -err);			goto fail;		}		/* Wait the connect callback */		goto done;	}fail:	pending_connect_remove(pc);done:	if (rec)		sdp_record_free(rec);	dbus_message_unref(reply);}static int get_record(struct pending_connect *pc, uint32_t handle,					DBusPendingCallNotifyFunction cb){

⌨️ 快捷键说明

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