[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAN+gG=HZsREeuc0h53juGg3L6kseuEUeiBWe8ZCaNLaVpth1Qw@mail.gmail.com>
Date: Thu, 1 Sep 2011 14:43:32 +0200
From: Benjamin Tissoires <benjamin.tissoires@...il.com>
To: Dmitry Torokhov <dmitry.torokhov@...il.com>,
Jiri Kosina <jkosina@...e.cz>, rtitmuss@...itech.com,
ogay@...itech.com, linux-input@...r.kernel.org,
linux-kernel@...r.kernel.org
Cc: Benjamin Tissoires <benjamin.tissoires@...il.com>,
Nestor Lopez Casado <nlopezcasad@...itech.com>
Subject: Re: [PATCH v3] HID: Add full support for Logitech Unifying receivers
Hi guys,
just a little up to this patch, and a more technical question:
On Thu, Aug 25, 2011 at 10:11, Benjamin Tissoires
<benjamin.tissoires@...il.com> wrote:
> From: Nestor Lopez Casado <nlopezcasad@...itech.com>
>
> With this driver, all the devices paired to a single Unifying
> receiver are exposed to user processes in separated /input/dev
> nodes.
>
> Keyboards with different layouts can be treated differently,
> Multiplayer games on single PC (like home theater PC) can
> differentiate input coming from different kbds paired to the
> same receiver.
>
> Up to now, when Logitech Unifying receivers are connected to a
> Linux based system, a single keyboard and a single mouse are
> presented to the HID Layer, even if the Unifying receiver can
> pair up to six compatible devices. The Unifying receiver by default
> multiplexes all incoming events (from multiple keyboards/mice)
> into these two.
>
> Signed-off-by: Nestor Lopez Casado <nlopezcasad@...itech.com>
> Signed-off-by: Benjamin Tissoires <btissoires@...itech.com>
> ---
>
> Hi guys,
>
> this is the third version of the unifying receiver. I made the changes requested
> by Dmitry:
> - switch to kfifo
> - introduce __func__ in all debugs/errors messages
> - in logi_dj_ll_input_event, do not use the report of the device, but a copy
> (is it the right way to do it to avoid the DMA problem?)
> - switch to BUS_USB for the created devices
> - small other cosmetic changes
>
> One side note:
> We really thing using BUS_USB is not the right way to do it when we will have
> more drivers:
> by keeping it, we will have no choice but to provide the VIP/PID of the
> receiver to the created devices. We then will have to handle a manual parsing
> of the wireless id to know which driver to use. This will result in having
> all the Logitech's drivers loaded when someone plug an unifying receiver.
> if we switch to a new BUS_DJ, we could then rely on the automatic user-space
> loading of the drivers to load only the right drivers.
>
> BTW, as long as Logitech does not provide those drivers (I hope they will come
> out soon), there is no point using BUS_DJ. So it's fine not using it right now.
>
> Thanks,
> Benjamin
>
> drivers/hid/Kconfig | 9 +
> drivers/hid/Makefile | 1 +
> drivers/hid/hid-core.c | 2 +
> drivers/hid/hid-ids.h | 2 +
> drivers/hid/hid-logitech-dj.c | 893 +++++++++++++++++++++++++++++++++++++++++
> drivers/hid/hid-logitech-dj.h | 123 ++++++
> 6 files changed, 1030 insertions(+), 0 deletions(-)
> create mode 100644 drivers/hid/hid-logitech-dj.c
> create mode 100644 drivers/hid/hid-logitech-dj.h
>
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 36ca465..0eb7f04 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -231,6 +231,15 @@ config HID_LOGITECH
> ---help---
> Support for Logitech devices that are not fully compliant with HID standard.
>
> +config HID_LOGITECH_DJ
> + tristate "Logitech Unifying receivers full support"
> + depends on HID_LOGITECH
> + default m
> + ---help---
> + Say Y if you want support for Logitech Unifying receivers and devices.
> + Unifying receivers are capable of pairing up to 6 Logitech compliant
> + devices to the same receiver.
> +
> config LOGITECH_FF
> bool "Logitech force feedback support"
> depends on HID_LOGITECH
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index a24cee4..3aba5e7 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
> obj-$(CONFIG_HID_KYE) += hid-kye.o
> obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
> obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
> +obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
> obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
> obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
> obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index b0e618b..ebe4fdd 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1423,6 +1423,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL) },
> + { 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) },
> { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) },
> { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
> { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index a756ee6..c42764b 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -440,6 +440,8 @@
> #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
> #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
> #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
> +#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b
> +#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532
> #define USB_DEVICE_ID_SPACETRAVELLER 0xc623
> #define USB_DEVICE_ID_SPACENAVIGATOR 0xc626
> #define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704
> diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
> new file mode 100644
> index 0000000..896328d
> --- /dev/null
> +++ b/drivers/hid/hid-logitech-dj.c
> @@ -0,0 +1,893 @@
> +/*
> + * HID driver for Logitech Unifying receivers
> + *
> + * Copyright (c) 2011 Logitech (c)
> + */
> +
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Should you need to contact me, the author, you can do so by e-mail send
> + * your message to Nestor Lopez Casado <xnlopez at gmail com>
> + *
> + */
> +
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/module.h>
> +#include <linux/usb.h>
> +#include "usbhid/usbhid.h"
> +#include "hid-ids.h"
> +#include "hid-logitech-dj.h"
> +
> +/* Keyboard descriptor (1) */
> +static const char kbd_descriptor[] = {
> + 0x05, 0x01, /* USAGE_PAGE (generic Desktop) */
> + 0x09, 0x06, /* USAGE (Keyboard) */
> + 0xA1, 0x01, /* COLLECTION (Application) */
> + 0x85, 0x01, /* REPORT_ID (1) */
> + 0x95, 0x08, /* REPORT_COUNT (8) */
> + 0x75, 0x01, /* REPORT_SIZE (1) */
> + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
> + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
> + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
> + 0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */
> + 0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */
> + 0x81, 0x02, /* INPUT (Data,Var,Abs) */
> + 0x95, 0x05, /* REPORT COUNT (5) */
> + 0x05, 0x08, /* USAGE PAGE (LED page) */
> + 0x19, 0x01, /* USAGE MINIMUM (1) */
> + 0x29, 0x05, /* USAGE MAXIMUM (5) */
> + 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */
> + 0x95, 0x01, /* REPORT COUNT (1) */
> + 0x75, 0x03, /* REPORT SIZE (3) */
> + 0x91, 0x01, /* OUTPUT (Constant) */
> + 0x95, 0x06, /* REPORT_COUNT (6) */
> + 0x75, 0x08, /* REPORT_SIZE (8) */
> + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
> + 0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */
> + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
> + 0x19, 0x00, /* USAGE_MINIMUM (no event) */
> + 0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */
> + 0x81, 0x00, /* INPUT (Data,Ary,Abs) */
> + 0xC0
> +};
> +
> +/* Mouse descriptor (2) */
> +static const char mse_descriptor[] = {
> + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
> + 0x09, 0x02, /* USAGE (Mouse) */
> + 0xA1, 0x01, /* COLLECTION (Application) */
> + 0x85, 0x02, /* REPORT_ID = 2 */
> + 0x09, 0x01, /* USAGE (pointer) */
> + 0xA1, 0x00, /* COLLECTION (physical) */
> + 0x05, 0x09, /* USAGE_PAGE (buttons) */
> + 0x19, 0x01, /* USAGE_MIN (1) */
> + 0x29, 0x10, /* USAGE_MAX (16) */
> + 0x15, 0x00, /* LOGICAL_MIN (0) */
> + 0x25, 0x01, /* LOGICAL_MAX (1) */
> + 0x95, 0x10, /* REPORT_COUNT (16) */
> + 0x75, 0x01, /* REPORT_SIZE (1) */
> + 0x81, 0x02, /* INPUT (data var abs) */
> + 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
> + 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */
> + 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */
> + 0x75, 0x0C, /* REPORT_SIZE (12) */
> + 0x95, 0x02, /* REPORT_COUNT (2) */
> + 0x09, 0x30, /* USAGE (X) */
> + 0x09, 0x31, /* USAGE (Y) */
> + 0x81, 0x06, /* INPUT */
> + 0x15, 0x81, /* LOGICAL_MIN (-127) */
> + 0x25, 0x7F, /* LOGICAL_MAX (127) */
> + 0x75, 0x08, /* REPORT_SIZE (8) */
> + 0x95, 0x01, /* REPORT_COUNT (1) */
> + 0x09, 0x38, /* USAGE (wheel) */
> + 0x81, 0x06, /* INPUT */
> + 0x05, 0x0C, /* USAGE_PAGE(consumer) */
> + 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
> + 0x95, 0x01, /* REPORT_COUNT (1) */
> + 0x81, 0x06, /* INPUT */
> + 0xC0, /* END_COLLECTION */
> + 0xC0, /* END_COLLECTION */
> +};
> +
> +/* Consumer Control descriptor (3) */
> +static const char consumer_descriptor[] = {
> + 0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
> + 0x09, 0x01, /* USAGE (Consumer Control) */
> + 0xA1, 0x01, /* COLLECTION (Application) */
> + 0x85, 0x03, /* REPORT_ID = 3 */
> + 0x75, 0x10, /* REPORT_SIZE (16) */
> + 0x95, 0x02, /* REPORT_COUNT (2) */
> + 0x15, 0x01, /* LOGICAL_MIN (1) */
> + 0x26, 0x8C, 0x02, /* LOGICAL_MAX (652) */
> + 0x19, 0x01, /* USAGE_MIN (1) */
> + 0x2A, 0x8C, 0x02, /* USAGE_MAX (652) */
> + 0x81, 0x00, /* INPUT (Data Ary Abs) */
> + 0xC0, /* END_COLLECTION */
> +}; /* */
> +
> +/* System control descriptor (4) */
> +static const char syscontrol_descriptor[] = {
> + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
> + 0x09, 0x80, /* USAGE (System Control) */
> + 0xA1, 0x01, /* COLLECTION (Application) */
> + 0x85, 0x04, /* REPORT_ID = 4 */
> + 0x75, 0x02, /* REPORT_SIZE (2) */
> + 0x95, 0x01, /* REPORT_COUNT (1) */
> + 0x15, 0x01, /* LOGICAL_MIN (1) */
> + 0x25, 0x03, /* LOGICAL_MAX (3) */
> + 0x09, 0x82, /* USAGE (System Sleep) */
> + 0x09, 0x81, /* USAGE (System Power Down) */
> + 0x09, 0x83, /* USAGE (System Wake Up) */
> + 0x81, 0x60, /* INPUT (Data Ary Abs NPrf Null) */
> + 0x75, 0x06, /* REPORT_SIZE (6) */
> + 0x81, 0x03, /* INPUT (Cnst Var Abs) */
> + 0xC0, /* END_COLLECTION */
> +};
> +
> +/* Media descriptor (8) */
> +static const char media_descriptor[] = {
> + 0x06, 0xbc, 0xff, /* Usage Page 0xffbc */
> + 0x09, 0x88, /* Usage 0x0088 */
> + 0xa1, 0x01, /* BeginCollection */
> + 0x85, 0x08, /* Report ID 8 */
> + 0x19, 0x01, /* Usage Min 0x0001 */
> + 0x29, 0xff, /* Usage Max 0x00ff */
> + 0x15, 0x01, /* Logical Min 1 */
> + 0x26, 0xff, 0x00, /* Logical Max 255 */
> + 0x75, 0x08, /* Report Size 8 */
> + 0x95, 0x01, /* Report Count 1 */
> + 0x81, 0x00, /* Input */
> + 0xc0, /* EndCollection */
> +}; /* */
> +
> +/* Maximum size of all defined hid reports in bytes (including report id) */
> +#define MAX_REPORT_SIZE 8
> +
> +/* Number of possible hid report types that can be created by this driver.
> + *
> + * Right now, RF report types have the same report types (or report id's)
> + * than the hid report created from those RF reports. In the future
> + * this doesnt have to be true.
> + *
> + * For instance, RF report type 0x01 which has a size of 8 bytes, corresponds
> + * to hid report id 0x01, this is standard keyboard. Same thing applies to mice
> + * reports and consumer control, etc. If a new RF report is created, it doesn't
> + * has to have the same report id as its corresponding hid report, so an
> + * translation may have to take place for future report types.
> + */
> +#define NUMBER_OF_HID_REPORTS 32
> +static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
> + [1] = 8, /* Standard keyboard */
> + [2] = 8, /* Standard mouse */
> + [3] = 5, /* Consumer control */
> + [4] = 2, /* System control */
> + [8] = 2, /* Media Center */
> +};
> +
> +
> +#define LOGITECH_DJ_INTERFACE_NUMBER 0x02
> +
> +static struct hid_ll_driver logi_dj_ll_driver;
> +
> +static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
> + size_t count,
> + unsigned char report_type);
> +
> +static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
> + struct dj_report *dj_report)
> +{
> + /* Called in delayed work context */
> + struct dj_device *dj_dev;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&djrcv_dev->lock, flags);
> + dj_dev = djrcv_dev->paired_dj_devices[dj_report->device_index];
> + djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL;
> + spin_unlock_irqrestore(&djrcv_dev->lock, flags);
> +
> + if (dj_dev != NULL) {
> + hid_destroy_device(dj_dev->hdev);
> + kfree(dj_dev);
> + } else {
> + dev_err(&djrcv_dev->hdev->dev, "%s: can't destroy a NULL device\n",
> + __func__);
> + }
> +}
> +
> +static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
> + struct dj_report *dj_report)
> +{
> + /* Called in delayed work context */
> + struct hid_device *djrcv_hdev = djrcv_dev->hdev;
> + struct usb_interface *intf = to_usb_interface(djrcv_hdev->dev.parent);
> + struct usb_device *usbdev = interface_to_usbdev(intf);
> + struct hid_device *dj_hiddev;
> + struct dj_device *dj_dev;
> + unsigned char tmpstr[20];
> +
> + if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
> + SPFUNCTION_DEVICE_LIST_EMPTY) {
> + dbg_hid("%s: device list is empty\n", __func__);
> + return;
> + }
> +
> + dj_hiddev = hid_allocate_device();
> + if (IS_ERR(dj_hiddev)) {
> + dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
> + __func__);
> + return;
> + }
> +
> + dj_hiddev->ll_driver = &logi_dj_ll_driver;
> + dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report;
> +
> + 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);
> + 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]);
> +
> + usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys));
> + snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index);
> + strlcat(dj_hiddev->phys, tmpstr, sizeof(dj_hiddev->phys));
> +
> + dj_dev = kzalloc(sizeof(struct dj_device), GFP_KERNEL);
> +
> + if (!dj_dev) {
> + dev_err(&djrcv_hdev->dev, "%s: failed allocating dj_device\n",
> + __func__);
> + goto dj_device_allocate_fail;
> + }
> +
> + dj_dev->reports_supported = le32_to_cpu(
> + dj_report->report_params[DEVICE_PAIRED_RF_REPORT_TYPE]);
> + dj_dev->hdev = dj_hiddev;
> + dj_dev->dj_receiver_dev = djrcv_dev;
> + dj_dev->device_index = dj_report->device_index;
> + dj_hiddev->driver_data = dj_dev;
> +
> + djrcv_dev->paired_dj_devices[dj_report->device_index] = dj_dev;
> +
> + if (hid_add_device(dj_hiddev)) {
> + dev_err(&djrcv_hdev->dev, "%s: failed adding dj_device\n",
> + __func__);
> + goto hid_add_device_fail;
> + }
> +
> + return;
> +
> +hid_add_device_fail:
> + djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL;
> + kfree(dj_dev);
> +dj_device_allocate_fail:
> + hid_destroy_device(dj_hiddev);
> +}
> +
> +static void delayedwork_callback(struct work_struct *work)
> +{
> + struct dj_receiver_dev *djrcv_dev =
> + container_of(work, struct dj_receiver_dev, work);
> +
> + struct dj_report dj_report;
> + unsigned long flags;
> + int count;
> +
> + dbg_hid("%s\n", __func__);
> +
> + spin_lock_irqsave(&djrcv_dev->lock, flags);
> +
> + count = kfifo_out(&djrcv_dev->notif_fifo, &dj_report,
> + sizeof(struct dj_report));
> +
> + if (count != sizeof(struct dj_report)) {
> + dev_err(&djrcv_dev->hdev->dev, "%s: workitem triggered without "
> + "notifications available\n", __func__);
> + spin_unlock_irqrestore(&djrcv_dev->lock, flags);
> + return;
> + }
> +
> + if (!kfifo_is_empty(&djrcv_dev->notif_fifo)) {
> + if (schedule_work(&djrcv_dev->work) == 0) {
> + dbg_hid("%s: did not schedule the work item, was "
> + "already queued\n", __func__);
> + }
> + }
> +
> + spin_unlock_irqrestore(&djrcv_dev->lock, flags);
> +
> + switch (dj_report.report_type) {
> + case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
> + logi_dj_recv_add_djhid_device(djrcv_dev, &dj_report);
> + break;
> + case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
> + logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
> + break;
> + default:
> + dbg_hid("%s: unexpected report type\n", __func__);
> + }
> +}
> +
> +static void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev,
> + struct dj_report *dj_report)
> +{
> + /* We are called from atomic context (tasklet && djrcv->lock held) */
> +
> + 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__);
> + }
> +}
> +
> +static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
> + struct dj_report *dj_report)
> +{
> + /* We are called from atomic context (tasklet && djrcv->lock held) */
> + unsigned int i;
> + u8 reportbuffer[MAX_REPORT_SIZE];
> + struct dj_device *djdev;
> +
> + 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);
> + return;
> + }
> +
> + memset(reportbuffer, 0, sizeof(reportbuffer));
> +
> + for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) {
> + if (djdev->reports_supported & (1 << i)) {
> + reportbuffer[0] = i;
> + if (hid_input_report(djdev->hdev,
> + HID_INPUT_REPORT,
> + reportbuffer,
> + hid_reportid_size_map[i], 1)) {
> + dbg_hid("hid_input_report error sending null "
> + "report\n");
> + }
> + }
> + }
> +}
> +
> +static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
> + struct dj_report *dj_report)
> +{
> + /* We are called from atomic context (tasklet && djrcv->lock held) */
> + struct dj_device *dj_device;
> +
> + 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);
> + 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);
> + return;
> + }
> +
> + if (hid_input_report(dj_device->hdev,
> + HID_INPUT_REPORT, &dj_report->report_type,
> + hid_reportid_size_map[dj_report->report_type], 1)) {
> + dbg_hid("hid_input_report error\n");
> + }
> +}
> +
> +
> +static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
> + struct dj_report *dj_report)
> +{
> + struct hid_device *hdev = djrcv_dev->hdev;
> + int sent_bytes;
> +
> + if (!hdev->hid_output_raw_report) {
> + dev_err(&hdev->dev, "%s:"
> + "hid_output_raw_report is null\n", __func__);
> + return -ENODEV;
> + }
> +
> + sent_bytes = hdev->hid_output_raw_report(hdev, (u8 *) dj_report,
> + sizeof(struct dj_report),
> + HID_OUTPUT_REPORT);
> +
> + return (sent_bytes < 0) ? sent_bytes : 0;
> +}
> +
> +static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
> +{
> + struct dj_report dj_report;
> +
> + memset(&dj_report, 0, sizeof(dj_report));
> + dj_report.report_id = REPORT_ID_DJ_SHORT;
> + dj_report.device_index = 0xFF;
> + dj_report.report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES;
> + return logi_dj_recv_send_report(djrcv_dev, &dj_report);
> +}
> +
> +static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
> + unsigned timeout)
> +{
> + struct dj_report dj_report;
> +
> + memset(&dj_report, 0, sizeof(dj_report));
> + dj_report.report_id = REPORT_ID_DJ_SHORT;
> + dj_report.device_index = 0xFF;
> + dj_report.report_type = REPORT_TYPE_CMD_SWITCH;
> + dj_report.report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x1F;
> + dj_report.report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout;
> + return logi_dj_recv_send_report(djrcv_dev, &dj_report);
> +}
> +
> +
> +static int logi_dj_ll_open(struct hid_device *hid)
> +{
> + dbg_hid("%s:%s\n", __func__, hid->phys);
> + return 0;
> +
> +}
> +
> +static void logi_dj_ll_close(struct hid_device *hid)
> +{
> + dbg_hid("%s:%s\n", __func__, hid->phys);
> +}
> +
> +static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
> + size_t count,
> + unsigned char report_type)
> +{
> + /* Called by hid raw to send data */
> + dbg_hid("%s\n", __func__);
> +
> + return 0;
> +}
> +
> +static int logi_dj_ll_parse(struct hid_device *hid)
> +{
> + struct dj_device *djdev = hid->driver_data;
> + int retval;
> +
> + dbg_hid("%s\n", __func__);
> +
> + djdev->hdev->version = 0x0111;
> + djdev->hdev->country = 0x00;
> +
> + if (djdev->reports_supported & STD_KEYBOARD) {
> + dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n",
> + __func__, djdev->reports_supported);
> + retval = hid_parse_report(hid,
> + (u8 *) kbd_descriptor,
> + sizeof(kbd_descriptor));
> + if (retval) {
> + dbg_hid("%s: sending a kbd descriptor, hid_parse failed"
> + " error: %d\n", __func__, retval);
> + return retval;
> + }
> + }
> +
> + if (djdev->reports_supported & STD_MOUSE) {
> + dbg_hid("%s: sending a mouse descriptor, reports_supported: "
> + "%x\n", __func__, djdev->reports_supported);
> + retval = hid_parse_report(hid,
> + (u8 *) mse_descriptor,
> + sizeof(mse_descriptor));
> + if (retval) {
> + dbg_hid("%s: sending a mouse descriptor, hid_parse "
> + "failed error: %d\n", __func__, retval);
> + return retval;
> + }
> + }
> +
> + if (djdev->reports_supported & MULTIMEDIA) {
> + dbg_hid("%s: sending a multimedia report descriptor: %x\n",
> + __func__, djdev->reports_supported);
> + retval = hid_parse_report(hid,
> + (u8 *) consumer_descriptor,
> + sizeof(consumer_descriptor));
> + if (retval) {
> + dbg_hid("%s: sending a consumer_descriptor, hid_parse "
> + "failed error: %d\n", __func__, retval);
> + return retval;
> + }
> + }
> +
> + if (djdev->reports_supported & POWER_KEYS) {
> + dbg_hid("%s: sending a power keys report descriptor: %x\n",
> + __func__, djdev->reports_supported);
> + retval = hid_parse_report(hid,
> + (u8 *) syscontrol_descriptor,
> + sizeof(syscontrol_descriptor));
> + if (retval) {
> + dbg_hid("%s: sending a syscontrol_descriptor, "
> + "hid_parse failed error: %d\n",
> + __func__, retval);
> + return retval;
> + }
> + }
> +
> + if (djdev->reports_supported & MEDIA_CENTER) {
> + dbg_hid("%s: sending a media center report descriptor: %x\n",
> + __func__, djdev->reports_supported);
> + retval = hid_parse_report(hid,
> + (u8 *) media_descriptor,
> + sizeof(media_descriptor));
> + if (retval) {
> + dbg_hid("%s: sending a media_descriptor, hid_parse "
> + "failed error: %d\n", __func__, retval);
> + return retval;
> + }
> + }
> +
> + if (djdev->reports_supported & KBD_LEDS) {
> + dbg_hid("%s: need to send kbd leds report descriptor: %x\n",
> + __func__, djdev->reports_supported);
> + }
> +
> + return 0;
> +}
> +
> +static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
> + unsigned int code, int value)
> +{
> + /* Sent by the input layer to handle leds and Force Feedback */
> + struct hid_device *dj_hiddev = input_get_drvdata(dev);
> + struct dj_device *dj_dev = dj_hiddev->driver_data;
> +
> + struct dj_receiver_dev *djrcv_dev =
> + dev_get_drvdata(dj_hiddev->dev.parent);
> + struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev;
> + struct hid_report_enum *output_report_enum;
> +
> + struct hid_field *field;
> + struct hid_report report;
> + unsigned char data[8];
> + int offset;
> +
> + dbg_hid("%s: %s, type:%d | code:%d | value:%d\n",
> + __func__, dev->phys, type, code, value);
> +
> + if (type != EV_LED)
> + return -1;
> +
> + offset = hidinput_find_field(dj_hiddev, type, code, &field);
> +
> + if (offset == -1) {
> + dev_warn(&dev->dev, "event field not found\n");
> + return -1;
> + }
> + hid_set_field(field, offset, value);
> + hid_output_report(field->report, &data[0]);
> +
> + output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT];
> + report = *output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
> + hid_set_field(report.field[0], 0, dj_dev->device_index);
> + hid_set_field(report.field[0], 1, REPORT_TYPE_LEDS);
> + hid_set_field(report.field[0], 2, data[1]);
> +
> + usbhid_submit_report(dj_rcv_hiddev, &report, USB_DIR_OUT);
This is not working when the receiver is paired to a keyboard. When
the session launches, Gnome try to send leds commands to the keyboard
and this results in an oops.
Using a pointer to the struct hid_report solves the crash.
However, Dmitry, you told us about a DMA stack problem. Can you give
us a better way to handle that?
Thanks,
Benjamin
> +
> + return 0;
> +
> +}
> +
> +static int logi_dj_ll_start(struct hid_device *hid)
> +{
> + dbg_hid("%s\n", __func__);
> + return 0;
> +}
> +
> +static void logi_dj_ll_stop(struct hid_device *hid)
> +{
> + dbg_hid("%s\n", __func__);
> +}
> +
> +
> +static struct hid_ll_driver logi_dj_ll_driver = {
> + .parse = logi_dj_ll_parse,
> + .start = logi_dj_ll_start,
> + .stop = logi_dj_ll_stop,
> + .open = logi_dj_ll_open,
> + .close = logi_dj_ll_close,
> + .hidinput_input_event = logi_dj_ll_input_event,
> +};
> +
> +
> +static int logi_dj_raw_event(struct hid_device *hdev,
> + struct hid_report *report, u8 *data,
> + int size)
> +{
> + struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
> + struct dj_report *dj_report = (struct dj_report *) data;
> + unsigned long flags;
> + bool report_processed = false;
> +
> + dbg_hid("%s, size:%d\n", __func__, size);
> +
> + /* Here we receive all data coming from iface 2, there are 4 cases:
> + *
> + * 1) Data should continue its normal processing i.e. data does not
> + * come from the DJ collection, in which case we do nothing and
> + * return 0, so hid-core can continue normal processing (will forward
> + * to associated hidraw device)
> + *
> + * 2) Data is from DJ collection, and is intended for this driver i. e.
> + * data contains arrival, departure, etc notifications, in which case
> + * we queue them for delayed processing by the work queue. We return 1
> + * to hid-core as no further processing is required from it.
> + *
> + * 3) Data is from DJ collection, and informs a connection change,
> + * if the change means rf link loss, then we must send a null report
> + * to the upper layer to discard potentially pressed keys that may be
> + * repeated forever by the input layer. Return 1 to hid-core as no
> + * further processing is required.
> + *
> + * 4) Data is from DJ collection and is an actual input event from
> + * a paired DJ device in which case we forward it to the correct hid
> + * device (via hid_input_report() ) and return 1 so hid-core does not do
> + * anything else with it.
> + */
> +
> + spin_lock_irqsave(&djrcv_dev->lock, flags);
> + if (dj_report->report_id == REPORT_ID_DJ_SHORT) {
> + switch (dj_report->report_type) {
> + case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
> + case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
> + logi_dj_recv_queue_notification(djrcv_dev, dj_report);
> + break;
> + case REPORT_TYPE_NOTIF_CONNECTION_STATUS:
> + if (dj_report->report_params[CONNECTION_STATUS_PARAM_STATUS] ==
> + STATUS_LINKLOSS) {
> + logi_dj_recv_forward_null_report(djrcv_dev, dj_report);
> + }
> + break;
> + default:
> + logi_dj_recv_forward_report(djrcv_dev, dj_report);
> + }
> + report_processed = true;
> + }
> + spin_unlock_irqrestore(&djrcv_dev->lock, flags);
> +
> + return report_processed;
> +}
> +
> +static int logi_dj_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
> + 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);
> +
> + /* Ignore interfaces 0 and 1, they will not carry any data, dont create
> + * any hid_device for them */
> + if (intf->cur_altsetting->desc.bInterfaceNumber !=
> + LOGITECH_DJ_INTERFACE_NUMBER) {
> + dbg_hid("%s: ignoring ifnum %d\n", __func__,
> + intf->cur_altsetting->desc.bInterfaceNumber);
> + return -ENODEV;
> + }
> +
> + /* Treat interface 2 */
> +
> + djrcv_dev = kzalloc(sizeof(struct dj_receiver_dev), GFP_KERNEL);
> + if (!djrcv_dev) {
> + dev_err(&hdev->dev,
> + "%s:failed allocating dj_receiver_dev\n", __func__);
> + return -ENOMEM;
> + }
> + djrcv_dev->hdev = hdev;
> + INIT_WORK(&djrcv_dev->work, delayedwork_callback);
> + spin_lock_init(&djrcv_dev->lock);
> + if (kfifo_alloc(&djrcv_dev->notif_fifo,
> + DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report),
> + GFP_KERNEL)) {
> + dev_err(&hdev->dev,
> + "%s:failed allocating notif_fifo\n", __func__);
> + kfree(djrcv_dev);
> + return -ENOMEM;
> + }
> + hid_set_drvdata(hdev, djrcv_dev);
> +
> + /* Call to usbhid to fetch the HID descriptors of interface 2 and
> + * subsequently call to the hid/hid-core to parse the fetched
> + * descriptors, this will in turn create the hidraw and hiddev nodes
> + * for interface 2 of the receiver */
> + retval = hid_parse(hdev);
> + if (retval) {
> + dev_err(&hdev->dev,
> + "%s:parse of interface 2 failed\n", __func__);
> + goto hid_parse_fail;
> + }
> +
> + /* Starts the usb device and connects to upper interfaces hiddev and
> + * hidraw */
> + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
> + if (retval) {
> + dev_err(&hdev->dev,
> + "%s:hid_hw_start returned error\n", __func__);
> + goto hid_hw_start_fail;
> + }
> +
> + retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
> + if (retval < 0) {
> + dev_err(&hdev->dev,
> + "%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n",
> + __func__, retval);
> + goto switch_to_dj_mode_fail;
> + }
> +
> + /* This is enabling the polling urb on the IN endpoint */
> + retval = hdev->ll_driver->open(hdev);
> + if (retval < 0) {
> + dev_err(&hdev->dev, "%s:hdev->ll_driver->open returned "
> + "error:%d\n", __func__, retval);
> + goto llopen_failed;
> + }
> +
> + retval = logi_dj_recv_query_paired_devices(djrcv_dev);
> + if (retval < 0) {
> + dev_err(&hdev->dev, "%s:logi_dj_recv_query_paired_devices "
> + "error:%d\n", __func__, retval);
> + goto logi_dj_recv_query_paired_devices_failed;
> + }
> +
> + return retval;
> +
> +logi_dj_recv_query_paired_devices_failed:
> + hdev->ll_driver->close(hdev);
> +
> +llopen_failed:
> +switch_to_dj_mode_fail:
> + hid_hw_stop(hdev);
> +
> +hid_hw_start_fail:
> +hid_parse_fail:
> + kfifo_free(&djrcv_dev->notif_fifo);
> + kfree(djrcv_dev);
> + hid_set_drvdata(hdev, NULL);
> + return retval;
> +
> +}
> +
> +static void logi_dj_remove(struct hid_device *hdev)
> +{
> + struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
> + struct dj_device *dj_dev;
> + int i;
> +
> + dbg_hid("%s\n", __func__);
> +
> + cancel_work_sync(&djrcv_dev->work);
> +
> + hdev->ll_driver->close(hdev);
> + hid_hw_stop(hdev);
> +
> + /* I suppose that at this point the only context that can access
> + * the djrecv_data is this thread as the work item is guaranteed to
> + * have finished and no more raw_event callbacks should arrive after
> + * the remove callback was triggered so no locks are put around the
> + * code below */
> + for (i = 0; i < DJ_MAX_PAIRED_DEVICES; i++) {
> + dj_dev = djrcv_dev->paired_dj_devices[i];
> + if (dj_dev != NULL) {
> + hid_destroy_device(dj_dev->hdev);
> + kfree(dj_dev);
> + djrcv_dev->paired_dj_devices[i] = NULL;
> + }
> + }
> +
> + kfifo_free(&djrcv_dev->notif_fifo);
> + kfree(djrcv_dev);
> + 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)},
> + {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
> + USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(hid, logi_dj_receivers);
> +
> +static struct hid_driver logi_djreceiver_driver = {
> + .name = "logitech-djreceiver",
> + .id_table = logi_dj_receivers,
> + .probe = logi_dj_probe,
> + .remove = logi_dj_remove,
> + .raw_event = logi_dj_raw_event
> +};
> +
> +
> +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");
> diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
> new file mode 100644
> index 0000000..8dcdccd
> --- /dev/null
> +++ b/drivers/hid/hid-logitech-dj.h
> @@ -0,0 +1,123 @@
> +#ifndef __HID_LOGITECH_DJ_H
> +#define __HID_LOGITECH_DJ_H
> +
> +/*
> + * HID driver for Logitech Unifying receivers
> + *
> + * Copyright (c) 2011 Logitech (c)
> + */
> +
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Should you need to contact me, the author, you can do so by e-mail send
> + * your message to Nestor Lopez Casado <xnlopez at gmail com>
> + *
> + */
> +
> +#include <linux/kfifo.h>
> +
> +#define DJ_MAX_PAIRED_DEVICES 6
> +#define DJ_MAX_NUMBER_NOTIFICATIONS 8
> +
> +#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_FIRST 0x01
> +#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];
> + struct work_struct work;
> + struct kfifo notif_fifo;
> + spinlock_t lock;
> +};
> +
> +struct dj_device {
> + struct hid_device *hdev;
> + struct dj_receiver_dev *dj_receiver_dev;
> + u32 reports_supported;
> + u8 device_index;
> +};
> +
> +/**
> + * is_dj_device - know if the given dj_device is not the receiver.
> + * @dj_dev: the dj device to test
> + *
> + * This macro tests if a struct dj_device pointer is a device created
> + * by the bus enumarator.
> + */
> +#define is_dj_device(dj_dev) \
> + (&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent)
> +
> +#endif
> --
> 1.7.4.4
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists