HID: logitech: move dj devices to the HID++ module
[linux-block.git] / drivers / hid / hid-logitech-dj.c
index 9bf8637747a57f1b362680eeb36faf649b35f2fd..45a7eacdfe9875ddcebb3ca05f4d946f2d4059e2 100644 (file)
 #include <linux/hid.h>
 #include <linux/module.h>
 #include <linux/usb.h>
+#include <linux/kfifo.h>
 #include <asm/unaligned.h>
 #include "hid-ids.h"
-#include "hid-logitech-dj.h"
+
+#define DJ_MAX_PAIRED_DEVICES                  6
+#define DJ_MAX_NUMBER_NOTIFICATIONS            8
+#define DJ_RECEIVER_INDEX                      0
+#define DJ_DEVICE_INDEX_MIN                    1
+#define DJ_DEVICE_INDEX_MAX                    6
+
+#define DJREPORT_SHORT_LENGTH                  15
+#define DJREPORT_LONG_LENGTH                   32
+
+#define REPORT_ID_DJ_SHORT                     0x20
+#define REPORT_ID_DJ_LONG                      0x21
+
+#define REPORT_TYPE_RFREPORT_LAST              0x1F
+
+/* Command Switch to DJ mode */
+#define REPORT_TYPE_CMD_SWITCH                 0x80
+#define CMD_SWITCH_PARAM_DEVBITFIELD           0x00
+#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS       0x01
+#define TIMEOUT_NO_KEEPALIVE                   0x00
+
+/* Command to Get the list of Paired devices */
+#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES     0x81
+
+/* Device Paired Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_PAIRED                0x41
+#define SPFUNCTION_MORE_NOTIF_EXPECTED         0x01
+#define SPFUNCTION_DEVICE_LIST_EMPTY           0x02
+#define DEVICE_PAIRED_PARAM_SPFUNCTION         0x00
+#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB       0x01
+#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB       0x02
+#define DEVICE_PAIRED_RF_REPORT_TYPE           0x03
+
+/* Device Un-Paired Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED      0x40
+
+
+/* Connection Status Notification */
+#define REPORT_TYPE_NOTIF_CONNECTION_STATUS    0x42
+#define CONNECTION_STATUS_PARAM_STATUS         0x00
+#define STATUS_LINKLOSS                                0x01
+
+/* Error Notification */
+#define REPORT_TYPE_NOTIF_ERROR                        0x7F
+#define NOTIF_ERROR_PARAM_ETYPE                        0x00
+#define ETYPE_KEEPALIVE_TIMEOUT                        0x01
+
+/* supported DJ HID && RF report types */
+#define REPORT_TYPE_KEYBOARD                   0x01
+#define REPORT_TYPE_MOUSE                      0x02
+#define REPORT_TYPE_CONSUMER_CONTROL           0x03
+#define REPORT_TYPE_SYSTEM_CONTROL             0x04
+#define REPORT_TYPE_MEDIA_CENTER               0x08
+#define REPORT_TYPE_LEDS                       0x0E
+
+/* RF Report types bitfield */
+#define STD_KEYBOARD                           0x00000002
+#define STD_MOUSE                              0x00000004
+#define MULTIMEDIA                             0x00000008
+#define POWER_KEYS                             0x00000010
+#define MEDIA_CENTER                           0x00000100
+#define KBD_LEDS                               0x00004000
+
+struct dj_report {
+       u8 report_id;
+       u8 device_index;
+       u8 report_type;
+       u8 report_params[DJREPORT_SHORT_LENGTH - 3];
+};
+
+struct dj_receiver_dev {
+       struct hid_device *hdev;
+       struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
+                                           DJ_DEVICE_INDEX_MIN];
+       struct work_struct work;
+       struct kfifo notif_fifo;
+       spinlock_t lock;
+       bool querying_devices;
+};
+
+struct dj_device {
+       struct hid_device *hdev;
+       struct dj_receiver_dev *dj_receiver_dev;
+       u32 reports_supported;
+       u8 device_index;
+};
 
 /* Keyboard descriptor (1) */
 static const char kbd_descriptor[] = {
@@ -256,11 +342,15 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
        dj_hiddev->dev.parent = &djrcv_hdev->dev;
        dj_hiddev->bus = BUS_USB;
        dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor);
-       dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct);
+       dj_hiddev->product =
+               (dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB]
+                                                                       << 8) |
+               dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB];
        snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
-               "Logitech Unifying Device. Wireless PID:%02x%02x",
-               dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB],
-               dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]);
+               "Logitech Unifying Device. Wireless PID:%04x",
+               dj_hiddev->product);
+
+       dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE;
 
        usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys));
        snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index);
