linux/drivers/hid/hid-zydacron.c
<<
>>
Prefs
   1/*
   2*  HID driver for zydacron remote control
   3*
   4*  Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
   5*/
   6
   7/*
   8* This program is free software; you can redistribute it and/or modify it
   9* under the terms of the GNU General Public License as published by the Free
  10* Software Foundation; either version 2 of the License, or (at your option)
  11* any later version.
  12*/
  13
  14#include <linux/device.h>
  15#include <linux/hid.h>
  16#include <linux/module.h>
  17
  18#include "hid-ids.h"
  19
  20struct zc_device {
  21        struct input_dev        *input_ep81;
  22        unsigned short          last_key[4];
  23};
  24
  25
  26/*
  27* Zydacron remote control has an invalid HID report descriptor,
  28* that needs fixing before we can parse it.
  29*/
  30static __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
  31        unsigned int *rsize)
  32{
  33        if (*rsize >= 253 &&
  34                rdesc[0x96] == 0xbc && rdesc[0x97] == 0xff &&
  35                rdesc[0xca] == 0xbc && rdesc[0xcb] == 0xff &&
  36                rdesc[0xe1] == 0xbc && rdesc[0xe2] == 0xff) {
  37                        hid_info(hdev,
  38                                "fixing up zydacron remote control report descriptor\n");
  39                        rdesc[0x96] = rdesc[0xca] = rdesc[0xe1] = 0x0c;
  40                        rdesc[0x97] = rdesc[0xcb] = rdesc[0xe2] = 0x00;
  41                }
  42        return rdesc;
  43}
  44
  45#define zc_map_key_clear(c) \
  46        hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
  47
  48static int zc_input_mapping(struct hid_device *hdev, struct hid_input *hi,
  49        struct hid_field *field, struct hid_usage *usage,
  50        unsigned long **bit, int *max)
  51{
  52        int i;
  53        struct zc_device *zc = hid_get_drvdata(hdev);
  54        zc->input_ep81 = hi->input;
  55
  56        if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
  57                return 0;
  58
  59        dbg_hid("zynacron input mapping event [0x%x]\n",
  60                usage->hid & HID_USAGE);
  61
  62        switch (usage->hid & HID_USAGE) {
  63        /* report 2 */
  64        case 0x10:
  65                zc_map_key_clear(KEY_MODE);
  66                break;
  67        case 0x30:
  68                zc_map_key_clear(KEY_SCREEN);
  69                break;
  70        case 0x70:
  71                zc_map_key_clear(KEY_INFO);
  72                break;
  73        /* report 3 */
  74        case 0x04:
  75                zc_map_key_clear(KEY_RADIO);
  76                break;
  77        /* report 4 */
  78        case 0x0d:
  79                zc_map_key_clear(KEY_PVR);
  80                break;
  81        case 0x25:
  82                zc_map_key_clear(KEY_TV);
  83                break;
  84        case 0x47:
  85                zc_map_key_clear(KEY_AUDIO);
  86                break;
  87        case 0x49:
  88                zc_map_key_clear(KEY_AUX);
  89                break;
  90        case 0x4a:
  91                zc_map_key_clear(KEY_VIDEO);
  92                break;
  93        case 0x48:
  94                zc_map_key_clear(KEY_DVD);
  95                break;
  96        case 0x24:
  97                zc_map_key_clear(KEY_MENU);
  98                break;
  99        case 0x32:
 100                zc_map_key_clear(KEY_TEXT);
 101                break;
 102        default:
 103                return 0;
 104        }
 105
 106        for (i = 0; i < 4; i++)
 107                zc->last_key[i] = 0;
 108
 109        return 1;
 110}
 111
 112static int zc_raw_event(struct hid_device *hdev, struct hid_report *report,
 113         u8 *data, int size)
 114{
 115        struct zc_device *zc = hid_get_drvdata(hdev);
 116        int ret = 0;
 117        unsigned key;
 118        unsigned short index;
 119
 120        if (report->id == data[0]) {
 121
 122                /* break keys */
 123                for (index = 0; index < 4; index++) {
 124                        key = zc->last_key[index];
 125                        if (key) {
 126                                input_event(zc->input_ep81, EV_KEY, key, 0);
 127                                zc->last_key[index] = 0;
 128                        }
 129                }
 130
 131                key = 0;
 132                switch (report->id) {
 133                case 0x02:
 134                case 0x03:
 135                        switch (data[1]) {
 136                        case 0x10:
 137                                key = KEY_MODE;
 138                                index = 0;
 139                                break;
 140                        case 0x30:
 141                                key = KEY_SCREEN;
 142                                index = 1;
 143                                break;
 144                        case 0x70:
 145                                key = KEY_INFO;
 146                                index = 2;
 147                                break;
 148                        case 0x04:
 149                                key = KEY_RADIO;
 150                                index = 3;
 151                                break;
 152                        }
 153
 154                        if (key) {
 155                                input_event(zc->input_ep81, EV_KEY, key, 1);
 156                                zc->last_key[index] = key;
 157                        }
 158
 159                        ret = 1;
 160                        break;
 161                }
 162        }
 163
 164        return ret;
 165}
 166
 167static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 168{
 169        int ret;
 170        struct zc_device *zc;
 171
 172        zc = devm_kzalloc(&hdev->dev, sizeof(*zc), GFP_KERNEL);
 173        if (zc == NULL) {
 174                hid_err(hdev, "can't alloc descriptor\n");
 175                return -ENOMEM;
 176        }
 177
 178        hid_set_drvdata(hdev, zc);
 179
 180        ret = hid_parse(hdev);
 181        if (ret) {
 182                hid_err(hdev, "parse failed\n");
 183                return ret;
 184        }
 185
 186        ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 187        if (ret) {
 188                hid_err(hdev, "hw start failed\n");
 189                return ret;
 190        }
 191
 192        return 0;
 193}
 194
 195static const struct hid_device_id zc_devices[] = {
 196        { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
 197        { }
 198};
 199MODULE_DEVICE_TABLE(hid, zc_devices);
 200
 201static struct hid_driver zc_driver = {
 202        .name = "zydacron",
 203        .id_table = zc_devices,
 204        .report_fixup = zc_report_fixup,
 205        .input_mapping = zc_input_mapping,
 206        .raw_event = zc_raw_event,
 207        .probe = zc_probe,
 208};
 209module_hid_driver(zc_driver);
 210
 211MODULE_LICENSE("GPL");
 212
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.