162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for Google Fiber TV Box remote controls 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014-2015 Google Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Petri Gynther <pgynther@google.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/hid.h> 1162306a36Sopenharmony_ci#include <linux/input.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "hid-ids.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define GFRM100 1 /* Google Fiber GFRM100 (Bluetooth classic) */ 1762306a36Sopenharmony_ci#define GFRM200 2 /* Google Fiber GFRM200 (Bluetooth LE) */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define GFRM100_SEARCH_KEY_REPORT_ID 0xF7 2062306a36Sopenharmony_ci#define GFRM100_SEARCH_KEY_DOWN 0x0 2162306a36Sopenharmony_ci#define GFRM100_SEARCH_KEY_AUDIO_DATA 0x1 2262306a36Sopenharmony_ci#define GFRM100_SEARCH_KEY_UP 0x2 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic u8 search_key_dn[3] = {0x40, 0x21, 0x02}; 2562306a36Sopenharmony_cistatic u8 search_key_up[3] = {0x40, 0x00, 0x00}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int gfrm_input_mapping(struct hid_device *hdev, struct hid_input *hi, 2862306a36Sopenharmony_ci struct hid_field *field, struct hid_usage *usage, 2962306a36Sopenharmony_ci unsigned long **bit, int *max) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci unsigned long hdev_type = (unsigned long) hid_get_drvdata(hdev); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (hdev_type == GFRM100) { 3462306a36Sopenharmony_ci if (usage->hid == (HID_UP_CONSUMER | 0x4)) { 3562306a36Sopenharmony_ci /* Consumer.0004 -> KEY_INFO */ 3662306a36Sopenharmony_ci hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_INFO); 3762306a36Sopenharmony_ci return 1; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (usage->hid == (HID_UP_CONSUMER | 0x41)) { 4162306a36Sopenharmony_ci /* Consumer.0041 -> KEY_OK */ 4262306a36Sopenharmony_ci hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_OK); 4362306a36Sopenharmony_ci return 1; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report, 5162306a36Sopenharmony_ci u8 *data, int size) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci unsigned long hdev_type = (unsigned long) hid_get_drvdata(hdev); 5462306a36Sopenharmony_ci int ret = 0; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (hdev_type != GFRM100) 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (size < 2 || data[0] != GFRM100_SEARCH_KEY_REPORT_ID) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * Convert GFRM100 Search key reports into Consumer.0221 (Key.Search) 6462306a36Sopenharmony_ci * reports. Ignore audio data. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci switch (data[1]) { 6762306a36Sopenharmony_ci case GFRM100_SEARCH_KEY_DOWN: 6862306a36Sopenharmony_ci ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn, 6962306a36Sopenharmony_ci sizeof(search_key_dn), 1); 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci case GFRM100_SEARCH_KEY_AUDIO_DATA: 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci case GFRM100_SEARCH_KEY_UP: 7662306a36Sopenharmony_ci ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up, 7762306a36Sopenharmony_ci sizeof(search_key_up), 1); 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci default: 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return (ret < 0) ? ret : -1; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int gfrm_input_configured(struct hid_device *hid, struct hid_input *hidinput) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci /* 9062306a36Sopenharmony_ci * Enable software autorepeat with: 9162306a36Sopenharmony_ci * - repeat delay: 400 msec 9262306a36Sopenharmony_ci * - repeat period: 100 msec 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci input_enable_softrepeat(hidinput->input, 400, 100); 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int gfrm_probe(struct hid_device *hdev, const struct hid_device_id *id) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci int ret; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci hid_set_drvdata(hdev, (void *) id->driver_data); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ret = hid_parse(hdev); 10562306a36Sopenharmony_ci if (ret) 10662306a36Sopenharmony_ci goto done; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (id->driver_data == GFRM100) { 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * GFRM100 HID Report Descriptor does not describe the Search 11162306a36Sopenharmony_ci * key reports. Thus, we need to add it manually here, so that 11262306a36Sopenharmony_ci * those reports reach gfrm_raw_event() from hid_input_report(). 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci if (!hid_register_report(hdev, HID_INPUT_REPORT, 11562306a36Sopenharmony_ci GFRM100_SEARCH_KEY_REPORT_ID, 0)) { 11662306a36Sopenharmony_ci ret = -ENOMEM; 11762306a36Sopenharmony_ci goto done; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 12262306a36Sopenharmony_cidone: 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic const struct hid_device_id gfrm_devices[] = { 12762306a36Sopenharmony_ci { HID_BLUETOOTH_DEVICE(0x58, 0x2000), 12862306a36Sopenharmony_ci .driver_data = GFRM100 }, 12962306a36Sopenharmony_ci { HID_BLUETOOTH_DEVICE(0x471, 0x2210), 13062306a36Sopenharmony_ci .driver_data = GFRM200 }, 13162306a36Sopenharmony_ci { } 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, gfrm_devices); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct hid_driver gfrm_driver = { 13662306a36Sopenharmony_ci .name = "gfrm", 13762306a36Sopenharmony_ci .id_table = gfrm_devices, 13862306a36Sopenharmony_ci .probe = gfrm_probe, 13962306a36Sopenharmony_ci .input_mapping = gfrm_input_mapping, 14062306a36Sopenharmony_ci .raw_event = gfrm_raw_event, 14162306a36Sopenharmony_ci .input_configured = gfrm_input_configured, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cimodule_hid_driver(gfrm_driver); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciMODULE_AUTHOR("Petri Gynther <pgynther@google.com>"); 14762306a36Sopenharmony_ciMODULE_DESCRIPTION("Google Fiber TV Box remote control driver"); 14862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 149