root/drivers/hid/hid-creative-sb0540.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. reverse
  2. get_key
  3. creative_sb0540_raw_event
  4. creative_sb0540_input_configured
  5. creative_sb0540_input_mapping
  6. creative_sb0540_probe

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * HID driver for the Creative SB0540 receiver
   4  *
   5  * Copyright (C) 2019 Red Hat Inc. All Rights Reserved
   6  *
   7  */
   8 
   9 #include <linux/device.h>
  10 #include <linux/hid.h>
  11 #include <linux/module.h>
  12 #include "hid-ids.h"
  13 
  14 MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
  15 MODULE_DESCRIPTION("HID Creative SB0540 receiver");
  16 MODULE_LICENSE("GPL");
  17 
  18 static const unsigned short creative_sb0540_key_table[] = {
  19         KEY_POWER,
  20         KEY_RESERVED,           /* text: 24bit */
  21         KEY_RESERVED,           /* 24bit wheel up */
  22         KEY_RESERVED,           /* 24bit wheel down */
  23         KEY_RESERVED,           /* text: CMSS */
  24         KEY_RESERVED,           /* CMSS wheel Up */
  25         KEY_RESERVED,           /* CMSS wheel Down */
  26         KEY_RESERVED,           /* text: EAX */
  27         KEY_RESERVED,           /* EAX wheel up */
  28         KEY_RESERVED,           /* EAX wheel down */
  29         KEY_RESERVED,           /* text: 3D Midi */
  30         KEY_RESERVED,           /* 3D Midi wheel up */
  31         KEY_RESERVED,           /* 3D Midi wheel down */
  32         KEY_MUTE,
  33         KEY_VOLUMEUP,
  34         KEY_VOLUMEDOWN,
  35         KEY_UP,
  36         KEY_LEFT,
  37         KEY_RIGHT,
  38         KEY_REWIND,
  39         KEY_OK,
  40         KEY_FASTFORWARD,
  41         KEY_DOWN,
  42         KEY_AGAIN,              /* text: Return, symbol: Jump to */
  43         KEY_PLAY,               /* text: Start */
  44         KEY_ESC,                /* text: Cancel */
  45         KEY_RECORD,
  46         KEY_OPTION,
  47         KEY_MENU,               /* text: Display */
  48         KEY_PREVIOUS,
  49         KEY_PLAYPAUSE,
  50         KEY_NEXT,
  51         KEY_SLOW,
  52         KEY_STOP,
  53         KEY_NUMERIC_1,
  54         KEY_NUMERIC_2,
  55         KEY_NUMERIC_3,
  56         KEY_NUMERIC_4,
  57         KEY_NUMERIC_5,
  58         KEY_NUMERIC_6,
  59         KEY_NUMERIC_7,
  60         KEY_NUMERIC_8,
  61         KEY_NUMERIC_9,
  62         KEY_NUMERIC_0
  63 };
  64 
  65 /*
  66  * Codes and keys from lirc's
  67  * remotes/creative/lircd.conf.alsa_usb
  68  * order and size must match creative_sb0540_key_table[] above
  69  */
  70 static const unsigned short creative_sb0540_codes[] = {
  71         0x619E,
  72         0x916E,
  73         0x926D,
  74         0x936C,
  75         0x718E,
  76         0x946B,
  77         0x956A,
  78         0x8C73,
  79         0x9669,
  80         0x9768,
  81         0x9867,
  82         0x9966,
  83         0x9A65,
  84         0x6E91,
  85         0x629D,
  86         0x639C,
  87         0x7B84,
  88         0x6B94,
  89         0x728D,
  90         0x8778,
  91         0x817E,
  92         0x758A,
  93         0x8D72,
  94         0x8E71,
  95         0x8877,
  96         0x7C83,
  97         0x738C,
  98         0x827D,
  99         0x7689,
 100         0x7F80,
 101         0x7986,
 102         0x7A85,
 103         0x7D82,
 104         0x857A,
 105         0x8B74,
 106         0x8F70,
 107         0x906F,
 108         0x8A75,
 109         0x847B,
 110         0x7887,
 111         0x8976,
 112         0x837C,
 113         0x7788,
 114         0x807F
 115 };
 116 
 117 struct creative_sb0540 {
 118         struct input_dev *input_dev;
 119         struct hid_device *hid;
 120         unsigned short keymap[ARRAY_SIZE(creative_sb0540_key_table)];
 121 };
 122 
 123 static inline u64 reverse(u64 data, int bits)
 124 {
 125         int i;
 126         u64 c;
 127 
 128         c = 0;
 129         for (i = 0; i < bits; i++) {
 130                 c |= (u64) (((data & (((u64) 1) << i)) ? 1 : 0))
 131                         << (bits - 1 - i);
 132         }
 133         return (c);
 134 }
 135 
 136 static int get_key(struct creative_sb0540 *creative_sb0540, u64 keycode)
 137 {
 138         int i;
 139 
 140         for (i = 0; i < ARRAY_SIZE(creative_sb0540_codes); i++) {
 141                 if (creative_sb0540_codes[i] == keycode)
 142                         return creative_sb0540->keymap[i];
 143         }
 144 
 145         return 0;
 146 
 147 }
 148 
 149 static int creative_sb0540_raw_event(struct hid_device *hid,
 150         struct hid_report *report, u8 *data, int len)
 151 {
 152         struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
 153         u64 code, main_code;
 154         int key;
 155 
 156         if (len != 6)
 157                 return 0;
 158 
 159         /* From daemons/hw_hiddev.c sb0540_rec() in lirc */
 160         code = reverse(data[5], 8);
 161         main_code = (code << 8) + ((~code) & 0xff);
 162 
 163         /*
 164          * Flip to get values in the same format as
 165          * remotes/creative/lircd.conf.alsa_usb in lirc
 166          */
 167         main_code = ((main_code & 0xff) << 8) +
 168                 ((main_code & 0xff00) >> 8);
 169 
 170         key = get_key(creative_sb0540, main_code);
 171         if (key == 0 || key == KEY_RESERVED) {
 172                 hid_err(hid, "Could not get a key for main_code %llX\n",
 173                         main_code);
 174                 return 0;
 175         }
 176 
 177         input_report_key(creative_sb0540->input_dev, key, 1);
 178         input_report_key(creative_sb0540->input_dev, key, 0);
 179         input_sync(creative_sb0540->input_dev);
 180 
 181         /* let hidraw and hiddev handle the report */
 182         return 0;
 183 }
 184 
 185 static int creative_sb0540_input_configured(struct hid_device *hid,
 186                 struct hid_input *hidinput)
 187 {
 188         struct input_dev *input_dev = hidinput->input;
 189         struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
 190         int i;
 191 
 192         creative_sb0540->input_dev = input_dev;
 193 
 194         input_dev->keycode = creative_sb0540->keymap;
 195         input_dev->keycodesize = sizeof(unsigned short);
 196         input_dev->keycodemax = ARRAY_SIZE(creative_sb0540->keymap);
 197 
 198         input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
 199 
 200         memcpy(creative_sb0540->keymap, creative_sb0540_key_table,
 201                 sizeof(creative_sb0540->keymap));
 202         for (i = 0; i < ARRAY_SIZE(creative_sb0540_key_table); i++)
 203                 set_bit(creative_sb0540->keymap[i], input_dev->keybit);
 204         clear_bit(KEY_RESERVED, input_dev->keybit);
 205 
 206         return 0;
 207 }
 208 
 209 static int creative_sb0540_input_mapping(struct hid_device *hid,
 210                 struct hid_input *hi, struct hid_field *field,
 211                 struct hid_usage *usage, unsigned long **bit, int *max)
 212 {
 213         /*
 214          * We are remapping the keys ourselves, so ignore the hid-input
 215          * keymap processing.
 216          */
 217         return -1;
 218 }
 219 
 220 static int creative_sb0540_probe(struct hid_device *hid,
 221                 const struct hid_device_id *id)
 222 {
 223         int ret;
 224         struct creative_sb0540 *creative_sb0540;
 225 
 226         creative_sb0540 = devm_kzalloc(&hid->dev,
 227                 sizeof(struct creative_sb0540), GFP_KERNEL);
 228 
 229         if (!creative_sb0540)
 230                 return -ENOMEM;
 231 
 232         creative_sb0540->hid = hid;
 233 
 234         /* force input as some remotes bypass the input registration */
 235         hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
 236 
 237         hid_set_drvdata(hid, creative_sb0540);
 238 
 239         ret = hid_parse(hid);
 240         if (ret) {
 241                 hid_err(hid, "parse failed\n");
 242                 return ret;
 243         }
 244 
 245         ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
 246         if (ret) {
 247                 hid_err(hid, "hw start failed\n");
 248                 return ret;
 249         }
 250 
 251         return ret;
 252 }
 253 
 254 static const struct hid_device_id creative_sb0540_devices[] = {
 255         { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB0540) },
 256         { }
 257 };
 258 MODULE_DEVICE_TABLE(hid, creative_sb0540_devices);
 259 
 260 static struct hid_driver creative_sb0540_driver = {
 261         .name = "creative-sb0540",
 262         .id_table = creative_sb0540_devices,
 263         .raw_event = creative_sb0540_raw_event,
 264         .input_configured = creative_sb0540_input_configured,
 265         .probe = creative_sb0540_probe,
 266         .input_mapping = creative_sb0540_input_mapping,
 267 };
 268 module_hid_driver(creative_sb0540_driver);

/* [<][>][^][v][top][bottom][index][help] */