xref: /openbmc/linux/drivers/hid/hid-vivaldi.c (revision 45ceaf14)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * HID support for Vivaldi Keyboard
4  *
5  * Copyright 2020 Google LLC.
6  * Author: Sean O'Brien <seobrien@chromium.org>
7  */
8 
9 #include <linux/device.h>
10 #include <linux/hid.h>
11 #include <linux/input/vivaldi-fmap.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/sysfs.h>
15 
16 #define MIN_FN_ROW_KEY 1
17 #define MAX_FN_ROW_KEY VIVALDI_MAX_FUNCTION_ROW_KEYS
18 #define HID_VD_FN_ROW_PHYSMAP 0x00000001
19 #define HID_USAGE_FN_ROW_PHYSMAP (HID_UP_GOOGLEVENDOR | HID_VD_FN_ROW_PHYSMAP)
20 
21 static ssize_t function_row_physmap_show(struct device *dev,
22 					 struct device_attribute *attr,
23 					 char *buf)
24 {
25 	struct hid_device *hdev = to_hid_device(dev);
26 	struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
27 
28 	return vivaldi_function_row_physmap_show(drvdata, buf);
29 }
30 
31 static DEVICE_ATTR_RO(function_row_physmap);
32 static struct attribute *sysfs_attrs[] = {
33 	&dev_attr_function_row_physmap.attr,
34 	NULL
35 };
36 
37 static const struct attribute_group input_attribute_group = {
38 	.attrs = sysfs_attrs
39 };
40 
41 static int vivaldi_probe(struct hid_device *hdev,
42 			 const struct hid_device_id *id)
43 {
44 	struct vivaldi_data *drvdata;
45 	int ret;
46 
47 	drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
48 	if (!drvdata)
49 		return -ENOMEM;
50 
51 	hid_set_drvdata(hdev, drvdata);
52 
53 	ret = hid_parse(hdev);
54 	if (ret)
55 		return ret;
56 
57 	return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
58 }
59 
60 static void vivaldi_feature_mapping(struct hid_device *hdev,
61 				    struct hid_field *field,
62 				    struct hid_usage *usage)
63 {
64 	struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
65 	struct hid_report *report = field->report;
66 	int fn_key;
67 	int ret;
68 	u32 report_len;
69 	u8 *report_data, *buf;
70 
71 	if (field->logical != HID_USAGE_FN_ROW_PHYSMAP ||
72 	    (usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
73 		return;
74 
75 	fn_key = usage->hid & HID_USAGE;
76 	if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
77 		return;
78 	if (fn_key > drvdata->num_function_row_keys)
79 		drvdata->num_function_row_keys = fn_key;
80 
81 	report_data = buf = hid_alloc_report_buf(report, GFP_KERNEL);
82 	if (!report_data)
83 		return;
84 
85 	report_len = hid_report_len(report);
86 	if (!report->id) {
87 		/*
88 		 * hid_hw_raw_request() will stuff report ID (which will be 0)
89 		 * into the first byte of the buffer even for unnumbered
90 		 * reports, so we need to account for this to avoid getting
91 		 * -EOVERFLOW in return.
92 		 * Note that hid_alloc_report_buf() adds 7 bytes to the size
93 		 * so we can safely say that we have space for an extra byte.
94 		 */
95 		report_len++;
96 	}
97 
98 	ret = hid_hw_raw_request(hdev, report->id, report_data,
99 				 report_len, HID_FEATURE_REPORT,
100 				 HID_REQ_GET_REPORT);
101 	if (ret < 0) {
102 		dev_warn(&hdev->dev, "failed to fetch feature %d\n",
103 			 field->report->id);
104 		goto out;
105 	}
106 
107 	if (!report->id) {
108 		/*
109 		 * Undo the damage from hid_hw_raw_request() for unnumbered
110 		 * reports.
111 		 */
112 		report_data++;
113 		report_len--;
114 	}
115 
116 	ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
117 				   report_len, 0);
118 	if (ret) {
119 		dev_warn(&hdev->dev, "failed to report feature %d\n",
120 			 field->report->id);
121 		goto out;
122 	}
123 
124 	drvdata->function_row_physmap[fn_key - MIN_FN_ROW_KEY] =
125 	    field->value[usage->usage_index];
126 
127 out:
128 	kfree(buf);
129 }
130 
131 static int vivaldi_input_configured(struct hid_device *hdev,
132 				    struct hid_input *hidinput)
133 {
134 	return devm_device_add_group(&hdev->dev, &input_attribute_group);
135 }
136 
137 static const struct hid_device_id vivaldi_table[] = {
138 	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_VIVALDI, HID_ANY_ID,
139 		     HID_ANY_ID) },
140 	{ }
141 };
142 
143 MODULE_DEVICE_TABLE(hid, vivaldi_table);
144 
145 static struct hid_driver hid_vivaldi = {
146 	.name = "hid-vivaldi",
147 	.id_table = vivaldi_table,
148 	.probe = vivaldi_probe,
149 	.feature_mapping = vivaldi_feature_mapping,
150 	.input_configured = vivaldi_input_configured,
151 };
152 
153 module_hid_driver(hid_vivaldi);
154 
155 MODULE_AUTHOR("Sean O'Brien");
156 MODULE_DESCRIPTION("HID vivaldi driver");
157 MODULE_LICENSE("GPL");
158