summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/control.c155
-rw-r--r--audio/device.c62
-rw-r--r--audio/device.h1
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;