Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * HID driver for the Creative SB0540 receiver
0004  *
0005  * Copyright (C) 2019 Red Hat Inc. All Rights Reserved
0006  *
0007  */
0008 
0009 #include <linux/device.h>
0010 #include <linux/hid.h>
0011 #include <linux/module.h>
0012 #include "hid-ids.h"
0013 
0014 MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
0015 MODULE_DESCRIPTION("HID Creative SB0540 receiver");
0016 MODULE_LICENSE("GPL");
0017 
0018 static const unsigned short creative_sb0540_key_table[] = {
0019     KEY_POWER,
0020     KEY_RESERVED,       /* text: 24bit */
0021     KEY_RESERVED,       /* 24bit wheel up */
0022     KEY_RESERVED,       /* 24bit wheel down */
0023     KEY_RESERVED,       /* text: CMSS */
0024     KEY_RESERVED,       /* CMSS wheel Up */
0025     KEY_RESERVED,       /* CMSS wheel Down */
0026     KEY_RESERVED,       /* text: EAX */
0027     KEY_RESERVED,       /* EAX wheel up */
0028     KEY_RESERVED,       /* EAX wheel down */
0029     KEY_RESERVED,       /* text: 3D Midi */
0030     KEY_RESERVED,       /* 3D Midi wheel up */
0031     KEY_RESERVED,       /* 3D Midi wheel down */
0032     KEY_MUTE,
0033     KEY_VOLUMEUP,
0034     KEY_VOLUMEDOWN,
0035     KEY_UP,
0036     KEY_LEFT,
0037     KEY_RIGHT,
0038     KEY_REWIND,
0039     KEY_OK,
0040     KEY_FASTFORWARD,
0041     KEY_DOWN,
0042     KEY_AGAIN,      /* text: Return, symbol: Jump to */
0043     KEY_PLAY,       /* text: Start */
0044     KEY_ESC,        /* text: Cancel */
0045     KEY_RECORD,
0046     KEY_OPTION,
0047     KEY_MENU,       /* text: Display */
0048     KEY_PREVIOUS,
0049     KEY_PLAYPAUSE,
0050     KEY_NEXT,
0051     KEY_SLOW,
0052     KEY_STOP,
0053     KEY_NUMERIC_1,
0054     KEY_NUMERIC_2,
0055     KEY_NUMERIC_3,
0056     KEY_NUMERIC_4,
0057     KEY_NUMERIC_5,
0058     KEY_NUMERIC_6,
0059     KEY_NUMERIC_7,
0060     KEY_NUMERIC_8,
0061     KEY_NUMERIC_9,
0062     KEY_NUMERIC_0
0063 };
0064 
0065 /*
0066  * Codes and keys from lirc's
0067  * remotes/creative/lircd.conf.alsa_usb
0068  * order and size must match creative_sb0540_key_table[] above
0069  */
0070 static const unsigned short creative_sb0540_codes[] = {
0071     0x619E,
0072     0x916E,
0073     0x926D,
0074     0x936C,
0075     0x718E,
0076     0x946B,
0077     0x956A,
0078     0x8C73,
0079     0x9669,
0080     0x9768,
0081     0x9867,
0082     0x9966,
0083     0x9A65,
0084     0x6E91,
0085     0x629D,
0086     0x639C,
0087     0x7B84,
0088     0x6B94,
0089     0x728D,
0090     0x8778,
0091     0x817E,
0092     0x758A,
0093     0x8D72,
0094     0x8E71,
0095     0x8877,
0096     0x7C83,
0097     0x738C,
0098     0x827D,
0099     0x7689,
0100     0x7F80,
0101     0x7986,
0102     0x7A85,
0103     0x7D82,
0104     0x857A,
0105     0x8B74,
0106     0x8F70,
0107     0x906F,
0108     0x8A75,
0109     0x847B,
0110     0x7887,
0111     0x8976,
0112     0x837C,
0113     0x7788,
0114     0x807F
0115 };
0116 
0117 struct creative_sb0540 {
0118     struct input_dev *input_dev;
0119     struct hid_device *hid;
0120     unsigned short keymap[ARRAY_SIZE(creative_sb0540_key_table)];
0121 };
0122 
0123 static inline u64 reverse(u64 data, int bits)
0124 {
0125     int i;
0126     u64 c;
0127 
0128     c = 0;
0129     for (i = 0; i < bits; i++) {
0130         c |= (u64) (((data & (((u64) 1) << i)) ? 1 : 0))
0131             << (bits - 1 - i);
0132     }
0133     return (c);
0134 }
0135 
0136 static int get_key(struct creative_sb0540 *creative_sb0540, u64 keycode)
0137 {
0138     int i;
0139 
0140     for (i = 0; i < ARRAY_SIZE(creative_sb0540_codes); i++) {
0141         if (creative_sb0540_codes[i] == keycode)
0142             return creative_sb0540->keymap[i];
0143     }
0144 
0145     return 0;
0146 
0147 }
0148 
0149 static int creative_sb0540_raw_event(struct hid_device *hid,
0150     struct hid_report *report, u8 *data, int len)
0151 {
0152     struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
0153     u64 code, main_code;
0154     int key;
0155 
0156     if (len != 6)
0157         return 0;
0158 
0159     /* From daemons/hw_hiddev.c sb0540_rec() in lirc */
0160     code = reverse(data[5], 8);
0161     main_code = (code << 8) + ((~code) & 0xff);
0162 
0163     /*
0164      * Flip to get values in the same format as
0165      * remotes/creative/lircd.conf.alsa_usb in lirc
0166      */
0167     main_code = ((main_code & 0xff) << 8) +
0168         ((main_code & 0xff00) >> 8);
0169 
0170     key = get_key(creative_sb0540, main_code);
0171     if (key == 0 || key == KEY_RESERVED) {
0172         hid_err(hid, "Could not get a key for main_code %llX\n",
0173             main_code);
0174         return 0;
0175     }
0176 
0177     input_report_key(creative_sb0540->input_dev, key, 1);
0178     input_report_key(creative_sb0540->input_dev, key, 0);
0179     input_sync(creative_sb0540->input_dev);
0180 
0181     /* let hidraw and hiddev handle the report */
0182     return 0;
0183 }
0184 
0185 static int creative_sb0540_input_configured(struct hid_device *hid,
0186         struct hid_input *hidinput)
0187 {
0188     struct input_dev *input_dev = hidinput->input;
0189     struct creative_sb0540 *creative_sb0540 = hid_get_drvdata(hid);
0190     int i;
0191 
0192     creative_sb0540->input_dev = input_dev;
0193 
0194     input_dev->keycode = creative_sb0540->keymap;
0195     input_dev->keycodesize = sizeof(unsigned short);
0196     input_dev->keycodemax = ARRAY_SIZE(creative_sb0540->keymap);
0197 
0198     input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
0199 
0200     memcpy(creative_sb0540->keymap, creative_sb0540_key_table,
0201         sizeof(creative_sb0540->keymap));
0202     for (i = 0; i < ARRAY_SIZE(creative_sb0540_key_table); i++)
0203         set_bit(creative_sb0540->keymap[i], input_dev->keybit);
0204     clear_bit(KEY_RESERVED, input_dev->keybit);
0205 
0206     return 0;
0207 }
0208 
0209 static int creative_sb0540_input_mapping(struct hid_device *hid,
0210         struct hid_input *hi, struct hid_field *field,
0211         struct hid_usage *usage, unsigned long **bit, int *max)
0212 {
0213     /*
0214      * We are remapping the keys ourselves, so ignore the hid-input
0215      * keymap processing.
0216      */
0217     return -1;
0218 }
0219 
0220 static int creative_sb0540_probe(struct hid_device *hid,
0221         const struct hid_device_id *id)
0222 {
0223     int ret;
0224     struct creative_sb0540 *creative_sb0540;
0225 
0226     creative_sb0540 = devm_kzalloc(&hid->dev,
0227         sizeof(struct creative_sb0540), GFP_KERNEL);
0228 
0229     if (!creative_sb0540)
0230         return -ENOMEM;
0231 
0232     creative_sb0540->hid = hid;
0233 
0234     /* force input as some remotes bypass the input registration */
0235     hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
0236 
0237     hid_set_drvdata(hid, creative_sb0540);
0238 
0239     ret = hid_parse(hid);
0240     if (ret) {
0241         hid_err(hid, "parse failed\n");
0242         return ret;
0243     }
0244 
0245     ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
0246     if (ret) {
0247         hid_err(hid, "hw start failed\n");
0248         return ret;
0249     }
0250 
0251     return ret;
0252 }
0253 
0254 static const struct hid_device_id creative_sb0540_devices[] = {
0255     { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB0540) },
0256     { }
0257 };
0258 MODULE_DEVICE_TABLE(hid, creative_sb0540_devices);
0259 
0260 static struct hid_driver creative_sb0540_driver = {
0261     .name = "creative-sb0540",
0262     .id_table = creative_sb0540_devices,
0263     .raw_event = creative_sb0540_raw_event,
0264     .input_configured = creative_sb0540_input_configured,
0265     .probe = creative_sb0540_probe,
0266     .input_mapping = creative_sb0540_input_mapping,
0267 };
0268 module_hid_driver(creative_sb0540_driver);