Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
b95dd3ca
Commit
b95dd3ca
authored
Apr 01, 2014
by
Jiri Kosina
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-3.15/hid-core-ll-transport-cleanup' into for-3.15/sony
parents
2078b9bb
c3d77fab
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
1288 additions
and
200 deletions
+1288
-200
Documentation/hid/hid-transport.txt
Documentation/hid/hid-transport.txt
+2
-1
drivers/hid/Kconfig
drivers/hid/Kconfig
+9
-0
drivers/hid/Makefile
drivers/hid/Makefile
+1
-0
drivers/hid/hid-core.c
drivers/hid/hid-core.c
+50
-1
drivers/hid/hid-cp2112.c
drivers/hid/hid-cp2112.c
+1073
-0
drivers/hid/hid-hyperv.c
drivers/hid/hid-hyperv.c
+10
-0
drivers/hid/hid-ids.h
drivers/hid/hid-ids.h
+1
-0
drivers/hid/hid-input.c
drivers/hid/hid-input.c
+6
-4
drivers/hid/hid-lg.c
drivers/hid/hid-lg.c
+4
-4
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-logitech-dj.c
+6
-15
drivers/hid/hid-magicmouse.c
drivers/hid/hid-magicmouse.c
+2
-2
drivers/hid/hid-sony.c
drivers/hid/hid-sony.c
+16
-48
drivers/hid/hid-thingm.c
drivers/hid/hid-thingm.c
+2
-2
drivers/hid/hid-wacom.c
drivers/hid/hid-wacom.c
+15
-11
drivers/hid/hid-wiimote-core.c
drivers/hid/hid-wiimote-core.c
+2
-2
drivers/hid/hidraw.c
drivers/hid/hidraw.c
+15
-5
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/i2c-hid/i2c-hid.c
+38
-32
drivers/hid/uhid.c
drivers/hid/uhid.c
+16
-21
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-core.c
+7
-14
include/linux/hid.h
include/linux/hid.h
+13
-24
net/bluetooth/hidp/core.c
net/bluetooth/hidp/core.c
+0
-14
No files found.
Documentation/hid/hid-transport.txt
View file @
b95dd3ca
...
...
@@ -283,7 +283,8 @@ The available HID callbacks are:
int reqtype)
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
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)
Send raw output report via intr channel. Used by some HID device drivers
...
...
drivers/hid/Kconfig
View file @
b95dd3ca
...
...
@@ -175,6 +175,15 @@ config HID_PRODIKEYS
multimedia keyboard, but will lack support for the musical keyboard
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
tristate "Cypress mouse and barcode readers" if EXPERT
depends on HID
...
...
drivers/hid/Makefile
View file @
b95dd3ca
...
...
@@ -41,6 +41,7 @@ obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
obj-$(CONFIG_HID_BELKIN)
+=
hid-belkin.o
obj-$(CONFIG_HID_CHERRY)
+=
hid-cherry.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_DRAGONRISE)
+=
hid-dr.o
obj-$(CONFIG_HID_EMS_FF)
+=
hid-emsff.o
...
...
drivers/hid/hid-core.c
View file @
b95dd3ca
...
...
@@ -1248,6 +1248,11 @@ void hid_output_report(struct hid_report *report, __u8 *data)
}
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()
*/
...
...
@@ -1258,7 +1263,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)
* 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
);
}
...
...
@@ -1314,6 +1319,41 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
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
interrupt
)
{
...
...
@@ -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_AK1D
)
},
{
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_2
)
},
{
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)
if
(
hid_ignore
(
hdev
))
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
* for the driver-specific modifications.
...
...
drivers/hid/hid-cp2112.c
0 → 100644
View file @
b95dd3ca
/*
* 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"
);
drivers/hid/hid-hyperv.c
View file @
b95dd3ca
...
...
@@ -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
=
{
.
parse
=
mousevsc_hid_parse
,
.
open
=
mousevsc_hid_open
,
.
close
=
mousevsc_hid_close
,
.
start
=
mousevsc_hid_start
,
.
stop
=
mousevsc_hid_stop
,
.
raw_request
=
mousevsc_hid_raw_request
,
};
static
struct
hid_driver
mousevsc_hid_driver
;
...
...
drivers/hid/hid-ids.h
View file @
b95dd3ca
...
...
@@ -240,6 +240,7 @@
#define USB_VENDOR_ID_CYGNAL 0x10c4
#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a
#define USB_DEVICE_ID_CYGNAL_CP2112 0xea90
#define USB_VENDOR_ID_CYPRESS 0x04b4
#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001
...
...
drivers/hid/hid-input.c
View file @
b95dd3ca
...
...
@@ -1150,7 +1150,7 @@ static void hidinput_led_worker(struct work_struct *work)
led_work
);
struct
hid_field
*
field
;
struct
hid_report
*
report
;
int
len
;
int
len
,
ret
;
__u8
*
buf
;
field
=
hidinput_get_led_field
(
hid
);
...
...
@@ -1184,7 +1184,10 @@ static void hidinput_led_worker(struct work_struct *work)
hid_output_report
(
report
,
buf
);
/* 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
);
}
...
...
@@ -1263,8 +1266,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *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
->
close
=
hidinput_close
;
input_dev
->
setkeycode
=
hidinput_setkeycode
;
...
...
drivers/hid/hid-lg.c
View file @
b95dd3ca
...
...
@@ -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
)
{
unsigned
char
buf
[]
=
{
0x00
,
0xAF
,
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
};
ret
=
hid_
output_raw_report
(
hdev
,
buf
,
sizeof
(
buf
),
HID_FEATURE
_REPORT
);
ret
=
hid_
hw_raw_request
(
hdev
,
buf
[
0
]
,
buf
,
sizeof
(
buf
),
HID_FEATURE_REPORT
,
HID_REQ_SET
_REPORT
);
if
(
ret
>=
0
)
{
/* 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)
buf
[
1
]
=
0xB2
;
get_random_bytes
(
&
buf
[
2
],
2
);
ret
=
hid_
output_raw_report
(
hdev
,
buf
,
sizeof
(
buf
),
HID_FEATURE
_REPORT
);
ret
=
hid_
hw_raw_request
(
hdev
,
buf
[
0
]
,
buf
,
sizeof
(
buf
),
HID_FEATURE_REPORT
,
HID_REQ_SET
_REPORT
);
}
}
...
...
drivers/hid/hid-logitech-dj.c
View file @
b95dd3ca
...
...
@@ -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
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
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,
}
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
;
...
...
@@ -544,9 +540,10 @@ 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
)
static
int
logi_dj_ll_raw_request
(
struct
hid_device
*
hid
,
unsigned
char
reportnum
,
__u8
*
buf
,
size_t
count
,
unsigned
char
report_type
,
int
reqtype
)
{
struct
dj_device
*
djdev
=
hid
->
driver_data
;
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,
out_buf
[
1
]
=
djdev
->
device_index
;
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
,
DJREPORT_SHORT_LENGTH
,
report_type
,
HID_REQ_SET_REPORT
);
DJREPORT_SHORT_LENGTH
,
report_type
,
reqtype
);
kfree
(
out_buf
);
return
ret
;
...
...
@@ -662,6 +652,7 @@ static struct hid_ll_driver logi_dj_ll_driver = {
.
stop
=
logi_dj_ll_stop
,
.
open
=
logi_dj_ll_open
,
.
close
=
logi_dj_ll_close
,
.
raw_request
=
logi_dj_ll_raw_request
,
};
...
...
drivers/hid/hid-magicmouse.c
View file @
b95dd3ca
...
...
@@ -538,8 +538,8 @@ static int magicmouse_probe(struct hid_device *hdev,
* but there seems to be no other way of switching the mode.
* Thus the super-ugly hacky success check below.
*/
ret
=
hid_
output_raw_report
(
hdev
,
feature
,
sizeof
(
feature
),
HID_FEATURE
_REPORT
);
ret
=
hid_
hw_raw_request
(
hdev
,
feature
[
0
]
,
feature
,
sizeof
(
feature
),
HID_FEATURE_REPORT
,
HID_REQ_SET
_REPORT
);
if
(
ret
!=
-
EIO
&&
ret
!=
sizeof
(
feature
))
{
hid_err
(
hdev
,
"unable to request touch data (%d)
\n
"
,
ret
);
goto
err_stop_hw
;
...
...
drivers/hid/hid-sony.c
View file @
b95dd3ca
...
...
@@ -29,7 +29,6 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/leds.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>
...
...
@@ -1006,45 +1005,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
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
* 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)
static
int
sixaxis_set_operational_bt
(
struct
hid_device
*
hdev
)
{
unsigned
char
buf
[]
=
{
0xf4
,
0x42
,
0x03
,
0x00
,
0x00
};
return
hid_
output_raw_report
(
hdev
,
buf
,
sizeof
(
buf
),
HID_FEATURE
_REPORT
);
return
hid_
hw_raw_request
(
hdev
,
buf
[
0
]
,
buf
,
sizeof
(
buf
),
HID_FEATURE_REPORT
,
HID_REQ_SET
_REPORT
);
}
/*
...
...
@@ -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
[
3
]
<<
4
;
if
(
sc
->
quirks
&
SIXAXIS_CONTROLLER_USB
)
hid_output_raw_report
(
sc
->
hdev
,
buf
,
sizeof
(
buf
),
HID_OUTPUT_REPORT
);
else
hid_hw_raw_request
(
sc
->
hdev
,
0x01
,
buf
,
sizeof
(
buf
),
HID_OUTPUT_REPORT
,
HID_REQ_SET_REPORT
);
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
)
...
...
@@ -1659,7 +1616,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
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
);
sc
->
worker_initialized
=
1
;
INIT_WORK
(
&
sc
->
state_worker
,
sixaxis_state_worker
);
...
...
drivers/hid/hid-thingm.c
View file @
b95dd3ca
...
...
@@ -48,8 +48,8 @@ static int blink1_send_command(struct blink1_data *data,
buf
[
0
],
buf
[
1
],
buf
[
2
],
buf
[
3
],
buf
[
4
],
buf
[
5
],
buf
[
6
],
buf
[
7
],
buf
[
8
]);
ret
=
hid_
output_raw_report
(
data
->
hdev
,
buf
,
BLINK1_CMD_SIZE
,
HID_FEATURE
_REPORT
);
ret
=
hid_
hw_raw_request
(
data
->
hdev
,
buf
[
0
]
,
buf
,
BLINK1_CMD_SIZE
,
HID_FEATURE_REPORT
,
HID_REQ_SET
_REPORT
);
return
ret
<
0
?
ret
:
0
;
}
...
...
drivers/hid/hid-wacom.c
View file @
b95dd3ca
...
...
@@ -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
[
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
)
goto
err
;
...
...
@@ -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
[
2
]
=
i
;
ret
=
hid_
output_raw_report
(
hdev
,
rep_data
,
67
,
HID_FEATURE_REPORT
);
ret
=
hid_
hw_raw_request
(
hdev
,
rep_data
[
0
]
,
rep_data
,
67
,
HID_FEATURE_REPORT
,
HID_REQ_SET_REPORT
);
}
rep_data
[
0
]
=
WAC_CMD_ICON_START_STOP
;
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:
return
;
...
...
@@ -181,7 +183,8 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
buf
[
3
]
=
value
;
/* use fixed brightness for OLEDs */
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
);
}
...
...
@@ -337,8 +340,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data
[
0
]
=
0x03
;
rep_data
[
1
]
=
0x00
;
limit
=
3
;
do
{
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
);
}
while
(
ret
<
0
&&
limit
--
>
0
);
if
(
ret
>=
0
)
{
...
...
@@ -350,8 +353,9 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data
[
1
]
=
0x00
;
limit
=
3
;
do
{
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
);
}
while
(
ret
<
0
&&
limit
--
>
0
);
if
(
ret
>=
0
)
{
...
...
@@ -376,8 +380,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
rep_data
[
0
]
=
0x03
;
rep_data
[
1
]
=
wdata
->
features
;
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
)
wdata
->
high_speed
=
speed
;
break
;
...
...
drivers/hid/hid-wiimote-core.c
View file @
b95dd3ca
...
...
@@ -28,14 +28,14 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
__u8
*
buf
;
int
ret
;
if
(
!
hdev
->
hid_output_raw
_report
)
if
(
!
hdev
->
ll_driver
->
output
_report
)
return
-
ENODEV
;
buf
=
kmemdup
(
buffer
,
count
,
GFP_KERNEL
);
if
(
!
buf
)
return
-
ENOMEM
;
ret
=
hid_
output_raw_report
(
hdev
,
buf
,
count
,
HID_OUTPUT_REPORT
);
ret
=
hid_
hw_output_report
(
hdev
,
buf
,
count
);
kfree
(
buf
);
return
ret
;
...
...
drivers/hid/hidraw.c
View file @
b95dd3ca
...
...
@@ -123,10 +123,6 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
dev
=
hidraw_table
[
minor
]
->
hid
;
if
(
!
dev
->
hid_output_raw_report
)
{
ret
=
-
ENODEV
;
goto
out
;
}
if
(
count
>
HID_MAX_BUFFER_SIZE
)
{
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,
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:
kfree
(
buf
);
out:
...
...
drivers/hid/i2c-hid/i2c-hid.c
View file @
b95dd3ca
...
...
@@ -256,18 +256,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
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
);
u8
*
args
=
ihid
->
argsbuf
;
const
struct
i2c_hid_cmd
*
hidcmd
=
&
hid_set_report_
cmd
;
const
struct
i2c_hid_cmd
*
hid
cmd
;
int
ret
;
u16
dataRegister
=
le16_to_cpu
(
ihid
->
hdesc
.
wDataRegister
);
u16
outputRegister
=
le16_to_cpu
(
ihid
->
hdesc
.
wOutputRegister
);
u16
maxOutputLength
=
le16_to_cpu
(
ihid
->
hdesc
.
wMaxOutputLength
);
/* hid
raw
already checked that data_len < HID_MAX_BUFFER_SIZE */
/* hid
_hw_*
already checked that data_len < HID_MAX_BUFFER_SIZE */
u16
size
=
2
/* size */
+
(
reportID
?
1
:
0
)
/* reportID */
+
data_len
/* buf */
;
...
...
@@ -278,6 +287,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
i2c_hid_dbg
(
ihid
,
"%s
\n
"
,
__func__
);
if
(
!
use_data
&&
maxOutputLength
==
0
)
return
-
ENOSYS
;
if
(
reportID
>=
0x0F
)
{
args
[
index
++
]
=
reportID
;
reportID
=
0x0F
;
...
...
@@ -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
* support the output register
*/
if
(
reportType
==
0x03
||
maxOutputLength
==
0
)
{
if
(
use_data
)
{
args
[
index
++
]
=
dataRegister
&
0xFF
;
args
[
index
++
]
=
dataRegister
>>
8
;
hidcmd
=
&
hid_set_report_cmd
;
}
else
{
args
[
index
++
]
=
outputRegister
&
0xFF
;
args
[
index
++
]
=
outputRegister
>>
8
;
...
...
@@ -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
,
size_t
count
,
unsigned
char
report_type
)
size_t
count
,
unsigned
char
report_type
,
bool
use_data
)
{
struct
i2c_client
*
client
=
hid
->
driver_data
;
int
report_id
=
buf
[
0
];
...
...
@@ -564,9 +577,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
count
--
;
}
ret
=
i2c_hid_set_report
(
client
,
ret
=
i2c_hid_set_
or_send_
report
(
client
,
report_type
==
HID_FEATURE_REPORT
?
0x03
:
0x02
,
report_id
,
buf
,
count
);
report_id
,
buf
,
count
,
use_data
);
if
(
report_id
&&
ret
>=
0
)
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,
return
ret
;
}
static
void
i2c_hid_request
(
struct
hid_device
*
hid
,
struct
hid_report
*
rep
,
int
reqtype
)
static
int
i2c_hid_output_report
(
struct
hid_device
*
hid
,
__u8
*
buf
,
size_t
count
)
{
struct
i2c_client
*
client
=
hid
->
driver_data
;
char
*
buf
;
int
ret
;
int
len
=
i2c_hid_get_report_length
(
rep
)
-
2
;
buf
=
kzalloc
(
len
,
GFP_KERNEL
);
if
(
!
buf
)
return
;
return
i2c_hid_output_raw_report
(
hid
,
buf
,
count
,
HID_OUTPUT_REPORT
,
false
);
}
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
)
{
case
HID_REQ_GET_REPORT
:
ret
=
i2c_hid_get_raw_report
(
hid
,
rep
->
id
,
buf
,
len
,
rep
->
type
);
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
;
return
i2c_hid_get_raw_report
(
hid
,
reportnum
,
buf
,
len
,
rtype
);
case
HID_REQ_SET_REPORT
:
hid_output_report
(
rep
,
buf
);
i2c_hid_output_raw_report
(
hid
,
buf
,
len
,
rep
->
type
);
break
;
if
(
buf
[
0
]
!=
reportnum
)
return
-
EINVAL
;
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
)
...
...
@@ -760,7 +766,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
.
open
=
i2c_hid_open
,
.
close
=
i2c_hid_close
,
.
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
)
...
...
@@ -1005,7 +1012,6 @@ static int i2c_hid_probe(struct i2c_client *client,
hid
->
driver_data
=
client
;
hid
->
ll_driver
=
&
i2c_hid_ll_driver
;
hid
->
hid_output_raw_report
=
i2c_hid_output_raw_report
;
hid
->
dev
.
parent
=
&
client
->
dev
;
ACPI_COMPANION_SET
(
&
hid
->
dev
,
ACPI_COMPANION
(
&
client
->
dev
));
hid
->
bus
=
BUS_I2C
;
...
...
drivers/hid/uhid.c
View file @
b95dd3ca
...
...
@@ -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
,
size_t
count
)
{
struct
uhid_device
*
uhid
=
hid
->
driver_data
;
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
uhid_hid_output_raw
(
hid
,
buf
,
count
,
HID_OUTPUT_REPORT
);
}
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
=
{
...
...
@@ -277,6 +272,7 @@ static struct hid_ll_driver uhid_hid_driver = {
.
close
=
uhid_hid_close
,
.
parse
=
uhid_hid_parse
,
.
output_report
=
uhid_hid_output_report
,
.
raw_request
=
uhid_raw_request
,
};
#ifdef CONFIG_COMPAT
...
...
@@ -404,7 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid,
hid
->
uniq
[
63
]
=
0
;
hid
->
ll_driver
=
&
uhid_hid_driver
;
hid
->
hid_output_raw_report
=
uhid_hid_output_raw
;
hid
->
bus
=
ev
->
u
.
create
.
bus
;
hid
->
vendor
=
ev
->
u
.
create
.
vendor
;
hid
->
product
=
ev
->
u
.
create
.
product
;
...
...
drivers/hid/usbhid/hid-core.c
View file @
b95dd3ca
...
...
@@ -894,7 +894,12 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
int
ret
,
skipped_report_id
=
0
;
/* Byte 0 is the report number. Report data starts at byte 1.*/
buf
[
0
]
=
reportnum
;
if
((
rtype
==
HID_OUTPUT_REPORT
)
&&
(
hid
->
quirks
&
HID_QUIRK_SKIP_OUTPUT_REPORT_ID
))
buf
[
0
]
=
0
;
else
buf
[
0
]
=
reportnum
;
if
(
buf
[
0
]
==
0x0
)
{
/* Don't send the Report ID */
buf
++
;
...
...
@@ -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
;
if
(
!
usbhid
->
urbout
)
return
-
E
IO
;
return
-
E
NOSYS
;
if
(
buf
[
0
]
==
0x0
)
{
/* Don't send the Report ID */
...
...
@@ -945,17 +950,6 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
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
)
{
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 *
usb_set_intfdata
(
intf
,
hid
);
hid
->
ll_driver
=
&
usb_hid_driver
;
hid
->
hid_output_raw_report
=
usbhid_output_raw_report
;
hid
->
ff_init
=
hid_pidff_init
;
#ifdef CONFIG_USB_HIDDEV
hid
->
hiddev_connect
=
hiddev_connect
;
...
...
include/linux/hid.h
View file @
b95dd3ca
...
...
@@ -287,6 +287,8 @@ struct hid_item {
#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
#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_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000
...
...
@@ -508,9 +510,6 @@ struct hid_device { /* device report descriptor */
struct
hid_usage
*
,
__s32
);
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 */
unsigned
short
debug
;
struct
dentry
*
debug_dir
;
...
...
@@ -753,6 +752,7 @@ struct hid_field *hidinput_get_led_field(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
);
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
);
struct
hid_device
*
hid_allocate_device
(
void
);
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,
struct
hid_report
*
report
,
int
reqtype
)
{
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,
unsigned
char
reportnum
,
__u8
*
buf
,
size_t
len
,
unsigned
char
rtype
,
int
reqtype
)
{
if
(
hdev
->
ll_driver
->
raw_request
)
return
hdev
->
ll_driver
->
raw_request
(
hdev
,
reportnum
,
buf
,
len
,
rtype
,
reqtype
);
if
(
len
<
1
||
len
>
HID_MAX_BUFFER_SIZE
||
!
buf
)
return
-
EINVAL
;
return
-
ENOSYS
;
return
hdev
->
ll_driver
->
raw_request
(
hdev
,
reportnum
,
buf
,
len
,
rtype
,
reqtype
);
}
/**
...
...
@@ -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
,
size_t
len
)
{
if
(
len
<
1
||
len
>
HID_MAX_BUFFER_SIZE
||
!
buf
)
return
-
EINVAL
;
if
(
hdev
->
ll_driver
->
output_report
)
return
hdev
->
ll_driver
->
output_report
(
hdev
,
buf
,
len
);
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
*
...
...
net/bluetooth/hidp/core.c
View file @
b95dd3ca
...
...
@@ -382,18 +382,6 @@ static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t 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
,
__u8
*
buf
,
size_t
len
,
unsigned
char
rtype
,
int
reqtype
)
...
...
@@ -776,8 +764,6 @@ static int hidp_setup_hid(struct hidp_session *session,
hid
->
dev
.
parent
=
&
session
->
conn
->
hcon
->
dev
;
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 */
if
(
hid_ignore
(
hid
))
{
hid_destroy_device
(
session
->
hid
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment