diff options
| -rw-r--r-- | audio/control.c | 155 | ||||
| -rw-r--r-- | audio/device.c | 62 | ||||
| -rw-r--r-- | audio/device.h | 1 | 
3 files changed, 186 insertions, 32 deletions
| diff --git a/audio/control.c b/audio/control.c index c69c610f..bd100331 100644 --- a/audio/control.c +++ b/audio/control.c @@ -32,6 +32,9 @@  #include <unistd.h>  #include <assert.h>  #include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h>  #include <netinet/in.h>  #include <glib.h> @@ -45,6 +48,7 @@  #include "dbus.h"  #include "dbus-helper.h"  #include "logging.h" +#include "uinput.h"  #include "device.h"  #include "manager.h"  #include "avdtp.h" @@ -84,6 +88,8 @@  #define PLAY_OP			0x44  #define STOP_OP			0x45  #define PAUSE_OP		0x46 +#define REWIND_OP		0x48 +#define FAST_FORWARD_OP		0x49  #define NEXT_OP			0x4b  #define PREV_OP			0x4c @@ -150,6 +156,8 @@ struct avctp {  	bdaddr_t src;  	bdaddr_t dst; +	int uinput; +  	int sock;  	guint io; @@ -163,7 +171,6 @@ struct control {  	struct avctp *session;  }; -  static int avrcp_ct_record(sdp_buf_t *buf)  {  	sdp_list_t *svclass_id, *pfseq, *apseq, *root; @@ -385,10 +392,77 @@ static void avctp_unref(struct avctp *session)  		close(session->sock);  	if (session->io)  		g_source_remove(session->io); +  	session->dev->control->session = NULL; + +	if (session->uinput >= 0) { +		ioctl(session->uinput, UI_DEV_DESTROY); +		close(session->uinput); +	} +  	g_free(session);  } +static int uinput_create(char *name) +{ +	struct uinput_dev dev; +	int fd, err; + +	fd = open("/dev/uinput", O_RDWR); +	if (fd < 0) { +		fd = open("/dev/input/uinput", O_RDWR); +		if (fd < 0) { +			fd = open("/dev/misc/uinput", O_RDWR); +			if (fd < 0) { +				err = errno; +				error("Can't open input device: %s (%d)", +							strerror(err), err); +				return -err; +			} +		} +	} + +	memset(&dev, 0, sizeof(dev)); +	if (name) +		strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE); + +	dev.id.bustype = BUS_BLUETOOTH; +	dev.id.vendor  = 0x0000; +	dev.id.product = 0x0000; +	dev.id.version = 0x0000; + +	if (write(fd, &dev, sizeof(dev)) < 0) { +		err = errno; +		error("Can't write device information: %s (%d)", +						strerror(err), err); +		close(fd); +		errno = err; +		return -err; +	} + +	ioctl(fd, UI_SET_EVBIT, EV_KEY); +	ioctl(fd, UI_SET_EVBIT, EV_REL); +	ioctl(fd, UI_SET_EVBIT, EV_REP); + +	ioctl(fd, UI_SET_KEYBIT, KEY_PLAYPAUSE); +	ioctl(fd, UI_SET_KEYBIT, KEY_STOP); +	ioctl(fd, UI_SET_KEYBIT, KEY_NEXT); +	ioctl(fd, UI_SET_KEYBIT, KEY_PREVIOUS); +	ioctl(fd, UI_SET_KEYBIT, KEY_REWIND); +	ioctl(fd, UI_SET_KEYBIT, KEY_FORWARD); + +	if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { +		err = errno; +		error("Can't create uinput device: %s (%d)", +						strerror(err), err); +		close(fd); +		errno = err; +		return -err; +	} + +	return fd; +} +  static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst)  {  	struct avctp *session; @@ -406,6 +480,7 @@ static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst)  	session = g_new0(struct avctp, 1); +	session->uinput = -1;  	session->sock = -1;  	bacpy(&session->src, src);  	bacpy(&session->dst, dst); @@ -415,33 +490,88 @@ static struct avctp *avctp_get(bdaddr_t *src, bdaddr_t *dst)  	return session;  } -static void handle_panel_passthrough(const unsigned char *operands, int operand_count) +static void init_uinput(struct avctp *session) +{ +	char address[18], *name; + +	ba2str(&session->dst, address); + +	name = session->dev->name ? session->dev->name : address; + +	session->uinput = uinput_create(name); +	if (session->uinput < 0) +		error("AVRCP: failed to init uinput for %s", name); +	else +		debug("AVRCP: uinput initialized for %s", name); +} + +static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) +{ +	struct uinput_event event; + +	memset(&event, 0, sizeof(event)); +	event.type	= type; +	event.code	= code; +	event.value	= value; + +	return write(fd, &event, sizeof(event)); +} + +static void send_key(int fd, uint16_t key, int pressed) +{ +	if (fd < 0) +		return; + +	send_event(fd, EV_KEY, key, pressed); +	send_event(fd, EV_SYN, SYN_REPORT, 0); +} + +static void handle_panel_passthrough(struct avctp *session, +					const unsigned char *operands, +					int operand_count)  {  	const char *status; +	int pressed;  	if (operand_count == 0)  		return; -	if (operands[0] & 0x80) +	if (operands[0] & 0x80) {  		status = "released"; -	else +		pressed = 0; +	} else {  		status = "pressed"; +		pressed = 1; +	}  	switch (operands[0] & 0x7F) {  	case PLAY_OP:  		debug("AVRCP: PLAY %s", status); +		send_key(session->uinput, KEY_PLAYPAUSE, pressed);  		break;  	case STOP_OP:  		debug("AVRCP: STOP %s", status); +		send_key(session->uinput, KEY_STOP, pressed);  		break;  	case PAUSE_OP:  		debug("AVRCP: PAUSE %s", status); +		send_key(session->uinput, KEY_PLAYPAUSE, pressed);  		break;  	case NEXT_OP:  		debug("AVRCP: NEXT %s", status); +		send_key(session->uinput, KEY_NEXT, pressed);  		break;  	case PREV_OP:  		debug("AVRCP: PREV %s", status); +		send_key(session->uinput, KEY_PREVIOUS, pressed); +		break; +	case REWIND_OP: +		debug("AVRCP: REWIND %s", status); +		send_key(session->uinput, KEY_REWIND, pressed); +		break; +	case FAST_FORWARD_OP: +		debug("AVRCP: FAST FORWARD %s", status); +		send_key(session->uinput, KEY_FORWARD, pressed);  		break;  	default:  		debug("AVRCP: unknown button 0x%02X %s", operands[0] & 0x7F, status); @@ -506,7 +636,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,  			avrcp->code == CTYPE_CONTROL &&  			avrcp->subunit_type == SUBUNIT_PANEL &&  			avrcp->opcode == OP_PASSTHROUGH) { -		handle_panel_passthrough(operands, operand_count); +		handle_panel_passthrough(session, operands, operand_count);  		avctp->cr = AVCTP_RESPONSE;  		avrcp->code = CTYPE_ACCEPTED;  		ret = write(session->sock, buf, packet_size); @@ -553,6 +683,7 @@ static void auth_cb(DBusPendingCall *call, void *data)  	session->dev = manager_device_connected(&session->dst,  						AVRCP_TARGET_UUID);  	session->dev->control->session = session; +	init_uinput(session);  	dbus_connection_emit_signal(session->dev->conn, session->dev->path,  					AUDIO_CONTROL_INTERFACE, "Connected", @@ -623,6 +754,12 @@ static gboolean avctp_server_cb(GIOChannel *chan, GIOCondition cond, void *data)  	session = avctp_get(&src, &dst); +	if (!session) { +		error("Unable to create new AVCTP session"); +		close(cli_sk); +		return TRUE; +	} +  	if (session->sock >= 0) {  		error("Refusing unexpected connect from %s", address);  		close(cli_sk); @@ -651,6 +788,7 @@ proceed:  		session->dev = manager_device_connected(&dst,  							AVRCP_TARGET_UUID);  		session->dev->control->session = session; +		init_uinput(session);  		flags |= G_IO_IN;  		dbus_connection_emit_signal(session->dev->conn,  						session->dev->path, @@ -712,6 +850,8 @@ static gboolean avctp_connect_cb(GIOChannel *chan, GIOCondition cond,  		goto failed;  	} +	init_uinput(session); +  	dbus_connection_emit_signal(session->dev->conn, session->dev->path,  					AUDIO_CONTROL_INTERFACE, "Connected",  					DBUS_TYPE_INVALID); @@ -744,6 +884,11 @@ gboolean avrcp_connect(struct device *dev)  		return TRUE;  	session = avctp_get(&dev->src, &dev->dst); +	if (!session) { +		error("Unable to create new AVCTP session"); +		return FALSE; +	} +  	session->dev = dev;  	memset(&l2a, 0, sizeof(l2a)); diff --git a/audio/device.c b/audio/device.c index f3567189..c86f711d 100644 --- a/audio/device.c +++ b/audio/device.c @@ -73,51 +73,60 @@ static DBusHandlerResult device_get_address(DBusConnection *conn,  	return send_message_and_unref(conn, reply);  } -static DBusHandlerResult device_get_name(DBusConnection *conn, -						DBusMessage *msg, void *data) +static char *get_dev_name(DBusConnection *conn, const char *adapter_path, +				bdaddr_t *bda)  { -	struct device *device = data; -	DBusMessage *reply, *reply2, *msg2; +	DBusMessage *msg, *reply;  	DBusError derr;  	const char *name; -	char address[18], *addr_ptr = address; +	char address[18], *addr_ptr = address, *ret; -	msg2 = dbus_message_new_method_call("org.bluez", device->adapter_path, +	msg = dbus_message_new_method_call("org.bluez", adapter_path,  					"org.bluez.Adapter", "GetRemoteName"); -	if (!msg2) -		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	if (!msg) +		return NULL; -	ba2str(&device->dst, address); -	dbus_message_append_args(msg2, DBUS_TYPE_STRING, &addr_ptr, +	ba2str(bda, address); +	dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr,  					DBUS_TYPE_INVALID);  	dbus_error_init(&derr); -	reply2 = dbus_connection_send_with_reply_and_block(conn, msg2, -1, +	reply = dbus_connection_send_with_reply_and_block(conn, msg, -1,  								&derr); - -	dbus_message_unref(msg2); +	dbus_message_unref(msg);  	if (dbus_error_is_set(&derr)) { -		error("%s GetRemoteName(): %s", device->adapter_path, -				derr.message); +		error("%s GetRemoteName(): %s", adapter_path, derr.message);  		dbus_error_free(&derr); -		return err_failed(conn, msg, "Unable to get remote name"); +		return NULL;  	} +	if (!dbus_message_get_args(reply, NULL, +					DBUS_TYPE_STRING, &name, +					DBUS_TYPE_INVALID)) +		return NULL; + +	ret = g_strdup(name); + +	dbus_message_unref(reply); + +	return ret; +} + +static DBusHandlerResult device_get_name(DBusConnection *conn, +						DBusMessage *msg, void *data) +{ +	struct device *dev = data; +	DBusMessage *reply; +	const char *name = dev->name ? dev->name : "";  	reply = dbus_message_new_method_return(msg);  	if (!reply)  		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	dbus_message_get_args(reply2, NULL, -		       		DBUS_TYPE_STRING, &name, -				DBUS_TYPE_INVALID); -  	dbus_message_append_args(reply, DBUS_TYPE_STRING, &name,  					DBUS_TYPE_INVALID); -	dbus_message_unref(reply2); -  	return send_message_and_unref(conn, reply);  } @@ -193,11 +202,9 @@ static void device_free(struct device *dev)  	if (dev->conn)  		dbus_connection_unref(dev->conn); -	if (dev->adapter_path) -		g_free(dev->adapter_path); - -	if (dev->path) -		g_free(dev->path); +	g_free(dev->adapter_path); +	g_free(dev->path); +	g_free(dev->name);  	g_free(dev);  } @@ -303,6 +310,7 @@ struct device *device_register(DBusConnection *conn,  		return NULL;  	} +	dev->name = get_dev_name(conn, dev->adapter_path, bda);  	dev->path = g_strdup(path);  	bacpy(&dev->dst, bda);  	bacpy(&dev->src, &src); diff --git a/audio/device.h b/audio/device.h index 3fbf29de..04ce36de 100644 --- a/audio/device.h +++ b/audio/device.h @@ -56,6 +56,7 @@ struct device {  	DBusConnection *conn;  	char *adapter_path;  	char *path; +	char *name;  	bdaddr_t store;  	bdaddr_t src;  	bdaddr_t dst; | 