@@ -385,18 +475,6 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
 
        djdev = djrcv_dev->paired_dj_devices[dj_report->device_index];
 
-       if (!djdev) {
-               dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
-                       " is NULL, index %d\n", dj_report->device_index);
-               kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
-
-               if (schedule_work(&djrcv_dev->work) == 0) {
-                       dbg_hid("%s: did not schedule the work item, was already "
-                       "queued\n", __func__);
-               }
-               return;
-       }
-
        memset(reportbuffer, 0, sizeof(reportbuffer));
 
        for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) {
@@ -421,18 +499,6 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
 
        dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index];
 
-       if (dj_device == NULL) {
-               dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
-                       " is NULL, index %d\n", dj_report->device_index);
-               kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
-
-               if (schedule_work(&djrcv_dev->work) == 0) {
-                       dbg_hid("%s: did not schedule the work item, was already "
-                       "queued\n", __func__);
-               }
-               return;
-       }
-
        if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) ||
            (hid_reportid_size_map[dj_report->report_type] == 0)) {
                dbg_hid("invalid report type:%x\n", dj_report->report_type);
@@ -701,8 +767,17 @@ static int logi_dj_raw_event(struct hid_device *hdev,
        }
 
        spin_lock_irqsave(&djrcv_dev->lock, flags);
+
+       if (!djrcv_dev->paired_dj_devices[dj_report->device_index]) {
+               /* received an event for an unknown device, bail out */
+               logi_dj_recv_queue_notification(djrcv_dev, dj_report);
+               goto out;
+       }
+
        switch (dj_report->report_type) {
        case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
+               /* pairing notifications are handled above the switch */
+               break;
        case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
                logi_dj_recv_queue_notification(djrcv_dev, dj_report);
                break;
@@ -715,6 +790,8 @@ static int logi_dj_raw_event(struct hid_device *hdev,
        default:
                logi_dj_recv_forward_report(djrcv_dev, dj_report);
        }
+
+out:
        spin_unlock_irqrestore(&djrcv_dev->lock, flags);
 
        return true;
@@ -727,9 +804,6 @@ static int logi_dj_probe(struct hid_device *hdev,
        struct dj_receiver_dev *djrcv_dev;
        int retval;
 
-       if (is_dj_device((struct dj_device *)hdev->driver_data))
-               return -ENODEV;
-
        dbg_hid("%s called for ifnum %d\n", __func__,
                intf->cur_altsetting->desc.bInterfaceNumber);
 
@@ -882,22 +956,6 @@ static void logi_dj_remove(struct hid_device *hdev)
        hid_set_drvdata(hdev, NULL);
 }
 
-static int logi_djdevice_probe(struct hid_device *hdev,
-                        const struct hid_device_id *id)
-{
-       int ret;
-       struct dj_device *dj_dev = hdev->driver_data;
-
-       if (!is_dj_device(dj_dev))
-               return -ENODEV;
-
-       ret = hid_parse(hdev);
-       if (!ret)
-               ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-
-       return ret;
-}
-
 static const struct hid_device_id logi_dj_receivers[] = {
        {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
@@ -919,51 +977,8 @@ static struct hid_driver logi_djreceiver_driver = {
 #endif
 };
 
+module_hid_driver(logi_djreceiver_driver);
 
-static const struct hid_device_id logi_dj_devices[] = {
-       {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
-               USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
-       {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
-               USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
-       {}
-};
-
-static struct hid_driver logi_djdevice_driver = {
-       .name = "logitech-djdevice",
-       .id_table = logi_dj_devices,
-       .probe = logi_djdevice_probe,
-};
-
-
-static int __init logi_dj_init(void)
-{
-       int retval;
-
-       dbg_hid("Logitech-DJ:%s\n", __func__);
-
-       retval = hid_register_driver(&logi_djreceiver_driver);
-       if (retval)
-               return retval;
-
-       retval = hid_register_driver(&logi_djdevice_driver);
-       if (retval)
-               hid_unregister_driver(&logi_djreceiver_driver);
-
-       return retval;
-
-}
-
-static void __exit logi_dj_exit(void)
-{
-       dbg_hid("Logitech-DJ:%s\n", __func__);
-
-       hid_unregister_driver(&logi_djdevice_driver);
-       hid_unregister_driver(&logi_djreceiver_driver);
-
-}
-
-module_init(logi_dj_init);
-module_exit(logi_dj_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Logitech");
 MODULE_AUTHOR("Nestor Lopez Casado");