]>
Commit | Line | Data |
---|---|---|
ad8ddc57 BC |
1 | /* |
2 | * HID driver for CMedia CM6533 audio jack controls | |
3 | * | |
4 | * Copyright (C) 2015 Ben Chen <[email protected]> | |
5 | * | |
6 | * This software is licensed under the terms of the GNU General Public | |
7 | * License version 2, as published by the Free Software Foundation, and | |
8 | * may be copied, distributed, and modified under those terms. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | #include <linux/device.h> | |
17 | #include <linux/hid.h> | |
18 | #include <linux/module.h> | |
19 | #include "hid-ids.h" | |
20 | ||
21 | MODULE_AUTHOR("Ben Chen"); | |
22 | MODULE_DESCRIPTION("CM6533 HID jack controls"); | |
23 | MODULE_LICENSE("GPL"); | |
24 | ||
25 | #define CM6533_JD_TYPE_COUNT 1 | |
26 | #define CM6533_JD_RAWEV_LEN 16 | |
27 | #define CM6533_JD_SFX_OFFSET 8 | |
28 | ||
29 | /* | |
30 | * | |
31 | *CM6533 audio jack HID raw events: | |
32 | * | |
33 | *Plug in: | |
34 | *01000600 002083xx 080008c0 10000000 | |
35 | *about 3 seconds later... | |
36 | *01000a00 002083xx 08000380 10000000 | |
37 | *01000600 002083xx 08000380 10000000 | |
38 | * | |
39 | *Plug out: | |
40 | *01000400 002083xx 080008c0 x0000000 | |
41 | */ | |
42 | ||
43 | static const u8 ji_sfx[] = { 0x08, 0x00, 0x08, 0xc0 }; | |
44 | static const u8 ji_in[] = { 0x01, 0x00, 0x06, 0x00 }; | |
45 | static const u8 ji_out[] = { 0x01, 0x00, 0x04, 0x00 }; | |
46 | ||
47 | static int jack_switch_types[CM6533_JD_TYPE_COUNT] = { | |
48 | SW_HEADPHONE_INSERT, | |
49 | }; | |
50 | ||
51 | struct cmhid { | |
52 | struct input_dev *input_dev; | |
53 | struct hid_device *hid; | |
54 | unsigned short switch_map[CM6533_JD_TYPE_COUNT]; | |
55 | }; | |
56 | ||
57 | static void hp_ev(struct hid_device *hid, struct cmhid *cm, int value) | |
58 | { | |
59 | input_report_switch(cm->input_dev, SW_HEADPHONE_INSERT, value); | |
60 | input_sync(cm->input_dev); | |
61 | } | |
62 | ||
63 | static int cmhid_raw_event(struct hid_device *hid, struct hid_report *report, | |
64 | u8 *data, int len) | |
65 | { | |
66 | struct cmhid *cm = hid_get_drvdata(hid); | |
67 | ||
68 | if (len != CM6533_JD_RAWEV_LEN) | |
69 | goto out; | |
70 | if (memcmp(data+CM6533_JD_SFX_OFFSET, ji_sfx, sizeof(ji_sfx))) | |
71 | goto out; | |
72 | ||
73 | if (!memcmp(data, ji_out, sizeof(ji_out))) { | |
74 | hp_ev(hid, cm, 0); | |
75 | goto out; | |
76 | } | |
77 | if (!memcmp(data, ji_in, sizeof(ji_in))) { | |
78 | hp_ev(hid, cm, 1); | |
79 | goto out; | |
80 | } | |
81 | ||
82 | out: | |
83 | return 0; | |
84 | } | |
85 | ||
86 | static int cmhid_input_configured(struct hid_device *hid, | |
87 | struct hid_input *hidinput) | |
88 | { | |
89 | struct input_dev *input_dev = hidinput->input; | |
90 | struct cmhid *cm = hid_get_drvdata(hid); | |
91 | int i; | |
92 | ||
93 | cm->input_dev = input_dev; | |
94 | memcpy(cm->switch_map, jack_switch_types, sizeof(cm->switch_map)); | |
95 | input_dev->evbit[0] = BIT(EV_SW); | |
96 | for (i = 0; i < CM6533_JD_TYPE_COUNT; i++) | |
97 | input_set_capability(cm->input_dev, | |
98 | EV_SW, jack_switch_types[i]); | |
99 | return 0; | |
100 | } | |
101 | ||
102 | static int cmhid_input_mapping(struct hid_device *hid, | |
103 | struct hid_input *hi, struct hid_field *field, | |
104 | struct hid_usage *usage, unsigned long **bit, int *max) | |
105 | { | |
106 | return -1; | |
107 | } | |
108 | ||
109 | static int cmhid_probe(struct hid_device *hid, const struct hid_device_id *id) | |
110 | { | |
111 | int ret; | |
112 | struct cmhid *cm; | |
113 | ||
114 | cm = kzalloc(sizeof(struct cmhid), GFP_KERNEL); | |
115 | if (!cm) { | |
116 | ret = -ENOMEM; | |
117 | goto allocfail; | |
118 | } | |
119 | ||
120 | cm->hid = hid; | |
121 | ||
122 | hid->quirks |= HID_QUIRK_HIDINPUT_FORCE; | |
123 | hid_set_drvdata(hid, cm); | |
124 | ||
125 | ret = hid_parse(hid); | |
126 | if (ret) { | |
127 | hid_err(hid, "parse failed\n"); | |
128 | goto fail; | |
129 | } | |
130 | ||
131 | ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE); | |
132 | if (ret) { | |
133 | hid_err(hid, "hw start failed\n"); | |
134 | goto fail; | |
135 | } | |
136 | ||
137 | return 0; | |
138 | fail: | |
139 | kfree(cm); | |
140 | allocfail: | |
141 | return ret; | |
142 | } | |
143 | ||
144 | static void cmhid_remove(struct hid_device *hid) | |
145 | { | |
146 | struct cmhid *cm = hid_get_drvdata(hid); | |
147 | ||
148 | hid_hw_stop(hid); | |
149 | kfree(cm); | |
150 | } | |
151 | ||
152 | static const struct hid_device_id cmhid_devices[] = { | |
153 | { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) }, | |
154 | { } | |
155 | }; | |
156 | MODULE_DEVICE_TABLE(hid, cmhid_devices); | |
157 | ||
158 | static struct hid_driver cmhid_driver = { | |
159 | .name = "cm6533_jd", | |
160 | .id_table = cmhid_devices, | |
161 | .raw_event = cmhid_raw_event, | |
162 | .input_configured = cmhid_input_configured, | |
163 | .probe = cmhid_probe, | |
164 | .remove = cmhid_remove, | |
165 | .input_mapping = cmhid_input_mapping, | |
166 | }; | |
167 | module_hid_driver(cmhid_driver); | |
168 |