715 lines
18 KiB
C
715 lines
18 KiB
C
/* arch/arm/mach-msm/rpc_server_handset.c
|
|
*
|
|
* Copyright (c) 2008-2010,2012 The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/input.h>
|
|
#include <linux/switch.h>
|
|
|
|
#include <asm/mach-types.h>
|
|
|
|
#include <mach/msm_rpcrouter.h>
|
|
#include <mach/board.h>
|
|
#include <mach/rpc_server_handset.h>
|
|
|
|
#define DRIVER_NAME "msm-handset"
|
|
|
|
#define HS_SERVER_PROG 0x30000062
|
|
#define HS_SERVER_VERS 0x00010001
|
|
|
|
#define HS_RPC_PROG 0x30000091
|
|
|
|
#define HS_PROCESS_CMD_PROC 0x02
|
|
#define HS_SUBSCRIBE_SRVC_PROC 0x03
|
|
#define HS_REPORT_EVNT_PROC 0x05
|
|
#define HS_EVENT_CB_PROC 1
|
|
#define HS_EVENT_DATA_VER 1
|
|
|
|
#define RPC_KEYPAD_NULL_PROC 0
|
|
#define RPC_KEYPAD_PASS_KEY_CODE_PROC 2
|
|
#define RPC_KEYPAD_SET_PWR_KEY_STATE_PROC 3
|
|
|
|
#define HS_PWR_K 0x6F /* Power key */
|
|
#define HS_END_K 0x51 /* End key or Power key */
|
|
#define HS_STEREO_HEADSET_K 0x82
|
|
#define HS_HEADSET_SWITCH_K 0x84
|
|
#define HS_HEADSET_SWITCH_2_K 0xF0
|
|
#define HS_HEADSET_SWITCH_3_K 0xF1
|
|
#define HS_HEADSET_HEADPHONE_K 0xF6
|
|
#define HS_HEADSET_MICROPHONE_K 0xF7
|
|
#define HS_REL_K 0xFF /* key release */
|
|
|
|
#define SW_HEADPHONE_INSERT_W_MIC 1 /* HS with mic */
|
|
|
|
#define KEY(hs_key, input_key) ((hs_key << 24) | input_key)
|
|
|
|
enum hs_event {
|
|
HS_EVNT_EXT_PWR = 0, /* External Power status */
|
|
HS_EVNT_HSD, /* Headset Detection */
|
|
HS_EVNT_HSTD, /* Headset Type Detection */
|
|
HS_EVNT_HSSD, /* Headset Switch Detection */
|
|
HS_EVNT_KPD,
|
|
HS_EVNT_FLIP, /* Flip / Clamshell status (open/close) */
|
|
HS_EVNT_CHARGER, /* Battery is being charged or not */
|
|
HS_EVNT_ENV, /* Events from runtime environment like DEM */
|
|
HS_EVNT_REM, /* Events received from HS counterpart on a
|
|
remote processor*/
|
|
HS_EVNT_DIAG, /* Diag Events */
|
|
HS_EVNT_LAST, /* Should always be the last event type */
|
|
HS_EVNT_MAX /* Force enum to be an 32-bit number */
|
|
};
|
|
|
|
enum hs_src_state {
|
|
HS_SRC_STATE_UNKWN = 0,
|
|
HS_SRC_STATE_LO,
|
|
HS_SRC_STATE_HI,
|
|
};
|
|
|
|
struct hs_event_data {
|
|
uint32_t ver; /* Version number */
|
|
enum hs_event event_type; /* Event Type */
|
|
enum hs_event enum_disc; /* discriminator */
|
|
uint32_t data_length; /* length of the next field */
|
|
enum hs_src_state data; /* Pointer to data */
|
|
uint32_t data_size; /* Elements to be processed in data */
|
|
};
|
|
|
|
enum hs_return_value {
|
|
HS_EKPDLOCKED = -2, /* Operation failed because keypad is locked */
|
|
HS_ENOTSUPPORTED = -1, /* Functionality not supported */
|
|
HS_FALSE = 0, /* Inquired condition is not true */
|
|
HS_FAILURE = 0, /* Requested operation was not successful */
|
|
HS_TRUE = 1, /* Inquired condition is true */
|
|
HS_SUCCESS = 1, /* Requested operation was successful */
|
|
HS_MAX_RETURN = 0x7FFFFFFF/* Force enum to be a 32 bit number */
|
|
};
|
|
|
|
struct hs_key_data {
|
|
uint32_t ver; /* Version number to track sturcture changes */
|
|
uint32_t code; /* which key? */
|
|
uint32_t parm; /* key status. Up/down or pressed/released */
|
|
};
|
|
|
|
enum hs_subs_srvc {
|
|
HS_SUBS_SEND_CMD = 0, /* Subscribe to send commands to HS */
|
|
HS_SUBS_RCV_EVNT, /* Subscribe to receive Events from HS */
|
|
HS_SUBS_SRVC_MAX
|
|
};
|
|
|
|
enum hs_subs_req {
|
|
HS_SUBS_REGISTER, /* Subscribe */
|
|
HS_SUBS_CANCEL, /* Unsubscribe */
|
|
HS_SUB_STATUS_MAX
|
|
};
|
|
|
|
enum hs_event_class {
|
|
HS_EVNT_CLASS_ALL = 0, /* All HS events */
|
|
HS_EVNT_CLASS_LAST, /* Should always be the last class type */
|
|
HS_EVNT_CLASS_MAX
|
|
};
|
|
|
|
enum hs_cmd_class {
|
|
HS_CMD_CLASS_LCD = 0, /* Send LCD related commands */
|
|
HS_CMD_CLASS_KPD, /* Send KPD related commands */
|
|
HS_CMD_CLASS_LAST, /* Should always be the last class type */
|
|
HS_CMD_CLASS_MAX
|
|
};
|
|
|
|
/*
|
|
* Receive events or send command
|
|
*/
|
|
union hs_subs_class {
|
|
enum hs_event_class evnt;
|
|
enum hs_cmd_class cmd;
|
|
};
|
|
|
|
struct hs_subs {
|
|
uint32_t ver;
|
|
enum hs_subs_srvc srvc; /* commands or events */
|
|
enum hs_subs_req req; /* subscribe or unsubscribe */
|
|
uint32_t host_os;
|
|
enum hs_subs_req disc; /* discriminator */
|
|
union hs_subs_class id;
|
|
};
|
|
|
|
struct hs_event_cb_recv {
|
|
uint32_t cb_id;
|
|
uint32_t hs_key_data_ptr;
|
|
struct hs_key_data key;
|
|
};
|
|
enum hs_ext_cmd_type {
|
|
HS_EXT_CMD_KPD_SEND_KEY = 0, /* Send Key */
|
|
HS_EXT_CMD_KPD_BKLT_CTRL, /* Keypad backlight intensity */
|
|
HS_EXT_CMD_LCD_BKLT_CTRL, /* LCD Backlight intensity */
|
|
HS_EXT_CMD_DIAG_KEYMAP, /* Emulating a Diag key sequence */
|
|
HS_EXT_CMD_DIAG_LOCK, /* Device Lock/Unlock */
|
|
HS_EXT_CMD_GET_EVNT_STATUS, /* Get the status for one of the drivers */
|
|
HS_EXT_CMD_KPD_GET_KEYS_STATUS,/* Get a list of keys status */
|
|
HS_EXT_CMD_KPD_SET_PWR_KEY_RST_THOLD, /* PWR Key HW Reset duration */
|
|
HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD, /* Set pwr key threshold duration */
|
|
HS_EXT_CMD_LAST, /* Should always be the last command type */
|
|
HS_EXT_CMD_MAX = 0x7FFFFFFF /* Force enum to be an 32-bit number */
|
|
};
|
|
|
|
struct hs_cmd_data_type {
|
|
uint32_t hs_cmd_data_type_ptr; /* hs_cmd_data_type ptr length */
|
|
uint32_t ver; /* version */
|
|
enum hs_ext_cmd_type id; /* command id */
|
|
uint32_t handle; /* handle returned from subscribe proc */
|
|
enum hs_ext_cmd_type disc_id1; /* discriminator id */
|
|
uint32_t input_ptr; /* input ptr length */
|
|
uint32_t input_val; /* command specific data */
|
|
uint32_t input_len; /* length of command input */
|
|
enum hs_ext_cmd_type disc_id2; /* discriminator id */
|
|
uint32_t output_len; /* length of output data */
|
|
uint32_t delayed; /* execution context for modem
|
|
true - caller context
|
|
false - hs task context*/
|
|
};
|
|
|
|
static const uint32_t hs_key_map[] = {
|
|
KEY(HS_PWR_K, KEY_POWER),
|
|
KEY(HS_END_K, KEY_END),
|
|
KEY(HS_STEREO_HEADSET_K, SW_HEADPHONE_INSERT_W_MIC),
|
|
KEY(HS_HEADSET_HEADPHONE_K, SW_HEADPHONE_INSERT),
|
|
KEY(HS_HEADSET_MICROPHONE_K, SW_MICROPHONE_INSERT),
|
|
KEY(HS_HEADSET_SWITCH_K, KEY_MEDIA),
|
|
KEY(HS_HEADSET_SWITCH_2_K, KEY_VOLUMEUP),
|
|
KEY(HS_HEADSET_SWITCH_3_K, KEY_VOLUMEDOWN),
|
|
0
|
|
};
|
|
|
|
enum {
|
|
NO_DEVICE = 0,
|
|
MSM_HEADSET = 1,
|
|
};
|
|
/* Add newer versions at the top of array */
|
|
static const unsigned int rpc_vers[] = {
|
|
0x00030001,
|
|
0x00020001,
|
|
0x00010001,
|
|
};
|
|
/* hs subscription request parameters */
|
|
struct hs_subs_rpc_req {
|
|
uint32_t hs_subs_ptr;
|
|
struct hs_subs hs_subs;
|
|
uint32_t hs_cb_id;
|
|
uint32_t hs_handle_ptr;
|
|
uint32_t hs_handle_data;
|
|
};
|
|
|
|
static struct hs_subs_rpc_req *hs_subs_req;
|
|
|
|
struct msm_handset {
|
|
struct input_dev *ipdev;
|
|
struct switch_dev sdev;
|
|
struct msm_handset_platform_data *hs_pdata;
|
|
bool mic_on, hs_on;
|
|
};
|
|
|
|
static struct msm_rpc_client *rpc_client;
|
|
static struct msm_handset *hs;
|
|
|
|
static int hs_find_key(uint32_t hscode)
|
|
{
|
|
int i, key;
|
|
|
|
key = KEY(hscode, 0);
|
|
|
|
for (i = 0; hs_key_map[i] != 0; i++) {
|
|
if ((hs_key_map[i] & 0xff000000) == key)
|
|
return hs_key_map[i] & 0x00ffffff;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void update_state(void)
|
|
{
|
|
int state;
|
|
|
|
if (hs->mic_on && hs->hs_on)
|
|
state = 1 << 0;
|
|
else if (hs->hs_on)
|
|
state = 1 << 1;
|
|
else if (hs->mic_on)
|
|
state = 1 << 2;
|
|
else
|
|
state = 0;
|
|
|
|
switch_set_state(&hs->sdev, state);
|
|
}
|
|
|
|
/*
|
|
* tuple format: (key_code, key_param)
|
|
*
|
|
* old-architecture:
|
|
* key-press = (key_code, 0)
|
|
* key-release = (0xff, key_code)
|
|
*
|
|
* new-architecutre:
|
|
* key-press = (key_code, 0)
|
|
* key-release = (key_code, 0xff)
|
|
*/
|
|
static void report_hs_key(uint32_t key_code, uint32_t key_parm)
|
|
{
|
|
int key, temp_key_code;
|
|
|
|
if (key_code == HS_REL_K)
|
|
key = hs_find_key(key_parm);
|
|
else
|
|
key = hs_find_key(key_code);
|
|
|
|
temp_key_code = key_code;
|
|
|
|
if (key_parm == HS_REL_K)
|
|
key_code = key_parm;
|
|
|
|
switch (key) {
|
|
case KEY_POWER:
|
|
case KEY_END:
|
|
if (hs->hs_pdata->ignore_end_key)
|
|
input_report_key(hs->ipdev, KEY_POWER,
|
|
(key_code != HS_REL_K));
|
|
else
|
|
input_report_key(hs->ipdev, key,
|
|
(key_code != HS_REL_K));
|
|
break;
|
|
case KEY_MEDIA:
|
|
case KEY_VOLUMEUP:
|
|
case KEY_VOLUMEDOWN:
|
|
input_report_key(hs->ipdev, key, (key_code != HS_REL_K));
|
|
break;
|
|
case SW_HEADPHONE_INSERT_W_MIC:
|
|
hs->mic_on = hs->hs_on = (key_code != HS_REL_K) ? 1 : 0;
|
|
input_report_switch(hs->ipdev, SW_HEADPHONE_INSERT,
|
|
hs->hs_on);
|
|
input_report_switch(hs->ipdev, SW_MICROPHONE_INSERT,
|
|
hs->mic_on);
|
|
update_state();
|
|
break;
|
|
|
|
case SW_HEADPHONE_INSERT:
|
|
hs->hs_on = (key_code != HS_REL_K) ? 1 : 0;
|
|
input_report_switch(hs->ipdev, key, hs->hs_on);
|
|
update_state();
|
|
break;
|
|
case SW_MICROPHONE_INSERT:
|
|
hs->mic_on = (key_code != HS_REL_K) ? 1 : 0;
|
|
input_report_switch(hs->ipdev, key, hs->mic_on);
|
|
update_state();
|
|
break;
|
|
case -1:
|
|
printk(KERN_ERR "%s: No mapping for remote handset event %d\n",
|
|
__func__, temp_key_code);
|
|
return;
|
|
}
|
|
input_sync(hs->ipdev);
|
|
}
|
|
|
|
static int handle_hs_rpc_call(struct msm_rpc_server *server,
|
|
struct rpc_request_hdr *req, unsigned len)
|
|
{
|
|
struct rpc_keypad_pass_key_code_args {
|
|
uint32_t key_code;
|
|
uint32_t key_parm;
|
|
};
|
|
|
|
switch (req->procedure) {
|
|
case RPC_KEYPAD_NULL_PROC:
|
|
return 0;
|
|
|
|
case RPC_KEYPAD_PASS_KEY_CODE_PROC: {
|
|
struct rpc_keypad_pass_key_code_args *args;
|
|
|
|
args = (struct rpc_keypad_pass_key_code_args *)(req + 1);
|
|
args->key_code = be32_to_cpu(args->key_code);
|
|
args->key_parm = be32_to_cpu(args->key_parm);
|
|
|
|
report_hs_key(args->key_code, args->key_parm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case RPC_KEYPAD_SET_PWR_KEY_STATE_PROC:
|
|
/* This RPC function must be available for the ARM9
|
|
* to function properly. This function is redundant
|
|
* when RPC_KEYPAD_PASS_KEY_CODE_PROC is handled. So
|
|
* input_report_key is not needed.
|
|
*/
|
|
return 0;
|
|
default:
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
static struct msm_rpc_server hs_rpc_server = {
|
|
.prog = HS_SERVER_PROG,
|
|
.vers = HS_SERVER_VERS,
|
|
.rpc_call = handle_hs_rpc_call,
|
|
};
|
|
|
|
static int process_subs_srvc_callback(struct hs_event_cb_recv *recv)
|
|
{
|
|
if (!recv)
|
|
return -ENODATA;
|
|
|
|
report_hs_key(be32_to_cpu(recv->key.code), be32_to_cpu(recv->key.parm));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void process_hs_rpc_request(uint32_t proc, void *data)
|
|
{
|
|
if (proc == HS_EVENT_CB_PROC)
|
|
process_subs_srvc_callback(data);
|
|
else
|
|
pr_err("%s: unknown rpc proc %d\n", __func__, proc);
|
|
}
|
|
|
|
static int hs_rpc_report_event_arg(struct msm_rpc_client *client,
|
|
void *buffer, void *data)
|
|
{
|
|
struct hs_event_rpc_req {
|
|
uint32_t hs_event_data_ptr;
|
|
struct hs_event_data data;
|
|
};
|
|
|
|
struct hs_event_rpc_req *req = buffer;
|
|
|
|
req->hs_event_data_ptr = cpu_to_be32(0x1);
|
|
req->data.ver = cpu_to_be32(HS_EVENT_DATA_VER);
|
|
req->data.event_type = cpu_to_be32(HS_EVNT_HSD);
|
|
req->data.enum_disc = cpu_to_be32(HS_EVNT_HSD);
|
|
req->data.data_length = cpu_to_be32(0x1);
|
|
req->data.data = cpu_to_be32(*(enum hs_src_state *)data);
|
|
req->data.data_size = cpu_to_be32(sizeof(enum hs_src_state));
|
|
|
|
return sizeof(*req);
|
|
}
|
|
|
|
static int hs_rpc_report_event_res(struct msm_rpc_client *client,
|
|
void *buffer, void *data)
|
|
{
|
|
enum hs_return_value result;
|
|
|
|
result = be32_to_cpu(*(enum hs_return_value *)buffer);
|
|
pr_debug("%s: request completed: 0x%x\n", __func__, result);
|
|
|
|
if (result == HS_SUCCESS)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void report_headset_status(bool connected)
|
|
{
|
|
int rc = -1;
|
|
enum hs_src_state status;
|
|
|
|
if (connected == true)
|
|
status = HS_SRC_STATE_HI;
|
|
else
|
|
status = HS_SRC_STATE_LO;
|
|
|
|
rc = msm_rpc_client_req(rpc_client, HS_REPORT_EVNT_PROC,
|
|
hs_rpc_report_event_arg, &status,
|
|
hs_rpc_report_event_res, NULL, -1);
|
|
|
|
if (rc)
|
|
pr_err("%s: couldn't send rpc client request\n", __func__);
|
|
}
|
|
EXPORT_SYMBOL(report_headset_status);
|
|
|
|
static int hs_rpc_pwr_cmd_arg(struct msm_rpc_client *client,
|
|
void *buffer, void *data)
|
|
{
|
|
struct hs_cmd_data_type *hs_pwr_cmd = buffer;
|
|
|
|
hs_pwr_cmd->hs_cmd_data_type_ptr = cpu_to_be32(0x01);
|
|
|
|
hs_pwr_cmd->ver = cpu_to_be32(0x03);
|
|
hs_pwr_cmd->id = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD);
|
|
hs_pwr_cmd->handle = cpu_to_be32(hs_subs_req->hs_handle_data);
|
|
hs_pwr_cmd->disc_id1 = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD);
|
|
hs_pwr_cmd->input_ptr = cpu_to_be32(0x01);
|
|
hs_pwr_cmd->input_val = cpu_to_be32(hs->hs_pdata->pwr_key_delay_ms);
|
|
hs_pwr_cmd->input_len = cpu_to_be32(0x01);
|
|
hs_pwr_cmd->disc_id2 = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD);
|
|
hs_pwr_cmd->output_len = cpu_to_be32(0x00);
|
|
hs_pwr_cmd->delayed = cpu_to_be32(0x00);
|
|
|
|
return sizeof(*hs_pwr_cmd);
|
|
}
|
|
|
|
static int hs_rpc_pwr_cmd_res(struct msm_rpc_client *client,
|
|
void *buffer, void *data)
|
|
{
|
|
uint32_t result;
|
|
|
|
result = be32_to_cpu(*((uint32_t *)buffer));
|
|
pr_debug("%s: request completed: 0x%x\n", __func__, result);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hs_rpc_register_subs_arg(struct msm_rpc_client *client,
|
|
void *buffer, void *data)
|
|
{
|
|
hs_subs_req = buffer;
|
|
|
|
hs_subs_req->hs_subs_ptr = cpu_to_be32(0x1);
|
|
hs_subs_req->hs_subs.ver = cpu_to_be32(0x1);
|
|
hs_subs_req->hs_subs.srvc = cpu_to_be32(HS_SUBS_RCV_EVNT);
|
|
hs_subs_req->hs_subs.req = cpu_to_be32(HS_SUBS_REGISTER);
|
|
hs_subs_req->hs_subs.host_os = cpu_to_be32(0x4); /* linux */
|
|
hs_subs_req->hs_subs.disc = cpu_to_be32(HS_SUBS_RCV_EVNT);
|
|
hs_subs_req->hs_subs.id.evnt = cpu_to_be32(HS_EVNT_CLASS_ALL);
|
|
|
|
hs_subs_req->hs_cb_id = cpu_to_be32(0x1);
|
|
|
|
hs_subs_req->hs_handle_ptr = cpu_to_be32(0x1);
|
|
hs_subs_req->hs_handle_data = cpu_to_be32(0x0);
|
|
|
|
return sizeof(*hs_subs_req);
|
|
}
|
|
|
|
static int hs_rpc_register_subs_res(struct msm_rpc_client *client,
|
|
void *buffer, void *data)
|
|
{
|
|
uint32_t result;
|
|
|
|
result = be32_to_cpu(*((uint32_t *)buffer));
|
|
pr_debug("%s: request completed: 0x%x\n", __func__, result);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hs_cb_func(struct msm_rpc_client *client, void *buffer, int in_size)
|
|
{
|
|
int rc = -1;
|
|
|
|
struct rpc_request_hdr *hdr = buffer;
|
|
|
|
hdr->type = be32_to_cpu(hdr->type);
|
|
hdr->xid = be32_to_cpu(hdr->xid);
|
|
hdr->rpc_vers = be32_to_cpu(hdr->rpc_vers);
|
|
hdr->prog = be32_to_cpu(hdr->prog);
|
|
hdr->vers = be32_to_cpu(hdr->vers);
|
|
hdr->procedure = be32_to_cpu(hdr->procedure);
|
|
|
|
process_hs_rpc_request(hdr->procedure,
|
|
(void *) (hdr + 1));
|
|
|
|
msm_rpc_start_accepted_reply(client, hdr->xid,
|
|
RPC_ACCEPTSTAT_SUCCESS);
|
|
rc = msm_rpc_send_accepted_reply(client, 0);
|
|
if (rc) {
|
|
pr_err("%s: sending reply failed: %d\n", __func__, rc);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __devinit hs_rpc_cb_init(void)
|
|
{
|
|
int rc = 0, i, num_vers;
|
|
|
|
num_vers = ARRAY_SIZE(rpc_vers);
|
|
|
|
for (i = 0; i < num_vers; i++) {
|
|
rpc_client = msm_rpc_register_client("hs",
|
|
HS_RPC_PROG, rpc_vers[i], 0, hs_cb_func);
|
|
|
|
if (IS_ERR(rpc_client))
|
|
pr_debug("%s: RPC Client version %d failed, fallback\n",
|
|
__func__, rpc_vers[i]);
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (IS_ERR(rpc_client)) {
|
|
pr_err("%s: Incompatible RPC version error %ld\n",
|
|
__func__, PTR_ERR(rpc_client));
|
|
return PTR_ERR(rpc_client);
|
|
}
|
|
|
|
rc = msm_rpc_client_req(rpc_client, HS_SUBSCRIBE_SRVC_PROC,
|
|
hs_rpc_register_subs_arg, NULL,
|
|
hs_rpc_register_subs_res, NULL, -1);
|
|
if (rc) {
|
|
pr_err("%s: RPC client request failed for subscribe services\n",
|
|
__func__);
|
|
goto err_client_req;
|
|
}
|
|
|
|
rc = msm_rpc_client_req(rpc_client, HS_PROCESS_CMD_PROC,
|
|
hs_rpc_pwr_cmd_arg, NULL,
|
|
hs_rpc_pwr_cmd_res, NULL, -1);
|
|
if (rc)
|
|
pr_err("%s: RPC client request failed for pwr key"
|
|
" delay cmd, using normal mode\n", __func__);
|
|
return 0;
|
|
err_client_req:
|
|
msm_rpc_unregister_client(rpc_client);
|
|
return rc;
|
|
}
|
|
|
|
static int __devinit hs_rpc_init(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = hs_rpc_cb_init();
|
|
if (rc) {
|
|
pr_err("%s: failed to initialize rpc client, try server...\n",
|
|
__func__);
|
|
|
|
rc = msm_rpc_create_server(&hs_rpc_server);
|
|
if (rc) {
|
|
pr_err("%s: failed to create rpc server\n", __func__);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void __devexit hs_rpc_deinit(void)
|
|
{
|
|
if (rpc_client)
|
|
msm_rpc_unregister_client(rpc_client);
|
|
}
|
|
|
|
static ssize_t msm_headset_print_name(struct switch_dev *sdev, char *buf)
|
|
{
|
|
switch (switch_get_state(&hs->sdev)) {
|
|
case NO_DEVICE:
|
|
return sprintf(buf, "No Device\n");
|
|
case MSM_HEADSET:
|
|
return sprintf(buf, "Headset\n");
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int __devinit hs_probe(struct platform_device *pdev)
|
|
{
|
|
int rc = 0;
|
|
struct input_dev *ipdev;
|
|
|
|
hs = kzalloc(sizeof(struct msm_handset), GFP_KERNEL);
|
|
if (!hs)
|
|
return -ENOMEM;
|
|
|
|
hs->sdev.name = "h2w";
|
|
hs->sdev.print_name = msm_headset_print_name;
|
|
|
|
rc = switch_dev_register(&hs->sdev);
|
|
if (rc)
|
|
goto err_switch_dev_register;
|
|
|
|
ipdev = input_allocate_device();
|
|
if (!ipdev) {
|
|
rc = -ENOMEM;
|
|
goto err_alloc_input_dev;
|
|
}
|
|
input_set_drvdata(ipdev, hs);
|
|
|
|
hs->ipdev = ipdev;
|
|
|
|
if (pdev->dev.platform_data)
|
|
hs->hs_pdata = pdev->dev.platform_data;
|
|
|
|
if (hs->hs_pdata->hs_name)
|
|
ipdev->name = hs->hs_pdata->hs_name;
|
|
else
|
|
ipdev->name = DRIVER_NAME;
|
|
|
|
ipdev->id.vendor = 0x0001;
|
|
ipdev->id.product = 1;
|
|
ipdev->id.version = 1;
|
|
|
|
input_set_capability(ipdev, EV_KEY, KEY_MEDIA);
|
|
input_set_capability(ipdev, EV_KEY, KEY_VOLUMEUP);
|
|
input_set_capability(ipdev, EV_KEY, KEY_VOLUMEDOWN);
|
|
input_set_capability(ipdev, EV_SW, SW_HEADPHONE_INSERT);
|
|
input_set_capability(ipdev, EV_SW, SW_MICROPHONE_INSERT);
|
|
input_set_capability(ipdev, EV_KEY, KEY_POWER);
|
|
input_set_capability(ipdev, EV_KEY, KEY_END);
|
|
|
|
rc = input_register_device(ipdev);
|
|
if (rc) {
|
|
dev_err(&ipdev->dev,
|
|
"hs_probe: input_register_device rc=%d\n", rc);
|
|
goto err_reg_input_dev;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, hs);
|
|
|
|
rc = hs_rpc_init();
|
|
if (rc) {
|
|
dev_err(&ipdev->dev, "rpc init failure\n");
|
|
goto err_hs_rpc_init;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_hs_rpc_init:
|
|
input_unregister_device(ipdev);
|
|
ipdev = NULL;
|
|
err_reg_input_dev:
|
|
input_free_device(ipdev);
|
|
err_alloc_input_dev:
|
|
switch_dev_unregister(&hs->sdev);
|
|
err_switch_dev_register:
|
|
kfree(hs);
|
|
return rc;
|
|
}
|
|
|
|
static int __devexit hs_remove(struct platform_device *pdev)
|
|
{
|
|
struct msm_handset *hs = platform_get_drvdata(pdev);
|
|
|
|
input_unregister_device(hs->ipdev);
|
|
switch_dev_unregister(&hs->sdev);
|
|
kfree(hs);
|
|
hs_rpc_deinit();
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver hs_driver = {
|
|
.probe = hs_probe,
|
|
.remove = __devexit_p(hs_remove),
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static int __init hs_init(void)
|
|
{
|
|
return platform_driver_register(&hs_driver);
|
|
}
|
|
late_initcall(hs_init);
|
|
|
|
static void __exit hs_exit(void)
|
|
{
|
|
platform_driver_unregister(&hs_driver);
|
|
}
|
|
module_exit(hs_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:msm-handset");
|