1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
234fc1322SPetri Gynther /*
334fc1322SPetri Gynther * HID driver for Google Fiber TV Box remote controls
434fc1322SPetri Gynther *
534fc1322SPetri Gynther * Copyright (c) 2014-2015 Google Inc.
634fc1322SPetri Gynther *
734fc1322SPetri Gynther * Author: Petri Gynther <pgynther@google.com>
834fc1322SPetri Gynther */
934fc1322SPetri Gynther #include <linux/device.h>
1034fc1322SPetri Gynther #include <linux/hid.h>
1134fc1322SPetri Gynther #include <linux/input.h>
1234fc1322SPetri Gynther #include <linux/module.h>
1334fc1322SPetri Gynther
1434fc1322SPetri Gynther #include "hid-ids.h"
1534fc1322SPetri Gynther
1634fc1322SPetri Gynther #define GFRM100 1 /* Google Fiber GFRM100 (Bluetooth classic) */
1734fc1322SPetri Gynther #define GFRM200 2 /* Google Fiber GFRM200 (Bluetooth LE) */
1834fc1322SPetri Gynther
1934fc1322SPetri Gynther #define GFRM100_SEARCH_KEY_REPORT_ID 0xF7
2034fc1322SPetri Gynther #define GFRM100_SEARCH_KEY_DOWN 0x0
2134fc1322SPetri Gynther #define GFRM100_SEARCH_KEY_AUDIO_DATA 0x1
2234fc1322SPetri Gynther #define GFRM100_SEARCH_KEY_UP 0x2
2334fc1322SPetri Gynther
2434fc1322SPetri Gynther static u8 search_key_dn[3] = {0x40, 0x21, 0x02};
2534fc1322SPetri Gynther static u8 search_key_up[3] = {0x40, 0x00, 0x00};
2634fc1322SPetri Gynther
gfrm_input_mapping(struct hid_device * hdev,struct hid_input * hi,struct hid_field * field,struct hid_usage * usage,unsigned long ** bit,int * max)2734fc1322SPetri Gynther static int gfrm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
2834fc1322SPetri Gynther struct hid_field *field, struct hid_usage *usage,
2934fc1322SPetri Gynther unsigned long **bit, int *max)
3034fc1322SPetri Gynther {
3134fc1322SPetri Gynther unsigned long hdev_type = (unsigned long) hid_get_drvdata(hdev);
3234fc1322SPetri Gynther
3334fc1322SPetri Gynther if (hdev_type == GFRM100) {
3434fc1322SPetri Gynther if (usage->hid == (HID_UP_CONSUMER | 0x4)) {
3534fc1322SPetri Gynther /* Consumer.0004 -> KEY_INFO */
3634fc1322SPetri Gynther hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_INFO);
3734fc1322SPetri Gynther return 1;
3834fc1322SPetri Gynther }
3934fc1322SPetri Gynther
4034fc1322SPetri Gynther if (usage->hid == (HID_UP_CONSUMER | 0x41)) {
4134fc1322SPetri Gynther /* Consumer.0041 -> KEY_OK */
4234fc1322SPetri Gynther hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_OK);
4334fc1322SPetri Gynther return 1;
4434fc1322SPetri Gynther }
4534fc1322SPetri Gynther }
4634fc1322SPetri Gynther
4734fc1322SPetri Gynther return 0;
4834fc1322SPetri Gynther }
4934fc1322SPetri Gynther
gfrm_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)5034fc1322SPetri Gynther static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
5134fc1322SPetri Gynther u8 *data, int size)
5234fc1322SPetri Gynther {
5334fc1322SPetri Gynther unsigned long hdev_type = (unsigned long) hid_get_drvdata(hdev);
5434fc1322SPetri Gynther int ret = 0;
5534fc1322SPetri Gynther
5634fc1322SPetri Gynther if (hdev_type != GFRM100)
5734fc1322SPetri Gynther return 0;
5834fc1322SPetri Gynther
5934fc1322SPetri Gynther if (size < 2 || data[0] != GFRM100_SEARCH_KEY_REPORT_ID)
6034fc1322SPetri Gynther return 0;
6134fc1322SPetri Gynther
6234fc1322SPetri Gynther /*
6334fc1322SPetri Gynther * Convert GFRM100 Search key reports into Consumer.0221 (Key.Search)
6434fc1322SPetri Gynther * reports. Ignore audio data.
6534fc1322SPetri Gynther */
6634fc1322SPetri Gynther switch (data[1]) {
6734fc1322SPetri Gynther case GFRM100_SEARCH_KEY_DOWN:
6834fc1322SPetri Gynther ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn,
6934fc1322SPetri Gynther sizeof(search_key_dn), 1);
7034fc1322SPetri Gynther break;
7134fc1322SPetri Gynther
7234fc1322SPetri Gynther case GFRM100_SEARCH_KEY_AUDIO_DATA:
7334fc1322SPetri Gynther break;
7434fc1322SPetri Gynther
7534fc1322SPetri Gynther case GFRM100_SEARCH_KEY_UP:
7634fc1322SPetri Gynther ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up,
7734fc1322SPetri Gynther sizeof(search_key_up), 1);
7834fc1322SPetri Gynther break;
7934fc1322SPetri Gynther
8034fc1322SPetri Gynther default:
8134fc1322SPetri Gynther break;
8234fc1322SPetri Gynther }
8334fc1322SPetri Gynther
8434fc1322SPetri Gynther return (ret < 0) ? ret : -1;
8534fc1322SPetri Gynther }
8634fc1322SPetri Gynther
gfrm_input_configured(struct hid_device * hid,struct hid_input * hidinput)87e60e063cSArnd Bergmann static int gfrm_input_configured(struct hid_device *hid, struct hid_input *hidinput)
8834fc1322SPetri Gynther {
8934fc1322SPetri Gynther /*
9034fc1322SPetri Gynther * Enable software autorepeat with:
9134fc1322SPetri Gynther * - repeat delay: 400 msec
9234fc1322SPetri Gynther * - repeat period: 100 msec
9334fc1322SPetri Gynther */
9434fc1322SPetri Gynther input_enable_softrepeat(hidinput->input, 400, 100);
95e60e063cSArnd Bergmann return 0;
9634fc1322SPetri Gynther }
9734fc1322SPetri Gynther
gfrm_probe(struct hid_device * hdev,const struct hid_device_id * id)9834fc1322SPetri Gynther static int gfrm_probe(struct hid_device *hdev, const struct hid_device_id *id)
9934fc1322SPetri Gynther {
10034fc1322SPetri Gynther int ret;
10134fc1322SPetri Gynther
10234fc1322SPetri Gynther hid_set_drvdata(hdev, (void *) id->driver_data);
10334fc1322SPetri Gynther
10434fc1322SPetri Gynther ret = hid_parse(hdev);
10534fc1322SPetri Gynther if (ret)
10634fc1322SPetri Gynther goto done;
10734fc1322SPetri Gynther
10834fc1322SPetri Gynther if (id->driver_data == GFRM100) {
10934fc1322SPetri Gynther /*
11034fc1322SPetri Gynther * GFRM100 HID Report Descriptor does not describe the Search
11134fc1322SPetri Gynther * key reports. Thus, we need to add it manually here, so that
11234fc1322SPetri Gynther * those reports reach gfrm_raw_event() from hid_input_report().
11334fc1322SPetri Gynther */
11434fc1322SPetri Gynther if (!hid_register_report(hdev, HID_INPUT_REPORT,
115f07b3c1dSBenjamin Tissoires GFRM100_SEARCH_KEY_REPORT_ID, 0)) {
11634fc1322SPetri Gynther ret = -ENOMEM;
11734fc1322SPetri Gynther goto done;
11834fc1322SPetri Gynther }
11934fc1322SPetri Gynther }
12034fc1322SPetri Gynther
12134fc1322SPetri Gynther ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
12234fc1322SPetri Gynther done:
12334fc1322SPetri Gynther return ret;
12434fc1322SPetri Gynther }
12534fc1322SPetri Gynther
12634fc1322SPetri Gynther static const struct hid_device_id gfrm_devices[] = {
12734fc1322SPetri Gynther { HID_BLUETOOTH_DEVICE(0x58, 0x2000),
12834fc1322SPetri Gynther .driver_data = GFRM100 },
12934fc1322SPetri Gynther { HID_BLUETOOTH_DEVICE(0x471, 0x2210),
13034fc1322SPetri Gynther .driver_data = GFRM200 },
13134fc1322SPetri Gynther { }
13234fc1322SPetri Gynther };
13334fc1322SPetri Gynther MODULE_DEVICE_TABLE(hid, gfrm_devices);
13434fc1322SPetri Gynther
13534fc1322SPetri Gynther static struct hid_driver gfrm_driver = {
13634fc1322SPetri Gynther .name = "gfrm",
13734fc1322SPetri Gynther .id_table = gfrm_devices,
13834fc1322SPetri Gynther .probe = gfrm_probe,
13934fc1322SPetri Gynther .input_mapping = gfrm_input_mapping,
14034fc1322SPetri Gynther .raw_event = gfrm_raw_event,
14134fc1322SPetri Gynther .input_configured = gfrm_input_configured,
14234fc1322SPetri Gynther };
14334fc1322SPetri Gynther
14434fc1322SPetri Gynther module_hid_driver(gfrm_driver);
14534fc1322SPetri Gynther
14634fc1322SPetri Gynther MODULE_AUTHOR("Petri Gynther <pgynther@google.com>");
14734fc1322SPetri Gynther MODULE_DESCRIPTION("Google Fiber TV Box remote control driver");
14834fc1322SPetri Gynther MODULE_LICENSE("GPL");
149