Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * HID driver for CMedia CM6533 audio jack controls
0004  * and HS100B mute buttons
0005  *
0006  * Copyright (C) 2015 Ben Chen <ben_chen@bizlinktech.com>
0007  * Copyright (C) 2021 Thomas Weißschuh <linux@weissschuh.net>
0008  */
0009 
0010 #include <linux/device.h>
0011 #include <linux/hid.h>
0012 #include <linux/module.h>
0013 #include "hid-ids.h"
0014 
0015 MODULE_AUTHOR("Ben Chen");
0016 MODULE_AUTHOR("Thomas Weißschuh");
0017 MODULE_DESCRIPTION("CM6533 HID jack controls and HS100B mute button");
0018 MODULE_LICENSE("GPL");
0019 
0020 #define CM6533_JD_TYPE_COUNT      1
0021 #define CM6533_JD_RAWEV_LEN  16
0022 #define CM6533_JD_SFX_OFFSET      8
0023 
0024 #define HS100B_RDESC_ORIG_SIZE   60
0025 
0026 /* Fixed report descriptor of HS-100B audio chip
0027  * Bit 4 is an abolute Microphone mute usage instead of being unassigned.
0028  */
0029 static __u8 hs100b_rdesc_fixed[] = {
0030     0x05, 0x0C,         /*  Usage Page (Consumer),          */
0031     0x09, 0x01,         /*  Usage (Consumer Control),       */
0032     0xA1, 0x01,         /*  Collection (Application),       */
0033     0x15, 0x00,         /*      Logical Minimum (0),        */
0034     0x25, 0x01,         /*      Logical Maximum (1),        */
0035     0x09, 0xE9,         /*      Usage (Volume Inc),         */
0036     0x09, 0xEA,         /*      Usage (Volume Dec),         */
0037     0x75, 0x01,         /*      Report Size (1),            */
0038     0x95, 0x02,         /*      Report Count (2),           */
0039     0x81, 0x02,         /*      Input (Variable),           */
0040     0x09, 0xE2,         /*      Usage (Mute),               */
0041     0x95, 0x01,         /*      Report Count (1),           */
0042     0x81, 0x06,         /*      Input (Variable, Relative), */
0043     0x05, 0x0B,         /*      Usage Page (Telephony),     */
0044     0x09, 0x2F,         /*      Usage (2Fh),                */
0045     0x81, 0x02,         /*      Input (Variable),           */
0046     0x09, 0x20,         /*      Usage (20h),                */
0047     0x81, 0x06,         /*      Input (Variable, Relative), */
0048     0x05, 0x0C,         /*      Usage Page (Consumer),      */
0049     0x09, 0x00,         /*      Usage (00h),                */
0050     0x95, 0x03,         /*      Report Count (3),           */
0051     0x81, 0x02,         /*      Input (Variable),           */
0052     0x26, 0xFF, 0x00,   /*      Logical Maximum (255),      */
0053     0x09, 0x00,         /*      Usage (00h),                */
0054     0x75, 0x08,         /*      Report Size (8),            */
0055     0x95, 0x03,         /*      Report Count (3),           */
0056     0x81, 0x02,         /*      Input (Variable),           */
0057     0x09, 0x00,         /*      Usage (00h),                */
0058     0x95, 0x04,         /*      Report Count (4),           */
0059     0x91, 0x02,         /*      Output (Variable),          */
0060     0xC0                /*  End Collection                  */
0061 };
0062 
0063 /*
0064 *
0065 *CM6533 audio jack HID raw events:
0066 *
0067 *Plug in:
0068 *01000600 002083xx 080008c0 10000000
0069 *about 3 seconds later...
0070 *01000a00 002083xx 08000380 10000000
0071 *01000600 002083xx 08000380 10000000
0072 *
0073 *Plug out:
0074 *01000400 002083xx 080008c0 x0000000
0075 */
0076 
0077 static const u8 ji_sfx[] = { 0x08, 0x00, 0x08, 0xc0 };
0078 static const u8 ji_in[]  = { 0x01, 0x00, 0x06, 0x00 };
0079 static const u8 ji_out[] = { 0x01, 0x00, 0x04, 0x00 };
0080 
0081 static int jack_switch_types[CM6533_JD_TYPE_COUNT] = {
0082     SW_HEADPHONE_INSERT,
0083 };
0084 
0085 struct cmhid {
0086     struct input_dev *input_dev;
0087     struct hid_device *hid;
0088     unsigned short switch_map[CM6533_JD_TYPE_COUNT];
0089 };
0090 
0091 static void hp_ev(struct hid_device *hid, struct cmhid *cm, int value)
0092 {
0093     input_report_switch(cm->input_dev, SW_HEADPHONE_INSERT, value);
0094     input_sync(cm->input_dev);
0095 }
0096 
0097 static int cmhid_raw_event(struct hid_device *hid, struct hid_report *report,
0098      u8 *data, int len)
0099 {
0100     struct cmhid *cm = hid_get_drvdata(hid);
0101 
0102     if (len != CM6533_JD_RAWEV_LEN)
0103         goto out;
0104     if (memcmp(data+CM6533_JD_SFX_OFFSET, ji_sfx, sizeof(ji_sfx)))
0105         goto out;
0106 
0107     if (!memcmp(data, ji_out, sizeof(ji_out))) {
0108         hp_ev(hid, cm, 0);
0109         goto out;
0110     }
0111     if (!memcmp(data, ji_in, sizeof(ji_in))) {
0112         hp_ev(hid, cm, 1);
0113         goto out;
0114     }
0115 
0116 out:
0117     return 0;
0118 }
0119 
0120 static int cmhid_input_configured(struct hid_device *hid,
0121         struct hid_input *hidinput)
0122 {
0123     struct input_dev *input_dev = hidinput->input;
0124     struct cmhid *cm = hid_get_drvdata(hid);
0125     int i;
0126 
0127     cm->input_dev = input_dev;
0128     memcpy(cm->switch_map, jack_switch_types, sizeof(cm->switch_map));
0129     input_dev->evbit[0] = BIT(EV_SW);
0130     for (i = 0; i < CM6533_JD_TYPE_COUNT; i++)
0131         input_set_capability(cm->input_dev,
0132                 EV_SW, jack_switch_types[i]);
0133     return 0;
0134 }
0135 
0136 static int cmhid_input_mapping(struct hid_device *hid,
0137         struct hid_input *hi, struct hid_field *field,
0138         struct hid_usage *usage, unsigned long **bit, int *max)
0139 {
0140     return -1;
0141 }
0142 
0143 static int cmhid_probe(struct hid_device *hid, const struct hid_device_id *id)
0144 {
0145     int ret;
0146     struct cmhid *cm;
0147 
0148     cm = kzalloc(sizeof(struct cmhid), GFP_KERNEL);
0149     if (!cm) {
0150         ret = -ENOMEM;
0151         goto allocfail;
0152     }
0153 
0154     cm->hid = hid;
0155 
0156     hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
0157     hid_set_drvdata(hid, cm);
0158 
0159     ret = hid_parse(hid);
0160     if (ret) {
0161         hid_err(hid, "parse failed\n");
0162         goto fail;
0163     }
0164 
0165     ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE);
0166     if (ret) {
0167         hid_err(hid, "hw start failed\n");
0168         goto fail;
0169     }
0170 
0171     return 0;
0172 fail:
0173     kfree(cm);
0174 allocfail:
0175     return ret;
0176 }
0177 
0178 static void cmhid_remove(struct hid_device *hid)
0179 {
0180     struct cmhid *cm = hid_get_drvdata(hid);
0181 
0182     hid_hw_stop(hid);
0183     kfree(cm);
0184 }
0185 
0186 static const struct hid_device_id cmhid_devices[] = {
0187     { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
0188     { }
0189 };
0190 MODULE_DEVICE_TABLE(hid, cmhid_devices);
0191 
0192 static struct hid_driver cmhid_driver = {
0193     .name = "cm6533_jd",
0194     .id_table = cmhid_devices,
0195     .raw_event = cmhid_raw_event,
0196     .input_configured = cmhid_input_configured,
0197     .probe = cmhid_probe,
0198     .remove = cmhid_remove,
0199     .input_mapping = cmhid_input_mapping,
0200 };
0201 
0202 static __u8 *cmhid_hs100b_report_fixup(struct hid_device *hid, __u8 *rdesc,
0203                        unsigned int *rsize)
0204 {
0205     if (*rsize == HS100B_RDESC_ORIG_SIZE) {
0206         hid_info(hid, "Fixing CMedia HS-100B report descriptor\n");
0207         rdesc = hs100b_rdesc_fixed;
0208         *rsize = sizeof(hs100b_rdesc_fixed);
0209     }
0210     return rdesc;
0211 }
0212 
0213 static const struct hid_device_id cmhid_hs100b_devices[] = {
0214     { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CMEDIA_HS100B) },
0215     { }
0216 };
0217 MODULE_DEVICE_TABLE(hid, cmhid_hs100b_devices);
0218 
0219 static struct hid_driver cmhid_hs100b_driver = {
0220     .name = "cmedia_hs100b",
0221     .id_table = cmhid_hs100b_devices,
0222     .report_fixup = cmhid_hs100b_report_fixup,
0223 };
0224 
0225 static int cmedia_init(void)
0226 {
0227     int ret;
0228 
0229     ret = hid_register_driver(&cmhid_driver);
0230     if (ret)
0231         return ret;
0232 
0233     ret = hid_register_driver(&cmhid_hs100b_driver);
0234     if (ret)
0235         hid_unregister_driver(&cmhid_driver);
0236 
0237     return ret;
0238 }
0239 module_init(cmedia_init);
0240 
0241 static void cmedia_exit(void)
0242 {
0243         hid_unregister_driver(&cmhid_driver);
0244         hid_unregister_driver(&cmhid_hs100b_driver);
0245 }
0246 module_exit(cmedia_exit);