summaryrefslogtreecommitdiffstats
path: root/btif/src/btif_rc.c
diff options
context:
space:
mode:
Diffstat (limited to 'btif/src/btif_rc.c')
-rw-r--r--btif/src/btif_rc.c435
1 files changed, 435 insertions, 0 deletions
diff --git a/btif/src/btif_rc.c b/btif/src/btif_rc.c
new file mode 100644
index 0000000..04d5681
--- /dev/null
+++ b/btif/src/btif_rc.c
@@ -0,0 +1,435 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+
+
+/*****************************************************************************
+ *
+ * Filename: btif_rc.c
+ *
+ * Description: Bluetooth AVRC implementation
+ *
+ *****************************************************************************/
+#include <hardware/bluetooth.h>
+#include <fcntl.h>
+#include "bta_api.h"
+#include "bta_av_api.h"
+#include "avrc_defs.h"
+#include "bd.h"
+#include "gki.h"
+
+#define LOG_TAG "BTIF_RC"
+#include "btif_common.h"
+
+/*****************************************************************************
+** Constants & Macros
+******************************************************************************/
+#if (!defined(AVRC_METADATA_INCLUDED) || (AVRC_METADATA_INCLUDED == FALSE))
+#define BTIF_RC_USE_UINPUT TRUE
+#include "uinput.h"
+#else
+#error "AVRCP 1.3 is not supported on Bluedroid yet."
+#endif
+
+/*****************************************************************************
+** Local type definitions
+******************************************************************************/
+typedef struct {
+ BOOLEAN rc_connected;
+ UINT8 rc_handle;
+ BD_ADDR rc_addr;
+ UINT16 rc_pending_play;
+} btif_rc_cb_t;
+
+#ifdef BTIF_RC_USE_UINPUT
+#define MAX_UINPUT_PATHS 3
+static const char* uinput_dev_path[] =
+ {"/dev/uinput", "/dev/input/uinput", "/dev/misc/uinput" };
+static int uinput_fd = -1;
+
+static int send_event (int fd, uint16_t type, uint16_t code, int32_t value);
+static void send_key (int fd, uint16_t key, int pressed);
+static int uinput_driver_check();
+static int uinput_create(char *name);
+static int init_uinput (void);
+static void close_uinput (void);
+
+static struct {
+ const char *name;
+ uint8_t avrcp;
+ uint16_t mapped_id;
+ uint8_t release_quirk;
+} key_map[] = {
+ { "PLAY", AVRC_ID_PLAY, KEY_PLAYCD, 1 },
+ { "STOP", AVRC_ID_STOP, KEY_STOPCD, 0 },
+ { "PAUSE", AVRC_ID_PAUSE, KEY_PAUSECD, 1 },
+ { "FORWARD", AVRC_ID_FORWARD, KEY_NEXTSONG, 0 },
+ { "BACKWARD", AVRC_ID_BACKWARD, KEY_PREVIOUSSONG, 0 },
+ { "REWIND", AVRC_ID_REWIND, KEY_REWIND, 0 },
+ { "FAST FORWARD", AVRC_ID_FAST_FOR, KEY_FORWARD, 0 },
+ { NULL, 0, 0, 0 }
+};
+#endif /* BTIF_RC_USE_UINPUT */
+
+
+/*****************************************************************************
+** Static variables
+******************************************************************************/
+static btif_rc_cb_t btif_rc_cb;
+
+/*****************************************************************************
+** Static functions
+******************************************************************************/
+
+/*****************************************************************************
+** Externs
+******************************************************************************/
+
+/*****************************************************************************
+** Functions
+******************************************************************************/
+
+
+#ifdef BTIF_RC_USE_UINPUT
+/*****************************************************************************
+** Local uinput helper functions
+******************************************************************************/
+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));
+}
+
+void send_key (int fd, uint16_t key, int pressed)
+{
+ if (fd < 0) {
+ return;
+ }
+
+ BTIF_TRACE_DEBUG3("AVRCP: Send key %d (%d) fd=%d", key, pressed, fd);
+ send_event(fd, EV_KEY, key, pressed);
+ send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+/************** uinput related functions **************/
+int uinput_driver_check()
+{
+ uint32_t i;
+ for (i=0; i < MAX_UINPUT_PATHS; i++)
+ {
+ if (access(uinput_dev_path[i], O_RDWR) == 0) {
+ return 0;
+ }
+ }
+ BTIF_TRACE_ERROR1("%s ERROR: uinput device is not in the system", __FUNCTION__);
+ return -1;
+}
+
+int uinput_create(char *name)
+{
+ struct uinput_dev dev;
+ int fd, err, x = 0;
+
+ for(x=0; x < MAX_UINPUT_PATHS; x++)
+ {
+ fd = open(uinput_dev_path[x], O_RDWR);
+ if (fd < 0)
+ continue;
+ break;
+ }
+ if (x == MAX_UINPUT_PATHS) {
+ BTIF_TRACE_ERROR1("%s ERROR: uinput device open failed", __FUNCTION__);
+ return -1;
+ }
+ 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) {
+ BTIF_TRACE_ERROR1("%s Unable to write device information", __FUNCTION__);
+ close(fd);
+ return -1;
+ }
+
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+ for (x = 0; key_map[x].name != NULL; x++)
+ ioctl(fd, UI_SET_KEYBIT, key_map[x].mapped_id);
+
+ for(x = 0; x < KEY_MAX; x++)
+ ioctl(fd, UI_SET_KEYBIT, x);
+
+ if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+ BTIF_TRACE_ERROR1("%s Unable to create uinput device", __FUNCTION__);
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+int init_uinput (void)
+{
+ char *name = "AVRCP";
+
+ BTIF_TRACE_DEBUG1("%s", __FUNCTION__);
+ uinput_fd = uinput_create(name);
+ if (uinput_fd < 0) {
+ BTIF_TRACE_ERROR3("%s AVRCP: Failed to initialize uinput for %s (%d)",
+ __FUNCTION__, name, uinput_fd);
+ } else {
+ BTIF_TRACE_DEBUG3("%s AVRCP: Initialized uinput for %s (fd=%d)",
+ __FUNCTION__, name, uinput_fd);
+ }
+ return uinput_fd;
+}
+
+void close_uinput (void)
+{
+ BTIF_TRACE_DEBUG1("%s", __FUNCTION__);
+ if (uinput_fd > 0) {
+ ioctl(uinput_fd, UI_DEV_DESTROY);
+
+ close(uinput_fd);
+ uinput_fd = -1;
+ }
+}
+#endif // BTA_AVRCP_FORCE_USE_UINPUT
+
+const char *dump_rc_event_name(tBTA_AV_EVT event)
+{
+ switch(event) {
+ case BTA_AV_RC_OPEN_EVT: return "BTA_AV_RC_OPEN_EVT";
+ case BTA_AV_RC_CLOSE_EVT: return "BTA_AV_RC_CLOSE_EVT";
+ case BTA_AV_REMOTE_CMD_EVT: return "BTA_AV_REMOTE_CMD_EVT";
+ case BTA_AV_REMOTE_RSP_EVT: return "BTA_AV_REMOTE_RSP_EVT";
+ case BTA_AV_VENDOR_CMD_EVT: return "BTA_AV_VENDOR_CMD_EVT";
+ case BTA_AV_VENDOR_RSP_EVT: return "BTA_AV_VENDOR_RSP_EVT";
+ default: return "UNKNOWN_EVENT";
+ }
+}
+
+/***************************************************************************
+ * Function handle_rc_connect
+ *
+ * - Argument: tBTA_AV_RC_OPEN RC open data structure
+ *
+ * - Description: RC connection event handler
+ *
+ ***************************************************************************/
+void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open)
+{
+ BTIF_TRACE_DEBUG2("%s: rc_handle: %d", __FUNCTION__, p_rc_open->rc_handle);
+
+#ifdef BTIF_RC_USE_UINPUT
+ init_uinput();
+#endif
+
+ memcpy(btif_rc_cb.rc_addr, p_rc_open->peer_addr, sizeof(BD_ADDR));
+ btif_rc_cb.rc_connected = TRUE;
+ btif_rc_cb.rc_handle = p_rc_open->rc_handle;
+}
+
+/***************************************************************************
+ * Function handle_rc_disconnect
+ *
+ * - Argument: tBTA_AV_RC_CLOSE RC close data structure
+ *
+ * - Description: RC disconnection event handler
+ *
+ ***************************************************************************/
+void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close)
+{
+ BTIF_TRACE_DEBUG2("%s: rc_handle: %d", __FUNCTION__, p_rc_close->rc_handle);
+
+ btif_rc_cb.rc_handle = 0;
+ btif_rc_cb.rc_connected = FALSE;
+ memset(btif_rc_cb.rc_addr, 0, sizeof(BD_ADDR));
+#ifdef BTIF_RC_USE_UINPUT
+ close_uinput();
+#endif /* BTIF_RC_USE_UINPUT */
+}
+
+/***************************************************************************
+ * Function handle_rc_passthrough_cmd
+ *
+ * - Argument: tBTA_AV_RC rc_id remote control command ID
+ * tBTA_AV_STATE key_state status of key press
+ *
+ * - Description: Remote control command handler
+ *
+ ***************************************************************************/
+void handle_rc_passthrough_cmd ( tBTA_AV_REMOTE_CMD *p_remote_cmd)
+{
+ const char *status;
+ int pressed, i;
+
+ if (p_remote_cmd->key_state == AVRC_STATE_RELEASE) {
+ status = "released";
+ pressed = 0;
+ } else {
+ status = "pressed";
+ pressed = 1;
+ }
+
+ for (i = 0; key_map[i].name != NULL; i++) {
+ if (p_remote_cmd->rc_id == key_map[i].avrcp) {
+ BTIF_TRACE_DEBUG3("%s: %s %s", __FUNCTION__, key_map[i].name, status);
+
+ /* MusicPlayer uses a long_press_timeout of 1 second for PLAYPAUSE button
+ * and maps that to autoshuffle. So if for some reason release for PLAY/PAUSE
+ * comes 1 second after the press, the MediaPlayer UI goes into a bad state.
+ * The reason for the delay could be sniff mode exit or some AVDTP procedure etc.
+ * The fix is to generate a release right after the press and drown the 'actual'
+ * release.
+ */
+ if ((key_map[i].release_quirk == 1) && (pressed == 0))
+ {
+ BTIF_TRACE_DEBUG2("%s: AVRC %s Release Faked earlier, drowned now",
+ __FUNCTION__, key_map[i].name);
+ return;
+ }
+#ifdef BTIF_RC_USE_UINPUT
+ send_key(uinput_fd, key_map[i].mapped_id, pressed);
+#endif
+ if ((key_map[i].release_quirk == 1) && (pressed == 1))
+ {
+ GKI_delay(30); // 30ms
+ BTIF_TRACE_DEBUG2("%s: AVRC %s Release quirk enabled, send release now",
+ __FUNCTION__, key_map[i].name);
+#ifdef BTIF_RC_USE_UINPUT
+ send_key(uinput_fd, key_map[i].mapped_id, 0);
+#endif
+ }
+ break;
+ }
+ }
+
+ if (key_map[i].name == NULL)
+ BTIF_TRACE_ERROR3("%s AVRCP: unknown button 0x%02X %s", __FUNCTION__,
+ p_remote_cmd->rc_id, status);
+}
+
+/*****************************************************************************
+**
+** Function btif_rc_init
+**
+** Description Initialize RC
+**
+** Returns Returns 0 on success, -1 otherwise
+**
+*******************************************************************************/
+int btif_rc_init()
+{
+ BTIF_TRACE_DEBUG1("%s", __FUNCTION__);
+ memset (&btif_rc_cb, 0, sizeof(btif_rc_cb));
+
+#ifdef BTIF_RC_USE_UINPUT
+ return uinput_driver_check();
+#endif /* BTIF_RC_USE_UINPUT */
+}
+
+/***************************************************************************
+ **
+ ** Function btif_rc_handler
+ **
+ ** Description RC event handler
+ **
+ ***************************************************************************/
+void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data)
+{
+ BTIF_TRACE_DEBUG2 ("%s event:%s", __FUNCTION__, dump_rc_event_name(event));
+ switch (event)
+ {
+ case BTA_AV_RC_OPEN_EVT:
+ {
+ BTIF_TRACE_DEBUG1("Peer_features:%x", p_data->rc_open.peer_features);
+ handle_rc_connect( &(p_data->rc_open) );
+ }break;
+
+ case BTA_AV_RC_CLOSE_EVT:
+ {
+ handle_rc_disconnect( &(p_data->rc_close) );
+ }break;
+
+ case BTA_AV_REMOTE_CMD_EVT:
+ {
+ BTIF_TRACE_DEBUG2("rc_id:0x%x key_state:%d", p_data->remote_cmd.rc_id,
+ p_data->remote_cmd.key_state);
+ handle_rc_passthrough_cmd( (&p_data->remote_cmd) );
+ }break;
+ default:
+ BTIF_TRACE_DEBUG0("Unhandled RC event");
+ }
+}
+
+/***************************************************************************
+ **
+ ** Function btif_rc_get_connected_peer
+ **
+ ** Description Fetches the connected headset's BD_ADDR if any
+ **
+ ***************************************************************************/
+BOOLEAN btif_rc_get_connected_peer(BD_ADDR peer_addr)
+{
+ if (btif_rc_cb.rc_connected == TRUE) {
+ bdcpy(peer_addr, btif_rc_cb.rc_addr);
+ return TRUE;
+ }
+ return FALSE;
+}