Commit b95dd3ca authored by Jiri Kosina's avatar Jiri Kosina

Merge branch 'for-3.15/hid-core-ll-transport-cleanup' into for-3.15/sony

parents 2078b9bb c3d77fab
...@@ -283,7 +283,8 @@ The available HID callbacks are: ...@@ -283,7 +283,8 @@ The available HID callbacks are:
int reqtype) int reqtype)
Same as ->request() but provides the report as raw buffer. This request shall Same as ->request() but provides the report as raw buffer. This request shall
be synchronous. A transport driver must not use ->wait() to complete such be synchronous. A transport driver must not use ->wait() to complete such
requests. requests. This request is mandatory and hid core will reject the device if
it is missing.
- int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len) - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
Send raw output report via intr channel. Used by some HID device drivers Send raw output report via intr channel. Used by some HID device drivers
......
...@@ -175,6 +175,15 @@ config HID_PRODIKEYS ...@@ -175,6 +175,15 @@ config HID_PRODIKEYS
multimedia keyboard, but will lack support for the musical keyboard multimedia keyboard, but will lack support for the musical keyboard
and some additional multimedia keys. and some additional multimedia keys.
config HID_CP2112
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
depends on USB_HID && I2C && GPIOLIB
---help---
Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge.
This is a HID device driver which registers as an i2c adapter
and gpiochip to expose these functions of the CP2112. The
customizable USB descriptor fields are exposed as sysfs attributes.
config HID_CYPRESS config HID_CYPRESS
tristate "Cypress mouse and barcode readers" if EXPERT tristate "Cypress mouse and barcode readers" if EXPERT
depends on HID depends on HID
......
...@@ -41,6 +41,7 @@ obj-$(CONFIG_HID_AUREAL) += hid-aureal.o ...@@ -41,6 +41,7 @@ obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
......
...@@ -1248,6 +1248,11 @@ void hid_output_report(struct hid_report *report, __u8 *data) ...@@ -1248,6 +1248,11 @@ void hid_output_report(struct hid_report *report, __u8 *data)
} }
EXPORT_SYMBOL_GPL(hid_output_report); EXPORT_SYMBOL_GPL(hid_output_report);
static int hid_report_len(struct hid_report *report)
{
return ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7;
}
/* /*
* Allocator for buffer that is going to be passed to hid_output_report() * Allocator for buffer that is going to be passed to hid_output_report()
*/ */
...@@ -1258,7 +1263,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) ...@@ -1258,7 +1263,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)
* of implement() working on 8 byte chunks * of implement() working on 8 byte chunks
*/ */
int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; int len = hid_report_len(report);
return kmalloc(len, flags); return kmalloc(len, flags);
} }
...@@ -1314,6 +1319,41 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, ...@@ -1314,6 +1319,41 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
return report; return report;
} }
/*
* Implement a generic .request() callback, using .raw_request()
* DO NOT USE in hid drivers directly, but through hid_hw_request instead.
*/
void __hid_request(struct hid_device *hid, struct hid_report *report,
int reqtype)
{
char *buf;
int ret;
int len;
buf = hid_alloc_report_buf(report, GFP_KERNEL);
if (!buf)
return;
len = hid_report_len(report);
if (reqtype == HID_REQ_SET_REPORT)
hid_output_report(report, buf);
ret = hid->ll_driver->raw_request(hid, report->id, buf, len,
report->type, reqtype);
if (ret < 0) {
dbg_hid("unable to complete request: %d\n", ret);
goto out;
}
if (reqtype == HID_REQ_GET_REPORT)
hid_input_report(hid, report->type, buf, ret, 0);
out:
kfree(buf);
}
EXPORT_SYMBOL_GPL(__hid_request);
int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
int interrupt) int interrupt)
{ {
...@@ -1692,6 +1732,7 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -1692,6 +1732,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
...@@ -2428,6 +2469,14 @@ int hid_add_device(struct hid_device *hdev) ...@@ -2428,6 +2469,14 @@ int hid_add_device(struct hid_device *hdev)
if (hid_ignore(hdev)) if (hid_ignore(hdev))
return -ENODEV; return -ENODEV;
/*
* Check for the mandatory transport channel.
*/
if (!hdev->ll_driver->raw_request) {
hid_err(hdev, "transport driver missing .raw_request()\n");
return -EINVAL;
}
/* /*
* Read the device report descriptor once and use as template * Read the device report descriptor once and use as template
* for the driver-specific modifications. * for the driver-specific modifications.
......
/*
* hid-cp2112.c - Silicon Labs HID USB to SMBus master bridge
* Copyright (c) 2013,2014 Uplogix, Inc.
* David Barksdale <dbarksdale@uplogix.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
/*
* The Silicon Labs CP2112 chip is a USB HID device which provides an
* SMBus controller for talking to slave devices and 8 GPIO pins. The
* host communicates with the CP2112 via raw HID reports.
*
* Data Sheet:
* http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf
* Programming Interface Specification:
* http://www.silabs.com/Support%20Documents/TechnicalDocs/AN495.pdf
*/
#include <linux/gpio.h>
#include <linux/hid.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/nls.h>
#include <linux/usb/ch9.h>
#include "hid-ids.h"
enum {
CP2112_GPIO_CONFIG = 0x02,
CP2112_GPIO_GET = 0x03,
CP2112_GPIO_SET = 0x04,
CP2112_GET_VERSION_INFO = 0x05,
CP2112_SMBUS_CONFIG = 0x06,
CP2112_DATA_READ_REQUEST = 0x10,
CP2112_DATA_WRITE_READ_REQUEST = 0x11,
CP2112_DATA_READ_FORCE_SEND = 0x12,
CP2112_DATA_READ_RESPONSE = 0x13,
CP2112_DATA_WRITE_REQUEST = 0x14,
CP2112_TRANSFER_STATUS_REQUEST = 0x15,
CP2112_TRANSFER_STATUS_RESPONSE = 0x16,
CP2112_CANCEL_TRANSFER = 0x17,
CP2112_LOCK_BYTE = 0x20,
CP2112_USB_CONFIG = 0x21,
CP2112_MANUFACTURER_STRING = 0x22,
CP2112_PRODUCT_STRING = 0x23,
CP2112_SERIAL_STRING = 0x24,
};
enum {
STATUS0_IDLE = 0x00,
STATUS0_BUSY = 0x01,
STATUS0_COMPLETE = 0x02,
STATUS0_ERROR = 0x03,
};
enum {
STATUS1_TIMEOUT_NACK = 0x00,
STATUS1_TIMEOUT_BUS = 0x01,
STATUS1_ARBITRATION_LOST = 0x02,
STATUS1_READ_INCOMPLETE = 0x03,
STATUS1_WRITE_INCOMPLETE = 0x04,
STATUS1_SUCCESS = 0x05,
};
struct cp2112_smbus_config_report {
u8 report; /* CP2112_SMBUS_CONFIG */
__be32 clock_speed; /* Hz */
u8 device_address; /* Stored in the upper 7 bits */
u8 auto_send_read; /* 1 = enabled, 0 = disabled */
__be16 write_timeout; /* ms, 0 = no timeout */
__be16 read_timeout; /* ms, 0 = no timeout */
u8 scl_low_timeout; /* 1 = enabled, 0 = disabled */
__be16 retry_time; /* # of retries, 0 = no limit */
} __packed;
struct cp2112_usb_config_report {
u8 report; /* CP2112_USB_CONFIG */
__le16 vid; /* Vendor ID */
__le16 pid; /* Product ID */
u8 max_power; /* Power requested in 2mA units */
u8 power_mode; /* 0x00 = bus powered
0x01 = self powered & regulator off
0x02 = self powered & regulator on */
u8 release_major;
u8 release_minor;
u8 mask; /* What fields to program */
} __packed;
struct cp2112_read_req_report {
u8 report; /* CP2112_DATA_READ_REQUEST */
u8 slave_address;
__be16 length;
} __packed;
struct cp2112_write_read_req_report {
u8 report; /* CP2112_DATA_WRITE_READ_REQUEST */
u8 slave_address;
__be16 length;
u8 target_address_length;
u8 target_address[16];
} __packed;
struct cp2112_write_req_report {
u8 report; /* CP2112_DATA_WRITE_REQUEST */
u8 slave_address;
u8 length;
u8 data[61];
} __packed;
struct cp2112_force_read_report {
u8 report; /* CP2112_DATA_READ_FORCE_SEND */
__be16 length;
} __packed;
struct cp2112_xfer_status_report {
u8 report; /* CP2112_TRANSFER_STATUS_RESPONSE */
u8 status0; /* STATUS0_* */
u8 status1; /* STATUS1_* */
__be16 retries;
__be16 length;
} __packed;
struct cp2112_string_report {
u8 dummy; /* force .string to be aligned */
u8 report; /* CP2112_*_STRING */
u8 length; /* length in bytes of everyting after .report */
u8 type; /* USB_DT_STRING */
wchar_t string[30]; /* UTF16_LITTLE_ENDIAN string */
} __packed;
/* Number of times to request transfer status before giving up waiting for a
transfer to complete. This may need to be changed if SMBUS clock, retries,
or read/write/scl_low timeout settings are changed. */
static const int XFER_STATUS_RETRIES = 10;
/* Time in ms to wait for a CP2112_DATA_READ_RESPONSE or
CP2112_TRANSFER_STATUS_RESPONSE. */
static const int RESPONSE_TIMEOUT = 50;
static const struct hid_device_id cp2112_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
{ }
};
MODULE_DEVICE_TABLE(hid, cp2112_devices);
struct cp2112_device {
struct i2c_adapter adap;
struct hid_device *hdev;
wait_queue_head_t wait;
u8 read_data[61];
u8 read_length;
int xfer_status;
atomic_t read_avail;
atomic_t xfer_avail;
struct gpio_chip gc;
};
static int gpio_push_pull = 0xFF;
module_param(gpio_push_pull, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(gpio_push_pull, "GPIO push-pull configuration bitmask");
static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct cp2112_device *dev = container_of(chip, struct cp2112_device,
gc);
struct hid_device *hdev = dev->hdev;
u8 buf[5];
int ret;
ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
sizeof(buf), HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret != sizeof(buf)) {
hid_err(hdev, "error requesting GPIO config: %d\n", ret);
return ret;
}
buf[1] &= ~(1 << offset);
buf[2] = gpio_push_pull;
ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0) {
hid_err(hdev, "error setting GPIO config: %d\n", ret);
return ret;
}
return 0;
}
static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct cp2112_device *dev = container_of(chip, struct cp2112_device,
gc);
struct hid_device *hdev = dev->hdev;
u8 buf[3];
int ret;
buf[0] = CP2112_GPIO_SET;
buf[1] = value ? 0xff : 0;
buf[2] = 1 << offset;
ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0)
hid_err(hdev, "error setting GPIO values: %d\n", ret);
}
static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct cp2112_device *dev = container_of(chip, struct cp2112_device,
gc);
struct hid_device *hdev = dev->hdev;
u8 buf[2];
int ret;
ret = hid_hw_raw_request(hdev, CP2112_GPIO_GET, buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret != sizeof(buf)) {
hid_err(hdev, "error requesting GPIO values: %d\n", ret);
return ret;
}
return (buf[1] >> offset) & 1;
}
static int cp2112_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
struct cp2112_device *dev = container_of(chip, struct cp2112_device,
gc);
struct hid_device *hdev = dev->hdev;
u8 buf[5];
int ret;
cp2112_gpio_set(chip, offset, value);
ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
sizeof(buf), HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret != sizeof(buf)) {
hid_err(hdev, "error requesting GPIO config: %d\n", ret);
return ret;
}
buf[1] |= 1 << offset;
buf[2] = gpio_push_pull;
ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0) {
hid_err(hdev, "error setting GPIO config: %d\n", ret);
return ret;
}
return 0;
}
static int cp2112_hid_get(struct hid_device *hdev, unsigned char report_number,
u8 *data, size_t count, unsigned char report_type)
{
u8 *buf;
int ret;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = hid_hw_raw_request(hdev, report_number, buf, count,
report_type, HID_REQ_GET_REPORT);
memcpy(data, buf, count);
kfree(buf);
return ret;
}
static int cp2112_hid_output(struct hid_device *hdev, u8 *data, size_t count,
unsigned char report_type)
{
u8 *buf;
int ret;
buf = kmemdup(data, count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (report_type == HID_OUTPUT_REPORT)
ret = hid_hw_output_report(hdev, buf, count);
else
ret = hid_hw_raw_request(hdev, buf[0], buf, count, report_type,
HID_REQ_SET_REPORT);
kfree(buf);
return ret;
}
static int cp2112_wait(struct cp2112_device *dev, atomic_t *avail)
{
int ret = 0;
/* We have sent either a CP2112_TRANSFER_STATUS_REQUEST or a
* CP2112_DATA_READ_FORCE_SEND and we are waiting for the response to
* come in cp2112_raw_event or timeout. There will only be one of these
* in flight at any one time. The timeout is extremely large and is a
* last resort if the CP2112 has died. If we do timeout we don't expect
* to receive the response which would cause data races, it's not like
* we can do anything about it anyway.
*/
ret = wait_event_interruptible_timeout(dev->wait,
atomic_read(avail), msecs_to_jiffies(RESPONSE_TIMEOUT));
if (-ERESTARTSYS == ret)
return ret;
if (!ret)
return -ETIMEDOUT;
atomic_set(avail, 0);
return 0;
}
static int cp2112_xfer_status(struct cp2112_device *dev)
{
struct hid_device *hdev = dev->hdev;
u8 buf[2];
int ret;
buf[0] = CP2112_TRANSFER_STATUS_REQUEST;
buf[1] = 0x01;
atomic_set(&dev->xfer_avail, 0);
ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
if (ret < 0) {
hid_warn(hdev, "Error requesting status: %d\n", ret);
return ret;
}
ret = cp2112_wait(dev, &dev->xfer_avail);
if (ret)
return ret;
return dev->xfer_status;
}
static int cp2112_read(struct cp2112_device *dev, u8 *data, size_t size)
{
struct hid_device *hdev = dev->hdev;
struct cp2112_force_read_report report;
int ret;
report.report = CP2112_DATA_READ_FORCE_SEND;
report.length = cpu_to_be16(size);
atomic_set(&dev->read_avail, 0);
ret = cp2112_hid_output(hdev, &report.report, sizeof(report),
HID_OUTPUT_REPORT);
if (ret < 0) {
hid_warn(hdev, "Error requesting data: %d\n", ret);
return ret;
}
ret = cp2112_wait(dev, &dev->read_avail);
if (ret)
return ret;
hid_dbg(hdev, "read %d of %zd bytes requested\n",
dev->read_length, size);
if (size > dev->read_length)
size = dev->read_length;
memcpy(data, dev->read_data, size);
return dev->read_length;
}
static int cp2112_read_req(void *buf, u8 slave_address, u16 length)
{
struct cp2112_read_req_report *report = buf;
if (length < 1 || length > 512)
return -EINVAL;
report->report = CP2112_DATA_READ_REQUEST;
report->slave_address = slave_address << 1;
report->length = cpu_to_be16(length);
return sizeof(*report);
}
static int cp2112_write_read_req(void *buf, u8 slave_address, u16 length,
u8 command, u8 *data, u8 data_length)
{
struct cp2112_write_read_req_report *report = buf;
if (length < 1 || length > 512
|| data_length > sizeof(report->target_address) - 1)
return -EINVAL;
report->report = CP2112_DATA_WRITE_READ_REQUEST;
report->slave_address = slave_address << 1;
report->length = cpu_to_be16(length);
report->target_address_length = data_length + 1;
report->target_address[0] = command;
memcpy(&report->target_address[1], data, data_length);
return data_length + 6;
}
static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data,
u8 data_length)
{
struct cp2112_write_req_report *report = buf;
if (data_length > sizeof(report->data) - 1)
return -EINVAL;
report->report = CP2112_DATA_WRITE_REQUEST;
report->slave_address = slave_address << 1;
report->length = data_length + 1;
report->data[0] = command;
memcpy(&report->data[1], data, data_length);
return data_length + 4;
}
static int cp2112_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data;
struct hid_device *hdev = dev->hdev;
u8 buf[64];
__be16 word;
ssize_t count;
size_t read_length = 0;
unsigned int retries;
int ret;
hid_dbg(hdev, "%s addr 0x%x flags 0x%x cmd 0x%x size %d\n",
read_write == I2C_SMBUS_WRITE ? "write" : "read",
addr, flags, command, size);
switch (size) {
case I2C_SMBUS_BYTE:
read_length = 1;
if (I2C_SMBUS_READ == read_write)
count = cp2112_read_req(buf, addr, read_length);
else
count = cp2112_write_req(buf, addr, data->byte, NULL,
0);
break;
case I2C_SMBUS_BYTE_DATA:
read_length = 1;
if (I2C_SMBUS_READ == read_write)
count = cp2112_write_read_req(buf, addr, read_length,
command, NULL, 0);
else
count = cp2112_write_req(buf, addr, command,
&data->byte, 1);
break;
case I2C_SMBUS_WORD_DATA:
read_length = 2;
word = cpu_to_be16(data->word);
if (I2C_SMBUS_READ == read_write)
count = cp2112_write_read_req(buf, addr, read_length,
command, NULL, 0);
else
count = cp2112_write_req(buf, addr, command,
(u8 *)&word, 2);
break;
case I2C_SMBUS_PROC_CALL:
size = I2C_SMBUS_WORD_DATA;
read_write = I2C_SMBUS_READ;
read_length = 2;
word = cpu_to_be16(data->word);
count = cp2112_write_read_req(buf, addr, read_length, command,
(u8 *)&word, 2);
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
size = I2C_SMBUS_BLOCK_DATA;
/* fallthrough */
case I2C_SMBUS_BLOCK_DATA:
if (I2C_SMBUS_READ == read_write) {
count = cp2112_write_read_req(buf, addr,
I2C_SMBUS_BLOCK_MAX,
command, NULL, 0);
} else {
count = cp2112_write_req(buf, addr, command,
data->block,
data->block[0] + 1);
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
size = I2C_SMBUS_BLOCK_DATA;
read_write = I2C_SMBUS_READ;
count = cp2112_write_read_req(buf, addr, I2C_SMBUS_BLOCK_MAX,
command, data->block,
data->block[0] + 1);
break;
default:
hid_warn(hdev, "Unsupported transaction %d\n", size);
return -EOPNOTSUPP;
}
if (count < 0)
return count;
ret = hid_hw_power(hdev, PM_HINT_FULLON);
if (ret < 0) {
hid_err(hdev, "power management error: %d\n", ret);
return ret;
}
ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT);
if (ret < 0) {
hid_warn(hdev, "Error starting transaction: %d\n", ret);
goto power_normal;
}
for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) {
ret = cp2112_xfer_status(dev);
if (-EBUSY == ret)
continue;
if (ret < 0)
goto power_normal;
break;
}
if (XFER_STATUS_RETRIES <= retries) {
hid_warn(hdev, "Transfer timed out, cancelling.\n");
buf[0] = CP2112_CANCEL_TRANSFER;
buf[1] = 0x01;
ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
if (ret < 0)
hid_warn(hdev, "Error cancelling transaction: %d\n",
ret);
ret = -ETIMEDOUT;
goto power_normal;
}
if (I2C_SMBUS_WRITE == read_write) {
ret = 0;
goto power_normal;
}
if (I2C_SMBUS_BLOCK_DATA == size)
read_length = ret;
ret = cp2112_read(dev, buf, read_length);
if (ret < 0)
goto power_normal;
if (ret != read_length) {
hid_warn(hdev, "short read: %d < %zd\n", ret, read_length);
ret = -EIO;
goto power_normal;
}
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
data->byte = buf[0];
break;
case I2C_SMBUS_WORD_DATA:
data->word = be16_to_cpup((__be16 *)buf);
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_length > I2C_SMBUS_BLOCK_MAX) {
ret = -EPROTO;
goto power_normal;
}
memcpy(data->block, buf, read_length);
break;
}
ret = 0;
power_normal:
hid_hw_power(hdev, PM_HINT_NORMAL);
hid_dbg(hdev, "transfer finished: %d\n", ret);
return ret;
}
static u32 cp2112_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK |
I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
}
static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = cp2112_xfer,
.functionality = cp2112_functionality,
};
static int cp2112_get_usb_config(struct hid_device *hdev,
struct cp2112_usb_config_report *cfg)
{
int ret;
ret = cp2112_hid_get(hdev, CP2112_USB_CONFIG, (u8 *)cfg, sizeof(*cfg),
HID_FEATURE_REPORT);
if (ret != sizeof(*cfg)) {
hid_err(hdev, "error reading usb config: %d\n", ret);
if (ret < 0)
return ret;
return -EIO;
}
return 0;
}
static int cp2112_set_usb_config(struct hid_device *hdev,
struct cp2112_usb_config_report *cfg)
{
int ret;
BUG_ON(cfg->report != CP2112_USB_CONFIG);
ret = cp2112_hid_output(hdev, (u8 *)cfg, sizeof(*cfg),
HID_FEATURE_REPORT);
if (ret != sizeof(*cfg)) {
hid_err(hdev, "error writing usb config: %d\n", ret);
if (ret < 0)
return ret;
return -EIO;
}
return 0;
}
static void chmod_sysfs_attrs(struct hid_device *hdev);
#define CP2112_CONFIG_ATTR(name, store, format, ...) \
static ssize_t name##_store(struct device *kdev, \
struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \
struct cp2112_usb_config_report cfg; \
int ret = cp2112_get_usb_config(hdev, &cfg); \
if (ret) \
return ret; \
store; \
ret = cp2112_set_usb_config(hdev, &cfg); \
if (ret) \
return ret; \
chmod_sysfs_attrs(hdev); \
return count; \
} \
static ssize_t name##_show(struct device *kdev, \
struct device_attribute *attr, char *buf) \
{ \
struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \
struct cp2112_usb_config_report cfg; \
int ret = cp2112_get_usb_config(hdev, &cfg); \
if (ret) \
return ret; \
return scnprintf(buf, PAGE_SIZE, format, ##__VA_ARGS__); \
} \
static DEVICE_ATTR_RW(name);
CP2112_CONFIG_ATTR(vendor_id, ({
u16 vid;
if (sscanf(buf, "%hi", &vid) != 1)
return -EINVAL;
cfg.vid = cpu_to_le16(vid);
cfg.mask = 0x01;
}), "0x%04x\n", le16_to_cpu(cfg.vid));
CP2112_CONFIG_ATTR(product_id, ({
u16 pid;
if (sscanf(buf, "%hi", &pid) != 1)
return -EINVAL;
cfg.pid = cpu_to_le16(pid);
cfg.mask = 0x02;
}), "0x%04x\n", le16_to_cpu(cfg.pid));
CP2112_CONFIG_ATTR(max_power, ({
int mA;
if (sscanf(buf, "%i", &mA) != 1)
return -EINVAL;
cfg.max_power = (mA + 1) / 2;
cfg.mask = 0x04;
}), "%u mA\n", cfg.max_power * 2);
CP2112_CONFIG_ATTR(power_mode, ({
if (sscanf(buf, "%hhi", &cfg.power_mode) != 1)
return -EINVAL;
cfg.mask = 0x08;
}), "%u\n", cfg.power_mode);
CP2112_CONFIG_ATTR(release_version, ({
if (sscanf(buf, "%hhi.%hhi", &cfg.release_major, &cfg.release_minor)
!= 2)
return -EINVAL;
cfg.mask = 0x10;
}), "%u.%u\n", cfg.release_major, cfg.release_minor);
#undef CP2112_CONFIG_ATTR
struct cp2112_pstring_attribute {
struct device_attribute attr;
unsigned char report;
};
static ssize_t pstr_store(struct device *kdev,
struct device_attribute *kattr, const char *buf,
size_t count)
{
struct hid_device *hdev = container_of(kdev, struct hid_device, dev);
struct cp2112_pstring_attribute *attr =
container_of(kattr, struct cp2112_pstring_attribute, attr);
struct cp2112_string_report report;
int ret;
memset(&report, 0, sizeof(report));
ret = utf8s_to_utf16s(buf, count, UTF16_LITTLE_ENDIAN,
report.string, ARRAY_SIZE(report.string));
report.report = attr->report;
report.length = ret * sizeof(report.string[0]) + 2;
report.type = USB_DT_STRING;
ret = cp2112_hid_output(hdev, &report.report, report.length + 1,
HID_FEATURE_REPORT);
if (ret != report.length + 1) {
hid_err(hdev, "error writing %s string: %d\n", kattr->attr.name,
ret);
if (ret < 0)
return ret;
return -EIO;
}
chmod_sysfs_attrs(hdev);
return count;
}
static ssize_t pstr_show(struct device *kdev,
struct device_attribute *kattr, char *buf)
{
struct hid_device *hdev = container_of(kdev, struct hid_device, dev);
struct cp2112_pstring_attribute *attr =
container_of(kattr, struct cp2112_pstring_attribute, attr);
struct cp2112_string_report report;
u8 length;
int ret;
ret = cp2112_hid_get(hdev, attr->report, &report.report,
sizeof(report) - 1, HID_FEATURE_REPORT);
if (ret < 3) {
hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name,
ret);
if (ret < 0)
return ret;
return -EIO;
}
if (report.length < 2) {
hid_err(hdev, "invalid %s string length: %d\n",
kattr->attr.name, report.length);
return -EIO;
}
length = report.length > ret - 1 ? ret - 1 : report.length;
length = (length - 2) / sizeof(report.string[0]);
ret = utf16s_to_utf8s(report.string, length, UTF16_LITTLE_ENDIAN, buf,
PAGE_SIZE - 1);
buf[ret++] = '\n';
return ret;
}
#define CP2112_PSTR_ATTR(name, _report) \
static struct cp2112_pstring_attribute dev_attr_##name = { \
.attr = __ATTR(name, (S_IWUSR | S_IRUGO), pstr_show, pstr_store), \
.report = _report, \
};
CP2112_PSTR_ATTR(manufacturer, CP2112_MANUFACTURER_STRING);
CP2112_PSTR_ATTR(product, CP2112_PRODUCT_STRING);
CP2112_PSTR_ATTR(serial, CP2112_SERIAL_STRING);
#undef CP2112_PSTR_ATTR
static const struct attribute_group cp2112_attr_group = {
.attrs = (struct attribute *[]){
&dev_attr_vendor_id.attr,
&dev_attr_product_id.attr,
&dev_attr_max_power.attr,
&dev_attr_power_mode.attr,
&dev_attr_release_version.attr,
&dev_attr_manufacturer.attr.attr,
&dev_attr_product.attr.attr,
&dev_attr_serial.attr.attr,
NULL
}
};
/* Chmoding our sysfs attributes is simply a way to expose which fields in the
* PROM have already been programmed. We do not depend on this preventing
* writing to these attributes since the CP2112 will simply ignore writes to
* already-programmed fields. This is why there is no sense in fixing this
* racy behaviour.
*/
static void chmod_sysfs_attrs(struct hid_device *hdev)
{
struct attribute **attr;
u8 buf[2];
int ret;
ret = cp2112_hid_get(hdev, CP2112_LOCK_BYTE, buf, sizeof(buf),
HID_FEATURE_REPORT);
if (ret != sizeof(buf)) {
hid_err(hdev, "error reading lock byte: %d\n", ret);
return;
}
for (attr = cp2112_attr_group.attrs; *attr; ++attr) {
umode_t mode = (buf[1] & 1) ? S_IWUSR | S_IRUGO : S_IRUGO;
ret = sysfs_chmod_file(&hdev->dev.kobj, *attr, mode);
if (ret < 0)
hid_err(hdev, "error chmoding sysfs file %s\n",
(*attr)->name);
buf[1] >>= 1;
}
}
static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct cp2112_device *dev;
u8 buf[3];
struct cp2112_smbus_config_report config;
int ret;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
return ret;
}
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (ret) {
hid_err(hdev, "hw start failed\n");
return ret;
}
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev, "hw open failed\n");
goto err_hid_stop;
}
ret = hid_hw_power(hdev, PM_HINT_FULLON);
if (ret < 0) {
hid_err(hdev, "power management error: %d\n", ret);
goto err_hid_close;
}
ret = cp2112_hid_get(hdev, CP2112_GET_VERSION_INFO, buf, sizeof(buf),
HID_FEATURE_REPORT);
if (ret != sizeof(buf)) {
hid_err(hdev, "error requesting version\n");
if (ret >= 0)
ret = -EIO;
goto err_power_normal;
}
hid_info(hdev, "Part Number: 0x%02X Device Version: 0x%02X\n",
buf[1], buf[2]);
ret = cp2112_hid_get(hdev, CP2112_SMBUS_CONFIG, (u8 *)&config,
sizeof(config), HID_FEATURE_REPORT);
if (ret != sizeof(config)) {
hid_err(hdev, "error requesting SMBus config\n");
if (ret >= 0)
ret = -EIO;
goto err_power_normal;
}
config.retry_time = cpu_to_be16(1);
ret = cp2112_hid_output(hdev, (u8 *)&config, sizeof(config),
HID_FEATURE_REPORT);
if (ret != sizeof(config)) {
hid_err(hdev, "error setting SMBus config\n");
if (ret >= 0)
ret = -EIO;
goto err_power_normal;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
ret = -ENOMEM;
goto err_power_normal;
}
hid_set_drvdata(hdev, (void *)dev);
dev->hdev = hdev;
dev->adap.owner = THIS_MODULE;
dev->adap.class = I2C_CLASS_HWMON;
dev->adap.algo = &smbus_algorithm;
dev->adap.algo_data = dev;
dev->adap.dev.parent = &hdev->dev;
snprintf(dev->adap.name, sizeof(dev->adap.name),
"CP2112 SMBus Bridge on hiddev%d", hdev->minor);
init_waitqueue_head(&dev->wait);
hid_device_io_start(hdev);
ret = i2c_add_adapter(&dev->adap);
hid_device_io_stop(hdev);
if (ret) {
hid_err(hdev, "error registering i2c adapter\n");
goto err_free_dev;
}
hid_dbg(hdev, "adapter registered\n");
dev->gc.label = "cp2112_gpio";
dev->gc.direction_input = cp2112_gpio_direction_input;
dev->gc.direction_output = cp2112_gpio_direction_output;
dev->gc.set = cp2112_gpio_set;
dev->gc.get = cp2112_gpio_get;
dev->gc.base = -1;
dev->gc.ngpio = 8;
dev->gc.can_sleep = 1;
dev->gc.dev = &hdev->dev;
ret = gpiochip_add(&dev->gc);
if (ret < 0) {
hid_err(hdev, "error registering gpio chip\n");
goto err_free_i2c;
}
ret = sysfs_create_group(&hdev->dev.kobj, &cp2112_attr_group);
if (ret < 0) {
hid_err(hdev, "error creating sysfs attrs\n");
goto err_gpiochip_remove;
}
chmod_sysfs_attrs(hdev);
hid_hw_power(hdev, PM_HINT_NORMAL);
return ret;
err_gpiochip_remove:
if (gpiochip_remove(&dev->gc) < 0)
hid_err(hdev, "error removing gpio chip\n");
err_free_i2c:
i2c_del_adapter(&dev->adap);
err_free_dev:
kfree(dev);
err_power_normal:
hid_hw_power(hdev, PM_HINT_NORMAL);
err_hid_close:
hid_hw_close(hdev);
err_hid_stop:
hid_hw_stop(hdev);
return ret;
}
static void cp2112_remove(struct hid_device *hdev)
{
struct cp2112_device *dev = hid_get_drvdata(hdev);
sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
if (gpiochip_remove(&dev->gc))
hid_err(hdev, "unable to remove gpio chip\n");
i2c_del_adapter(&dev->adap);
/* i2c_del_adapter has finished removing all i2c devices from our
* adapter. Well behaved devices should no longer call our cp2112_xfer
* and should have waited for any pending calls to finish. It has also
* waited for device_unregister(&adap->dev) to complete. Therefore we
* can safely free our struct cp2112_device.
*/
hid_hw_close(hdev);
hid_hw_stop(hdev);
kfree(dev);
}
static int cp2112_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct cp2112_device *dev = hid_get_drvdata(hdev);
struct cp2112_xfer_status_report *xfer = (void *)data;
switch (data[0]) {
case CP2112_TRANSFER_STATUS_RESPONSE:
hid_dbg(hdev, "xfer status: %02x %02x %04x %04x\n",
xfer->status0, xfer->status1,
be16_to_cpu(xfer->retries), be16_to_cpu(xfer->length));
switch (xfer->status0) {
case STATUS0_IDLE:
dev->xfer_status = -EAGAIN;
break;
case STATUS0_BUSY:
dev->xfer_status = -EBUSY;
break;
case STATUS0_COMPLETE:
dev->xfer_status = be16_to_cpu(xfer->length);
break;
case STATUS0_ERROR:
switch (xfer->status1) {
case STATUS1_TIMEOUT_NACK:
case STATUS1_TIMEOUT_BUS:
dev->xfer_status = -ETIMEDOUT;
break;
default:
dev->xfer_status = -EIO;
break;
}
break;
default:
dev->xfer_status = -EINVAL;
break;
}
atomic_set(&dev->xfer_avail, 1);
break;
case CP2112_DATA_READ_RESPONSE:
hid_dbg(hdev, "read response: %02x %02x\n", data[1], data[2]);
dev->read_length = data[2];
if (dev->read_length > sizeof(dev->read_data))
dev->read_length = sizeof(dev->read_data);
memcpy(dev->read_data, &data[3], dev->read_length);
atomic_set(&dev->read_avail, 1);
break;
default:
hid_err(hdev, "unknown report\n");
return 0;
}
wake_up_interruptible(&dev->wait);
return 1;
}
static struct hid_driver cp2112_driver = {
.name = "cp2112",
.id_table = cp2112_devices,
.probe = cp2112_probe,
.remove = cp2112_remove,
.raw_event = cp2112_raw_event,
};
module_hid_driver(cp2112_driver);
MODULE_DESCRIPTION("Silicon Labs HID USB to SMBus master bridge");
MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>");
MODULE_LICENSE("GPL");
...@@ -455,12 +455,22 @@ static void mousevsc_hid_stop(struct hid_device *hid) ...@@ -455,12 +455,22 @@ static void mousevsc_hid_stop(struct hid_device *hid)
{ {
} }
static int mousevsc_hid_raw_request(struct hid_device *hid,
unsigned char report_num,
__u8 *buf, size_t len,
unsigned char rtype,
int reqtype)
{
return 0;
}
static struct hid_ll_driver mousevsc_ll_driver = { static struct hid_ll_driver mousevsc_ll_driver = {
.parse = mousevsc_hid_parse, .parse = mousevsc_hid_parse,
.open = mousevsc_hid_open, .open = mousevsc_hid_open,
.close = mousevsc_hid_close, .close = mousevsc_hid_close,
.start = mousevsc_hid_start, .start = mousevsc_hid_start,
.stop = mousevsc_hid_stop, .stop = mousevsc_hid_stop,
.raw_request = mousevsc_hid_raw_request,
}; };
static struct hid_driver mousevsc_hid_driver; static struct hid_driver mousevsc_hid_driver;
......
...@@ -240,6 +240,7 @@ ...@@ -240,6 +240,7 @@
#define USB_VENDOR_ID_CYGNAL 0x10c4 #define USB_VENDOR_ID_CYGNAL 0x10c4
#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a
#define USB_DEVICE_ID_CYGNAL_CP2112 0xea90
#define USB_VENDOR_ID_CYPRESS 0x04b4 #define USB_VENDOR_ID_CYPRESS 0x04b4
#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 #define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001
......
...@@ -1150,7 +1150,7 @@ static void hidinput_led_worker(struct work_struct *work) ...@@ -1150,7 +1150,7 @@ static void hidinput_led_worker(struct work_struct *work)
led_work); led_work);
struct hid_field *field; struct hid_field *field;
struct hid_report *report; struct hid_report *report;
int len; int len, ret;
__u8 *buf; __u8 *buf;
field = hidinput_get_led_field(hid); field = hidinput_get_led_field(hid);
...@@ -1184,7 +1184,10 @@ static void hidinput_led_worker(struct work_struct *work) ...@@ -1184,7 +1184,10 @@ static void hidinput_led_worker(struct work_struct *work)
hid_output_report(report, buf); hid_output_report(report, buf);
/* synchronous output report */ /* synchronous output report */
hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT); ret = hid_hw_output_report(hid, buf, len);
if (ret == -ENOSYS)
hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT,
HID_REQ_SET_REPORT);
kfree(buf); kfree(buf);
} }
...@@ -1263,7 +1266,6 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) ...@@ -1263,7 +1266,6 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)
} }
input_set_drvdata(input_dev, hid); input_set_drvdata(input_dev, hid);
if (hid->ll_driver->request || hid->hid_output_raw_report)
input_dev->event = hidinput_input_event; input_dev->event = hidinput_input_event;
input_dev->open = hidinput_open; input_dev->open = hidinput_open;
input_dev->close = hidinput_close; input_dev->close = hidinput_close;
......
...@@ -692,8 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -692,8 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ret = hid_output_raw_report(hdev, buf, sizeof(buf), ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret >= 0) { if (ret >= 0) {
/* insert a little delay of 10 jiffies ~ 40ms */ /* insert a little delay of 10 jiffies ~ 40ms */
...@@ -705,8 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -705,8 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
buf[1] = 0xB2; buf[1] = 0xB2;
get_random_bytes(&buf[2], 2); get_random_bytes(&buf[2], 2);
ret = hid_output_raw_report(hdev, buf, sizeof(buf), ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
} }
} }
......
...@@ -193,9 +193,6 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = { ...@@ -193,9 +193,6 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
static struct hid_ll_driver logi_dj_ll_driver; 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 int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev); static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev, static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
...@@ -262,7 +259,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, ...@@ -262,7 +259,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
} }
dj_hiddev->ll_driver = &logi_dj_ll_driver; 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->dev.parent = &djrcv_hdev->dev;
dj_hiddev->bus = BUS_USB; dj_hiddev->bus = BUS_USB;
...@@ -544,9 +540,10 @@ static void logi_dj_ll_close(struct hid_device *hid) ...@@ -544,9 +540,10 @@ static void logi_dj_ll_close(struct hid_device *hid)
dbg_hid("%s:%s\n", __func__, hid->phys); dbg_hid("%s:%s\n", __func__, hid->phys);
} }
static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, static int logi_dj_ll_raw_request(struct hid_device *hid,
size_t count, unsigned char reportnum, __u8 *buf,
unsigned char report_type) size_t count, unsigned char report_type,
int reqtype)
{ {
struct dj_device *djdev = hid->driver_data; struct dj_device *djdev = hid->driver_data;
struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev; struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev;
...@@ -567,15 +564,8 @@ static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, ...@@ -567,15 +564,8 @@ static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
out_buf[1] = djdev->device_index; out_buf[1] = djdev->device_index;
memcpy(out_buf + 2, buf, count); memcpy(out_buf + 2, buf, count);
/*
* hid-generic calls us with hid_output_raw_report(), but the LEDs
* are set through a SET_REPORT command. It works for USB-HID devices
* because usbhid either calls a SET_REPORT or directly send the output
* report depending if the device presents an urbout.
* Let be simple, send a SET_REPORT request.
*/
ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf, ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf,
DJREPORT_SHORT_LENGTH, report_type, HID_REQ_SET_REPORT); DJREPORT_SHORT_LENGTH, report_type, reqtype);
kfree(out_buf); kfree(out_buf);
return ret; return ret;
...@@ -662,6 +652,7 @@ static struct hid_ll_driver logi_dj_ll_driver = { ...@@ -662,6 +652,7 @@ static struct hid_ll_driver logi_dj_ll_driver = {
.stop = logi_dj_ll_stop, .stop = logi_dj_ll_stop,
.open = logi_dj_ll_open, .open = logi_dj_ll_open,
.close = logi_dj_ll_close, .close = logi_dj_ll_close,
.raw_request = logi_dj_ll_raw_request,
}; };
......
...@@ -538,8 +538,8 @@ static int magicmouse_probe(struct hid_device *hdev, ...@@ -538,8 +538,8 @@ static int magicmouse_probe(struct hid_device *hdev,
* but there seems to be no other way of switching the mode. * but there seems to be no other way of switching the mode.
* Thus the super-ugly hacky success check below. * Thus the super-ugly hacky success check below.
*/ */
ret = hid_output_raw_report(hdev, feature, sizeof(feature), ret = hid_hw_raw_request(hdev, feature[0], feature, sizeof(feature),
HID_FEATURE_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret != -EIO && ret != sizeof(feature)) { if (ret != -EIO && ret != sizeof(feature)) {
hid_err(hdev, "unable to request touch data (%d)\n", ret); hid_err(hdev, "unable to request touch data (%d)\n", ret);
goto err_stop_hw; goto err_stop_hw;
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/usb.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -1006,45 +1005,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -1006,45 +1005,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0; return 0;
} }
/*
* The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
* like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
* so we need to override that forcing HID Output Reports on the Control EP.
*
* There is also another issue about HID Output Reports via USB, the Sixaxis
* does not want the report_id as part of the data packet, so we have to
* discard buf[0] when sending the actual control message, even for numbered
* reports, humpf!
*/
static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
size_t count, unsigned char report_type)
{
struct usb_interface *intf = to_usb_interface(hid->dev.parent);
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface = intf->cur_altsetting;
int report_id = buf[0];
int ret;
if (report_type == HID_OUTPUT_REPORT) {
/* Don't send the Report ID */
buf++;
count--;
}
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
((report_type + 1) << 8) | report_id,
interface->desc.bInterfaceNumber, buf, count,
USB_CTRL_SET_TIMEOUT);
/* Count also the Report ID, in case of an Output report. */
if (ret > 0 && report_type == HID_OUTPUT_REPORT)
ret++;
return ret;
}
/* /*
* Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
* to "operational". Without this, the ps3 controller will not report any * to "operational". Without this, the ps3 controller will not report any
...@@ -1072,8 +1032,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) ...@@ -1072,8 +1032,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
static int sixaxis_set_operational_bt(struct hid_device *hdev) static int sixaxis_set_operational_bt(struct hid_device *hdev)
{ {
unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
return hid_output_raw_report(hdev, buf, sizeof(buf), return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
} }
/* /*
...@@ -1305,11 +1265,8 @@ static void sixaxis_state_worker(struct work_struct *work) ...@@ -1305,11 +1265,8 @@ static void sixaxis_state_worker(struct work_struct *work)
buf[10] |= sc->led_state[2] << 3; buf[10] |= sc->led_state[2] << 3;
buf[10] |= sc->led_state[3] << 4; buf[10] |= sc->led_state[3] << 4;
if (sc->quirks & SIXAXIS_CONTROLLER_USB) hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT); HID_REQ_SET_REPORT);
else
hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf),
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
} }
static void dualshock4_state_worker(struct work_struct *work) static void dualshock4_state_worker(struct work_struct *work)
...@@ -1659,7 +1616,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1659,7 +1616,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
} }
if (sc->quirks & SIXAXIS_CONTROLLER_USB) { if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; /*
* The Sony Sixaxis does not handle HID Output Reports on the
* Interrupt EP like it could, so we need to force HID Output
* Reports to use HID_REQ_SET_REPORT on the Control EP.
*
* There is also another issue about HID Output Reports via USB,
* the Sixaxis does not want the report_id as part of the data
* packet, so we have to discard buf[0] when sending the actual
* control message, even for numbered reports, humpf!
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
ret = sixaxis_set_operational_usb(hdev); ret = sixaxis_set_operational_usb(hdev);
sc->worker_initialized = 1; sc->worker_initialized = 1;
INIT_WORK(&sc->state_worker, sixaxis_state_worker); INIT_WORK(&sc->state_worker, sixaxis_state_worker);
......
...@@ -48,8 +48,8 @@ static int blink1_send_command(struct blink1_data *data, ...@@ -48,8 +48,8 @@ static int blink1_send_command(struct blink1_data *data,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[0], buf[1], buf[2], buf[3], buf[4],
buf[5], buf[6], buf[7], buf[8]); buf[5], buf[6], buf[7], buf[8]);
ret = hid_output_raw_report(data->hdev, buf, BLINK1_CMD_SIZE, ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE,
HID_FEATURE_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
return ret < 0 ? ret : 0; return ret < 0 ? ret : 0;
} }
......
...@@ -128,7 +128,8 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, ...@@ -128,7 +128,8 @@ static void wacom_set_image(struct hid_device *hdev, const char *image,
rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[0] = WAC_CMD_ICON_START_STOP;
rep_data[1] = 0; rep_data[1] = 0;
ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0) if (ret < 0)
goto err; goto err;
...@@ -142,14 +143,15 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, ...@@ -142,14 +143,15 @@ static void wacom_set_image(struct hid_device *hdev, const char *image,
rep_data[j + 3] = p[(i << 6) + j]; rep_data[j + 3] = p[(i << 6) + j];
rep_data[2] = i; rep_data[2] = i;
ret = hid_output_raw_report(hdev, rep_data, 67, ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67,
HID_FEATURE_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
} }
rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[0] = WAC_CMD_ICON_START_STOP;
rep_data[1] = 0; rep_data[1] = 0;
ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
err: err:
return; return;
...@@ -181,7 +183,8 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev, ...@@ -181,7 +183,8 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
buf[3] = value; buf[3] = value;
/* use fixed brightness for OLEDs */ /* use fixed brightness for OLEDs */
buf[4] = 0x08; buf[4] = 0x08;
hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
kfree(buf); kfree(buf);
} }
...@@ -337,8 +340,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) ...@@ -337,8 +340,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data[0] = 0x03 ; rep_data[1] = 0x00; rep_data[0] = 0x03 ; rep_data[1] = 0x00;
limit = 3; limit = 3;
do { do {
ret = hid_output_raw_report(hdev, rep_data, 2, ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
HID_FEATURE_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
} while (ret < 0 && limit-- > 0); } while (ret < 0 && limit-- > 0);
if (ret >= 0) { if (ret >= 0) {
...@@ -350,8 +353,9 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) ...@@ -350,8 +353,9 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data[1] = 0x00; rep_data[1] = 0x00;
limit = 3; limit = 3;
do { do {
ret = hid_output_raw_report(hdev, ret = hid_hw_raw_request(hdev, rep_data[0],
rep_data, 2, HID_FEATURE_REPORT); rep_data, 2, HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
} while (ret < 0 && limit-- > 0); } while (ret < 0 && limit-- > 0);
if (ret >= 0) { if (ret >= 0) {
...@@ -376,8 +380,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) ...@@ -376,8 +380,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data[0] = 0x03; rep_data[0] = 0x03;
rep_data[1] = wdata->features; rep_data[1] = wdata->features;
ret = hid_output_raw_report(hdev, rep_data, 2, ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
HID_FEATURE_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret >= 0) if (ret >= 0)
wdata->high_speed = speed; wdata->high_speed = speed;
break; break;
......
...@@ -28,14 +28,14 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, ...@@ -28,14 +28,14 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
__u8 *buf; __u8 *buf;
int ret; int ret;
if (!hdev->hid_output_raw_report) if (!hdev->ll_driver->output_report)
return -ENODEV; return -ENODEV;
buf = kmemdup(buffer, count, GFP_KERNEL); buf = kmemdup(buffer, count, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
ret = hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); ret = hid_hw_output_report(hdev, buf, count);
kfree(buf); kfree(buf);
return ret; return ret;
......
...@@ -123,10 +123,6 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, ...@@ -123,10 +123,6 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
dev = hidraw_table[minor]->hid; dev = hidraw_table[minor]->hid;
if (!dev->hid_output_raw_report) {
ret = -ENODEV;
goto out;
}
if (count > HID_MAX_BUFFER_SIZE) { if (count > HID_MAX_BUFFER_SIZE) {
hid_warn(dev, "pid %d passed too large report\n", hid_warn(dev, "pid %d passed too large report\n",
...@@ -153,7 +149,21 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, ...@@ -153,7 +149,21 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
goto out_free; goto out_free;
} }
ret = hid_output_raw_report(dev, buf, count, report_type); if ((report_type == HID_OUTPUT_REPORT) &&
!(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {
ret = hid_hw_output_report(dev, buf, count);
/*
* compatibility with old implementation of USB-HID and I2C-HID:
* if the device does not support receiving output reports,
* on an interrupt endpoint, fallback to SET_REPORT HID command.
*/
if (ret != -ENOSYS)
goto out_free;
}
ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type,
HID_REQ_SET_REPORT);
out_free: out_free:
kfree(buf); kfree(buf);
out: out:
......
...@@ -256,18 +256,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType, ...@@ -256,18 +256,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
return 0; return 0;
} }
static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, /**
u8 reportID, unsigned char *buf, size_t data_len) * i2c_hid_set_or_send_report: forward an incoming report to the device
* @client: the i2c_client of the device
* @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
* @reportID: the report ID
* @buf: the actual data to transfer, without the report ID
* @len: size of buf
* @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
*/
static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
{ {
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
u8 *args = ihid->argsbuf; u8 *args = ihid->argsbuf;
const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd; const struct i2c_hid_cmd *hidcmd;
int ret; int ret;
u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister); u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister); u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
/* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */ /* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */
u16 size = 2 /* size */ + u16 size = 2 /* size */ +
(reportID ? 1 : 0) /* reportID */ + (reportID ? 1 : 0) /* reportID */ +
data_len /* buf */; data_len /* buf */;
...@@ -278,6 +287,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, ...@@ -278,6 +287,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
i2c_hid_dbg(ihid, "%s\n", __func__); i2c_hid_dbg(ihid, "%s\n", __func__);
if (!use_data && maxOutputLength == 0)
return -ENOSYS;
if (reportID >= 0x0F) { if (reportID >= 0x0F) {
args[index++] = reportID; args[index++] = reportID;
reportID = 0x0F; reportID = 0x0F;
...@@ -287,9 +299,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, ...@@ -287,9 +299,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
* use the data register for feature reports or if the device does not * use the data register for feature reports or if the device does not
* support the output register * support the output register
*/ */
if (reportType == 0x03 || maxOutputLength == 0) { if (use_data) {
args[index++] = dataRegister & 0xFF; args[index++] = dataRegister & 0xFF;
args[index++] = dataRegister >> 8; args[index++] = dataRegister >> 8;
hidcmd = &hid_set_report_cmd;
} else { } else {
args[index++] = outputRegister & 0xFF; args[index++] = outputRegister & 0xFF;
args[index++] = outputRegister >> 8; args[index++] = outputRegister >> 8;
...@@ -550,7 +563,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, ...@@ -550,7 +563,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
} }
static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
size_t count, unsigned char report_type) size_t count, unsigned char report_type, bool use_data)
{ {
struct i2c_client *client = hid->driver_data; struct i2c_client *client = hid->driver_data;
int report_id = buf[0]; int report_id = buf[0];
...@@ -564,9 +577,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, ...@@ -564,9 +577,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
count--; count--;
} }
ret = i2c_hid_set_report(client, ret = i2c_hid_set_or_send_report(client,
report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
report_id, buf, count); report_id, buf, count, use_data);
if (report_id && ret >= 0) if (report_id && ret >= 0)
ret++; /* add report_id to the number of transfered bytes */ ret++; /* add report_id to the number of transfered bytes */
...@@ -574,34 +587,27 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, ...@@ -574,34 +587,27 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
return ret; return ret;
} }
static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
int reqtype) size_t count)
{ {
struct i2c_client *client = hid->driver_data; return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT,
char *buf; false);
int ret; }
int len = i2c_hid_get_report_length(rep) - 2;
buf = kzalloc(len, GFP_KERNEL);
if (!buf)
return;
static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
__u8 *buf, size_t len, unsigned char rtype,
int reqtype)
{
switch (reqtype) { switch (reqtype) {
case HID_REQ_GET_REPORT: case HID_REQ_GET_REPORT:
ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type); return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);
if (ret < 0)
dev_err(&client->dev, "%s: unable to get report: %d\n",
__func__, ret);
else
hid_input_report(hid, rep->type, buf, ret, 0);
break;
case HID_REQ_SET_REPORT: case HID_REQ_SET_REPORT:
hid_output_report(rep, buf); if (buf[0] != reportnum)
i2c_hid_output_raw_report(hid, buf, len, rep->type); return -EINVAL;
break; return i2c_hid_output_raw_report(hid, buf, len, rtype, true);
default:
return -EIO;
} }
kfree(buf);
} }
static int i2c_hid_parse(struct hid_device *hid) static int i2c_hid_parse(struct hid_device *hid)
...@@ -760,7 +766,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = { ...@@ -760,7 +766,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
.open = i2c_hid_open, .open = i2c_hid_open,
.close = i2c_hid_close, .close = i2c_hid_close,
.power = i2c_hid_power, .power = i2c_hid_power,
.request = i2c_hid_request, .output_report = i2c_hid_output_report,
.raw_request = i2c_hid_raw_request,
}; };
static int i2c_hid_init_irq(struct i2c_client *client) static int i2c_hid_init_irq(struct i2c_client *client)
...@@ -1005,7 +1012,6 @@ static int i2c_hid_probe(struct i2c_client *client, ...@@ -1005,7 +1012,6 @@ static int i2c_hid_probe(struct i2c_client *client,
hid->driver_data = client; hid->driver_data = client;
hid->ll_driver = &i2c_hid_ll_driver; hid->ll_driver = &i2c_hid_ll_driver;
hid->hid_output_raw_report = i2c_hid_output_raw_report;
hid->dev.parent = &client->dev; hid->dev.parent = &client->dev;
ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev)); ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
hid->bus = BUS_I2C; hid->bus = BUS_I2C;
......
...@@ -247,27 +247,22 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, ...@@ -247,27 +247,22 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
size_t count) size_t count)
{ {
struct uhid_device *uhid = hid->driver_data; return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
unsigned long flags; }
struct uhid_event *ev;
if (count < 1 || count > UHID_DATA_MAX)
return -EINVAL;
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
if (!ev)
return -ENOMEM;
ev->type = UHID_OUTPUT;
ev->u.output.size = count;
ev->u.output.rtype = UHID_OUTPUT_REPORT;
memcpy(ev->u.output.data, buf, count);
spin_lock_irqsave(&uhid->qlock, flags);
uhid_queue(uhid, ev);
spin_unlock_irqrestore(&uhid->qlock, flags);
return count; static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum,
__u8 *buf, size_t len, unsigned char rtype,
int reqtype)
{
switch (reqtype) {
case HID_REQ_GET_REPORT:
return uhid_hid_get_raw(hid, reportnum, buf, len, rtype);
case HID_REQ_SET_REPORT:
/* TODO: implement proper SET_REPORT functionality */
return -ENOSYS;
default:
return -EIO;
}
} }
static struct hid_ll_driver uhid_hid_driver = { static struct hid_ll_driver uhid_hid_driver = {
...@@ -277,6 +272,7 @@ static struct hid_ll_driver uhid_hid_driver = { ...@@ -277,6 +272,7 @@ static struct hid_ll_driver uhid_hid_driver = {
.close = uhid_hid_close, .close = uhid_hid_close,
.parse = uhid_hid_parse, .parse = uhid_hid_parse,
.output_report = uhid_hid_output_report, .output_report = uhid_hid_output_report,
.raw_request = uhid_raw_request,
}; };
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
...@@ -404,7 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid, ...@@ -404,7 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid,
hid->uniq[63] = 0; hid->uniq[63] = 0;
hid->ll_driver = &uhid_hid_driver; hid->ll_driver = &uhid_hid_driver;
hid->hid_output_raw_report = uhid_hid_output_raw;
hid->bus = ev->u.create.bus; hid->bus = ev->u.create.bus;
hid->vendor = ev->u.create.vendor; hid->vendor = ev->u.create.vendor;
hid->product = ev->u.create.product; hid->product = ev->u.create.product;
......
...@@ -894,7 +894,12 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum, ...@@ -894,7 +894,12 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
int ret, skipped_report_id = 0; int ret, skipped_report_id = 0;
/* Byte 0 is the report number. Report data starts at byte 1.*/ /* Byte 0 is the report number. Report data starts at byte 1.*/
if ((rtype == HID_OUTPUT_REPORT) &&
(hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID))
buf[0] = 0;
else
buf[0] = reportnum; buf[0] = reportnum;
if (buf[0] == 0x0) { if (buf[0] == 0x0) {
/* Don't send the Report ID */ /* Don't send the Report ID */
buf++; buf++;
...@@ -922,7 +927,7 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) ...@@ -922,7 +927,7 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
int actual_length, skipped_report_id = 0, ret; int actual_length, skipped_report_id = 0, ret;
if (!usbhid->urbout) if (!usbhid->urbout)
return -EIO; return -ENOSYS;
if (buf[0] == 0x0) { if (buf[0] == 0x0) {
/* Don't send the Report ID */ /* Don't send the Report ID */
...@@ -945,17 +950,6 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) ...@@ -945,17 +950,6 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
return ret; return ret;
} }
static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf,
size_t count, unsigned char report_type)
{
struct usbhid_device *usbhid = hid->driver_data;
if (usbhid->urbout && report_type != HID_FEATURE_REPORT)
return usbhid_output_report(hid, buf, count);
return usbhid_set_raw_report(hid, buf[0], buf, count, report_type);
}
static void usbhid_restart_queues(struct usbhid_device *usbhid) static void usbhid_restart_queues(struct usbhid_device *usbhid)
{ {
if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)) if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
...@@ -1289,7 +1283,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * ...@@ -1289,7 +1283,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
usb_set_intfdata(intf, hid); usb_set_intfdata(intf, hid);
hid->ll_driver = &usb_hid_driver; hid->ll_driver = &usb_hid_driver;
hid->hid_output_raw_report = usbhid_output_raw_report;
hid->ff_init = hid_pidff_init; hid->ff_init = hid_pidff_init;
#ifdef CONFIG_USB_HIDDEV #ifdef CONFIG_USB_HIDDEV
hid->hiddev_connect = hiddev_connect; hid->hiddev_connect = hiddev_connect;
......
...@@ -287,6 +287,8 @@ struct hid_item { ...@@ -287,6 +287,8 @@ struct hid_item {
#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100 #define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200 #define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000
#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP 0x00040000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000 #define HID_QUIRK_NO_IGNORE 0x40000000
...@@ -508,9 +510,6 @@ struct hid_device { /* device report descriptor */ ...@@ -508,9 +510,6 @@ struct hid_device { /* device report descriptor */
struct hid_usage *, __s32); struct hid_usage *, __s32);
void (*hiddev_report_event) (struct hid_device *, struct hid_report *); void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
/* handler for raw output data, used by hidraw */
int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char);
/* debugging support via debugfs */ /* debugging support via debugfs */
unsigned short debug; unsigned short debug;
struct dentry *debug_dir; struct dentry *debug_dir;
...@@ -753,6 +752,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid); ...@@ -753,6 +752,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid);
unsigned int hidinput_count_leds(struct hid_device *hid); unsigned int hidinput_count_leds(struct hid_device *hid);
__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
void hid_output_report(struct hid_report *report, __u8 *data); void hid_output_report(struct hid_report *report, __u8 *data);
void __hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype);
u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags); u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags);
struct hid_device *hid_allocate_device(void); struct hid_device *hid_allocate_device(void);
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
...@@ -965,7 +965,9 @@ static inline void hid_hw_request(struct hid_device *hdev, ...@@ -965,7 +965,9 @@ static inline void hid_hw_request(struct hid_device *hdev,
struct hid_report *report, int reqtype) struct hid_report *report, int reqtype)
{ {
if (hdev->ll_driver->request) if (hdev->ll_driver->request)
hdev->ll_driver->request(hdev, report, reqtype); return hdev->ll_driver->request(hdev, report, reqtype);
__hid_request(hdev, report, reqtype);
} }
/** /**
...@@ -986,11 +988,11 @@ static inline int hid_hw_raw_request(struct hid_device *hdev, ...@@ -986,11 +988,11 @@ static inline int hid_hw_raw_request(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf, unsigned char reportnum, __u8 *buf,
size_t len, unsigned char rtype, int reqtype) size_t len, unsigned char rtype, int reqtype)
{ {
if (hdev->ll_driver->raw_request) if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
return -EINVAL;
return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, return hdev->ll_driver->raw_request(hdev, reportnum, buf, len,
rtype, reqtype); rtype, reqtype);
return -ENOSYS;
} }
/** /**
...@@ -1005,28 +1007,15 @@ static inline int hid_hw_raw_request(struct hid_device *hdev, ...@@ -1005,28 +1007,15 @@ static inline int hid_hw_raw_request(struct hid_device *hdev,
static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf,
size_t len) size_t len)
{ {
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
return -EINVAL;
if (hdev->ll_driver->output_report) if (hdev->ll_driver->output_report)
return hdev->ll_driver->output_report(hdev, buf, len); return hdev->ll_driver->output_report(hdev, buf, len);
return -ENOSYS; return -ENOSYS;
} }
/**
* hid_output_raw_report - send an output or a feature report to the device
*
* @hdev: hid device
* @buf: raw data to transfer
* @len: length of buf
* @report_type: HID_FEATURE_REPORT or HID_OUTPUT_REPORT
*
* @return: count of data transfered, negative if error
*/
static inline int hid_output_raw_report(struct hid_device *hdev, __u8 *buf,
size_t len, unsigned char report_type)
{
return hdev->hid_output_raw_report(hdev, buf, len, report_type);
}
/** /**
* hid_hw_idle - send idle request to device * hid_hw_idle - send idle request to device
* *
......
...@@ -382,18 +382,6 @@ static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count) ...@@ -382,18 +382,6 @@ static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
data, count); data, count);
} }
static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data,
size_t count, unsigned char report_type)
{
if (report_type == HID_OUTPUT_REPORT) {
return hidp_output_report(hid, data, count);
} else if (report_type != HID_FEATURE_REPORT) {
return -EINVAL;
}
return hidp_set_raw_report(hid, data[0], data, count, report_type);
}
static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum, static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
__u8 *buf, size_t len, unsigned char rtype, __u8 *buf, size_t len, unsigned char rtype,
int reqtype) int reqtype)
...@@ -776,8 +764,6 @@ static int hidp_setup_hid(struct hidp_session *session, ...@@ -776,8 +764,6 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->dev.parent = &session->conn->hcon->dev; hid->dev.parent = &session->conn->hcon->dev;
hid->ll_driver = &hidp_hid_driver; hid->ll_driver = &hidp_hid_driver;
hid->hid_output_raw_report = hidp_output_raw_report;
/* True if device is blacklisted in drivers/hid/hid-core.c */ /* True if device is blacklisted in drivers/hid/hid-core.c */
if (hid_ignore(hid)) { if (hid_ignore(hid)) {
hid_destroy_device(session->hid); hid_destroy_device(session->hid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment