Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 1 | /* arch/arm/mach-msm/rpc_server_handset.c |
| 2 | * |
Stephen Boyd | 24db643 | 2012-04-25 11:40:08 -0700 | [diff] [blame] | 3 | * Copyright (c) 2008-2010,2012 Code Aurora Forum. All rights reserved. |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License version 2 and |
| 7 | * only version 2 as published by the Free Software Foundation. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/slab.h> |
| 16 | #include <linux/module.h> |
| 17 | #include <linux/kernel.h> |
| 18 | #include <linux/platform_device.h> |
| 19 | #include <linux/input.h> |
| 20 | #include <linux/switch.h> |
| 21 | |
| 22 | #include <asm/mach-types.h> |
| 23 | |
| 24 | #include <mach/msm_rpcrouter.h> |
| 25 | #include <mach/board.h> |
| 26 | #include <mach/rpc_server_handset.h> |
| 27 | |
| 28 | #define DRIVER_NAME "msm-handset" |
| 29 | |
| 30 | #define HS_SERVER_PROG 0x30000062 |
| 31 | #define HS_SERVER_VERS 0x00010001 |
| 32 | |
| 33 | #define HS_RPC_PROG 0x30000091 |
| 34 | |
| 35 | #define HS_PROCESS_CMD_PROC 0x02 |
| 36 | #define HS_SUBSCRIBE_SRVC_PROC 0x03 |
| 37 | #define HS_REPORT_EVNT_PROC 0x05 |
| 38 | #define HS_EVENT_CB_PROC 1 |
| 39 | #define HS_EVENT_DATA_VER 1 |
| 40 | |
| 41 | #define RPC_KEYPAD_NULL_PROC 0 |
| 42 | #define RPC_KEYPAD_PASS_KEY_CODE_PROC 2 |
| 43 | #define RPC_KEYPAD_SET_PWR_KEY_STATE_PROC 3 |
| 44 | |
| 45 | #define HS_PWR_K 0x6F /* Power key */ |
| 46 | #define HS_END_K 0x51 /* End key or Power key */ |
| 47 | #define HS_STEREO_HEADSET_K 0x82 |
| 48 | #define HS_HEADSET_SWITCH_K 0x84 |
| 49 | #define HS_HEADSET_SWITCH_2_K 0xF0 |
| 50 | #define HS_HEADSET_SWITCH_3_K 0xF1 |
| 51 | #define HS_HEADSET_HEADPHONE_K 0xF6 |
| 52 | #define HS_HEADSET_MICROPHONE_K 0xF7 |
| 53 | #define HS_REL_K 0xFF /* key release */ |
| 54 | |
| 55 | #define SW_HEADPHONE_INSERT_W_MIC 1 /* HS with mic */ |
| 56 | |
| 57 | #define KEY(hs_key, input_key) ((hs_key << 24) | input_key) |
| 58 | |
| 59 | enum hs_event { |
| 60 | HS_EVNT_EXT_PWR = 0, /* External Power status */ |
| 61 | HS_EVNT_HSD, /* Headset Detection */ |
| 62 | HS_EVNT_HSTD, /* Headset Type Detection */ |
| 63 | HS_EVNT_HSSD, /* Headset Switch Detection */ |
| 64 | HS_EVNT_KPD, |
| 65 | HS_EVNT_FLIP, /* Flip / Clamshell status (open/close) */ |
| 66 | HS_EVNT_CHARGER, /* Battery is being charged or not */ |
| 67 | HS_EVNT_ENV, /* Events from runtime environment like DEM */ |
| 68 | HS_EVNT_REM, /* Events received from HS counterpart on a |
| 69 | remote processor*/ |
| 70 | HS_EVNT_DIAG, /* Diag Events */ |
| 71 | HS_EVNT_LAST, /* Should always be the last event type */ |
| 72 | HS_EVNT_MAX /* Force enum to be an 32-bit number */ |
| 73 | }; |
| 74 | |
| 75 | enum hs_src_state { |
| 76 | HS_SRC_STATE_UNKWN = 0, |
| 77 | HS_SRC_STATE_LO, |
| 78 | HS_SRC_STATE_HI, |
| 79 | }; |
| 80 | |
| 81 | struct hs_event_data { |
| 82 | uint32_t ver; /* Version number */ |
| 83 | enum hs_event event_type; /* Event Type */ |
| 84 | enum hs_event enum_disc; /* discriminator */ |
| 85 | uint32_t data_length; /* length of the next field */ |
| 86 | enum hs_src_state data; /* Pointer to data */ |
| 87 | uint32_t data_size; /* Elements to be processed in data */ |
| 88 | }; |
| 89 | |
| 90 | enum hs_return_value { |
| 91 | HS_EKPDLOCKED = -2, /* Operation failed because keypad is locked */ |
| 92 | HS_ENOTSUPPORTED = -1, /* Functionality not supported */ |
| 93 | HS_FALSE = 0, /* Inquired condition is not true */ |
| 94 | HS_FAILURE = 0, /* Requested operation was not successful */ |
| 95 | HS_TRUE = 1, /* Inquired condition is true */ |
| 96 | HS_SUCCESS = 1, /* Requested operation was successful */ |
| 97 | HS_MAX_RETURN = 0x7FFFFFFF/* Force enum to be a 32 bit number */ |
| 98 | }; |
| 99 | |
| 100 | struct hs_key_data { |
| 101 | uint32_t ver; /* Version number to track sturcture changes */ |
| 102 | uint32_t code; /* which key? */ |
| 103 | uint32_t parm; /* key status. Up/down or pressed/released */ |
| 104 | }; |
| 105 | |
| 106 | enum hs_subs_srvc { |
| 107 | HS_SUBS_SEND_CMD = 0, /* Subscribe to send commands to HS */ |
| 108 | HS_SUBS_RCV_EVNT, /* Subscribe to receive Events from HS */ |
| 109 | HS_SUBS_SRVC_MAX |
| 110 | }; |
| 111 | |
| 112 | enum hs_subs_req { |
| 113 | HS_SUBS_REGISTER, /* Subscribe */ |
| 114 | HS_SUBS_CANCEL, /* Unsubscribe */ |
| 115 | HS_SUB_STATUS_MAX |
| 116 | }; |
| 117 | |
| 118 | enum hs_event_class { |
| 119 | HS_EVNT_CLASS_ALL = 0, /* All HS events */ |
| 120 | HS_EVNT_CLASS_LAST, /* Should always be the last class type */ |
| 121 | HS_EVNT_CLASS_MAX |
| 122 | }; |
| 123 | |
| 124 | enum hs_cmd_class { |
| 125 | HS_CMD_CLASS_LCD = 0, /* Send LCD related commands */ |
| 126 | HS_CMD_CLASS_KPD, /* Send KPD related commands */ |
| 127 | HS_CMD_CLASS_LAST, /* Should always be the last class type */ |
| 128 | HS_CMD_CLASS_MAX |
| 129 | }; |
| 130 | |
| 131 | /* |
| 132 | * Receive events or send command |
| 133 | */ |
| 134 | union hs_subs_class { |
| 135 | enum hs_event_class evnt; |
| 136 | enum hs_cmd_class cmd; |
| 137 | }; |
| 138 | |
| 139 | struct hs_subs { |
| 140 | uint32_t ver; |
| 141 | enum hs_subs_srvc srvc; /* commands or events */ |
| 142 | enum hs_subs_req req; /* subscribe or unsubscribe */ |
| 143 | uint32_t host_os; |
| 144 | enum hs_subs_req disc; /* discriminator */ |
| 145 | union hs_subs_class id; |
| 146 | }; |
| 147 | |
| 148 | struct hs_event_cb_recv { |
| 149 | uint32_t cb_id; |
| 150 | uint32_t hs_key_data_ptr; |
| 151 | struct hs_key_data key; |
| 152 | }; |
| 153 | enum hs_ext_cmd_type { |
| 154 | HS_EXT_CMD_KPD_SEND_KEY = 0, /* Send Key */ |
| 155 | HS_EXT_CMD_KPD_BKLT_CTRL, /* Keypad backlight intensity */ |
| 156 | HS_EXT_CMD_LCD_BKLT_CTRL, /* LCD Backlight intensity */ |
| 157 | HS_EXT_CMD_DIAG_KEYMAP, /* Emulating a Diag key sequence */ |
| 158 | HS_EXT_CMD_DIAG_LOCK, /* Device Lock/Unlock */ |
| 159 | HS_EXT_CMD_GET_EVNT_STATUS, /* Get the status for one of the drivers */ |
| 160 | HS_EXT_CMD_KPD_GET_KEYS_STATUS,/* Get a list of keys status */ |
| 161 | HS_EXT_CMD_KPD_SET_PWR_KEY_RST_THOLD, /* PWR Key HW Reset duration */ |
| 162 | HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD, /* Set pwr key threshold duration */ |
| 163 | HS_EXT_CMD_LAST, /* Should always be the last command type */ |
| 164 | HS_EXT_CMD_MAX = 0x7FFFFFFF /* Force enum to be an 32-bit number */ |
| 165 | }; |
| 166 | |
| 167 | struct hs_cmd_data_type { |
| 168 | uint32_t hs_cmd_data_type_ptr; /* hs_cmd_data_type ptr length */ |
| 169 | uint32_t ver; /* version */ |
| 170 | enum hs_ext_cmd_type id; /* command id */ |
| 171 | uint32_t handle; /* handle returned from subscribe proc */ |
| 172 | enum hs_ext_cmd_type disc_id1; /* discriminator id */ |
| 173 | uint32_t input_ptr; /* input ptr length */ |
| 174 | uint32_t input_val; /* command specific data */ |
| 175 | uint32_t input_len; /* length of command input */ |
| 176 | enum hs_ext_cmd_type disc_id2; /* discriminator id */ |
| 177 | uint32_t output_len; /* length of output data */ |
| 178 | uint32_t delayed; /* execution context for modem |
| 179 | true - caller context |
| 180 | false - hs task context*/ |
| 181 | }; |
| 182 | |
| 183 | static const uint32_t hs_key_map[] = { |
| 184 | KEY(HS_PWR_K, KEY_POWER), |
| 185 | KEY(HS_END_K, KEY_END), |
| 186 | KEY(HS_STEREO_HEADSET_K, SW_HEADPHONE_INSERT_W_MIC), |
| 187 | KEY(HS_HEADSET_HEADPHONE_K, SW_HEADPHONE_INSERT), |
| 188 | KEY(HS_HEADSET_MICROPHONE_K, SW_MICROPHONE_INSERT), |
| 189 | KEY(HS_HEADSET_SWITCH_K, KEY_MEDIA), |
| 190 | KEY(HS_HEADSET_SWITCH_2_K, KEY_VOLUMEUP), |
| 191 | KEY(HS_HEADSET_SWITCH_3_K, KEY_VOLUMEDOWN), |
| 192 | 0 |
| 193 | }; |
| 194 | |
| 195 | enum { |
| 196 | NO_DEVICE = 0, |
| 197 | MSM_HEADSET = 1, |
| 198 | }; |
| 199 | /* Add newer versions at the top of array */ |
| 200 | static const unsigned int rpc_vers[] = { |
| 201 | 0x00030001, |
| 202 | 0x00020001, |
| 203 | 0x00010001, |
| 204 | }; |
| 205 | /* hs subscription request parameters */ |
| 206 | struct hs_subs_rpc_req { |
| 207 | uint32_t hs_subs_ptr; |
| 208 | struct hs_subs hs_subs; |
| 209 | uint32_t hs_cb_id; |
| 210 | uint32_t hs_handle_ptr; |
| 211 | uint32_t hs_handle_data; |
| 212 | }; |
| 213 | |
| 214 | static struct hs_subs_rpc_req *hs_subs_req; |
| 215 | |
| 216 | struct msm_handset { |
| 217 | struct input_dev *ipdev; |
| 218 | struct switch_dev sdev; |
| 219 | struct msm_handset_platform_data *hs_pdata; |
| 220 | bool mic_on, hs_on; |
| 221 | }; |
| 222 | |
| 223 | static struct msm_rpc_client *rpc_client; |
| 224 | static struct msm_handset *hs; |
| 225 | |
| 226 | static int hs_find_key(uint32_t hscode) |
| 227 | { |
| 228 | int i, key; |
| 229 | |
| 230 | key = KEY(hscode, 0); |
| 231 | |
| 232 | for (i = 0; hs_key_map[i] != 0; i++) { |
| 233 | if ((hs_key_map[i] & 0xff000000) == key) |
| 234 | return hs_key_map[i] & 0x00ffffff; |
| 235 | } |
| 236 | return -1; |
| 237 | } |
| 238 | |
| 239 | static void update_state(void) |
| 240 | { |
| 241 | int state; |
| 242 | |
| 243 | if (hs->mic_on && hs->hs_on) |
| 244 | state = 1 << 0; |
| 245 | else if (hs->hs_on) |
| 246 | state = 1 << 1; |
| 247 | else if (hs->mic_on) |
| 248 | state = 1 << 2; |
| 249 | else |
| 250 | state = 0; |
| 251 | |
| 252 | switch_set_state(&hs->sdev, state); |
| 253 | } |
| 254 | |
| 255 | /* |
| 256 | * tuple format: (key_code, key_param) |
| 257 | * |
| 258 | * old-architecture: |
| 259 | * key-press = (key_code, 0) |
| 260 | * key-release = (0xff, key_code) |
| 261 | * |
| 262 | * new-architecutre: |
| 263 | * key-press = (key_code, 0) |
| 264 | * key-release = (key_code, 0xff) |
| 265 | */ |
| 266 | static void report_hs_key(uint32_t key_code, uint32_t key_parm) |
| 267 | { |
| 268 | int key, temp_key_code; |
| 269 | |
| 270 | if (key_code == HS_REL_K) |
| 271 | key = hs_find_key(key_parm); |
| 272 | else |
| 273 | key = hs_find_key(key_code); |
| 274 | |
| 275 | temp_key_code = key_code; |
| 276 | |
| 277 | if (key_parm == HS_REL_K) |
| 278 | key_code = key_parm; |
| 279 | |
| 280 | switch (key) { |
| 281 | case KEY_POWER: |
| 282 | case KEY_END: |
| 283 | case KEY_MEDIA: |
| 284 | case KEY_VOLUMEUP: |
| 285 | case KEY_VOLUMEDOWN: |
| 286 | input_report_key(hs->ipdev, key, (key_code != HS_REL_K)); |
| 287 | break; |
| 288 | case SW_HEADPHONE_INSERT_W_MIC: |
| 289 | hs->mic_on = hs->hs_on = (key_code != HS_REL_K) ? 1 : 0; |
| 290 | input_report_switch(hs->ipdev, SW_HEADPHONE_INSERT, |
| 291 | hs->hs_on); |
| 292 | input_report_switch(hs->ipdev, SW_MICROPHONE_INSERT, |
| 293 | hs->mic_on); |
| 294 | update_state(); |
| 295 | break; |
| 296 | |
| 297 | case SW_HEADPHONE_INSERT: |
| 298 | hs->hs_on = (key_code != HS_REL_K) ? 1 : 0; |
| 299 | input_report_switch(hs->ipdev, key, hs->hs_on); |
| 300 | update_state(); |
| 301 | break; |
| 302 | case SW_MICROPHONE_INSERT: |
| 303 | hs->mic_on = (key_code != HS_REL_K) ? 1 : 0; |
| 304 | input_report_switch(hs->ipdev, key, hs->mic_on); |
| 305 | update_state(); |
| 306 | break; |
| 307 | case -1: |
| 308 | printk(KERN_ERR "%s: No mapping for remote handset event %d\n", |
| 309 | __func__, temp_key_code); |
| 310 | return; |
| 311 | } |
| 312 | input_sync(hs->ipdev); |
| 313 | } |
| 314 | |
| 315 | static int handle_hs_rpc_call(struct msm_rpc_server *server, |
| 316 | struct rpc_request_hdr *req, unsigned len) |
| 317 | { |
| 318 | struct rpc_keypad_pass_key_code_args { |
| 319 | uint32_t key_code; |
| 320 | uint32_t key_parm; |
| 321 | }; |
| 322 | |
| 323 | switch (req->procedure) { |
| 324 | case RPC_KEYPAD_NULL_PROC: |
| 325 | return 0; |
| 326 | |
| 327 | case RPC_KEYPAD_PASS_KEY_CODE_PROC: { |
| 328 | struct rpc_keypad_pass_key_code_args *args; |
| 329 | |
| 330 | args = (struct rpc_keypad_pass_key_code_args *)(req + 1); |
| 331 | args->key_code = be32_to_cpu(args->key_code); |
| 332 | args->key_parm = be32_to_cpu(args->key_parm); |
| 333 | |
| 334 | report_hs_key(args->key_code, args->key_parm); |
| 335 | |
| 336 | return 0; |
| 337 | } |
| 338 | |
| 339 | case RPC_KEYPAD_SET_PWR_KEY_STATE_PROC: |
| 340 | /* This RPC function must be available for the ARM9 |
| 341 | * to function properly. This function is redundant |
| 342 | * when RPC_KEYPAD_PASS_KEY_CODE_PROC is handled. So |
| 343 | * input_report_key is not needed. |
| 344 | */ |
| 345 | return 0; |
| 346 | default: |
| 347 | return -ENODEV; |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | static struct msm_rpc_server hs_rpc_server = { |
| 352 | .prog = HS_SERVER_PROG, |
| 353 | .vers = HS_SERVER_VERS, |
| 354 | .rpc_call = handle_hs_rpc_call, |
| 355 | }; |
| 356 | |
| 357 | static int process_subs_srvc_callback(struct hs_event_cb_recv *recv) |
| 358 | { |
| 359 | if (!recv) |
| 360 | return -ENODATA; |
| 361 | |
| 362 | report_hs_key(be32_to_cpu(recv->key.code), be32_to_cpu(recv->key.parm)); |
| 363 | |
| 364 | return 0; |
| 365 | } |
| 366 | |
| 367 | static void process_hs_rpc_request(uint32_t proc, void *data) |
| 368 | { |
| 369 | if (proc == HS_EVENT_CB_PROC) |
| 370 | process_subs_srvc_callback(data); |
| 371 | else |
| 372 | pr_err("%s: unknown rpc proc %d\n", __func__, proc); |
| 373 | } |
| 374 | |
| 375 | static int hs_rpc_report_event_arg(struct msm_rpc_client *client, |
| 376 | void *buffer, void *data) |
| 377 | { |
| 378 | struct hs_event_rpc_req { |
| 379 | uint32_t hs_event_data_ptr; |
| 380 | struct hs_event_data data; |
| 381 | }; |
| 382 | |
| 383 | struct hs_event_rpc_req *req = buffer; |
| 384 | |
| 385 | req->hs_event_data_ptr = cpu_to_be32(0x1); |
| 386 | req->data.ver = cpu_to_be32(HS_EVENT_DATA_VER); |
| 387 | req->data.event_type = cpu_to_be32(HS_EVNT_HSD); |
| 388 | req->data.enum_disc = cpu_to_be32(HS_EVNT_HSD); |
| 389 | req->data.data_length = cpu_to_be32(0x1); |
| 390 | req->data.data = cpu_to_be32(*(enum hs_src_state *)data); |
| 391 | req->data.data_size = cpu_to_be32(sizeof(enum hs_src_state)); |
| 392 | |
| 393 | return sizeof(*req); |
| 394 | } |
| 395 | |
| 396 | static int hs_rpc_report_event_res(struct msm_rpc_client *client, |
| 397 | void *buffer, void *data) |
| 398 | { |
| 399 | enum hs_return_value result; |
| 400 | |
| 401 | result = be32_to_cpu(*(enum hs_return_value *)buffer); |
| 402 | pr_debug("%s: request completed: 0x%x\n", __func__, result); |
| 403 | |
| 404 | if (result == HS_SUCCESS) |
| 405 | return 0; |
| 406 | |
| 407 | return 1; |
| 408 | } |
| 409 | |
| 410 | void report_headset_status(bool connected) |
| 411 | { |
| 412 | int rc = -1; |
| 413 | enum hs_src_state status; |
| 414 | |
| 415 | if (connected == true) |
| 416 | status = HS_SRC_STATE_HI; |
| 417 | else |
| 418 | status = HS_SRC_STATE_LO; |
| 419 | |
| 420 | rc = msm_rpc_client_req(rpc_client, HS_REPORT_EVNT_PROC, |
| 421 | hs_rpc_report_event_arg, &status, |
| 422 | hs_rpc_report_event_res, NULL, -1); |
| 423 | |
| 424 | if (rc) |
| 425 | pr_err("%s: couldn't send rpc client request\n", __func__); |
| 426 | } |
| 427 | EXPORT_SYMBOL(report_headset_status); |
| 428 | |
| 429 | static int hs_rpc_pwr_cmd_arg(struct msm_rpc_client *client, |
| 430 | void *buffer, void *data) |
| 431 | { |
| 432 | struct hs_cmd_data_type *hs_pwr_cmd = buffer; |
| 433 | |
| 434 | hs_pwr_cmd->hs_cmd_data_type_ptr = cpu_to_be32(0x01); |
| 435 | |
| 436 | hs_pwr_cmd->ver = cpu_to_be32(0x03); |
| 437 | hs_pwr_cmd->id = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD); |
| 438 | hs_pwr_cmd->handle = cpu_to_be32(hs_subs_req->hs_handle_data); |
| 439 | hs_pwr_cmd->disc_id1 = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD); |
| 440 | hs_pwr_cmd->input_ptr = cpu_to_be32(0x01); |
| 441 | hs_pwr_cmd->input_val = cpu_to_be32(hs->hs_pdata->pwr_key_delay_ms); |
| 442 | hs_pwr_cmd->input_len = cpu_to_be32(0x01); |
| 443 | hs_pwr_cmd->disc_id2 = cpu_to_be32(HS_EXT_CMD_KPD_SET_PWR_KEY_THOLD); |
| 444 | hs_pwr_cmd->output_len = cpu_to_be32(0x00); |
| 445 | hs_pwr_cmd->delayed = cpu_to_be32(0x00); |
| 446 | |
| 447 | return sizeof(*hs_pwr_cmd); |
| 448 | } |
| 449 | |
| 450 | static int hs_rpc_pwr_cmd_res(struct msm_rpc_client *client, |
| 451 | void *buffer, void *data) |
| 452 | { |
| 453 | uint32_t result; |
| 454 | |
| 455 | result = be32_to_cpu(*((uint32_t *)buffer)); |
| 456 | pr_debug("%s: request completed: 0x%x\n", __func__, result); |
| 457 | |
| 458 | return 0; |
| 459 | } |
| 460 | |
| 461 | static int hs_rpc_register_subs_arg(struct msm_rpc_client *client, |
| 462 | void *buffer, void *data) |
| 463 | { |
| 464 | hs_subs_req = buffer; |
| 465 | |
| 466 | hs_subs_req->hs_subs_ptr = cpu_to_be32(0x1); |
| 467 | hs_subs_req->hs_subs.ver = cpu_to_be32(0x1); |
| 468 | hs_subs_req->hs_subs.srvc = cpu_to_be32(HS_SUBS_RCV_EVNT); |
| 469 | hs_subs_req->hs_subs.req = cpu_to_be32(HS_SUBS_REGISTER); |
| 470 | hs_subs_req->hs_subs.host_os = cpu_to_be32(0x4); /* linux */ |
| 471 | hs_subs_req->hs_subs.disc = cpu_to_be32(HS_SUBS_RCV_EVNT); |
| 472 | hs_subs_req->hs_subs.id.evnt = cpu_to_be32(HS_EVNT_CLASS_ALL); |
| 473 | |
| 474 | hs_subs_req->hs_cb_id = cpu_to_be32(0x1); |
| 475 | |
| 476 | hs_subs_req->hs_handle_ptr = cpu_to_be32(0x1); |
| 477 | hs_subs_req->hs_handle_data = cpu_to_be32(0x0); |
| 478 | |
| 479 | return sizeof(*hs_subs_req); |
| 480 | } |
| 481 | |
| 482 | static int hs_rpc_register_subs_res(struct msm_rpc_client *client, |
| 483 | void *buffer, void *data) |
| 484 | { |
| 485 | uint32_t result; |
| 486 | |
| 487 | result = be32_to_cpu(*((uint32_t *)buffer)); |
| 488 | pr_debug("%s: request completed: 0x%x\n", __func__, result); |
| 489 | |
| 490 | return 0; |
| 491 | } |
| 492 | |
| 493 | static int hs_cb_func(struct msm_rpc_client *client, void *buffer, int in_size) |
| 494 | { |
| 495 | int rc = -1; |
| 496 | |
| 497 | struct rpc_request_hdr *hdr = buffer; |
| 498 | |
| 499 | hdr->type = be32_to_cpu(hdr->type); |
| 500 | hdr->xid = be32_to_cpu(hdr->xid); |
| 501 | hdr->rpc_vers = be32_to_cpu(hdr->rpc_vers); |
| 502 | hdr->prog = be32_to_cpu(hdr->prog); |
| 503 | hdr->vers = be32_to_cpu(hdr->vers); |
| 504 | hdr->procedure = be32_to_cpu(hdr->procedure); |
| 505 | |
| 506 | process_hs_rpc_request(hdr->procedure, |
| 507 | (void *) (hdr + 1)); |
| 508 | |
| 509 | msm_rpc_start_accepted_reply(client, hdr->xid, |
| 510 | RPC_ACCEPTSTAT_SUCCESS); |
| 511 | rc = msm_rpc_send_accepted_reply(client, 0); |
| 512 | if (rc) { |
| 513 | pr_err("%s: sending reply failed: %d\n", __func__, rc); |
| 514 | return rc; |
| 515 | } |
| 516 | |
| 517 | return 0; |
| 518 | } |
| 519 | |
Stephen Boyd | 24db643 | 2012-04-25 11:40:08 -0700 | [diff] [blame] | 520 | static int __devinit hs_rpc_cb_init(void) |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 521 | { |
| 522 | int rc = 0, i, num_vers; |
| 523 | |
| 524 | num_vers = ARRAY_SIZE(rpc_vers); |
| 525 | |
| 526 | for (i = 0; i < num_vers; i++) { |
| 527 | rpc_client = msm_rpc_register_client("hs", |
| 528 | HS_RPC_PROG, rpc_vers[i], 0, hs_cb_func); |
| 529 | |
| 530 | if (IS_ERR(rpc_client)) |
| 531 | pr_debug("%s: RPC Client version %d failed, fallback\n", |
| 532 | __func__, rpc_vers[i]); |
| 533 | else |
| 534 | break; |
| 535 | } |
| 536 | |
| 537 | if (IS_ERR(rpc_client)) { |
| 538 | pr_err("%s: Incompatible RPC version error %ld\n", |
| 539 | __func__, PTR_ERR(rpc_client)); |
| 540 | return PTR_ERR(rpc_client); |
| 541 | } |
| 542 | |
| 543 | rc = msm_rpc_client_req(rpc_client, HS_SUBSCRIBE_SRVC_PROC, |
| 544 | hs_rpc_register_subs_arg, NULL, |
| 545 | hs_rpc_register_subs_res, NULL, -1); |
| 546 | if (rc) { |
| 547 | pr_err("%s: RPC client request failed for subscribe services\n", |
| 548 | __func__); |
| 549 | goto err_client_req; |
| 550 | } |
| 551 | |
| 552 | rc = msm_rpc_client_req(rpc_client, HS_PROCESS_CMD_PROC, |
| 553 | hs_rpc_pwr_cmd_arg, NULL, |
| 554 | hs_rpc_pwr_cmd_res, NULL, -1); |
| 555 | if (rc) |
| 556 | pr_err("%s: RPC client request failed for pwr key" |
| 557 | " delay cmd, using normal mode\n", __func__); |
| 558 | return 0; |
| 559 | err_client_req: |
| 560 | msm_rpc_unregister_client(rpc_client); |
| 561 | return rc; |
| 562 | } |
| 563 | |
| 564 | static int __devinit hs_rpc_init(void) |
| 565 | { |
| 566 | int rc; |
| 567 | |
| 568 | rc = hs_rpc_cb_init(); |
| 569 | if (rc) { |
| 570 | pr_err("%s: failed to initialize rpc client, try server...\n", |
| 571 | __func__); |
| 572 | |
| 573 | rc = msm_rpc_create_server(&hs_rpc_server); |
| 574 | if (rc) { |
| 575 | pr_err("%s: failed to create rpc server\n", __func__); |
| 576 | return rc; |
| 577 | } |
| 578 | } |
| 579 | |
| 580 | return rc; |
| 581 | } |
| 582 | |
| 583 | static void __devexit hs_rpc_deinit(void) |
| 584 | { |
| 585 | if (rpc_client) |
| 586 | msm_rpc_unregister_client(rpc_client); |
| 587 | } |
| 588 | |
| 589 | static ssize_t msm_headset_print_name(struct switch_dev *sdev, char *buf) |
| 590 | { |
| 591 | switch (switch_get_state(&hs->sdev)) { |
| 592 | case NO_DEVICE: |
| 593 | return sprintf(buf, "No Device\n"); |
| 594 | case MSM_HEADSET: |
| 595 | return sprintf(buf, "Headset\n"); |
| 596 | } |
| 597 | return -EINVAL; |
| 598 | } |
| 599 | |
| 600 | static int __devinit hs_probe(struct platform_device *pdev) |
| 601 | { |
| 602 | int rc = 0; |
| 603 | struct input_dev *ipdev; |
| 604 | |
| 605 | hs = kzalloc(sizeof(struct msm_handset), GFP_KERNEL); |
| 606 | if (!hs) |
| 607 | return -ENOMEM; |
| 608 | |
| 609 | hs->sdev.name = "h2w"; |
| 610 | hs->sdev.print_name = msm_headset_print_name; |
| 611 | |
| 612 | rc = switch_dev_register(&hs->sdev); |
| 613 | if (rc) |
| 614 | goto err_switch_dev_register; |
| 615 | |
| 616 | ipdev = input_allocate_device(); |
| 617 | if (!ipdev) { |
| 618 | rc = -ENOMEM; |
| 619 | goto err_alloc_input_dev; |
| 620 | } |
| 621 | input_set_drvdata(ipdev, hs); |
| 622 | |
| 623 | hs->ipdev = ipdev; |
| 624 | |
| 625 | if (pdev->dev.platform_data) |
| 626 | hs->hs_pdata = pdev->dev.platform_data; |
| 627 | |
| 628 | if (hs->hs_pdata->hs_name) |
| 629 | ipdev->name = hs->hs_pdata->hs_name; |
| 630 | else |
| 631 | ipdev->name = DRIVER_NAME; |
| 632 | |
| 633 | ipdev->id.vendor = 0x0001; |
| 634 | ipdev->id.product = 1; |
| 635 | ipdev->id.version = 1; |
| 636 | |
| 637 | input_set_capability(ipdev, EV_KEY, KEY_MEDIA); |
| 638 | input_set_capability(ipdev, EV_KEY, KEY_VOLUMEUP); |
| 639 | input_set_capability(ipdev, EV_KEY, KEY_VOLUMEDOWN); |
| 640 | input_set_capability(ipdev, EV_SW, SW_HEADPHONE_INSERT); |
| 641 | input_set_capability(ipdev, EV_SW, SW_MICROPHONE_INSERT); |
| 642 | input_set_capability(ipdev, EV_KEY, KEY_POWER); |
| 643 | input_set_capability(ipdev, EV_KEY, KEY_END); |
| 644 | |
| 645 | rc = input_register_device(ipdev); |
| 646 | if (rc) { |
| 647 | dev_err(&ipdev->dev, |
| 648 | "hs_probe: input_register_device rc=%d\n", rc); |
| 649 | goto err_reg_input_dev; |
| 650 | } |
| 651 | |
| 652 | platform_set_drvdata(pdev, hs); |
| 653 | |
| 654 | rc = hs_rpc_init(); |
| 655 | if (rc) { |
| 656 | dev_err(&ipdev->dev, "rpc init failure\n"); |
| 657 | goto err_hs_rpc_init; |
| 658 | } |
| 659 | |
| 660 | return 0; |
| 661 | |
| 662 | err_hs_rpc_init: |
| 663 | input_unregister_device(ipdev); |
| 664 | ipdev = NULL; |
| 665 | err_reg_input_dev: |
| 666 | input_free_device(ipdev); |
| 667 | err_alloc_input_dev: |
| 668 | switch_dev_unregister(&hs->sdev); |
| 669 | err_switch_dev_register: |
| 670 | kfree(hs); |
| 671 | return rc; |
| 672 | } |
| 673 | |
| 674 | static int __devexit hs_remove(struct platform_device *pdev) |
| 675 | { |
| 676 | struct msm_handset *hs = platform_get_drvdata(pdev); |
| 677 | |
| 678 | input_unregister_device(hs->ipdev); |
| 679 | switch_dev_unregister(&hs->sdev); |
| 680 | kfree(hs); |
| 681 | hs_rpc_deinit(); |
| 682 | return 0; |
| 683 | } |
| 684 | |
| 685 | static struct platform_driver hs_driver = { |
| 686 | .probe = hs_probe, |
| 687 | .remove = __devexit_p(hs_remove), |
| 688 | .driver = { |
| 689 | .name = DRIVER_NAME, |
| 690 | .owner = THIS_MODULE, |
| 691 | }, |
| 692 | }; |
| 693 | |
| 694 | static int __init hs_init(void) |
| 695 | { |
| 696 | return platform_driver_register(&hs_driver); |
| 697 | } |
| 698 | late_initcall(hs_init); |
| 699 | |
| 700 | static void __exit hs_exit(void) |
| 701 | { |
| 702 | platform_driver_unregister(&hs_driver); |
| 703 | } |
| 704 | module_exit(hs_exit); |
| 705 | |
| 706 | MODULE_LICENSE("GPL v2"); |
| 707 | MODULE_ALIAS("platform:msm-handset"); |