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

📄 headset.c

📁 基于LINUX内核驱动的开发
💻 C
📖 第 1 页 / 共 4 页
字号:
}static int cli_notification(struct device *device, const char *buf){	struct headset *hs = device->headset;	if (strlen(buf) < 9)		return -EINVAL;	hs->cli_active = buf[8] == '1' ? TRUE : FALSE;	return headset_send(hs, "\r\nOK\r\n");}static int signal_gain_setting(struct device *device, const char *buf){	struct headset *hs = device->headset;	const char *name;	dbus_uint16_t gain;	if (strlen(buf) < 8) {		error("Too short string for Gain setting");		return -EINVAL;	}	gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10);	if (gain > 15) {		error("Invalid gain value received: %u", gain);		return -EINVAL;	}	switch (buf[5]) {	case HEADSET_GAIN_SPEAKER:		if (hs->sp_gain == gain)			goto ok;		name = "SpeakerGainChanged";		hs->sp_gain = gain;		break;	case HEADSET_GAIN_MICROPHONE:		if (hs->mic_gain == gain)			goto ok;		name = "MicrophoneGainChanged";		hs->mic_gain = gain;		break;	default:		error("Unknown gain setting");		return -EINVAL;	}	dbus_connection_emit_signal(device->conn, device->path,				    AUDIO_HEADSET_INTERFACE, name,				    DBUS_TYPE_UINT16, &gain,				    DBUS_TYPE_INVALID);ok:	return headset_send(hs, "\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 },	{ "AT+CLIP", cli_notification },	{ 0 }};static int handle_event(struct device *device, const char *buf){	struct event *ev;	debug("Received %s", buf);	for (ev = event_callbacks; ev->cmd; ev++) {		if (!strncmp(buf, ev->cmd, strlen(ev->cmd)))			return ev->callback(device, buf);	}	return -EINVAL;}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;	if (g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1,				&bytes_read) != 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 == -EINVAL) {		error("Badly formated or unrecognized command: %s",				&hs->buf[hs->data_start]);		err = headset_send(hs, "\r\nERROR\r\n");	} else 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 gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond,					struct device *dev){	struct headset *hs;	struct pending_connect *p;	char hs_address[18];	int sk, ret;	socklen_t len;	if (cond & G_IO_NVAL)		return FALSE;	hs = dev->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;	}	ba2str(&dev->dst, hs_address);	hs->rfcomm = chan;	p->io = NULL;	if (server_is_enabled(HANDSFREE_SVCLASS_ID) && hs->hfp_handle != 0)		hs->hfp_active = TRUE;	else		hs->hfp_active = FALSE;	g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,			(GIOFunc) rfcomm_io_cb, dev);	debug("%s: Connected to %s", dev->path, hs_address);	/* In HFP mode wait for Service Level Connection */	if (hs->hfp_active)		return FALSE;	headset_set_state(dev, HEADSET_STATE_CONNECTED);	if (p->target_state == HEADSET_STATE_PLAYING) {		p->err = sco_connect(dev, NULL, NULL, NULL);		if (p->err < 0)			goto failed;		return FALSE;	}	if (p->msg) {		DBusMessage *reply = dbus_message_new_method_return(p->msg);		send_message_and_unref(dev->conn, reply);	}	pending_connect_finalize(dev);	return FALSE;failed:	if (p->msg)		error_connection_attempt_failed(dev->conn, p->msg, p->err);	pending_connect_finalize(dev);	if (hs->rfcomm)		headset_set_state(dev, HEADSET_STATE_CONNECTED);	else		headset_set_state(dev, HEADSET_STATE_DISCONNECTED);	return FALSE;}static void get_record_reply(DBusPendingCall *call, void *data){	DBusMessage *reply;	DBusError derr;	uint8_t *array;	int array_len, record_len, err = EIO, ch = -1;	sdp_record_t *record = NULL;	sdp_list_t *protos, *classes = NULL;	uuid_t uuid;	struct device *dev = data;	struct headset *hs = dev->headset;	struct pending_connect *p = hs->pending;	reply = dbus_pending_call_steal_reply(call);	dbus_error_init(&derr);	if (dbus_set_error_from_message(&derr, reply)) {		error("GetRemoteServiceRecord failed: %s", derr.message);		dbus_error_free(&derr);		goto failed_not_supported;	}	dbus_error_init(&derr);	if (!dbus_message_get_args(reply, &derr,				DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,				&array, &array_len,				DBUS_TYPE_INVALID)) {		error("Unable to get args from GetRecordReply: %s", derr.message);		dbus_error_free(&derr);		goto failed_not_supported;	}	if (!array) {		error("get_record_reply: Unable to get handle array from reply");		goto failed_not_supported;	}	record = sdp_extract_pdu(array, &record_len);	if (!record) {		error("Unable to extract service record from reply");		goto failed_not_supported;	}	if (record_len != array_len)		debug("warning: array len (%d) != record len (%d)",				array_len, record_len);	if (sdp_get_service_classes(record, &classes) < 0) {		error("Unable to get service classes from record");		goto failed_not_supported;	}	memcpy(&uuid, classes->data, sizeof(uuid));	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16) {		error("Not a 16 bit UUID");		goto failed_not_supported;	}	if (hs->search_hfp) {		if (uuid.value.uuid16 != HANDSFREE_SVCLASS_ID) {			error("Service record didn't contain the HFP UUID");			goto failed_not_supported;		}		hs->hfp_handle = record->handle;	} else {		if (uuid.value.uuid16 != HEADSET_SVCLASS_ID) {			error("Service record didn't contain the HSP UUID");			goto failed_not_supported;		}		hs->hsp_handle = record->handle;	}	if (!sdp_get_access_protos(record, &protos)) {		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);		protos = NULL;	}	if (ch == -1) {		error("Unable to extract RFCOMM channel from service record");		goto failed_not_supported;	}	hs->rfcomm_ch = ch;	err = rfcomm_connect(dev, NULL, NULL, NULL);	if (err < 0) {		error("Unable to connect: %s (%s)", strerror(-err), -err);		p->err = -err;		error_connection_attempt_failed(dev->conn, p->msg, p->err);		goto failed;	}	sdp_list_free(classes, free);	sdp_record_free(record);	dbus_message_unref(reply);	device_finish_sdp_transaction(dev);	return;failed_not_supported:	if (p->msg) {		error_not_supported(dev->conn, p->msg);		dbus_message_unref(p->msg);		p->msg = NULL;	}failed:	if (classes)		sdp_list_free(classes, free);	if (record)		sdp_record_free(record);	if (reply)		dbus_message_unref(reply);	pending_connect_finalize(dev);	headset_set_state(dev, HEADSET_STATE_DISCONNECTED);	device_finish_sdp_transaction(dev);}static void get_handles_reply(DBusPendingCall *call, void *data){	DBusMessage *msg = NULL, *reply;	DBusPendingCall *pending;	DBusError derr;	struct device *dev = data;	struct headset *hs = dev->headset;	struct pending_connect *p = hs->pending;	char address[18], *addr_ptr = address;	dbus_uint32_t *array = NULL;	dbus_uint32_t handle;	int array_len;	reply = dbus_pending_call_steal_reply(call);	dbus_error_init(&derr);	if (dbus_set_error_from_message(&derr, reply)) {		error("GetRemoteServiceHandles failed: %s", derr.message);		if (p->msg) {			if (dbus_error_has_name(&derr,						"org.bluez.Error.ConnectionAttemptFailed"))				error_connection_attempt_failed(dev->conn, p->msg,								EHOSTDOWN);			else				error_not_supported(dev->conn, p->msg);		}		dbus_error_free(&derr);		goto failed;	}	dbus_error_init(&derr);	if (!dbus_message_get_args(reply, &derr,				DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,				&array, &array_len,				DBUS_TYPE_INVALID)) {		error("Unable to get args from reply: %s", derr.message);		dbus_error_free(&derr);		if (p->msg)			error_not_supported(dev->conn, p->msg);		goto failed;	}	if (!array) {		error("get_handles_reply: Unable to get handle array from reply");		if (p->msg)			error_not_supported(dev->conn, p->msg);		goto failed;	}	if (array_len < 1) {		if (hs->search_hfp) {			debug("No record handles found for hfp");			hs->search_hfp = FALSE;			get_handles(dev, NULL, NULL, NULL);			dbus_message_unref(reply);			return;		}		debug("No record handles found for hsp");		if (p->msg)			error_not_supported(dev->conn, p->msg);		goto failed;	}	if (array_len > 1)		debug("Multiple records found. Using the first one.");	msg = dbus_message_new_method_call("org.bluez", dev->adapter_path,						"org.bluez.Adapter",						"GetRemoteServiceRecord");	if (!msg) {		error("Unable to allocate new method call");		if (p->msg)			error_out_of_memory(dev->conn, p->msg);		goto failed;	}	ba2str(&dev->dst, address);	handle = array[0];	dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr,					DBUS_TYPE_UINT32, &handle,					DBUS_TYPE_INVALID);	if (!dbus_connection_send_with_reply(dev->conn, msg, &pending, -1)) {		error("Sending GetRemoteServiceRecord failed");		if (p->msg)			error_connection_attempt_failed(dev->conn, p->msg, EIO);		goto failed;	}	dbus_pending_call_set_notify(pending, get_record_reply, dev, NULL);	dbus_pending_call_unref(pending);	dbus_message_unref(msg);	dbus_message_unref(reply);	return;failed:	if (msg)		dbus_message_unref(msg);	dbus_message_unref(reply);	p->err = EIO;	pending_connect_finalize(dev);	headset_set_state(dev, HEADSET_STATE_DISCONNECTED);}static int get_handles(struct device *device, headset_stream_cb_t cb,			void *user_data, unsigned int *cb_id){	DBusPendingCall *pending;	struct headset *hs = device->headset;	const char *hs_svc;	const char *addr_ptr;	char hs_address[18];	DBusMessage *msg;	msg = dbus_message_new_method_call("org.bluez", device->adapter_path,						"org.bluez.Adapter",						"GetRemoteServiceHandles");	if (!msg) {		error("Could not create a new dbus message");		return -ENOMEM;	}	if (hs->search_hfp)		hs_svc = "hfp";	else		hs_svc = "hsp";	ba2str(&device->dst, hs_address);	addr_ptr = hs_address;	dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr,					DBUS_TYPE_STRING, &hs_svc,					DBUS_TYPE_INVALID);	headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS);	if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) {		error("Sending GetRemoteServiceHandles failed");		dbus_message_unref(msg);		return -EIO;	}	pending_connect_init(hs, HEADSET_STATE_CONNECTED);	if (cb) {		unsigned int id;		id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,					cb, user_data);		if (cb_id)			*cb_id = id;	}	dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL);	if (hs->pending)		hs->pending->call = pending;	else		dbus_pending_call_unref(pending);

⌨️ 快捷键说明

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