2007-02-09 22:24:33 +08:00
|
|
|
/*
|
2005-04-17 06:20:36 +08:00
|
|
|
HIDP implementation for Linux Bluetooth stack (BlueZ).
|
|
|
|
Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
|
2013-04-07 02:28:46 +08:00
|
|
|
Copyright (C) 2013 David Herrmann <dh.herrmann@gmail.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License version 2 as
|
|
|
|
published by the Free Software Foundation;
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
|
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
2007-02-09 22:24:33 +08:00
|
|
|
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
2005-04-17 06:20:36 +08:00
|
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
2007-02-09 22:24:33 +08:00
|
|
|
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
|
|
|
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
2005-04-17 06:20:36 +08:00
|
|
|
SOFTWARE IS DISCLAIMED.
|
|
|
|
*/
|
|
|
|
|
2013-04-07 02:28:46 +08:00
|
|
|
#include <linux/kref.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/file.h>
|
2011-04-05 21:37:45 +08:00
|
|
|
#include <linux/kthread.h>
|
2009-08-23 05:15:53 +08:00
|
|
|
#include <linux/hidraw.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <net/bluetooth/bluetooth.h>
|
2006-07-06 19:09:02 +08:00
|
|
|
#include <net/bluetooth/hci_core.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <net/bluetooth/l2cap.h>
|
|
|
|
|
|
|
|
#include "hidp.h"
|
|
|
|
|
2007-02-18 06:58:49 +08:00
|
|
|
#define VERSION "1.2"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static DECLARE_RWSEM(hidp_session_sem);
|
2017-06-27 17:34:44 +08:00
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(hidp_session_wq);
|
2005-04-17 06:20:36 +08:00
|
|
|
static LIST_HEAD(hidp_session_list);
|
|
|
|
|
|
|
|
static unsigned char hidp_keycode[256] = {
|
2011-03-21 21:20:01 +08:00
|
|
|
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36,
|
|
|
|
37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45,
|
|
|
|
21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1,
|
|
|
|
14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52,
|
|
|
|
53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
|
|
|
|
99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69,
|
|
|
|
98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73,
|
|
|
|
82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190,
|
|
|
|
191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135,
|
|
|
|
136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94,
|
|
|
|
95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115,
|
|
|
|
114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
|
|
|
|
|
2013-04-07 02:28:46 +08:00
|
|
|
static int hidp_session_probe(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_user *user);
|
|
|
|
static void hidp_session_remove(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_user *user);
|
|
|
|
static int hidp_session_thread(void *arg);
|
|
|
|
static void hidp_session_terminate(struct hidp_session *s);
|
|
|
|
|
2013-04-07 02:28:47 +08:00
|
|
|
static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-04-02 04:51:53 +08:00
|
|
|
u32 valid_flags = 0;
|
2010-10-30 22:26:31 +08:00
|
|
|
memset(ci, 0, sizeof(*ci));
|
2005-04-17 06:20:36 +08:00
|
|
|
bacpy(&ci->bdaddr, &session->bdaddr);
|
|
|
|
|
2015-04-02 04:51:53 +08:00
|
|
|
ci->flags = session->flags & valid_flags;
|
2013-04-07 02:28:40 +08:00
|
|
|
ci->state = BT_CONNECTED;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (session->input) {
|
|
|
|
ci->vendor = session->input->id.vendor;
|
|
|
|
ci->product = session->input->id.product;
|
|
|
|
ci->version = session->input->id.version;
|
|
|
|
if (session->input->name)
|
2013-05-13 10:07:11 +08:00
|
|
|
strlcpy(ci->name, session->input->name, 128);
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2013-05-13 10:07:11 +08:00
|
|
|
strlcpy(ci->name, "HID Boot Device", 128);
|
|
|
|
} else if (session->hid) {
|
2007-02-18 06:58:49 +08:00
|
|
|
ci->vendor = session->hid->vendor;
|
|
|
|
ci->product = session->hid->product;
|
|
|
|
ci->version = session->hid->version;
|
2013-05-13 10:07:11 +08:00
|
|
|
strlcpy(ci->name, session->hid->name, 128);
|
2007-02-18 06:58:49 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:50 +08:00
|
|
|
/* assemble skb, queue message on @transmit and wake up the session thread */
|
|
|
|
static int hidp_send_message(struct hidp_session *session, struct socket *sock,
|
|
|
|
struct sk_buff_head *transmit, unsigned char hdr,
|
|
|
|
const unsigned char *data, int size)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct sock *sk = sock->sk;
|
2019-07-16 01:40:56 +08:00
|
|
|
int ret;
|
2013-04-07 02:28:50 +08:00
|
|
|
|
|
|
|
BT_DBG("session %p data %p size %d", session, data, size);
|
|
|
|
|
|
|
|
if (atomic_read(&session->terminate))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
skb = alloc_skb(size + 1, GFP_ATOMIC);
|
|
|
|
if (!skb) {
|
|
|
|
BT_ERR("Can't allocate memory for new frame");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
networking: add and use skb_put_u8()
Joe and Bjørn suggested that it'd be nicer to not have the
cast in the fairly common case of doing
*(u8 *)skb_put(skb, 1) = c;
Add skb_put_u8() for this case, and use it across the code,
using the following spatch:
@@
expression SKB, C, S;
typedef u8;
identifier fn = {skb_put};
fresh identifier fn2 = fn ## "_u8";
@@
- *(u8 *)fn(SKB, S) = C;
+ fn2(SKB, C);
Note that due to the "S", the spatch isn't perfect, it should
have checked that S is 1, but there's also places that use a
sizeof expression like sizeof(var) or sizeof(u8) etc. Turns
out that nobody ever did something like
*(u8 *)skb_put(skb, 2) = c;
which would be wrong anyway since the second byte wouldn't be
initialized.
Suggested-by: Joe Perches <joe@perches.com>
Suggested-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:24 +08:00
|
|
|
skb_put_u8(skb, hdr);
|
2019-07-16 01:40:56 +08:00
|
|
|
if (data && size > 0) {
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:20 +08:00
|
|
|
skb_put_data(skb, data, size);
|
2019-07-16 01:40:56 +08:00
|
|
|
ret = size;
|
|
|
|
} else {
|
|
|
|
ret = 0;
|
|
|
|
}
|
2013-04-07 02:28:50 +08:00
|
|
|
|
|
|
|
skb_queue_tail(transmit, skb);
|
|
|
|
wake_up_interruptible(sk_sleep(sk));
|
|
|
|
|
2019-07-16 01:40:56 +08:00
|
|
|
return ret;
|
2013-04-07 02:28:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int hidp_send_ctrl_message(struct hidp_session *session,
|
|
|
|
unsigned char hdr, const unsigned char *data,
|
|
|
|
int size)
|
|
|
|
{
|
|
|
|
return hidp_send_message(session, session->ctrl_sock,
|
|
|
|
&session->ctrl_transmit, hdr, data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidp_send_intr_message(struct hidp_session *session,
|
|
|
|
unsigned char hdr, const unsigned char *data,
|
|
|
|
int size)
|
|
|
|
{
|
|
|
|
return hidp_send_message(session, session->intr_sock,
|
|
|
|
&session->intr_transmit, hdr, data, size);
|
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:51 +08:00
|
|
|
static int hidp_input_event(struct input_dev *dev, unsigned int type,
|
|
|
|
unsigned int code, int value)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-04-07 02:28:51 +08:00
|
|
|
struct hidp_session *session = input_get_drvdata(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned char newleds;
|
2013-04-07 02:28:50 +08:00
|
|
|
unsigned char hdr, data[2];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-04-07 02:28:51 +08:00
|
|
|
BT_DBG("session %p type %d code %d value %d",
|
|
|
|
session, type, code, value);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (type != EV_LED)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
|
|
|
|
(!!test_bit(LED_COMPOSE, dev->led) << 3) |
|
|
|
|
(!!test_bit(LED_SCROLLL, dev->led) << 2) |
|
|
|
|
(!!test_bit(LED_CAPSL, dev->led) << 1) |
|
2014-07-30 14:08:45 +08:00
|
|
|
(!!test_bit(LED_NUML, dev->led) << 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (session->leds == newleds)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
session->leds = newleds;
|
|
|
|
|
2013-04-07 02:28:50 +08:00
|
|
|
hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
|
|
|
|
data[0] = 0x01;
|
|
|
|
data[1] = newleds;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-04-07 02:28:50 +08:00
|
|
|
return hidp_send_intr_message(session, hdr, data, 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct input_dev *dev = session->input;
|
|
|
|
unsigned char *keys = session->keys;
|
|
|
|
unsigned char *udata = skb->data + 1;
|
|
|
|
signed char *sdata = skb->data + 1;
|
|
|
|
int i, size = skb->len - 1;
|
|
|
|
|
|
|
|
switch (skb->data[0]) {
|
|
|
|
case 0x01: /* Keyboard report */
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
|
|
|
|
|
|
|
|
/* If all the key codes have been set to 0x01, it means
|
|
|
|
* too many keys were pressed at the same time. */
|
|
|
|
if (!memcmp(udata + 2, hidp_mkeyspat, 6))
|
|
|
|
break;
|
|
|
|
|
|
|
|
for (i = 2; i < 8; i++) {
|
|
|
|
if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
|
|
|
|
if (hidp_keycode[keys[i]])
|
|
|
|
input_report_key(dev, hidp_keycode[keys[i]], 0);
|
|
|
|
else
|
|
|
|
BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
|
|
|
|
if (hidp_keycode[udata[i]])
|
|
|
|
input_report_key(dev, hidp_keycode[udata[i]], 1);
|
|
|
|
else
|
|
|
|
BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(keys, udata, 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x02: /* Mouse report */
|
|
|
|
input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
|
|
|
|
input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
|
|
|
|
input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
|
|
|
|
input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
|
|
|
|
input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
|
|
|
|
|
|
|
|
input_report_rel(dev, REL_X, sdata[1]);
|
|
|
|
input_report_rel(dev, REL_Y, sdata[2]);
|
|
|
|
|
|
|
|
if (size > 3)
|
|
|
|
input_report_rel(dev, REL_WHEEL, sdata[3]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
input_sync(dev);
|
|
|
|
}
|
|
|
|
|
2011-01-18 16:04:40 +08:00
|
|
|
static int hidp_get_raw_report(struct hid_device *hid,
|
|
|
|
unsigned char report_number,
|
|
|
|
unsigned char *data, size_t count,
|
|
|
|
unsigned char report_type)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = hid->driver_data;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
size_t len;
|
|
|
|
int numbered_reports = hid->report_enum[report_type].numbered;
|
2011-08-26 20:06:02 +08:00
|
|
|
int ret;
|
2011-01-18 16:04:40 +08:00
|
|
|
|
2013-02-21 02:16:19 +08:00
|
|
|
if (atomic_read(&session->terminate))
|
|
|
|
return -EIO;
|
|
|
|
|
2011-01-18 16:04:40 +08:00
|
|
|
switch (report_type) {
|
|
|
|
case HID_FEATURE_REPORT:
|
|
|
|
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE;
|
|
|
|
break;
|
|
|
|
case HID_INPUT_REPORT:
|
|
|
|
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT;
|
|
|
|
break;
|
|
|
|
case HID_OUTPUT_REPORT:
|
|
|
|
report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mutex_lock_interruptible(&session->report_mutex))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
/* Set up our wait, and send the report request to the device. */
|
|
|
|
session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK;
|
|
|
|
session->waiting_report_number = numbered_reports ? report_number : -1;
|
|
|
|
set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
|
|
|
data[0] = report_number;
|
2013-04-07 02:28:50 +08:00
|
|
|
ret = hidp_send_ctrl_message(session, report_type, data, 1);
|
2019-09-06 19:06:44 +08:00
|
|
|
if (ret < 0)
|
2011-08-26 20:06:02 +08:00
|
|
|
goto err;
|
2011-01-18 16:04:40 +08:00
|
|
|
|
|
|
|
/* Wait for the return of the report. The returned report
|
|
|
|
gets put in session->report_return. */
|
2013-04-07 02:28:41 +08:00
|
|
|
while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
|
|
|
|
!atomic_read(&session->terminate)) {
|
2011-01-18 16:04:40 +08:00
|
|
|
int res;
|
|
|
|
|
|
|
|
res = wait_event_interruptible_timeout(session->report_queue,
|
2013-04-07 02:28:41 +08:00
|
|
|
!test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)
|
|
|
|
|| atomic_read(&session->terminate),
|
2011-01-18 16:04:40 +08:00
|
|
|
5*HZ);
|
|
|
|
if (res == 0) {
|
|
|
|
/* timeout */
|
2011-08-26 20:06:02 +08:00
|
|
|
ret = -EIO;
|
|
|
|
goto err;
|
2011-01-18 16:04:40 +08:00
|
|
|
}
|
|
|
|
if (res < 0) {
|
|
|
|
/* signal */
|
2011-08-26 20:06:02 +08:00
|
|
|
ret = -ERESTARTSYS;
|
|
|
|
goto err;
|
2011-01-18 16:04:40 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
skb = session->report_return;
|
|
|
|
if (skb) {
|
|
|
|
len = skb->len < count ? skb->len : count;
|
|
|
|
memcpy(data, skb->data, len);
|
|
|
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
session->report_return = NULL;
|
|
|
|
} else {
|
|
|
|
/* Device returned a HANDSHAKE, indicating protocol error. */
|
|
|
|
len = -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
|
|
|
mutex_unlock(&session->report_mutex);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
|
2011-08-26 20:06:02 +08:00
|
|
|
err:
|
2011-01-18 16:04:40 +08:00
|
|
|
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
|
|
|
mutex_unlock(&session->report_mutex);
|
2011-08-26 20:06:02 +08:00
|
|
|
return ret;
|
2011-01-18 16:04:40 +08:00
|
|
|
}
|
|
|
|
|
2014-01-23 02:49:44 +08:00
|
|
|
static int hidp_set_raw_report(struct hid_device *hid, unsigned char reportnum,
|
|
|
|
unsigned char *data, size_t count,
|
|
|
|
unsigned char report_type)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = hid->driver_data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
switch (report_type) {
|
|
|
|
case HID_FEATURE_REPORT:
|
|
|
|
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
|
|
|
|
break;
|
|
|
|
case HID_INPUT_REPORT:
|
|
|
|
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_INPUT;
|
|
|
|
break;
|
|
|
|
case HID_OUTPUT_REPORT:
|
|
|
|
report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mutex_lock_interruptible(&session->report_mutex))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
|
|
|
/* Set up our wait, and send the report request to the device. */
|
|
|
|
data[0] = reportnum;
|
|
|
|
set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
|
|
|
|
ret = hidp_send_ctrl_message(session, report_type, data, count);
|
2019-09-06 19:06:44 +08:00
|
|
|
if (ret < 0)
|
2014-01-23 02:49:44 +08:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* Wait for the ACK from the device. */
|
|
|
|
while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) &&
|
|
|
|
!atomic_read(&session->terminate)) {
|
|
|
|
int res;
|
|
|
|
|
|
|
|
res = wait_event_interruptible_timeout(session->report_queue,
|
|
|
|
!test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)
|
|
|
|
|| atomic_read(&session->terminate),
|
|
|
|
10*HZ);
|
|
|
|
if (res == 0) {
|
|
|
|
/* timeout */
|
|
|
|
ret = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (res < 0) {
|
|
|
|
/* signal */
|
|
|
|
ret = -ERESTARTSYS;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!session->output_report_success) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = count;
|
|
|
|
|
|
|
|
err:
|
|
|
|
clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
|
|
|
|
mutex_unlock(&session->report_mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-02-06 05:33:20 +08:00
|
|
|
static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
|
2009-11-26 23:20:56 +08:00
|
|
|
{
|
2011-01-18 16:04:38 +08:00
|
|
|
struct hidp_session *session = hid->driver_data;
|
|
|
|
|
2014-02-06 05:33:20 +08:00
|
|
|
return hidp_send_intr_message(session,
|
|
|
|
HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT,
|
|
|
|
data, count);
|
2009-11-26 23:20:56 +08:00
|
|
|
}
|
|
|
|
|
2014-01-23 02:49:44 +08:00
|
|
|
static int hidp_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 hidp_get_raw_report(hid, reportnum, buf, len, rtype);
|
|
|
|
case HID_REQ_SET_REPORT:
|
|
|
|
return hidp_set_raw_report(hid, reportnum, buf, len, rtype);
|
|
|
|
default:
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
static void hidp_idle_timeout(struct timer_list *t)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
struct hidp_session *session = from_timer(session, t, timer);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
Bluetooth: hidp: fix device disconnect on idle timeout
The HIDP specs define an idle-timeout which automatically disconnects a
device. This has always been implemented in the HIDP layer and forced a
synchronous shutdown of the hidp-scheduler. This works just fine, but
lacks a forced disconnect on the underlying l2cap channels. This has been
broken since:
commit 5205185d461d5902325e457ca80bd421127b7308
Author: David Herrmann <dh.herrmann@gmail.com>
Date: Sat Apr 6 20:28:47 2013 +0200
Bluetooth: hidp: remove old session-management
The old session-management always forced an l2cap error on the ctrl/intr
channels when shutting down. The new session-management skips this, as we
don't want to enforce channel policy on the caller. In other words, if
user-space removes an HIDP device, the underlying channels (which are
*owned* and *referenced* by user-space) are still left active. User-space
needs to call shutdown(2) or close(2) to release them.
Unfortunately, this does not work with idle-timeouts. There is no way to
signal user-space that the HIDP layer has been stopped. The API simply
does not support any event-passing except for poll(2). Hence, we restore
old behavior and force EUNATCH on the sockets if the HIDP layer is
disconnected due to idle-timeouts (behavior of explicit disconnects
remains unmodified). User-space can still call
getsockopt(..., SO_ERROR, ...)
..to retrieve the EUNATCH error and clear sk_err. Hence, the channels can
still be re-used (which nobody does so far, though). Therefore, the API
still supports the new behavior, but with this patch it's also compatible
to the old implicit channel shutdown.
Cc: <stable@vger.kernel.org> # 3.10+
Reported-by: Mark Haun <haunma@keteu.org>
Reported-by: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2015-09-07 18:05:41 +08:00
|
|
|
/* The HIDP user-space API only contains calls to add and remove
|
|
|
|
* devices. There is no way to forward events of any kind. Therefore,
|
|
|
|
* we have to forcefully disconnect a device on idle-timeouts. This is
|
|
|
|
* unfortunate and weird API design, but it is spec-compliant and
|
|
|
|
* required for backwards-compatibility. Hence, on idle-timeout, we
|
|
|
|
* signal driver-detach events, so poll() will be woken up with an
|
|
|
|
* error-condition on both sockets.
|
|
|
|
*/
|
|
|
|
|
|
|
|
session->intr_sock->sk->sk_err = EUNATCH;
|
|
|
|
session->ctrl_sock->sk->sk_err = EUNATCH;
|
|
|
|
wake_up_interruptible(sk_sleep(session->intr_sock->sk));
|
|
|
|
wake_up_interruptible(sk_sleep(session->ctrl_sock->sk));
|
|
|
|
|
2013-04-07 02:28:47 +08:00
|
|
|
hidp_session_terminate(session);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-02-05 19:07:58 +08:00
|
|
|
static void hidp_set_timer(struct hidp_session *session)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (session->idle_to > 0)
|
|
|
|
mod_timer(&session->timer, jiffies + HZ * session->idle_to);
|
|
|
|
}
|
|
|
|
|
2012-05-23 15:04:18 +08:00
|
|
|
static void hidp_del_timer(struct hidp_session *session)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (session->idle_to > 0)
|
|
|
|
del_timer(&session->timer);
|
|
|
|
}
|
|
|
|
|
2018-08-01 06:02:13 +08:00
|
|
|
static void hidp_process_report(struct hidp_session *session, int type,
|
|
|
|
const u8 *data, unsigned int len, int intr)
|
2013-12-19 19:09:32 +08:00
|
|
|
{
|
|
|
|
if (len > HID_MAX_BUFFER_SIZE)
|
|
|
|
len = HID_MAX_BUFFER_SIZE;
|
|
|
|
|
|
|
|
memcpy(session->input_buf, data, len);
|
|
|
|
hid_input_report(session->hid, type, session->input_buf, len, intr);
|
|
|
|
}
|
|
|
|
|
2008-02-05 19:07:58 +08:00
|
|
|
static void hidp_process_handshake(struct hidp_session *session,
|
|
|
|
unsigned char param)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
BT_DBG("session %p param 0x%02x", session, param);
|
2011-01-18 16:04:38 +08:00
|
|
|
session->output_report_success = 0; /* default condition */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (param) {
|
|
|
|
case HIDP_HSHK_SUCCESSFUL:
|
|
|
|
/* FIXME: Call into SET_ GET_ handlers here */
|
2011-01-18 16:04:38 +08:00
|
|
|
session->output_report_success = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HIDP_HSHK_NOT_READY:
|
|
|
|
case HIDP_HSHK_ERR_INVALID_REPORT_ID:
|
|
|
|
case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
|
|
|
|
case HIDP_HSHK_ERR_INVALID_PARAMETER:
|
2011-10-07 09:05:37 +08:00
|
|
|
if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags))
|
2011-01-18 16:04:40 +08:00
|
|
|
wake_up_interruptible(&session->report_queue);
|
2011-10-07 09:05:37 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* FIXME: Call into SET_ GET_ handlers here */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HIDP_HSHK_ERR_UNKNOWN:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HIDP_HSHK_ERR_FATAL:
|
|
|
|
/* Device requests a reboot, as this is the only way this error
|
2007-02-09 22:24:33 +08:00
|
|
|
* can be recovered. */
|
2013-04-07 02:28:50 +08:00
|
|
|
hidp_send_ctrl_message(session,
|
2005-04-17 06:20:36 +08:00
|
|
|
HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-04-07 02:28:50 +08:00
|
|
|
hidp_send_ctrl_message(session,
|
2005-04-17 06:20:36 +08:00
|
|
|
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
|
|
|
|
break;
|
|
|
|
}
|
2011-01-18 16:04:38 +08:00
|
|
|
|
|
|
|
/* Wake up the waiting thread. */
|
2011-10-07 09:05:37 +08:00
|
|
|
if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags))
|
2011-01-18 16:04:38 +08:00
|
|
|
wake_up_interruptible(&session->report_queue);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-02-05 19:07:58 +08:00
|
|
|
static void hidp_process_hid_control(struct hidp_session *session,
|
|
|
|
unsigned char param)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
BT_DBG("session %p param 0x%02x", session, param);
|
|
|
|
|
2008-02-05 19:07:14 +08:00
|
|
|
if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Flush the transmit queues */
|
|
|
|
skb_queue_purge(&session->ctrl_transmit);
|
|
|
|
skb_queue_purge(&session->intr_transmit);
|
|
|
|
|
2013-04-07 02:28:47 +08:00
|
|
|
hidp_session_terminate(session);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-18 16:04:40 +08:00
|
|
|
/* Returns true if the passed-in skb should be freed by the caller. */
|
|
|
|
static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
|
2008-02-05 19:07:58 +08:00
|
|
|
unsigned char param)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-01-18 16:04:40 +08:00
|
|
|
int done_with_skb = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param);
|
|
|
|
|
|
|
|
switch (param) {
|
|
|
|
case HIDP_DATA_RTYPE_INPUT:
|
|
|
|
hidp_set_timer(session);
|
|
|
|
|
|
|
|
if (session->input)
|
|
|
|
hidp_input_report(session, skb);
|
2007-02-18 06:58:49 +08:00
|
|
|
|
|
|
|
if (session->hid)
|
2013-12-19 19:09:32 +08:00
|
|
|
hidp_process_report(session, HID_INPUT_REPORT,
|
|
|
|
skb->data, skb->len, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HIDP_DATA_RTYPE_OTHER:
|
|
|
|
case HIDP_DATA_RTYPE_OUPUT:
|
|
|
|
case HIDP_DATA_RTYPE_FEATURE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-04-07 02:28:50 +08:00
|
|
|
hidp_send_ctrl_message(session,
|
2005-04-17 06:20:36 +08:00
|
|
|
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
|
|
|
|
}
|
2011-01-18 16:04:40 +08:00
|
|
|
|
|
|
|
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
|
|
|
|
param == session->waiting_report_type) {
|
|
|
|
if (session->waiting_report_number < 0 ||
|
|
|
|
session->waiting_report_number == skb->data[0]) {
|
|
|
|
/* hidp_get_raw_report() is waiting on this report. */
|
|
|
|
session->report_return = skb;
|
|
|
|
done_with_skb = 0;
|
|
|
|
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
|
|
|
|
wake_up_interruptible(&session->report_queue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return done_with_skb;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-02-05 19:07:58 +08:00
|
|
|
static void hidp_recv_ctrl_frame(struct hidp_session *session,
|
|
|
|
struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned char hdr, type, param;
|
2011-01-18 16:04:40 +08:00
|
|
|
int free_skb = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
|
|
|
|
|
|
|
|
hdr = skb->data[0];
|
|
|
|
skb_pull(skb, 1);
|
|
|
|
|
|
|
|
type = hdr & HIDP_HEADER_TRANS_MASK;
|
|
|
|
param = hdr & HIDP_HEADER_PARAM_MASK;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case HIDP_TRANS_HANDSHAKE:
|
|
|
|
hidp_process_handshake(session, param);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HIDP_TRANS_HID_CONTROL:
|
|
|
|
hidp_process_hid_control(session, param);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HIDP_TRANS_DATA:
|
2011-01-18 16:04:40 +08:00
|
|
|
free_skb = hidp_process_data(session, skb, param);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-04-07 02:28:50 +08:00
|
|
|
hidp_send_ctrl_message(session,
|
2005-04-17 06:20:36 +08:00
|
|
|
HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-01-18 16:04:40 +08:00
|
|
|
if (free_skb)
|
|
|
|
kfree_skb(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-02-05 19:07:58 +08:00
|
|
|
static void hidp_recv_intr_frame(struct hidp_session *session,
|
|
|
|
struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned char hdr;
|
|
|
|
|
|
|
|
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
|
|
|
|
|
|
|
|
hdr = skb->data[0];
|
|
|
|
skb_pull(skb, 1);
|
|
|
|
|
|
|
|
if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
|
|
|
|
hidp_set_timer(session);
|
2007-02-18 06:58:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (session->input)
|
|
|
|
hidp_input_report(session, skb);
|
2007-02-18 06:58:49 +08:00
|
|
|
|
|
|
|
if (session->hid) {
|
2013-12-19 19:09:32 +08:00
|
|
|
hidp_process_report(session, HID_INPUT_REPORT,
|
|
|
|
skb->data, skb->len, 1);
|
2007-02-18 06:58:49 +08:00
|
|
|
BT_DBG("report len %d", skb->len);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
BT_DBG("Unsupported protocol header 0x%02x", hdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
|
|
|
|
{
|
|
|
|
struct kvec iv = { data, len };
|
|
|
|
struct msghdr msg;
|
|
|
|
|
|
|
|
BT_DBG("sock %p data %p len %d", sock, data, len);
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
|
|
|
|
return kernel_sendmsg(sock, &msg, &iv, 1, len);
|
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:49 +08:00
|
|
|
/* dequeue message from @transmit and send via @sock */
|
|
|
|
static void hidp_process_transmit(struct hidp_session *session,
|
|
|
|
struct sk_buff_head *transmit,
|
|
|
|
struct socket *sock)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
2013-04-07 02:28:48 +08:00
|
|
|
int ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
BT_DBG("session %p", session);
|
|
|
|
|
2013-04-07 02:28:49 +08:00
|
|
|
while ((skb = skb_dequeue(transmit))) {
|
|
|
|
ret = hidp_send_frame(sock, skb->data, skb->len);
|
2013-04-07 02:28:48 +08:00
|
|
|
if (ret == -EAGAIN) {
|
2013-04-07 02:28:49 +08:00
|
|
|
skb_queue_head(transmit, skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2013-04-07 02:28:48 +08:00
|
|
|
} else if (ret < 0) {
|
|
|
|
hidp_session_terminate(session);
|
|
|
|
kfree_skb(skb);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
hidp_set_timer(session);
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-05 19:07:58 +08:00
|
|
|
static int hidp_setup_input(struct hidp_session *session,
|
2018-08-17 09:44:35 +08:00
|
|
|
const struct hidp_connadd_req *req)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-05-16 17:49:16 +08:00
|
|
|
struct input_dev *input;
|
2011-10-07 08:17:32 +08:00
|
|
|
int i;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
input = input_allocate_device();
|
|
|
|
if (!input)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
session->input = input;
|
|
|
|
|
2007-05-09 15:15:30 +08:00
|
|
|
input_set_drvdata(input, session);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-15 15:01:40 +08:00
|
|
|
input->name = "Bluetooth HID Boot Protocol Device";
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
input->id.bustype = BUS_BLUETOOTH;
|
|
|
|
input->id.vendor = req->vendor;
|
|
|
|
input->id.product = req->product;
|
|
|
|
input->id.version = req->version;
|
|
|
|
|
|
|
|
if (req->subclass & 0x40) {
|
|
|
|
set_bit(EV_KEY, input->evbit);
|
|
|
|
set_bit(EV_LED, input->evbit);
|
|
|
|
set_bit(EV_REP, input->evbit);
|
|
|
|
|
|
|
|
set_bit(LED_NUML, input->ledbit);
|
|
|
|
set_bit(LED_CAPSL, input->ledbit);
|
|
|
|
set_bit(LED_SCROLLL, input->ledbit);
|
|
|
|
set_bit(LED_COMPOSE, input->ledbit);
|
|
|
|
set_bit(LED_KANA, input->ledbit);
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(hidp_keycode); i++)
|
|
|
|
set_bit(hidp_keycode[i], input->keybit);
|
|
|
|
clear_bit(0, input->keybit);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->subclass & 0x80) {
|
2007-10-19 14:40:32 +08:00
|
|
|
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
|
|
|
|
input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
|
|
|
|
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
|
|
|
|
input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
|
|
|
|
input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
|
|
|
|
BIT_MASK(BTN_EXTRA);
|
|
|
|
input->relbit[0] |= BIT_MASK(REL_WHEEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:47 +08:00
|
|
|
input->dev.parent = &session->conn->hcon->dev;
|
2006-07-06 19:09:02 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
input->event = hidp_input_event;
|
|
|
|
|
2009-08-23 05:22:15 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-02-18 06:58:53 +08:00
|
|
|
static int hidp_open(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidp_close(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
static int hidp_parse(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = hid->driver_data;
|
|
|
|
|
2010-02-06 01:23:43 +08:00
|
|
|
return hid_parse_report(session->hid, session->rd_data,
|
|
|
|
session->rd_size);
|
2008-05-16 17:49:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int hidp_start(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hidp_stop(struct hid_device *hid)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = hid->driver_data;
|
|
|
|
|
|
|
|
skb_queue_purge(&session->ctrl_transmit);
|
|
|
|
skb_queue_purge(&session->intr_transmit);
|
|
|
|
|
|
|
|
hid->claimed = 0;
|
|
|
|
}
|
|
|
|
|
2017-07-25 00:46:18 +08:00
|
|
|
struct hid_ll_driver hidp_hid_driver = {
|
2008-05-16 17:49:16 +08:00
|
|
|
.parse = hidp_parse,
|
|
|
|
.start = hidp_start,
|
|
|
|
.stop = hidp_stop,
|
|
|
|
.open = hidp_open,
|
|
|
|
.close = hidp_close,
|
2014-01-23 02:49:44 +08:00
|
|
|
.raw_request = hidp_raw_request,
|
|
|
|
.output_report = hidp_output_report,
|
2008-05-16 17:49:16 +08:00
|
|
|
};
|
2017-07-25 00:46:18 +08:00
|
|
|
EXPORT_SYMBOL_GPL(hidp_hid_driver);
|
2008-05-16 17:49:16 +08:00
|
|
|
|
2011-01-18 16:04:37 +08:00
|
|
|
/* This function sets up the hid device. It does not add it
|
|
|
|
to the HID system. That is done in hidp_add_connection(). */
|
2008-05-16 17:49:15 +08:00
|
|
|
static int hidp_setup_hid(struct hidp_session *session,
|
2018-08-17 09:44:35 +08:00
|
|
|
const struct hidp_connadd_req *req)
|
2007-02-18 06:58:49 +08:00
|
|
|
{
|
2008-05-16 17:49:16 +08:00
|
|
|
struct hid_device *hid;
|
2009-08-23 05:22:15 +08:00
|
|
|
int err;
|
2007-02-18 06:58:49 +08:00
|
|
|
|
2014-11-15 02:35:05 +08:00
|
|
|
session->rd_data = memdup_user(req->rd_data, req->rd_size);
|
|
|
|
if (IS_ERR(session->rd_data))
|
|
|
|
return PTR_ERR(session->rd_data);
|
2010-02-06 01:23:43 +08:00
|
|
|
|
|
|
|
session->rd_size = req->rd_size;
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
hid = hid_allocate_device();
|
2010-02-06 01:23:43 +08:00
|
|
|
if (IS_ERR(hid)) {
|
|
|
|
err = PTR_ERR(hid);
|
|
|
|
goto fault;
|
|
|
|
}
|
2007-02-18 06:58:49 +08:00
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
session->hid = hid;
|
2010-02-06 01:23:43 +08:00
|
|
|
|
2007-02-18 06:58:49 +08:00
|
|
|
hid->driver_data = session;
|
|
|
|
|
|
|
|
hid->bus = BUS_BLUETOOTH;
|
|
|
|
hid->vendor = req->vendor;
|
|
|
|
hid->product = req->product;
|
|
|
|
hid->version = req->version;
|
2008-05-16 17:49:16 +08:00
|
|
|
hid->country = req->country;
|
2007-02-18 06:58:49 +08:00
|
|
|
|
2019-05-17 02:24:00 +08:00
|
|
|
strscpy(hid->name, req->name, sizeof(hid->name));
|
2012-09-25 17:49:44 +08:00
|
|
|
|
|
|
|
snprintf(hid->phys, sizeof(hid->phys), "%pMR",
|
2013-10-14 00:49:56 +08:00
|
|
|
&l2cap_pi(session->ctrl_sock->sk)->chan->src);
|
2012-09-25 17:49:44 +08:00
|
|
|
|
2014-02-21 00:36:04 +08:00
|
|
|
/* NOTE: Some device modules depend on the dst address being stored in
|
|
|
|
* uniq. Please be aware of this before making changes to this behavior.
|
|
|
|
*/
|
2012-09-25 17:49:44 +08:00
|
|
|
snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
|
2013-10-14 00:49:56 +08:00
|
|
|
&l2cap_pi(session->ctrl_sock->sk)->chan->dst);
|
2007-02-18 06:58:49 +08:00
|
|
|
|
2013-04-07 02:28:47 +08:00
|
|
|
hid->dev.parent = &session->conn->hcon->dev;
|
2008-05-16 17:49:16 +08:00
|
|
|
hid->ll_driver = &hidp_hid_driver;
|
2008-05-16 17:49:15 +08:00
|
|
|
|
2017-11-20 18:48:42 +08:00
|
|
|
/* True if device is blacklisted in drivers/hid/hid-quirks.c */
|
2012-12-06 22:39:55 +08:00
|
|
|
if (hid_ignore(hid)) {
|
|
|
|
hid_destroy_device(session->hid);
|
|
|
|
session->hid = NULL;
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2008-05-16 17:49:16 +08:00
|
|
|
return 0;
|
2009-08-23 05:22:15 +08:00
|
|
|
|
2010-02-06 01:23:43 +08:00
|
|
|
fault:
|
|
|
|
kfree(session->rd_data);
|
|
|
|
session->rd_data = NULL;
|
|
|
|
|
2009-08-23 05:22:15 +08:00
|
|
|
return err;
|
2007-02-18 06:58:49 +08:00
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:46 +08:00
|
|
|
/* initialize session devices */
|
|
|
|
static int hidp_session_dev_init(struct hidp_session *session,
|
2018-08-17 09:44:35 +08:00
|
|
|
const struct hidp_connadd_req *req)
|
2013-04-07 02:28:46 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (req->rd_size > 0) {
|
|
|
|
ret = hidp_setup_hid(session, req);
|
|
|
|
if (ret && ret != -ENODEV)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!session->hid) {
|
|
|
|
ret = hidp_setup_input(session, req);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* destroy session devices */
|
|
|
|
static void hidp_session_dev_destroy(struct hidp_session *session)
|
|
|
|
{
|
|
|
|
if (session->hid)
|
|
|
|
put_device(&session->hid->dev);
|
|
|
|
else if (session->input)
|
|
|
|
input_put_device(session->input);
|
|
|
|
|
|
|
|
kfree(session->rd_data);
|
|
|
|
session->rd_data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add HID/input devices to their underlying bus systems */
|
|
|
|
static int hidp_session_dev_add(struct hidp_session *session)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Both HID and input systems drop a ref-count when unregistering the
|
|
|
|
* device but they don't take a ref-count when registering them. Work
|
|
|
|
* around this by explicitly taking a refcount during registration
|
|
|
|
* which is dropped automatically by unregistering the devices. */
|
|
|
|
|
|
|
|
if (session->hid) {
|
|
|
|
ret = hid_add_device(session->hid);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
get_device(&session->hid->dev);
|
|
|
|
} else if (session->input) {
|
|
|
|
ret = input_register_device(session->input);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
input_get_device(session->input);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove HID/input devices from their bus systems */
|
|
|
|
static void hidp_session_dev_del(struct hidp_session *session)
|
|
|
|
{
|
|
|
|
if (session->hid)
|
|
|
|
hid_destroy_device(session->hid);
|
|
|
|
else if (session->input)
|
|
|
|
input_unregister_device(session->input);
|
|
|
|
}
|
|
|
|
|
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even
if we would add more fine-grained locking to HCI core, it would still be
called from the non-reentrant rx work-queue and thus block the event
processing.
However, if we want to perform synchronous I/O during HID device
registration (eg., to perform device-detection), we need the HCI core
to be able to dispatch incoming data.
Therefore, we now move device-registration to a separate worker. The HCI
core can continue running and we add devices asynchronously in another
kernel thread. Device removal is synchronized and waits for the worker
to exit before calling the usual device removal functions.
If l2cap_user->remove is called before the thread registered the devices,
we set "terminate" to true and the thread will skip it. If
l2cap_user->remove is called after it, we notice this as the device
is no longer in HIDP_SESSION_PREPARING state and simply unregister the
device as we did before.
There is no new deadlock as we now call hidp_session_add_dev() with
one lock less held (the HCI lock) and it cannot itself call back into
HCI as it was called with the HCI-lock held before.
One might wonder whether this can block during device unregistration.
But we set "terminate" to true and wake the HIDP thread up _before_
unregistering the HID/input devices. Therefore, all pending HID I/O
operations are canceled. All further I/O attempts will fail with ENODEV
or EIO. So all latency we can get are few context-switches, but no
timeouts or blocking I/O waits!
This change also prepares for a long standing HID bug. All HID devices
that register power_supply devices need to be able to handle callbacks
during registration (a power_supply oddity that cannot easily be fixed).
So with this patch available, we can allow HID I/O during registration
by calling the recently introduced hid_device_io_start/stop helpers,
which currently are a no-op for bluetooth due to this locking.
Note that we cannot do the same for input devices. input-core doesn't
allow us to call input_event() asynchronously to input_register_device(),
which HID-core kindly allows (for good reasons).
Fixing input-core to allow this isn't as easy as it sounds and is,
beside simplifying HIDP, not really an improvement. Hence, we still
register input devices synchronously as we did before. Only HID devices
are registered asynchronously.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Daniel Nicoletti <dantti12@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-05-23 19:10:25 +08:00
|
|
|
/*
|
|
|
|
* Asynchronous device registration
|
|
|
|
* HID device drivers might want to perform I/O during initialization to
|
|
|
|
* detect device types. Therefore, call device registration in a separate
|
|
|
|
* worker so the HIDP thread can schedule I/O operations.
|
|
|
|
* Note that this must be called after the worker thread was initialized
|
|
|
|
* successfully. This will then add the devices and increase session state
|
|
|
|
* on success, otherwise it will terminate the session thread.
|
|
|
|
*/
|
|
|
|
static void hidp_session_dev_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = container_of(work,
|
|
|
|
struct hidp_session,
|
|
|
|
dev_init);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = hidp_session_dev_add(session);
|
|
|
|
if (!ret)
|
|
|
|
atomic_inc(&session->state);
|
|
|
|
else
|
|
|
|
hidp_session_terminate(session);
|
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:46 +08:00
|
|
|
/*
|
|
|
|
* Create new session object
|
|
|
|
* Allocate session object, initialize static fields, copy input data into the
|
|
|
|
* object and take a reference to all sub-objects.
|
|
|
|
* This returns 0 on success and puts a pointer to the new session object in
|
|
|
|
* \out. Otherwise, an error code is returned.
|
|
|
|
* The new session object has an initial ref-count of 1.
|
|
|
|
*/
|
|
|
|
static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr,
|
|
|
|
struct socket *ctrl_sock,
|
|
|
|
struct socket *intr_sock,
|
2018-08-17 09:44:35 +08:00
|
|
|
const struct hidp_connadd_req *req,
|
2013-04-07 02:28:46 +08:00
|
|
|
struct l2cap_conn *conn)
|
|
|
|
{
|
|
|
|
struct hidp_session *session;
|
|
|
|
int ret;
|
|
|
|
struct bt_sock *ctrl, *intr;
|
|
|
|
|
|
|
|
ctrl = bt_sk(ctrl_sock->sk);
|
|
|
|
intr = bt_sk(intr_sock->sk);
|
|
|
|
|
|
|
|
session = kzalloc(sizeof(*session), GFP_KERNEL);
|
|
|
|
if (!session)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* object and runtime management */
|
|
|
|
kref_init(&session->ref);
|
|
|
|
atomic_set(&session->state, HIDP_SESSION_IDLING);
|
|
|
|
init_waitqueue_head(&session->state_queue);
|
2015-04-04 02:02:10 +08:00
|
|
|
session->flags = req->flags & BIT(HIDP_BLUETOOTH_VENDOR_ID);
|
2013-04-07 02:28:46 +08:00
|
|
|
|
|
|
|
/* connection management */
|
|
|
|
bacpy(&session->bdaddr, bdaddr);
|
2014-08-16 02:06:57 +08:00
|
|
|
session->conn = l2cap_conn_get(conn);
|
2013-04-07 02:28:46 +08:00
|
|
|
session->user.probe = hidp_session_probe;
|
|
|
|
session->user.remove = hidp_session_remove;
|
2015-07-05 10:11:33 +08:00
|
|
|
INIT_LIST_HEAD(&session->user.list);
|
2013-04-07 02:28:46 +08:00
|
|
|
session->ctrl_sock = ctrl_sock;
|
|
|
|
session->intr_sock = intr_sock;
|
|
|
|
skb_queue_head_init(&session->ctrl_transmit);
|
|
|
|
skb_queue_head_init(&session->intr_transmit);
|
|
|
|
session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl)->chan->omtu,
|
|
|
|
l2cap_pi(ctrl)->chan->imtu);
|
|
|
|
session->intr_mtu = min_t(uint, l2cap_pi(intr)->chan->omtu,
|
|
|
|
l2cap_pi(intr)->chan->imtu);
|
|
|
|
session->idle_to = req->idle_to;
|
|
|
|
|
|
|
|
/* device management */
|
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even
if we would add more fine-grained locking to HCI core, it would still be
called from the non-reentrant rx work-queue and thus block the event
processing.
However, if we want to perform synchronous I/O during HID device
registration (eg., to perform device-detection), we need the HCI core
to be able to dispatch incoming data.
Therefore, we now move device-registration to a separate worker. The HCI
core can continue running and we add devices asynchronously in another
kernel thread. Device removal is synchronized and waits for the worker
to exit before calling the usual device removal functions.
If l2cap_user->remove is called before the thread registered the devices,
we set "terminate" to true and the thread will skip it. If
l2cap_user->remove is called after it, we notice this as the device
is no longer in HIDP_SESSION_PREPARING state and simply unregister the
device as we did before.
There is no new deadlock as we now call hidp_session_add_dev() with
one lock less held (the HCI lock) and it cannot itself call back into
HCI as it was called with the HCI-lock held before.
One might wonder whether this can block during device unregistration.
But we set "terminate" to true and wake the HIDP thread up _before_
unregistering the HID/input devices. Therefore, all pending HID I/O
operations are canceled. All further I/O attempts will fail with ENODEV
or EIO. So all latency we can get are few context-switches, but no
timeouts or blocking I/O waits!
This change also prepares for a long standing HID bug. All HID devices
that register power_supply devices need to be able to handle callbacks
during registration (a power_supply oddity that cannot easily be fixed).
So with this patch available, we can allow HID I/O during registration
by calling the recently introduced hid_device_io_start/stop helpers,
which currently are a no-op for bluetooth due to this locking.
Note that we cannot do the same for input devices. input-core doesn't
allow us to call input_event() asynchronously to input_register_device(),
which HID-core kindly allows (for good reasons).
Fixing input-core to allow this isn't as easy as it sounds and is,
beside simplifying HIDP, not really an improvement. Hence, we still
register input devices synchronously as we did before. Only HID devices
are registered asynchronously.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Daniel Nicoletti <dantti12@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-05-23 19:10:25 +08:00
|
|
|
INIT_WORK(&session->dev_init, hidp_session_dev_work);
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-17 05:43:17 +08:00
|
|
|
timer_setup(&session->timer, hidp_idle_timeout, 0);
|
2013-04-07 02:28:46 +08:00
|
|
|
|
|
|
|
/* session data */
|
|
|
|
mutex_init(&session->report_mutex);
|
|
|
|
init_waitqueue_head(&session->report_queue);
|
|
|
|
|
|
|
|
ret = hidp_session_dev_init(session, req);
|
|
|
|
if (ret)
|
|
|
|
goto err_free;
|
|
|
|
|
|
|
|
get_file(session->intr_sock->file);
|
|
|
|
get_file(session->ctrl_sock->file);
|
|
|
|
*out = session;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_free:
|
2014-08-16 02:06:57 +08:00
|
|
|
l2cap_conn_put(session->conn);
|
2013-04-07 02:28:46 +08:00
|
|
|
kfree(session);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* increase ref-count of the given session by one */
|
|
|
|
static void hidp_session_get(struct hidp_session *session)
|
|
|
|
{
|
|
|
|
kref_get(&session->ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* release callback */
|
|
|
|
static void session_free(struct kref *ref)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = container_of(ref, struct hidp_session,
|
|
|
|
ref);
|
|
|
|
|
|
|
|
hidp_session_dev_destroy(session);
|
|
|
|
skb_queue_purge(&session->ctrl_transmit);
|
|
|
|
skb_queue_purge(&session->intr_transmit);
|
|
|
|
fput(session->intr_sock->file);
|
|
|
|
fput(session->ctrl_sock->file);
|
|
|
|
l2cap_conn_put(session->conn);
|
|
|
|
kfree(session);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* decrease ref-count of the given session by one */
|
|
|
|
static void hidp_session_put(struct hidp_session *session)
|
|
|
|
{
|
|
|
|
kref_put(&session->ref, session_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search the list of active sessions for a session with target address
|
|
|
|
* \bdaddr. You must hold at least a read-lock on \hidp_session_sem. As long as
|
|
|
|
* you do not release this lock, the session objects cannot vanish and you can
|
|
|
|
* safely take a reference to the session yourself.
|
|
|
|
*/
|
|
|
|
static struct hidp_session *__hidp_session_find(const bdaddr_t *bdaddr)
|
|
|
|
{
|
|
|
|
struct hidp_session *session;
|
|
|
|
|
|
|
|
list_for_each_entry(session, &hidp_session_list, list) {
|
|
|
|
if (!bacmp(bdaddr, &session->bdaddr))
|
|
|
|
return session;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Same as __hidp_session_find() but no locks must be held. This also takes a
|
|
|
|
* reference of the returned session (if non-NULL) so you must drop this
|
|
|
|
* reference if you no longer use the object.
|
|
|
|
*/
|
|
|
|
static struct hidp_session *hidp_session_find(const bdaddr_t *bdaddr)
|
|
|
|
{
|
|
|
|
struct hidp_session *session;
|
|
|
|
|
|
|
|
down_read(&hidp_session_sem);
|
|
|
|
|
|
|
|
session = __hidp_session_find(bdaddr);
|
|
|
|
if (session)
|
|
|
|
hidp_session_get(session);
|
|
|
|
|
|
|
|
up_read(&hidp_session_sem);
|
|
|
|
|
|
|
|
return session;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start session synchronously
|
|
|
|
* This starts a session thread and waits until initialization
|
|
|
|
* is done or returns an error if it couldn't be started.
|
|
|
|
* If this returns 0 the session thread is up and running. You must call
|
|
|
|
* hipd_session_stop_sync() before deleting any runtime resources.
|
|
|
|
*/
|
|
|
|
static int hidp_session_start_sync(struct hidp_session *session)
|
|
|
|
{
|
|
|
|
unsigned int vendor, product;
|
|
|
|
|
|
|
|
if (session->hid) {
|
|
|
|
vendor = session->hid->vendor;
|
|
|
|
product = session->hid->product;
|
|
|
|
} else if (session->input) {
|
|
|
|
vendor = session->input->id.vendor;
|
|
|
|
product = session->input->id.product;
|
|
|
|
} else {
|
|
|
|
vendor = 0x0000;
|
|
|
|
product = 0x0000;
|
|
|
|
}
|
|
|
|
|
|
|
|
session->task = kthread_run(hidp_session_thread, session,
|
|
|
|
"khidpd_%04x%04x", vendor, product);
|
|
|
|
if (IS_ERR(session->task))
|
|
|
|
return PTR_ERR(session->task);
|
|
|
|
|
|
|
|
while (atomic_read(&session->state) <= HIDP_SESSION_IDLING)
|
|
|
|
wait_event(session->state_queue,
|
|
|
|
atomic_read(&session->state) > HIDP_SESSION_IDLING);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Terminate session thread
|
|
|
|
* Wake up session thread and notify it to stop. This is asynchronous and
|
|
|
|
* returns immediately. Call this whenever a runtime error occurs and you want
|
|
|
|
* the session to stop.
|
2017-06-27 17:34:44 +08:00
|
|
|
* Note: wake_up_interruptible() performs any necessary memory-barriers for us.
|
2013-04-07 02:28:46 +08:00
|
|
|
*/
|
|
|
|
static void hidp_session_terminate(struct hidp_session *session)
|
|
|
|
{
|
|
|
|
atomic_inc(&session->terminate);
|
2018-08-15 02:41:06 +08:00
|
|
|
/*
|
|
|
|
* See the comment preceding the call to wait_woken()
|
|
|
|
* in hidp_session_run().
|
|
|
|
*/
|
2017-06-27 17:34:44 +08:00
|
|
|
wake_up_interruptible(&hidp_session_wq);
|
2013-04-07 02:28:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Probe HIDP session
|
|
|
|
* This is called from the l2cap_conn core when our l2cap_user object is bound
|
|
|
|
* to the hci-connection. We get the session via the \user object and can now
|
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even
if we would add more fine-grained locking to HCI core, it would still be
called from the non-reentrant rx work-queue and thus block the event
processing.
However, if we want to perform synchronous I/O during HID device
registration (eg., to perform device-detection), we need the HCI core
to be able to dispatch incoming data.
Therefore, we now move device-registration to a separate worker. The HCI
core can continue running and we add devices asynchronously in another
kernel thread. Device removal is synchronized and waits for the worker
to exit before calling the usual device removal functions.
If l2cap_user->remove is called before the thread registered the devices,
we set "terminate" to true and the thread will skip it. If
l2cap_user->remove is called after it, we notice this as the device
is no longer in HIDP_SESSION_PREPARING state and simply unregister the
device as we did before.
There is no new deadlock as we now call hidp_session_add_dev() with
one lock less held (the HCI lock) and it cannot itself call back into
HCI as it was called with the HCI-lock held before.
One might wonder whether this can block during device unregistration.
But we set "terminate" to true and wake the HIDP thread up _before_
unregistering the HID/input devices. Therefore, all pending HID I/O
operations are canceled. All further I/O attempts will fail with ENODEV
or EIO. So all latency we can get are few context-switches, but no
timeouts or blocking I/O waits!
This change also prepares for a long standing HID bug. All HID devices
that register power_supply devices need to be able to handle callbacks
during registration (a power_supply oddity that cannot easily be fixed).
So with this patch available, we can allow HID I/O during registration
by calling the recently introduced hid_device_io_start/stop helpers,
which currently are a no-op for bluetooth due to this locking.
Note that we cannot do the same for input devices. input-core doesn't
allow us to call input_event() asynchronously to input_register_device(),
which HID-core kindly allows (for good reasons).
Fixing input-core to allow this isn't as easy as it sounds and is,
beside simplifying HIDP, not really an improvement. Hence, we still
register input devices synchronously as we did before. Only HID devices
are registered asynchronously.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Daniel Nicoletti <dantti12@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-05-23 19:10:25 +08:00
|
|
|
* start the session thread, link it into the global session list and
|
|
|
|
* schedule HID/input device registration.
|
2013-04-07 02:28:46 +08:00
|
|
|
* The global session-list owns its own reference to the session object so you
|
|
|
|
* can drop your own reference after registering the l2cap_user object.
|
|
|
|
*/
|
|
|
|
static int hidp_session_probe(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_user *user)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = container_of(user,
|
|
|
|
struct hidp_session,
|
|
|
|
user);
|
|
|
|
struct hidp_session *s;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
down_write(&hidp_session_sem);
|
|
|
|
|
|
|
|
/* check that no other session for this device exists */
|
|
|
|
s = __hidp_session_find(&session->bdaddr);
|
|
|
|
if (s) {
|
|
|
|
ret = -EEXIST;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even
if we would add more fine-grained locking to HCI core, it would still be
called from the non-reentrant rx work-queue and thus block the event
processing.
However, if we want to perform synchronous I/O during HID device
registration (eg., to perform device-detection), we need the HCI core
to be able to dispatch incoming data.
Therefore, we now move device-registration to a separate worker. The HCI
core can continue running and we add devices asynchronously in another
kernel thread. Device removal is synchronized and waits for the worker
to exit before calling the usual device removal functions.
If l2cap_user->remove is called before the thread registered the devices,
we set "terminate" to true and the thread will skip it. If
l2cap_user->remove is called after it, we notice this as the device
is no longer in HIDP_SESSION_PREPARING state and simply unregister the
device as we did before.
There is no new deadlock as we now call hidp_session_add_dev() with
one lock less held (the HCI lock) and it cannot itself call back into
HCI as it was called with the HCI-lock held before.
One might wonder whether this can block during device unregistration.
But we set "terminate" to true and wake the HIDP thread up _before_
unregistering the HID/input devices. Therefore, all pending HID I/O
operations are canceled. All further I/O attempts will fail with ENODEV
or EIO. So all latency we can get are few context-switches, but no
timeouts or blocking I/O waits!
This change also prepares for a long standing HID bug. All HID devices
that register power_supply devices need to be able to handle callbacks
during registration (a power_supply oddity that cannot easily be fixed).
So with this patch available, we can allow HID I/O during registration
by calling the recently introduced hid_device_io_start/stop helpers,
which currently are a no-op for bluetooth due to this locking.
Note that we cannot do the same for input devices. input-core doesn't
allow us to call input_event() asynchronously to input_register_device(),
which HID-core kindly allows (for good reasons).
Fixing input-core to allow this isn't as easy as it sounds and is,
beside simplifying HIDP, not really an improvement. Hence, we still
register input devices synchronously as we did before. Only HID devices
are registered asynchronously.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Daniel Nicoletti <dantti12@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-05-23 19:10:25 +08:00
|
|
|
if (session->input) {
|
|
|
|
ret = hidp_session_dev_add(session);
|
|
|
|
if (ret)
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:46 +08:00
|
|
|
ret = hidp_session_start_sync(session);
|
|
|
|
if (ret)
|
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even
if we would add more fine-grained locking to HCI core, it would still be
called from the non-reentrant rx work-queue and thus block the event
processing.
However, if we want to perform synchronous I/O during HID device
registration (eg., to perform device-detection), we need the HCI core
to be able to dispatch incoming data.
Therefore, we now move device-registration to a separate worker. The HCI
core can continue running and we add devices asynchronously in another
kernel thread. Device removal is synchronized and waits for the worker
to exit before calling the usual device removal functions.
If l2cap_user->remove is called before the thread registered the devices,
we set "terminate" to true and the thread will skip it. If
l2cap_user->remove is called after it, we notice this as the device
is no longer in HIDP_SESSION_PREPARING state and simply unregister the
device as we did before.
There is no new deadlock as we now call hidp_session_add_dev() with
one lock less held (the HCI lock) and it cannot itself call back into
HCI as it was called with the HCI-lock held before.
One might wonder whether this can block during device unregistration.
But we set "terminate" to true and wake the HIDP thread up _before_
unregistering the HID/input devices. Therefore, all pending HID I/O
operations are canceled. All further I/O attempts will fail with ENODEV
or EIO. So all latency we can get are few context-switches, but no
timeouts or blocking I/O waits!
This change also prepares for a long standing HID bug. All HID devices
that register power_supply devices need to be able to handle callbacks
during registration (a power_supply oddity that cannot easily be fixed).
So with this patch available, we can allow HID I/O during registration
by calling the recently introduced hid_device_io_start/stop helpers,
which currently are a no-op for bluetooth due to this locking.
Note that we cannot do the same for input devices. input-core doesn't
allow us to call input_event() asynchronously to input_register_device(),
which HID-core kindly allows (for good reasons).
Fixing input-core to allow this isn't as easy as it sounds and is,
beside simplifying HIDP, not really an improvement. Hence, we still
register input devices synchronously as we did before. Only HID devices
are registered asynchronously.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Daniel Nicoletti <dantti12@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-05-23 19:10:25 +08:00
|
|
|
goto out_del;
|
2013-04-07 02:28:46 +08:00
|
|
|
|
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even
if we would add more fine-grained locking to HCI core, it would still be
called from the non-reentrant rx work-queue and thus block the event
processing.
However, if we want to perform synchronous I/O during HID device
registration (eg., to perform device-detection), we need the HCI core
to be able to dispatch incoming data.
Therefore, we now move device-registration to a separate worker. The HCI
core can continue running and we add devices asynchronously in another
kernel thread. Device removal is synchronized and waits for the worker
to exit before calling the usual device removal functions.
If l2cap_user->remove is called before the thread registered the devices,
we set "terminate" to true and the thread will skip it. If
l2cap_user->remove is called after it, we notice this as the device
is no longer in HIDP_SESSION_PREPARING state and simply unregister the
device as we did before.
There is no new deadlock as we now call hidp_session_add_dev() with
one lock less held (the HCI lock) and it cannot itself call back into
HCI as it was called with the HCI-lock held before.
One might wonder whether this can block during device unregistration.
But we set "terminate" to true and wake the HIDP thread up _before_
unregistering the HID/input devices. Therefore, all pending HID I/O
operations are canceled. All further I/O attempts will fail with ENODEV
or EIO. So all latency we can get are few context-switches, but no
timeouts or blocking I/O waits!
This change also prepares for a long standing HID bug. All HID devices
that register power_supply devices need to be able to handle callbacks
during registration (a power_supply oddity that cannot easily be fixed).
So with this patch available, we can allow HID I/O during registration
by calling the recently introduced hid_device_io_start/stop helpers,
which currently are a no-op for bluetooth due to this locking.
Note that we cannot do the same for input devices. input-core doesn't
allow us to call input_event() asynchronously to input_register_device(),
which HID-core kindly allows (for good reasons).
Fixing input-core to allow this isn't as easy as it sounds and is,
beside simplifying HIDP, not really an improvement. Hence, we still
register input devices synchronously as we did before. Only HID devices
are registered asynchronously.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Daniel Nicoletti <dantti12@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-05-23 19:10:25 +08:00
|
|
|
/* HID device registration is async to allow I/O during probe */
|
|
|
|
if (session->input)
|
|
|
|
atomic_inc(&session->state);
|
|
|
|
else
|
|
|
|
schedule_work(&session->dev_init);
|
2013-04-07 02:28:46 +08:00
|
|
|
|
|
|
|
hidp_session_get(session);
|
|
|
|
list_add(&session->list, &hidp_session_list);
|
|
|
|
ret = 0;
|
|
|
|
goto out_unlock;
|
|
|
|
|
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even
if we would add more fine-grained locking to HCI core, it would still be
called from the non-reentrant rx work-queue and thus block the event
processing.
However, if we want to perform synchronous I/O during HID device
registration (eg., to perform device-detection), we need the HCI core
to be able to dispatch incoming data.
Therefore, we now move device-registration to a separate worker. The HCI
core can continue running and we add devices asynchronously in another
kernel thread. Device removal is synchronized and waits for the worker
to exit before calling the usual device removal functions.
If l2cap_user->remove is called before the thread registered the devices,
we set "terminate" to true and the thread will skip it. If
l2cap_user->remove is called after it, we notice this as the device
is no longer in HIDP_SESSION_PREPARING state and simply unregister the
device as we did before.
There is no new deadlock as we now call hidp_session_add_dev() with
one lock less held (the HCI lock) and it cannot itself call back into
HCI as it was called with the HCI-lock held before.
One might wonder whether this can block during device unregistration.
But we set "terminate" to true and wake the HIDP thread up _before_
unregistering the HID/input devices. Therefore, all pending HID I/O
operations are canceled. All further I/O attempts will fail with ENODEV
or EIO. So all latency we can get are few context-switches, but no
timeouts or blocking I/O waits!
This change also prepares for a long standing HID bug. All HID devices
that register power_supply devices need to be able to handle callbacks
during registration (a power_supply oddity that cannot easily be fixed).
So with this patch available, we can allow HID I/O during registration
by calling the recently introduced hid_device_io_start/stop helpers,
which currently are a no-op for bluetooth due to this locking.
Note that we cannot do the same for input devices. input-core doesn't
allow us to call input_event() asynchronously to input_register_device(),
which HID-core kindly allows (for good reasons).
Fixing input-core to allow this isn't as easy as it sounds and is,
beside simplifying HIDP, not really an improvement. Hence, we still
register input devices synchronously as we did before. Only HID devices
are registered asynchronously.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Daniel Nicoletti <dantti12@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-05-23 19:10:25 +08:00
|
|
|
out_del:
|
|
|
|
if (session->input)
|
|
|
|
hidp_session_dev_del(session);
|
2013-04-07 02:28:46 +08:00
|
|
|
out_unlock:
|
|
|
|
up_write(&hidp_session_sem);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove HIDP session
|
|
|
|
* Called from the l2cap_conn core when either we explicitly unregistered
|
|
|
|
* the l2cap_user object or if the underlying connection is shut down.
|
|
|
|
* We signal the hidp-session thread to shut down, unregister the HID/input
|
|
|
|
* devices and unlink the session from the global list.
|
|
|
|
* This drops the reference to the session that is owned by the global
|
|
|
|
* session-list.
|
|
|
|
* Note: We _must_ not synchronosly wait for the session-thread to shut down.
|
|
|
|
* This is, because the session-thread might be waiting for an HCI lock that is
|
|
|
|
* held while we are called. Therefore, we only unregister the devices and
|
|
|
|
* notify the session-thread to terminate. The thread itself owns a reference
|
|
|
|
* to the session object so it can safely shut down.
|
|
|
|
*/
|
|
|
|
static void hidp_session_remove(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_user *user)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = container_of(user,
|
|
|
|
struct hidp_session,
|
|
|
|
user);
|
|
|
|
|
|
|
|
down_write(&hidp_session_sem);
|
|
|
|
|
|
|
|
hidp_session_terminate(session);
|
HID: Bluetooth: hidp: register HID devices async
While l2cap_user callbacks are running, the whole hci_dev is locked. Even
if we would add more fine-grained locking to HCI core, it would still be
called from the non-reentrant rx work-queue and thus block the event
processing.
However, if we want to perform synchronous I/O during HID device
registration (eg., to perform device-detection), we need the HCI core
to be able to dispatch incoming data.
Therefore, we now move device-registration to a separate worker. The HCI
core can continue running and we add devices asynchronously in another
kernel thread. Device removal is synchronized and waits for the worker
to exit before calling the usual device removal functions.
If l2cap_user->remove is called before the thread registered the devices,
we set "terminate" to true and the thread will skip it. If
l2cap_user->remove is called after it, we notice this as the device
is no longer in HIDP_SESSION_PREPARING state and simply unregister the
device as we did before.
There is no new deadlock as we now call hidp_session_add_dev() with
one lock less held (the HCI lock) and it cannot itself call back into
HCI as it was called with the HCI-lock held before.
One might wonder whether this can block during device unregistration.
But we set "terminate" to true and wake the HIDP thread up _before_
unregistering the HID/input devices. Therefore, all pending HID I/O
operations are canceled. All further I/O attempts will fail with ENODEV
or EIO. So all latency we can get are few context-switches, but no
timeouts or blocking I/O waits!
This change also prepares for a long standing HID bug. All HID devices
that register power_supply devices need to be able to handle callbacks
during registration (a power_supply oddity that cannot easily be fixed).
So with this patch available, we can allow HID I/O during registration
by calling the recently introduced hid_device_io_start/stop helpers,
which currently are a no-op for bluetooth due to this locking.
Note that we cannot do the same for input devices. input-core doesn't
allow us to call input_event() asynchronously to input_register_device(),
which HID-core kindly allows (for good reasons).
Fixing input-core to allow this isn't as easy as it sounds and is,
beside simplifying HIDP, not really an improvement. Hence, we still
register input devices synchronously as we did before. Only HID devices
are registered asynchronously.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.cz>
Acked-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Daniel Nicoletti <dantti12@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2013-05-23 19:10:25 +08:00
|
|
|
|
|
|
|
cancel_work_sync(&session->dev_init);
|
|
|
|
if (session->input ||
|
|
|
|
atomic_read(&session->state) > HIDP_SESSION_PREPARING)
|
|
|
|
hidp_session_dev_del(session);
|
|
|
|
|
2013-04-07 02:28:46 +08:00
|
|
|
list_del(&session->list);
|
|
|
|
|
|
|
|
up_write(&hidp_session_sem);
|
|
|
|
|
|
|
|
hidp_session_put(session);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Session Worker
|
|
|
|
* This performs the actual main-loop of the HIDP worker. We first check
|
|
|
|
* whether the underlying connection is still alive, then parse all pending
|
|
|
|
* messages and finally send all outstanding messages.
|
|
|
|
*/
|
|
|
|
static void hidp_session_run(struct hidp_session *session)
|
|
|
|
{
|
|
|
|
struct sock *ctrl_sk = session->ctrl_sock->sk;
|
|
|
|
struct sock *intr_sk = session->intr_sock->sk;
|
|
|
|
struct sk_buff *skb;
|
2017-06-27 17:34:44 +08:00
|
|
|
DEFINE_WAIT_FUNC(wait, woken_wake_function);
|
2013-04-07 02:28:46 +08:00
|
|
|
|
2017-06-27 17:34:44 +08:00
|
|
|
add_wait_queue(&hidp_session_wq, &wait);
|
2013-04-07 02:28:46 +08:00
|
|
|
for (;;) {
|
|
|
|
/*
|
|
|
|
* This thread can be woken up two ways:
|
|
|
|
* - You call hidp_session_terminate() which sets the
|
|
|
|
* session->terminate flag and wakes this thread up.
|
|
|
|
* - Via modifying the socket state of ctrl/intr_sock. This
|
|
|
|
* thread is woken up by ->sk_state_changed().
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (atomic_read(&session->terminate))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (ctrl_sk->sk_state != BT_CONNECTED ||
|
|
|
|
intr_sk->sk_state != BT_CONNECTED)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* parse incoming intr-skbs */
|
|
|
|
while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
|
|
|
|
skb_orphan(skb);
|
|
|
|
if (!skb_linearize(skb))
|
|
|
|
hidp_recv_intr_frame(session, skb);
|
|
|
|
else
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* send pending intr-skbs */
|
2013-04-07 02:28:49 +08:00
|
|
|
hidp_process_transmit(session, &session->intr_transmit,
|
|
|
|
session->intr_sock);
|
2013-04-07 02:28:46 +08:00
|
|
|
|
|
|
|
/* parse incoming ctrl-skbs */
|
|
|
|
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
|
|
|
|
skb_orphan(skb);
|
|
|
|
if (!skb_linearize(skb))
|
|
|
|
hidp_recv_ctrl_frame(session, skb);
|
|
|
|
else
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* send pending ctrl-skbs */
|
2013-04-07 02:28:49 +08:00
|
|
|
hidp_process_transmit(session, &session->ctrl_transmit,
|
|
|
|
session->ctrl_sock);
|
2013-04-07 02:28:46 +08:00
|
|
|
|
2018-08-15 02:41:06 +08:00
|
|
|
/*
|
|
|
|
* wait_woken() performs the necessary memory barriers
|
|
|
|
* for us; see the header comment for this primitive.
|
|
|
|
*/
|
2017-06-27 17:34:44 +08:00
|
|
|
wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
|
2013-04-07 02:28:46 +08:00
|
|
|
}
|
2017-06-27 17:34:44 +08:00
|
|
|
remove_wait_queue(&hidp_session_wq, &wait);
|
2013-04-07 02:28:46 +08:00
|
|
|
|
|
|
|
atomic_inc(&session->terminate);
|
2017-06-27 17:34:44 +08:00
|
|
|
}
|
|
|
|
|
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
"Reasonably busy this cycle, but perhaps not as busy as in the 4.12
merge window:
1) Several optimizations for UDP processing under high load from
Paolo Abeni.
2) Support pacing internally in TCP when using the sch_fq packet
scheduler for this is not practical. From Eric Dumazet.
3) Support mutliple filter chains per qdisc, from Jiri Pirko.
4) Move to 1ms TCP timestamp clock, from Eric Dumazet.
5) Add batch dequeueing to vhost_net, from Jason Wang.
6) Flesh out more completely SCTP checksum offload support, from
Davide Caratti.
7) More plumbing of extended netlink ACKs, from David Ahern, Pablo
Neira Ayuso, and Matthias Schiffer.
8) Add devlink support to nfp driver, from Simon Horman.
9) Add RTM_F_FIB_MATCH flag to RTM_GETROUTE queries, from Roopa
Prabhu.
10) Add stack depth tracking to BPF verifier and use this information
in the various eBPF JITs. From Alexei Starovoitov.
11) Support XDP on qed device VFs, from Yuval Mintz.
12) Introduce BPF PROG ID for better introspection of installed BPF
programs. From Martin KaFai Lau.
13) Add bpf_set_hash helper for TC bpf programs, from Daniel Borkmann.
14) For loads, allow narrower accesses in bpf verifier checking, from
Yonghong Song.
15) Support MIPS in the BPF selftests and samples infrastructure, the
MIPS eBPF JIT will be merged in via the MIPS GIT tree. From David
Daney.
16) Support kernel based TLS, from Dave Watson and others.
17) Remove completely DST garbage collection, from Wei Wang.
18) Allow installing TCP MD5 rules using prefixes, from Ivan
Delalande.
19) Add XDP support to Intel i40e driver, from Björn Töpel
20) Add support for TC flower offload in nfp driver, from Simon
Horman, Pieter Jansen van Vuuren, Benjamin LaHaise, Jakub
Kicinski, and Bert van Leeuwen.
21) IPSEC offloading support in mlx5, from Ilan Tayari.
22) Add HW PTP support to macb driver, from Rafal Ozieblo.
23) Networking refcount_t conversions, From Elena Reshetova.
24) Add sock_ops support to BPF, from Lawrence Brako. This is useful
for tuning the TCP sockopt settings of a group of applications,
currently via CGROUPs"
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1899 commits)
net: phy: dp83867: add workaround for incorrect RX_CTRL pin strap
dt-bindings: phy: dp83867: provide a workaround for incorrect RX_CTRL pin strap
cxgb4: Support for get_ts_info ethtool method
cxgb4: Add PTP Hardware Clock (PHC) support
cxgb4: time stamping interface for PTP
nfp: default to chained metadata prepend format
nfp: remove legacy MAC address lookup
nfp: improve order of interfaces in breakout mode
net: macb: remove extraneous return when MACB_EXT_DESC is defined
bpf: add missing break in for the TCP_BPF_SNDCWND_CLAMP case
bpf: fix return in load_bpf_file
mpls: fix rtm policy in mpls_getroute
net, ax25: convert ax25_cb.refcount from atomic_t to refcount_t
net, ax25: convert ax25_route.refcount from atomic_t to refcount_t
net, ax25: convert ax25_uid_assoc.refcount from atomic_t to refcount_t
net, sctp: convert sctp_ep_common.refcnt from atomic_t to refcount_t
net, sctp: convert sctp_transport.refcnt from atomic_t to refcount_t
net, sctp: convert sctp_chunk.refcnt from atomic_t to refcount_t
net, sctp: convert sctp_datamsg.refcnt from atomic_t to refcount_t
net, sctp: convert sctp_auth_bytes.refcnt from atomic_t to refcount_t
...
2017-07-06 03:31:59 +08:00
|
|
|
static int hidp_session_wake_function(wait_queue_entry_t *wait,
|
2017-06-27 17:34:44 +08:00
|
|
|
unsigned int mode,
|
|
|
|
int sync, void *key)
|
|
|
|
{
|
|
|
|
wake_up_interruptible(&hidp_session_wq);
|
|
|
|
return false;
|
2013-04-07 02:28:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* HIDP session thread
|
|
|
|
* This thread runs the I/O for a single HIDP session. Startup is synchronous
|
|
|
|
* which allows us to take references to ourself here instead of doing that in
|
|
|
|
* the caller.
|
|
|
|
* When we are ready to run we notify the caller and call hidp_session_run().
|
|
|
|
*/
|
|
|
|
static int hidp_session_thread(void *arg)
|
|
|
|
{
|
|
|
|
struct hidp_session *session = arg;
|
2017-06-27 17:34:44 +08:00
|
|
|
DEFINE_WAIT_FUNC(ctrl_wait, hidp_session_wake_function);
|
|
|
|
DEFINE_WAIT_FUNC(intr_wait, hidp_session_wake_function);
|
2013-04-07 02:28:46 +08:00
|
|
|
|
|
|
|
BT_DBG("session %p", session);
|
|
|
|
|
|
|
|
/* initialize runtime environment */
|
|
|
|
hidp_session_get(session);
|
|
|
|
__module_get(THIS_MODULE);
|
|
|
|
set_user_nice(current, -15);
|
|
|
|
hidp_set_timer(session);
|
|
|
|
|
|
|
|
add_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
|
|
|
|
add_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
|
|
|
|
/* This memory barrier is paired with wq_has_sleeper(). See
|
|
|
|
* sock_poll_wait() for more information why this is needed. */
|
2020-01-30 02:10:41 +08:00
|
|
|
smp_mb__before_atomic();
|
2013-04-07 02:28:46 +08:00
|
|
|
|
|
|
|
/* notify synchronous startup that we're ready */
|
|
|
|
atomic_inc(&session->state);
|
|
|
|
wake_up(&session->state_queue);
|
|
|
|
|
|
|
|
/* run session */
|
|
|
|
hidp_session_run(session);
|
|
|
|
|
|
|
|
/* cleanup runtime environment */
|
|
|
|
remove_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
|
2020-10-17 19:15:44 +08:00
|
|
|
remove_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
|
2013-04-07 02:28:46 +08:00
|
|
|
wake_up_interruptible(&session->report_queue);
|
|
|
|
hidp_del_timer(session);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we stopped ourself due to any internal signal, we should try to
|
|
|
|
* unregister our own session here to avoid having it linger until the
|
|
|
|
* parent l2cap_conn dies or user-space cleans it up.
|
|
|
|
* This does not deadlock as we don't do any synchronous shutdown.
|
|
|
|
* Instead, this call has the same semantics as if user-space tried to
|
|
|
|
* delete the session.
|
|
|
|
*/
|
|
|
|
l2cap_unregister_user(session->conn, &session->user);
|
|
|
|
hidp_session_put(session);
|
|
|
|
|
|
|
|
module_put_and_exit(0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hidp_verify_sockets(struct socket *ctrl_sock,
|
|
|
|
struct socket *intr_sock)
|
|
|
|
{
|
2013-10-14 00:49:56 +08:00
|
|
|
struct l2cap_chan *ctrl_chan, *intr_chan;
|
2013-04-07 02:28:46 +08:00
|
|
|
struct bt_sock *ctrl, *intr;
|
|
|
|
struct hidp_session *session;
|
|
|
|
|
|
|
|
if (!l2cap_is_socket(ctrl_sock) || !l2cap_is_socket(intr_sock))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-10-14 00:49:56 +08:00
|
|
|
ctrl_chan = l2cap_pi(ctrl_sock->sk)->chan;
|
|
|
|
intr_chan = l2cap_pi(intr_sock->sk)->chan;
|
|
|
|
|
|
|
|
if (bacmp(&ctrl_chan->src, &intr_chan->src) ||
|
|
|
|
bacmp(&ctrl_chan->dst, &intr_chan->dst))
|
|
|
|
return -ENOTUNIQ;
|
|
|
|
|
2013-04-07 02:28:46 +08:00
|
|
|
ctrl = bt_sk(ctrl_sock->sk);
|
|
|
|
intr = bt_sk(intr_sock->sk);
|
|
|
|
|
|
|
|
if (ctrl->sk.sk_state != BT_CONNECTED ||
|
|
|
|
intr->sk.sk_state != BT_CONNECTED)
|
|
|
|
return -EBADFD;
|
|
|
|
|
|
|
|
/* early session check, we check again during session registration */
|
2013-10-14 00:49:56 +08:00
|
|
|
session = hidp_session_find(&ctrl_chan->dst);
|
2013-04-07 02:28:46 +08:00
|
|
|
if (session) {
|
|
|
|
hidp_session_put(session);
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-17 09:44:35 +08:00
|
|
|
int hidp_connection_add(const struct hidp_connadd_req *req,
|
2013-04-07 02:28:46 +08:00
|
|
|
struct socket *ctrl_sock,
|
|
|
|
struct socket *intr_sock)
|
|
|
|
{
|
2015-04-18 04:34:18 +08:00
|
|
|
u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG) |
|
|
|
|
BIT(HIDP_BOOT_PROTOCOL_MODE);
|
2013-04-07 02:28:46 +08:00
|
|
|
struct hidp_session *session;
|
|
|
|
struct l2cap_conn *conn;
|
2014-12-19 14:20:57 +08:00
|
|
|
struct l2cap_chan *chan;
|
2013-04-07 02:28:46 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = hidp_verify_sockets(ctrl_sock, intr_sock);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2015-04-02 04:51:53 +08:00
|
|
|
if (req->flags & ~valid_flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-12-19 14:20:57 +08:00
|
|
|
chan = l2cap_pi(ctrl_sock->sk)->chan;
|
2013-04-07 02:28:46 +08:00
|
|
|
conn = NULL;
|
|
|
|
l2cap_chan_lock(chan);
|
2014-08-16 02:06:57 +08:00
|
|
|
if (chan->conn)
|
|
|
|
conn = l2cap_conn_get(chan->conn);
|
2013-04-07 02:28:46 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
|
|
|
|
if (!conn)
|
|
|
|
return -EBADFD;
|
|
|
|
|
2013-10-14 00:49:56 +08:00
|
|
|
ret = hidp_session_new(&session, &chan->dst, ctrl_sock,
|
2013-04-07 02:28:46 +08:00
|
|
|
intr_sock, req, conn);
|
|
|
|
if (ret)
|
|
|
|
goto out_conn;
|
|
|
|
|
|
|
|
ret = l2cap_register_user(conn, &session->user);
|
|
|
|
if (ret)
|
|
|
|
goto out_session;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out_session:
|
|
|
|
hidp_session_put(session);
|
|
|
|
out_conn:
|
|
|
|
l2cap_conn_put(conn);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int hidp_connection_del(struct hidp_conndel_req *req)
|
|
|
|
{
|
2015-04-02 04:51:53 +08:00
|
|
|
u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG);
|
2013-04-07 02:28:46 +08:00
|
|
|
struct hidp_session *session;
|
|
|
|
|
2015-04-02 04:51:53 +08:00
|
|
|
if (req->flags & ~valid_flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-04-07 02:28:46 +08:00
|
|
|
session = hidp_session_find(&req->bdaddr);
|
|
|
|
if (!session)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2015-04-04 02:02:10 +08:00
|
|
|
if (req->flags & BIT(HIDP_VIRTUAL_CABLE_UNPLUG))
|
2013-04-07 02:28:46 +08:00
|
|
|
hidp_send_ctrl_message(session,
|
|
|
|
HIDP_TRANS_HID_CONTROL |
|
|
|
|
HIDP_CTRL_VIRTUAL_CABLE_UNPLUG,
|
|
|
|
NULL, 0);
|
|
|
|
else
|
|
|
|
l2cap_unregister_user(session->conn, &session->user);
|
|
|
|
|
|
|
|
hidp_session_put(session);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int hidp_get_connlist(struct hidp_connlist_req *req)
|
|
|
|
{
|
2011-10-07 04:35:31 +08:00
|
|
|
struct hidp_session *session;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err = 0, n = 0;
|
|
|
|
|
|
|
|
BT_DBG("");
|
|
|
|
|
|
|
|
down_read(&hidp_session_sem);
|
|
|
|
|
2011-10-07 04:35:31 +08:00
|
|
|
list_for_each_entry(session, &hidp_session_list, list) {
|
2005-04-17 06:20:36 +08:00
|
|
|
struct hidp_conninfo ci;
|
|
|
|
|
2013-04-07 02:28:47 +08:00
|
|
|
hidp_copy_session(session, &ci);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
|
|
|
|
err = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (++n >= req->cnum)
|
|
|
|
break;
|
|
|
|
|
|
|
|
req->ci++;
|
|
|
|
}
|
|
|
|
req->cnum = n;
|
|
|
|
|
|
|
|
up_read(&hidp_session_sem);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int hidp_get_conninfo(struct hidp_conninfo *ci)
|
|
|
|
{
|
|
|
|
struct hidp_session *session;
|
|
|
|
|
2013-04-07 02:28:47 +08:00
|
|
|
session = hidp_session_find(&ci->bdaddr);
|
|
|
|
if (session) {
|
|
|
|
hidp_copy_session(session, ci);
|
|
|
|
hidp_session_put(session);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-04-07 02:28:47 +08:00
|
|
|
return session ? 0 : -ENOENT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __init hidp_init(void)
|
|
|
|
{
|
|
|
|
BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
|
|
|
|
|
2012-04-23 18:07:07 +08:00
|
|
|
return hidp_init_sockets();
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit hidp_exit(void)
|
|
|
|
{
|
|
|
|
hidp_cleanup_sockets();
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(hidp_init);
|
|
|
|
module_exit(hidp_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
2013-04-07 02:28:46 +08:00
|
|
|
MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
|
2005-04-17 06:20:36 +08:00
|
|
|
MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
|
|
|
|
MODULE_VERSION(VERSION);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS("bt-proto-6");
|