linux/drivers/hid/hid-cmedia.c
<<
>>
Prefs
   1/*
   2 * HID driver for CMedia CM6533 audio jack controls
   3 *
   4 * Copyright (C) 2015 Ben Chen <ben_chen@bizlinktech.com>
   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
  21MODULE_AUTHOR("Ben Chen");
  22MODULE_DESCRIPTION("CM6533 HID jack controls");
  23MODULE_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
  43static const u8 ji_sfx[] = { 0x08, 0x00, 0x08, 0xc0 };
  44static const u8 ji_in[]  = { 0x01, 0x00, 0x06, 0x00 };
  45static const u8 ji_out[] = { 0x01, 0x00, 0x04, 0x00 };
  46
  47static int jack_switch_types[CM6533_JD_TYPE_COUNT] = {
  48        SW_HEADPHONE_INSERT,
  49};
  50
  51struct cmhid {
  52        struct input_dev *input_dev;
  53        struct hid_device *hid;
  54        unsigned short switch_map[CM6533_JD_TYPE_COUNT];
  55};
  56
  57static 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
  63static 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
  82out:
  83        return 0;
  84}
  85
  86static 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
 102static 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
 109static 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;
 138fail:
 139        kfree(cm);
 140allocfail:
 141        return ret;
 142}
 143
 144static 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
 152static const struct hid_device_id cmhid_devices[] = {
 153        { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
 154        { }
 155};
 156MODULE_DEVICE_TABLE(hid, cmhid_devices);
 157
 158static 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};
 167module_hid_driver(cmhid_driver);
 168
 169