/****************************************************************************** * * 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 #include #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; }