From e1aaadd4d8162a2c33e41dd5a72234ea4d3b014f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 17 Feb 2007 23:58:49 +0100 Subject: [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 --- net/bluetooth/hidp/core.c | 170 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 161 insertions(+), 9 deletions(-) (limited to 'net/bluetooth/hidp/core.c') 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 @@ #include #include +#include #include #include @@ -50,7 +51,7 @@ #define BT_DBG(D...) #endif -#define VERSION "1.1" +#define VERSION "1.2" static DECLARE_RWSEM(hidp_session_sem); static LIST_HEAD(hidp_session_list); @@ -124,15 +125,22 @@ static void __hidp_copy_session(struct hidp_session *session, struct hidp_connin else strncpy(ci->name, "HID Boot Device", 128); } + + if (session->hid) { + ci->vendor = session->hid->vendor; + ci->product = session->hid->product; + ci->version = session->hid->version; + strncpy(ci->name, session->hid->name, 128); + } } -static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +static inline int hidp_queue_event(struct hidp_session *session, struct input_dev *dev, + unsigned int type, unsigned int code, int value) { - struct hidp_session *session = dev->private; - struct sk_buff *skb; unsigned char newleds; + struct sk_buff *skb; - BT_DBG("input %p type %d code %d value %d", dev, type, code, value); + BT_DBG("session %p type %d code %d value %d", session, type, code, value); if (type != EV_LED) return -1; @@ -164,6 +172,21 @@ static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned i return 0; } +static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct hid_device *hid = dev->private; + struct hidp_session *session = hid->driver_data; + + return hidp_queue_event(session, dev, type, code, value); +} + +static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct hidp_session *session = dev->private; + + return hidp_queue_event(session, dev, type, code, value); +} + static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) { struct input_dev *dev = session->input; @@ -219,6 +242,42 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) input_sync(dev); } +static inline int hidp_queue_report(struct hidp_session *session, unsigned char *data, int size) +{ + struct sk_buff *skb; + + BT_DBG("session %p hid %p data %p size %d", session, device, data, size); + + if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) { + BT_ERR("Can't allocate memory for new frame"); + return -ENOMEM; + } + + *skb_put(skb, 1) = 0xa2; + if (size > 0) + memcpy(skb_put(skb, size), data, size); + + skb_queue_tail(&session->intr_transmit, skb); + + hidp_schedule(session); + + return 0; +} + +static int hidp_send_report(struct hidp_session *session, struct hid_report *report) +{ + unsigned char buf[32]; + int rsize; + + rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); + if (rsize > sizeof(buf)) + return -EIO; + + hid_output_report(report, buf); + + return hidp_queue_report(session, buf, rsize); +} + static void hidp_idle_timeout(unsigned long arg) { 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 if (session->input) hidp_input_report(session, skb); + + if (session->hid) + hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); + break; case HIDP_DATA_RTYPE_OTHER: @@ -404,8 +467,14 @@ static inline void hidp_recv_intr_frame(struct hidp_session *session, struct sk_ if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { hidp_set_timer(session); + if (session->input) hidp_input_report(session, skb); + + if (session->hid) { + hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1); + BT_DBG("report len %d", skb->len); + } } else { BT_DBG("Unsupported protocol header 0x%02x", hdr); } @@ -471,6 +540,11 @@ static int hidp_session(void *arg) product = session->input->id.product; } + if (session->hid) { + vendor = session->hid->vendor; + product = session->hid->product; + } + daemonize("khidpd_%04x%04x", vendor, product); set_user_nice(current, -15); current->flags |= PF_NOFREEZE; @@ -521,6 +595,12 @@ static int hidp_session(void *arg) session->input = NULL; } + if (session->hid) { + if (session->hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(session->hid); + hid_free_device(session->hid); + } + up_write(&hidp_session_sem); kfree(session); @@ -590,6 +670,44 @@ static inline void hidp_setup_input(struct hidp_session *session, struct hidp_co input_register_device(input); } +static inline void hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) +{ + struct hid_device *hid = session->hid; + struct hid_report *report; + bdaddr_t src, dst; + + baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); + baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); + + hid->driver_data = session; + + hid->country = req->country; + + hid->bus = BUS_BLUETOOTH; + hid->vendor = req->vendor; + hid->product = req->product; + hid->version = req->version; + + strncpy(hid->name, req->name, 128); + strncpy(hid->phys, batostr(&src), 64); + strncpy(hid->uniq, batostr(&dst), 64); + + hid->dev = hidp_get_device(session); + + hid->hidinput_input_event = hidp_hidinput_event; + + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) + hidp_send_report(session, report); + + list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + hidp_send_report(session, report); + + if (hidinput_connect(hid) == 0) { + hid->claimed |= HID_CLAIMED_INPUT; + hid_ff_init(hid); + } +} + int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) { struct hidp_session *session, *s; @@ -605,10 +723,38 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, if (!session) return -ENOMEM; - session->input = input_allocate_device(); - if (!session->input) { - kfree(session); - return -ENOMEM; + BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); + + if (req->rd_size > 0) { + unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL); + + if (!buf) { + kfree(session); + return -ENOMEM; + } + + if (copy_from_user(buf, req->rd_data, req->rd_size)) { + kfree(buf); + kfree(session); + return -EFAULT; + } + + session->hid = hid_parse_report(buf, req->rd_size); + + kfree(buf); + + if (!session->hid) { + kfree(session); + return -EINVAL; + } + } + + if (!session->hid) { + session->input = input_allocate_device(); + if (!session->input) { + kfree(session); + return -ENOMEM; + } } down_write(&hidp_session_sem); @@ -644,6 +790,9 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, if (session->input) hidp_setup_input(session, req); + if (session->hid) + hidp_setup_hid(session, req); + __hidp_link_session(session); hidp_set_timer(session); @@ -677,6 +826,9 @@ unlink: failed: up_write(&hidp_session_sem); + if (session->hid) + hid_free_device(session->hid); + kfree(session->input); kfree(session); return err; -- cgit v1.2.3 From f5ffd4620aba9e55656483ae1ef5c79ba81f5403 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 17 Feb 2007 23:58:53 +0100 Subject: [Bluetooth] Add open and close callbacks for HID device The open and close callbacks for the HID device are not optional, but for the Bluetooth HID report mode support it is enough to add empty dummy callbacks. Signed-off-by: Marcel Holtmann --- net/bluetooth/hidp/core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'net/bluetooth/hidp/core.c') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 05e23bbcb0a1..4c914df5fd06 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -670,6 +670,15 @@ static inline void hidp_setup_input(struct hidp_session *session, struct hidp_co input_register_device(input); } +static int hidp_open(struct hid_device *hid) +{ + return 0; +} + +static void hidp_close(struct hid_device *hid) +{ +} + static inline void hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) { struct hid_device *hid = session->hid; @@ -694,6 +703,9 @@ static inline void hidp_setup_hid(struct hidp_session *session, struct hidp_conn hid->dev = hidp_get_device(session); + hid->hid_open = hidp_open; + hid->hid_close = hidp_close; + hid->hidinput_input_event = hidp_hidinput_event; list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) -- cgit v1.2.3 From b6f99a211957910a07437f46ce54dcfb1755cf84 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Thu, 22 Mar 2007 12:27:49 -0700 Subject: [NET]: fix up misplaced inlines. Turning up the warnings on gcc makes it emit warnings about the placement of 'inline' in function declarations. Here's everything that was under net/ Signed-off-by: Dave Jones Signed-off-by: David S. Miller --- net/bluetooth/hidp/core.c | 2 +- net/bridge/br_netfilter.c | 2 +- net/core/sock.c | 2 +- net/ipv6/addrconf.c | 6 +++--- net/ipv6/route.c | 4 ++-- net/ipv6/xfrm6_tunnel.c | 4 ++-- net/sched/cls_route.c | 2 +- net/xfrm/xfrm_user.c | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) (limited to 'net/bluetooth/hidp/core.c') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 4c914df5fd06..ecfe8da1ce6b 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -319,7 +319,7 @@ static int __hidp_send_ctrl_message(struct hidp_session *session, return 0; } -static int inline hidp_send_ctrl_message(struct hidp_session *session, +static inline int hidp_send_ctrl_message(struct hidp_session *session, unsigned char hdr, unsigned char *data, int size) { int err; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 7712d76f06ba..5439a3c46c3e 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -61,7 +61,7 @@ static int brnf_filter_vlan_tagged __read_mostly = 1; #define brnf_filter_vlan_tagged 1 #endif -static __be16 inline vlan_proto(const struct sk_buff *skb) +static inline __be16 vlan_proto(const struct sk_buff *skb) { return vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; } diff --git a/net/core/sock.c b/net/core/sock.c index 8d65d6478dcd..27c4f62382bd 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -808,7 +808,7 @@ lenout: * * (We also register the sk_lock with the lock validator.) */ -static void inline sock_lock_init(struct sock *sk) +static inline void sock_lock_init(struct sock *sk) { sock_lock_init_class_and_name(sk, af_family_slock_key_strings[sk->sk_family], diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index a7fee6b27320..1b616992d916 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -804,7 +804,7 @@ struct ipv6_saddr_score { #define IPV6_SADDR_SCORE_LABEL 0x0020 #define IPV6_SADDR_SCORE_PRIVACY 0x0040 -static int inline ipv6_saddr_preferred(int type) +static inline int ipv6_saddr_preferred(int type) { if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4| IPV6_ADDR_LOOPBACK|IPV6_ADDR_RESERVED)) @@ -813,7 +813,7 @@ static int inline ipv6_saddr_preferred(int type) } /* static matching label */ -static int inline ipv6_saddr_label(const struct in6_addr *addr, int type) +static inline int ipv6_saddr_label(const struct in6_addr *addr, int type) { /* * prefix (longest match) label @@ -3318,7 +3318,7 @@ errout: rtnl_set_sk_err(RTNLGRP_IPV6_IFADDR, err); } -static void inline ipv6_store_devconf(struct ipv6_devconf *cnf, +static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, __s32 *array, int bytes) { BUG_ON(bytes < (DEVCONF_MAX * 4)); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 0e1f4b2cd3dd..a6b3117df546 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -308,7 +308,7 @@ static inline void rt6_probe(struct rt6_info *rt) /* * Default Router Selection (RFC 2461 6.3.6) */ -static int inline rt6_check_dev(struct rt6_info *rt, int oif) +static inline int rt6_check_dev(struct rt6_info *rt, int oif) { struct net_device *dev = rt->rt6i_dev; int ret = 0; @@ -328,7 +328,7 @@ static int inline rt6_check_dev(struct rt6_info *rt, int oif) return ret; } -static int inline rt6_check_neigh(struct rt6_info *rt) +static inline int rt6_check_neigh(struct rt6_info *rt) { struct neighbour *neigh = rt->rt6i_nexthop; int m = 0; diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index ee4b84a33ff4..93c42232aa39 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -58,7 +58,7 @@ static struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly; static struct hlist_head xfrm6_tunnel_spi_byaddr[XFRM6_TUNNEL_SPI_BYADDR_HSIZE]; static struct hlist_head xfrm6_tunnel_spi_byspi[XFRM6_TUNNEL_SPI_BYSPI_HSIZE]; -static unsigned inline xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr) +static inline unsigned xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr) { unsigned h; @@ -70,7 +70,7 @@ static unsigned inline xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr) return h; } -static unsigned inline xfrm6_tunnel_spi_hash_byspi(u32 spi) +static inline unsigned xfrm6_tunnel_spi_hash_byspi(u32 spi) { return spi % XFRM6_TUNNEL_SPI_BYSPI_HSIZE; } diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index e85df07d8ce7..abc47cc48ad0 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -93,7 +93,7 @@ void route4_reset_fastmap(struct net_device *dev, struct route4_head *head, u32 spin_unlock_bh(&dev->queue_lock); } -static void __inline__ +static inline void route4_set_fastmap(struct route4_head *head, u32 id, int iif, struct route4_filter *f) { diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 96789952f6a3..e81e2fb3d429 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2025,7 +2025,7 @@ nlmsg_failure: return -1; } -static int inline xfrm_sa_len(struct xfrm_state *x) +static inline int xfrm_sa_len(struct xfrm_state *x) { int l = 0; if (x->aalg) -- cgit v1.2.3 From cb3fecc2f29056e89658e7eb371e7f9be66cda6d Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 29 Mar 2007 01:20:25 -0700 Subject: [PATCH] bluetooth hid quirks: mightymouse quirk I have a bugreport that scrollwheel of bluetooth version of apple mightymouse doesn't work. The USB version of mightymouse works, as there is a quirk for handling scrollwheel in hid/usbhid for it. Now that bluetooth git tree is hooked to generic hid layer, it could easily use the quirks which are already present in generic hid parser, hid-input, etc. Below is a simple patch against bluetooth git tree, which adds quirk handling to current bluetooth hidp code, and sets quirk flags for device 0x05ac/0x030c, which is the bluetooth version of the apple mightymouse. Signed-off-by: Jiri Kosina Signed-off-by: Marcel Holtmann Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- net/bluetooth/hidp/core.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'net/bluetooth/hidp/core.c') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index ecfe8da1ce6b..d342e89b8bdd 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -679,6 +679,27 @@ static void hidp_close(struct hid_device *hid) { } +static const struct { + __u16 idVendor; + __u16 idProduct; + unsigned quirks; +} hidp_blacklist[] = { + /* Apple wireless Mighty Mouse */ + { 0x05ac, 0x030c, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, + + { } /* Terminating entry */ +}; + +static void hidp_setup_quirks(struct hid_device *hid) +{ + unsigned int n; + + for (n = 0; hidp_blacklist[n].idVendor; n++) + if (hidp_blacklist[n].idVendor == le16_to_cpu(hid->vendor) && + hidp_blacklist[n].idProduct == le16_to_cpu(hid->product)) + hid->quirks = hidp_blacklist[n].quirks; +} + static inline void hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) { struct hid_device *hid = session->hid; @@ -708,6 +729,8 @@ static inline void hidp_setup_hid(struct hidp_session *session, struct hidp_conn hid->hidinput_input_event = hidp_hidinput_event; + hidp_setup_quirks(hid); + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) hidp_send_report(session, report); -- cgit v1.2.3