aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/hidp
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2007-02-17 17:58:49 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2007-02-26 14:42:38 -0500
commite1aaadd4d8162a2c33e41dd5a72234ea4d3b014f (patch)
tree5725bc4ce77c42fcf9fd6b3adc173f8403e492ef /net/bluetooth/hidp
parenta83d6c0de8811d7bcc4eb67ed199d1120ca6cad8 (diff)
[Bluetooth] Add support for using the HID subsystem
This patch extends the current Bluetooth HID support to use the new HID subsystem and adds full report mode support. Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/hidp')
-rw-r--r--net/bluetooth/hidp/Kconfig1
-rw-r--r--net/bluetooth/hidp/core.c170
-rw-r--r--net/bluetooth/hidp/hidp.h2
3 files changed, 164 insertions, 9 deletions
diff --git a/net/bluetooth/hidp/Kconfig b/net/bluetooth/hidp/Kconfig
index c6abf2a5a932..98fdfa1fbddd 100644
--- a/net/bluetooth/hidp/Kconfig
+++ b/net/bluetooth/hidp/Kconfig
@@ -1,6 +1,7 @@
1config BT_HIDP 1config BT_HIDP
2 tristate "HIDP protocol support" 2 tristate "HIDP protocol support"
3 depends on BT && BT_L2CAP && INPUT 3 depends on BT && BT_L2CAP && INPUT
4 select HID
4 help 5 help
5 HIDP (Human Interface Device Protocol) is a transport layer 6 HIDP (Human Interface Device Protocol) is a transport layer
6 for HID reports. HIDP is required for the Bluetooth Human 7 for HID reports. HIDP is required for the Bluetooth Human
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 4b99c5e4478d..05e23bbcb0a1 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -38,6 +38,7 @@
38#include <net/sock.h> 38#include <net/sock.h>
39 39
40#include <linux/input.h> 40#include <linux/input.h>
41#include <linux/hid.h>
41 42
42#include <net/bluetooth/bluetooth.h> 43#include <net/bluetooth/bluetooth.h>
43#include <net/bluetooth/hci_core.h> 44#include <net/bluetooth/hci_core.h>
@@ -50,7 +51,7 @@
50#define BT_DBG(D...) 51#define BT_DBG(D...)
51#endif 52#endif
52 53
53#define VERSION "1.1" 54#define VERSION "1.2"
54 55
55static DECLARE_RWSEM(hidp_session_sem); 56static DECLARE_RWSEM(hidp_session_sem);
56static LIST_HEAD(hidp_session_list); 57static LIST_HEAD(hidp_session_list);
@@ -124,15 +125,22 @@ static void __hidp_copy_session(struct hidp_session *session, struct hidp_connin
124 else 125 else
125 strncpy(ci->name, "HID Boot Device", 128); 126 strncpy(ci->name, "HID Boot Device", 128);
126 } 127 }
128
129 if (session->hid) {
130 ci->vendor = session->hid->vendor;
131 ci->product = session->hid->product;
132 ci->version = session->hid->version;
133 strncpy(ci->name, session->hid->name, 128);
134 }
127} 135}
128 136
129static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 137static inline int hidp_queue_event(struct hidp_session *session, struct input_dev *dev,
138 unsigned int type, unsigned int code, int value)
130{ 139{
131 struct hidp_session *session = dev->private;
132 struct sk_buff *skb;
133 unsigned char newleds; 140 unsigned char newleds;
141 struct sk_buff *skb;
134 142
135 BT_DBG("input %p type %d code %d value %d", dev, type, code, value); 143 BT_DBG("session %p type %d code %d value %d", session, type, code, value);
136 144
137 if (type != EV_LED) 145 if (type != EV_LED)
138 return -1; 146 return -1;
@@ -164,6 +172,21 @@ static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned i
164 return 0; 172 return 0;
165} 173}
166 174
175static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
176{
177 struct hid_device *hid = dev->private;
178 struct hidp_session *session = hid->driver_data;
179
180 return hidp_queue_event(session, dev, type, code, value);
181}
182
183static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
184{
185 struct hidp_session *session = dev->private;
186
187 return hidp_queue_event(session, dev, type, code, value);
188}
189
167static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) 190static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
168{ 191{
169 struct input_dev *dev = session->input; 192 struct input_dev *dev = session->input;
@@ -219,6 +242,42 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
219 input_sync(dev); 242 input_sync(dev);
220} 243}
221 244
245static inline int hidp_queue_report(struct hidp_session *session, unsigned char *data, int size)
246{
247 struct sk_buff *skb;
248
249 BT_DBG("session %p hid %p data %p size %d", session, device, data, size);
250
251 if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) {
252 BT_ERR("Can't allocate memory for new frame");
253 return -ENOMEM;
254 }
255
256 *skb_put(skb, 1) = 0xa2;
257 if (size > 0)
258 memcpy(skb_put(skb, size), data, size);
259
260 skb_queue_tail(&session->intr_transmit, skb);
261
262 hidp_schedule(session);
263
264 return 0;
265}
266
267static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
268{
269 unsigned char buf[32];
270 int rsize;
271
272 rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
273 if (rsize > sizeof(buf))
274 return -EIO;
275
276 hid_output_report(report, buf);
277
278 return hidp_queue_report(session, buf, rsize);
279}
280
222static void hidp_idle_timeout(unsigned long arg) 281static void hidp_idle_timeout(unsigned long arg)
223{ 282{
224 struct hidp_session *session = (struct hidp_session *) arg; 283 struct hidp_session *session = (struct hidp_session *) arg;
@@ -346,6 +405,10 @@ static inline void hidp_process_data(struct hidp_session *session, struct sk_buf
346 405
347 if (session->input) 406 if (session->input)
348 hidp_input_report(session, skb); 407 hidp_input_report(session, skb);
408
409 if (session->hid)
410 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
411
349 break; 412 break;
350 413
351 case HIDP_DATA_RTYPE_OTHER: 414 case HIDP_DATA_RTYPE_OTHER:
@@ -404,8 +467,14 @@ static inline void hidp_recv_intr_frame(struct hidp_session *session, struct sk_
404 467
405 if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { 468 if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
406 hidp_set_timer(session); 469 hidp_set_timer(session);
470
407 if (session->input) 471 if (session->input)
408 hidp_input_report(session, skb); 472 hidp_input_report(session, skb);
473
474 if (session->hid) {
475 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
476 BT_DBG("report len %d", skb->len);
477 }
409 } else { 478 } else {
410 BT_DBG("Unsupported protocol header 0x%02x", hdr); 479 BT_DBG("Unsupported protocol header 0x%02x", hdr);
411 } 480 }
@@ -471,6 +540,11 @@ static int hidp_session(void *arg)
471 product = session->input->id.product; 540 product = session->input->id.product;
472 } 541 }
473 542
543 if (session->hid) {
544 vendor = session->hid->vendor;
545 product = session->hid->product;
546 }
547
474 daemonize("khidpd_%04x%04x", vendor, product); 548 daemonize("khidpd_%04x%04x", vendor, product);
475 set_user_nice(current, -15); 549 set_user_nice(current, -15);
476 current->flags |= PF_NOFREEZE; 550 current->flags |= PF_NOFREEZE;
@@ -521,6 +595,12 @@ static int hidp_session(void *arg)
521 session->input = NULL; 595 session->input = NULL;
522 } 596 }
523 597
598 if (session->hid) {
599 if (session->hid->claimed & HID_CLAIMED_INPUT)
600 hidinput_disconnect(session->hid);
601 hid_free_device(session->hid);
602 }
603
524 up_write(&hidp_session_sem); 604 up_write(&hidp_session_sem);
525 605
526 kfree(session); 606 kfree(session);
@@ -590,6 +670,44 @@ static inline void hidp_setup_input(struct hidp_session *session, struct hidp_co
590 input_register_device(input); 670 input_register_device(input);
591} 671}
592 672
673static inline void hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req)
674{
675 struct hid_device *hid = session->hid;
676 struct hid_report *report;
677 bdaddr_t src, dst;
678
679 baswap(&src, &bt_sk(session->ctrl_sock->sk)->src);
680 baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst);
681
682 hid->driver_data = session;
683
684 hid->country = req->country;
685
686 hid->bus = BUS_BLUETOOTH;
687 hid->vendor = req->vendor;
688 hid->product = req->product;
689 hid->version = req->version;
690
691 strncpy(hid->name, req->name, 128);
692 strncpy(hid->phys, batostr(&src), 64);
693 strncpy(hid->uniq, batostr(&dst), 64);
694
695 hid->dev = hidp_get_device(session);
696
697 hid->hidinput_input_event = hidp_hidinput_event;
698
699 list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
700 hidp_send_report(session, report);
701
702 list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
703 hidp_send_report(session, report);
704
705 if (hidinput_connect(hid) == 0) {
706 hid->claimed |= HID_CLAIMED_INPUT;
707 hid_ff_init(hid);
708 }
709}
710
593int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) 711int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
594{ 712{
595 struct hidp_session *session, *s; 713 struct hidp_session *session, *s;
@@ -605,10 +723,38 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
605 if (!session) 723 if (!session)
606 return -ENOMEM; 724 return -ENOMEM;
607 725
608 session->input = input_allocate_device(); 726 BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
609 if (!session->input) { 727
610 kfree(session); 728 if (req->rd_size > 0) {
611 return -ENOMEM; 729 unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL);
730
731 if (!buf) {
732 kfree(session);
733 return -ENOMEM;
734 }
735
736 if (copy_from_user(buf, req->rd_data, req->rd_size)) {
737 kfree(buf);
738 kfree(session);
739 return -EFAULT;
740 }
741
742 session->hid = hid_parse_report(buf, req->rd_size);
743
744 kfree(buf);
745
746 if (!session->hid) {
747 kfree(session);
748 return -EINVAL;
749 }
750 }
751
752 if (!session->hid) {
753 session->input = input_allocate_device();
754 if (!session->input) {
755 kfree(session);
756 return -ENOMEM;
757 }
612 } 758 }
613 759
614 down_write(&hidp_session_sem); 760 down_write(&hidp_session_sem);
@@ -644,6 +790,9 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
644 if (session->input) 790 if (session->input)
645 hidp_setup_input(session, req); 791 hidp_setup_input(session, req);
646 792
793 if (session->hid)
794 hidp_setup_hid(session, req);
795
647 __hidp_link_session(session); 796 __hidp_link_session(session);
648 797
649 hidp_set_timer(session); 798 hidp_set_timer(session);
@@ -677,6 +826,9 @@ unlink:
677failed: 826failed:
678 up_write(&hidp_session_sem); 827 up_write(&hidp_session_sem);
679 828
829 if (session->hid)
830 hid_free_device(session->hid);
831
680 kfree(session->input); 832 kfree(session->input);
681 kfree(session); 833 kfree(session);
682 return err; 834 return err;
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
index a326601c8f41..343fb0566b3e 100644
--- a/net/bluetooth/hidp/hidp.h
+++ b/net/bluetooth/hidp/hidp.h
@@ -145,6 +145,8 @@ struct hidp_session {
145 145
146 struct input_dev *input; 146 struct input_dev *input;
147 147
148 struct hid_device *hid;
149
148 struct timer_list timer; 150 struct timer_list timer;
149 151
150 struct sk_buff_head ctrl_transmit; 152 struct sk_buff_head ctrl_transmit;