public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
From: Marcel Holtmann <marcel@holtmann.org>
To: BlueZ Mailing List <bluez-devel@lists.sourceforge.net>
Subject: [Bluez-devel] HIDP backport for 2.4
Date: Sat, 17 Jul 2004 10:58:59 +0200	[thread overview]
Message-ID: <1090054739.4558.2.camel@pegasus> (raw)

[-- Attachment #1: Type: text/plain, Size: 275 bytes --]

Hi Folks,

attached is a backport patch of the HIDP support from 2.6.8-rc1 for the
2.4 kernel series. I didn't tested this patch with any of my systems. I
only know that it compiles clean. So test this with the hidd daemon from
the bluez-utils-2.8 package.

Regards

Marcel


[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 30330 bytes --]

===== CREDITS 1.94 vs edited =====
--- 1.94/CREDITS	2004-04-01 09:03:37 +02:00
+++ edited/CREDITS	2004-07-15 20:59:55 +02:00
@@ -1348,6 +1348,7 @@
 D: Maintainer of the Linux Bluetooth Subsystem
 D: Author and maintainer of the various Bluetooth HCI drivers
 D: Author and maintainer of the CAPI message transport protocol driver
+D: Author and maintainer of the Bluetooth HID protocol driver
 D: Various other Bluetooth related patches, cleanups and fixes
 S: Germany
 
===== MAINTAINERS 1.139 vs edited =====
--- 1.139/MAINTAINERS	2004-06-20 23:04:08 +02:00
+++ edited/MAINTAINERS	2004-07-15 20:59:40 +02:00
@@ -357,6 +357,11 @@
 M:	marcel@holtmann.org
 S:	Maintained
 
+BLUETOOTH HIDP LAYER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
 BLUETOOTH HCI UART DRIVER
 P:	Marcel Holtmann
 M:	marcel@holtmann.org
===== Documentation/Configure.help 1.258 vs edited =====
--- 1.258/Documentation/Configure.help	2004-07-13 06:59:48 +02:00
+++ edited/Documentation/Configure.help	2004-07-15 20:58:54 +02:00
@@ -23158,6 +23158,7 @@
                RFCOMM Module (RFCOMM Protocol)
                BNEP Module (Bluetooth Network Encapsulation Protocol)
                CMTP Module (CAPI Message Transport Protocol)
+               HIDP Module (Human Interface Device Protocol)
 
   Say Y here to compile Bluetooth support into the kernel or say M to
   compile it as module (bluez.o).
@@ -23222,6 +23223,15 @@
 
   Say Y here to compile CMTP support into the kernel or say M to
   compile it as module (cmtp.o).
+
+HIDP protocol support
+CONFIG_BLUEZ_HIDP
+  HIDP (Human Interface Device Protocol) is a transport layer
+  for HID reports.  HIDP is required for the Bluetooth Human
+  Interface Device Profile.
+
+  Say Y here to compile HIDP support into the kernel or say M to
+  compile it as module (hidp.o).
 
 HCI UART driver
 CONFIG_BLUEZ_HCIUART
===== arch/sparc64/kernel/ioctl32.c 1.48 vs edited =====
--- 1.48/arch/sparc64/kernel/ioctl32.c	2004-04-27 20:45:56 +02:00
+++ edited/arch/sparc64/kernel/ioctl32.c	2004-07-16 11:54:50 +02:00
@@ -4322,6 +4322,11 @@
 #define CMTPGETCONNLIST	_IOR('C', 210, int)
 #define CMTPGETCONNINFO	_IOR('C', 211, int)
 
+#define HIDPCONNADD	_IOW('H', 200, int)
+#define HIDPCONNDEL	_IOW('H', 201, int)
+#define HIDPGETCONNLIST	_IOR('H', 210, int)
+#define HIDPGETCONNINFO	_IOR('H', 211, int)
+
 struct ioctl_trans {
 	unsigned int cmd;
 	unsigned int handler;
@@ -5050,6 +5055,10 @@
 COMPATIBLE_IOCTL(CMTPCONNDEL)
 COMPATIBLE_IOCTL(CMTPGETCONNLIST)
 COMPATIBLE_IOCTL(CMTPGETCONNINFO)
+COMPATIBLE_IOCTL(HIDPCONNADD)
+COMPATIBLE_IOCTL(HIDPCONNDEL)
+COMPATIBLE_IOCTL(HIDPGETCONNLIST)
+COMPATIBLE_IOCTL(HIDPGETCONNINFO)
 /* Scanner */
 COMPATIBLE_IOCTL(SCANNER_IOCTL_VENDOR)
 COMPATIBLE_IOCTL(SCANNER_IOCTL_PRODUCT)
===== net/bluetooth/Config.in 1.6 vs edited =====
--- 1.6/net/bluetooth/Config.in	2003-06-14 00:20:26 +02:00
+++ edited/net/bluetooth/Config.in	2004-07-15 21:03:17 +02:00
@@ -14,6 +14,7 @@
       source net/bluetooth/rfcomm/Config.in
       source net/bluetooth/bnep/Config.in
       source net/bluetooth/cmtp/Config.in
+      source net/bluetooth/hidp/Config.in
       source drivers/bluetooth/Config.in
    fi
 
===== net/bluetooth/Makefile 1.7 vs edited =====
--- 1.7/net/bluetooth/Makefile	2003-07-17 05:59:48 +02:00
+++ edited/net/bluetooth/Makefile	2004-07-15 21:04:23 +02:00
@@ -16,6 +16,7 @@
 subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
 subdir-$(CONFIG_BLUEZ_BNEP) += bnep
 subdir-$(CONFIG_BLUEZ_CMTP) += cmtp
+subdir-$(CONFIG_BLUEZ_HIDP) += hidp
 
 ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
 obj-y += rfcomm/rfcomm.o
@@ -23,6 +24,14 @@
 
 ifeq ($(CONFIG_BLUEZ_BNEP),y)
 obj-y += bnep/bnep.o
+endif
+
+ifeq ($(CONFIG_BLUEZ_CMTP),y)
+obj-y += cmtp/cmtp.o
+endif
+
+ifeq ($(CONFIG_BLUEZ_HIDP),y)
+obj-y += hidp/hidp.o
 endif
 
 include $(TOPDIR)/Rules.make
===== net/bluetooth/hidp/Config.in 1.1 vs edited =====
--- 1.1/net/bluetooth/hidp/Config.in	2004-07-15 21:01:05 +02:00
+++ edited/net/bluetooth/hidp/Config.in	2004-07-15 21:01:46 +02:00
@@ -0,0 +1,5 @@
+#
+# Bluetooth HIDP layer configuration
+#
+
+dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP
===== net/bluetooth/hidp/Makefile 1.1 vs edited =====
--- 1.1/net/bluetooth/hidp/Makefile	2004-07-15 21:01:09 +02:00
+++ edited/net/bluetooth/hidp/Makefile	2004-07-15 21:02:37 +02:00
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux Bluetooth HIDP layer
+#
+
+O_TARGET := hidp.o
+
+obj-y	:= core.o sock.o
+obj-m	+= $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
===== net/bluetooth/hidp/core.c 1.1 vs edited =====
--- 1.1/net/bluetooth/hidp/core.c	2004-07-15 21:00:37 +02:00
+++ edited/net/bluetooth/hidp/core.c	2004-07-16 11:47:17 +02:00
@@ -0,0 +1,653 @@
+/* 
+   HIDP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
+   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
+   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 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+#include <linux/input.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "hidp.h"
+
+#ifndef CONFIG_BT_HIDP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.0"
+
+static DECLARE_RWSEM(hidp_session_sem);
+static LIST_HEAD(hidp_session_list);
+
+static unsigned char hidp_keycode[256] = {
+	  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
+};
+
+static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
+{
+	struct hidp_session *session;
+	struct list_head *p;
+
+	BT_DBG("");
+
+	list_for_each(p, &hidp_session_list) {
+		session = list_entry(p, struct hidp_session, list);
+		if (!bacmp(bdaddr, &session->bdaddr))
+			return session;
+	}
+	return NULL;
+}
+
+static void __hidp_link_session(struct hidp_session *session)
+{
+	MOD_INC_USE_COUNT;
+	list_add(&session->list, &hidp_session_list);
+}
+
+static void __hidp_unlink_session(struct hidp_session *session)
+{
+	list_del(&session->list);
+	MOD_DEC_USE_COUNT;
+}
+
+static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
+{
+	bacpy(&ci->bdaddr, &session->bdaddr);
+
+	ci->flags = session->flags;
+	ci->state = session->state;
+
+	ci->vendor  = 0x0000;
+	ci->product = 0x0000;
+	ci->version = 0x0000;
+	memset(ci->name, 0, 128);
+
+	if (session->input) {
+		ci->vendor  = session->input->idvendor;
+		ci->product = session->input->idproduct;
+		ci->version = session->input->idversion;
+		if (session->input->name)
+			strncpy(ci->name, session->input->name, 128);
+		else
+			strncpy(ci->name, "HID Boot Device", 128);
+	}
+}
+
+static int hidp_input_event(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;
+
+	BT_DBG("session %p hid %p data %p size %d", session, device, data, size);
+
+	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) |
+		  (!!test_bit(LED_NUML,    dev->led));
+
+	if (session->leds == newleds)
+		return 0;
+
+	session->leds = newleds;
+
+	if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate memory for new frame");
+		return -ENOMEM;
+	}
+
+	*skb_put(skb, 1) = 0xa2;
+	*skb_put(skb, 1) = 0x01;
+	*skb_put(skb, 1) = newleds;
+
+	skb_queue_tail(&session->intr_transmit, skb);
+
+	hidp_schedule(session);
+
+	return 0;
+}
+
+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);
+
+		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;
+	}
+}
+
+static void hidp_idle_timeout(unsigned long arg)
+{
+	struct hidp_session *session = (struct hidp_session *) arg;
+
+	atomic_inc(&session->terminate);
+	hidp_schedule(session);
+}
+
+static inline void hidp_set_timer(struct hidp_session *session)
+{
+	if (session->idle_to > 0)
+		mod_timer(&session->timer, jiffies + HZ * session->idle_to);
+}
+
+static inline void hidp_del_timer(struct hidp_session *session)
+{
+	if (session->idle_to > 0)
+		del_timer(&session->timer);
+}
+
+static inline void hidp_send_message(struct hidp_session *session, unsigned char hdr)
+{
+	struct sk_buff *skb;
+
+	BT_DBG("session %p", session);
+
+	if (!(skb = alloc_skb(1, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate memory for message");
+		return;
+	}
+
+	*skb_put(skb, 1) = hdr;
+
+	skb_queue_tail(&session->ctrl_transmit, skb);
+
+	hidp_schedule(session);
+}
+
+static inline int hidp_recv_frame(struct hidp_session *session, struct sk_buff *skb)
+{
+	__u8 hdr;
+
+	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+	hdr = skb->data[0];
+	skb_pull(skb, 1);
+
+	if (hdr == 0xa1) {
+		hidp_set_timer(session);
+
+		if (session->input)
+			hidp_input_report(session, skb);
+	} else {
+		BT_DBG("Unsupported protocol header 0x%02x", hdr);
+	}
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
+{
+	struct iovec 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));
+	msg.msg_iovlen = 1;
+	msg.msg_iov = &iv;
+
+	return sock_sendmsg(sock, &msg, len);
+}
+
+static int hidp_process_transmit(struct hidp_session *session)
+{
+	struct sk_buff *skb;
+
+	BT_DBG("session %p", session);
+
+	while ((skb = skb_dequeue(&session->ctrl_transmit))) {
+		if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
+			skb_queue_head(&session->ctrl_transmit, skb);
+			break;
+		}
+
+		hidp_set_timer(session);
+		kfree_skb(skb);
+	}
+
+	while ((skb = skb_dequeue(&session->intr_transmit))) {
+		if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
+			skb_queue_head(&session->intr_transmit, skb);
+			break;
+		}
+
+		hidp_set_timer(session);
+		kfree_skb(skb);
+	}
+
+	return skb_queue_len(&session->ctrl_transmit) +
+				skb_queue_len(&session->intr_transmit);
+}
+
+static int hidp_session(void *arg)
+{
+	struct hidp_session *session = arg;
+	struct sock *ctrl_sk = session->ctrl_sock->sk;
+	struct sock *intr_sk = session->intr_sock->sk;
+	struct sk_buff *skb;
+	int vendor = 0x0000, product = 0x0000;
+	wait_queue_t ctrl_wait, intr_wait;
+	unsigned long timeo = HZ;
+
+	BT_DBG("session %p", session);
+
+	if (session->input) {
+		vendor  = session->input->idvendor;
+		product = session->input->idproduct;
+	}
+
+	daemonize(); reparent_to_init();
+
+	sprintf(current->comm, "khidpd_%04x%04x", vendor, product);
+
+	sigfillset(&current->blocked);
+	flush_signals(current);
+
+	current->nice = -15;
+
+	set_fs(KERNEL_DS);
+
+	init_waitqueue_entry(&ctrl_wait, current);
+	init_waitqueue_entry(&intr_wait, current);
+	add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
+	add_wait_queue(intr_sk->sleep, &intr_wait);
+	while (!atomic_read(&session->terminate)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (ctrl_sk->state != BT_CONNECTED || intr_sk->state != BT_CONNECTED)
+			break;
+
+		while ((skb = skb_dequeue(&ctrl_sk->receive_queue))) {
+			skb_orphan(skb);
+			hidp_recv_frame(session, skb);
+		}
+
+		while ((skb = skb_dequeue(&intr_sk->receive_queue))) {
+			skb_orphan(skb);
+			hidp_recv_frame(session, skb);
+		}
+
+		hidp_process_transmit(session);
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(intr_sk->sleep, &intr_wait);
+	remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
+
+	down_write(&hidp_session_sem);
+
+	hidp_del_timer(session);
+
+	if (intr_sk->state != BT_CONNECTED) {
+		init_waitqueue_entry(&ctrl_wait, current);
+		add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
+		while (timeo && ctrl_sk->state != BT_CLOSED) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			timeo = schedule_timeout(timeo);
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
+		timeo = HZ;
+	}
+
+	fput(session->ctrl_sock->file);
+
+	init_waitqueue_entry(&intr_wait, current);
+	add_wait_queue(intr_sk->sleep, &intr_wait);
+	while (timeo && intr_sk->state != BT_CLOSED) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		timeo = schedule_timeout(timeo);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(intr_sk->sleep, &intr_wait);
+
+	fput(session->intr_sock->file);
+
+	__hidp_unlink_session(session);
+
+	if (session->input) {
+		input_unregister_device(session->input);
+		kfree(session->input);
+	}
+
+	up_write(&hidp_session_sem);
+
+	kfree(session);
+	return 0;
+}
+
+static inline void hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req)
+{
+	struct input_dev *input = session->input;
+	int i;
+
+	input->private = session;
+
+	input->idbus     = BUS_BLUETOOTH;
+	input->idvendor  = req->vendor;
+	input->idproduct = req->product;
+	input->idversion = 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) {
+		input->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+		input->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+		input->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+		input->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+		input->relbit[0] |= BIT(REL_WHEEL);
+	}
+
+	input->event = hidp_input_event;
+
+	input_register_device(input);
+}
+
+int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
+{
+	struct hidp_session *session, *s;
+	int err;
+
+	BT_DBG("");
+
+	if (bacmp(&bluez_pi(ctrl_sock->sk)->src, &bluez_pi(intr_sock->sk)->src) ||
+			bacmp(&bluez_pi(ctrl_sock->sk)->dst, &bluez_pi(intr_sock->sk)->dst))
+		return -ENOTUNIQ;
+
+	session = kmalloc(sizeof(struct hidp_session), GFP_KERNEL);
+	if (!session) 
+		return -ENOMEM;
+	memset(session, 0, sizeof(struct hidp_session));
+
+	session->input = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
+	if (!session->input) {
+		kfree(session);
+		return -ENOMEM;
+	}
+	memset(session->input, 0, sizeof(struct input_dev));
+
+	down_write(&hidp_session_sem);
+
+	s = __hidp_get_session(&bluez_pi(ctrl_sock->sk)->dst);
+	if (s && s->state == BT_CONNECTED) {
+		err = -EEXIST;
+		goto failed;
+	}
+
+	bacpy(&session->bdaddr, &bluez_pi(ctrl_sock->sk)->dst);
+
+	session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
+	session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
+
+	BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
+
+	session->ctrl_sock = ctrl_sock;
+	session->intr_sock = intr_sock;
+	session->state     = BT_CONNECTED;
+
+	init_timer(&session->timer);
+
+	session->timer.function = hidp_idle_timeout;
+	session->timer.data     = (unsigned long) session;
+
+	skb_queue_head_init(&session->ctrl_transmit);
+	skb_queue_head_init(&session->intr_transmit);
+
+	session->flags   = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
+	session->idle_to = req->idle_to;
+
+	if (session->input)
+		hidp_setup_input(session, req);
+
+	__hidp_link_session(session);
+
+	hidp_set_timer(session);
+
+	err = kernel_thread(hidp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	if (err < 0)
+		goto unlink;
+
+	if (session->input) {
+		hidp_send_message(session, 0x70);
+		session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
+
+		session->leds = 0xff;
+		hidp_input_event(session->input, EV_LED, 0, 0);
+	}
+
+	up_write(&hidp_session_sem);
+	return 0;
+
+unlink:
+	hidp_del_timer(session);
+
+	__hidp_unlink_session(session);
+
+	if (session->input)
+		input_unregister_device(session->input);
+
+failed:
+	up_write(&hidp_session_sem);
+
+	if (session->input)
+		kfree(session->input);
+
+	kfree(session);
+	return err;
+}
+
+int hidp_del_connection(struct hidp_conndel_req *req)
+{
+	struct hidp_session *session;
+	int err = 0;
+
+	BT_DBG("");
+
+	down_read(&hidp_session_sem);
+
+	session = __hidp_get_session(&req->bdaddr);
+	if (session) {
+		if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
+			hidp_send_message(session, 0x15);
+		} else {
+			/* Flush the transmit queues */
+			skb_queue_purge(&session->ctrl_transmit);
+			skb_queue_purge(&session->intr_transmit);
+
+			/* Kill session thread */
+			atomic_inc(&session->terminate);
+			hidp_schedule(session);
+		}
+	} else
+		err = -ENOENT;
+
+	up_read(&hidp_session_sem);
+	return err;
+}
+
+int hidp_get_connlist(struct hidp_connlist_req *req)
+{
+	struct list_head *p;
+	int err = 0, n = 0;
+
+	BT_DBG("");
+
+	down_read(&hidp_session_sem);
+
+	list_for_each(p, &hidp_session_list) {
+		struct hidp_session *session;
+		struct hidp_conninfo ci;
+
+		session = list_entry(p, struct hidp_session, list);
+
+		__hidp_copy_session(session, &ci);
+
+		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;
+	int err = 0;
+
+	down_read(&hidp_session_sem);
+
+	session = __hidp_get_session(&ci->bdaddr);
+	if (session)
+		__hidp_copy_session(session, ci);
+	else
+		err = -ENOENT;
+
+	up_read(&hidp_session_sem);
+	return err;
+}
+
+static int __init hidp_init(void)
+{
+	l2cap_load();
+
+	hidp_init_sockets();
+
+	BT_INFO("BlueZ HIDP ver %s", VERSION);
+	BT_INFO("Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>");
+
+	return 0;
+}
+
+static void __exit hidp_exit(void)
+{
+	hidp_cleanup_sockets();
+}
+
+module_init(hidp_init);
+module_exit(hidp_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
+MODULE_LICENSE("GPL");
===== net/bluetooth/hidp/hidp.h 1.1 vs edited =====
--- 1.1/net/bluetooth/hidp/hidp.h	2004-07-16 11:24:21 +02:00
+++ edited/net/bluetooth/hidp/hidp.h	2004-07-16 11:29:15 +02:00
@@ -0,0 +1,122 @@
+/* 
+   HIDP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
+   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
+   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 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#ifndef __HIDP_H
+#define __HIDP_H
+
+#include <linux/types.h>
+#include <net/bluetooth/bluetooth.h>
+
+/* HIDP ioctl defines */
+#define HIDPCONNADD	_IOW('H', 200, int)
+#define HIDPCONNDEL	_IOW('H', 201, int)
+#define HIDPGETCONNLIST	_IOR('H', 210, int)
+#define HIDPGETCONNINFO	_IOR('H', 211, int)
+
+#define HIDP_VIRTUAL_CABLE_UNPLUG	0
+#define HIDP_BOOT_PROTOCOL_MODE		1
+#define HIDP_BLUETOOTH_VENDOR_ID	9
+
+struct hidp_connadd_req {
+	int   ctrl_sock;	// Connected control socket
+	int   intr_sock;	// Connteted interrupt socket
+	__u16 parser;
+	__u16 rd_size;
+	__u8 *rd_data;
+	__u8  country;
+	__u8  subclass;
+	__u16 vendor;
+	__u16 product;
+	__u16 version;
+	__u32 flags;
+	__u32 idle_to;
+	char  name[128];
+};
+
+struct hidp_conndel_req {
+	bdaddr_t bdaddr;
+	__u32    flags;
+};
+
+struct hidp_conninfo {
+	bdaddr_t bdaddr;
+	__u32    flags;
+	__u16    state;
+	__u16    vendor;
+	__u16    product;
+	__u16    version;
+	char     name[128];
+};
+
+struct hidp_connlist_req {
+	__u32  cnum;
+	struct hidp_conninfo __user *ci;
+};
+
+int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock);
+int hidp_del_connection(struct hidp_conndel_req *req);
+int hidp_get_connlist(struct hidp_connlist_req *req);
+int hidp_get_conninfo(struct hidp_conninfo *ci);
+
+/* HIDP session defines */
+struct hidp_session {
+	struct list_head list;
+
+	struct socket *ctrl_sock;
+	struct socket *intr_sock;
+
+	bdaddr_t bdaddr;
+
+	unsigned long state;
+	unsigned long flags;
+	unsigned long idle_to;
+
+	uint ctrl_mtu;
+	uint intr_mtu;
+
+	atomic_t terminate;
+
+	unsigned char keys[8];
+	unsigned char leds;
+
+	struct input_dev *input;
+
+	struct timer_list timer;
+
+	struct sk_buff_head ctrl_transmit;
+	struct sk_buff_head intr_transmit;
+};
+
+static inline void hidp_schedule(struct hidp_session *session)
+{
+	struct sock *ctrl_sk = session->ctrl_sock->sk;
+	struct sock *intr_sk = session->intr_sock->sk;
+
+	wake_up_interruptible(ctrl_sk->sleep);
+	wake_up_interruptible(intr_sk->sleep);
+}
+
+/* HIDP init defines */
+extern int __init hidp_init_sockets(void);
+extern void __exit hidp_cleanup_sockets(void);
+
+#endif /* __HIDP_H */
===== net/bluetooth/hidp/sock.c 1.1 vs edited =====
--- 1.1/net/bluetooth/hidp/sock.c	2004-07-15 21:00:42 +02:00
+++ edited/net/bluetooth/hidp/sock.c	2004-07-16 12:12:48 +02:00
@@ -0,0 +1,212 @@
+/* 
+   HIDP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
+   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
+   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 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+#include "hidp.h"
+
+#ifndef CONFIG_BT_HIDP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+static int hidp_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock_orphan(sk);
+	sock_put(sk);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct hidp_connadd_req ca;
+	struct hidp_conndel_req cd;
+	struct hidp_connlist_req cl;
+	struct hidp_conninfo ci;
+	struct socket *csock;
+	struct socket *isock;
+	int err;
+
+	BT_DBG("cmd %x arg %lx", cmd, arg);
+
+	switch (cmd) {
+	case HIDPCONNADD:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
+			return -EFAULT;
+
+		csock = sockfd_lookup(ca.ctrl_sock, &err);
+		if (!csock)
+			return err;
+
+		isock = sockfd_lookup(ca.intr_sock, &err);
+		if (!isock) {
+			fput(csock->file);
+			return err;
+		}
+
+		if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) {
+			fput(csock->file);
+			fput(isock->file);
+			return -EBADFD;
+		}
+
+		err = hidp_add_connection(&ca, csock, isock);
+		if (!err) {
+			if (copy_to_user((void *) arg, &ca, sizeof(ca)))
+				err = -EFAULT;
+		} else {
+			fput(csock->file);
+			fput(isock->file);
+		}
+
+		return err;
+
+	case HIDPCONNDEL:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
+			return -EFAULT;
+
+		return hidp_del_connection(&cd);
+
+	case HIDPGETCONNLIST:
+		if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
+			return -EFAULT;
+
+		if (cl.cnum <= 0)
+			return -EINVAL;
+
+		err = hidp_get_connlist(&cl);
+		if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
+			return -EFAULT;
+
+		return err;
+
+	case HIDPGETCONNINFO:
+		if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
+			return -EFAULT;
+
+		err = hidp_get_conninfo(&ci);
+		if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
+			return -EFAULT;
+
+		return err;
+	}
+
+	return -EINVAL;
+}
+
+static struct proto_ops hidp_sock_ops = {
+	family:		PF_BLUETOOTH,
+	release:	hidp_sock_release,
+	ioctl:		hidp_sock_ioctl,
+	bind:		sock_no_bind,
+	getname:	sock_no_getname,
+	sendmsg:	sock_no_sendmsg,
+	recvmsg:	sock_no_recvmsg,
+	poll:		sock_no_poll,
+	listen:		sock_no_listen,
+	shutdown:	sock_no_shutdown,
+	setsockopt:	sock_no_setsockopt,
+	getsockopt:	sock_no_getsockopt,
+	connect:	sock_no_connect,
+	socketpair:	sock_no_socketpair,
+	accept:		sock_no_accept,
+	mmap:		sock_no_mmap
+};
+
+static int hidp_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	if (sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &hidp_sock_ops;
+
+	if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
+		return -ENOMEM;
+
+	MOD_INC_USE_COUNT;
+
+	sock->state = SS_UNCONNECTED;
+	sock_init_data(sock, sk);
+
+	sk->destruct = NULL;
+	sk->protocol = protocol;
+
+	return 0;
+}
+
+static struct net_proto_family hidp_sock_family_ops = {
+	family:		PF_BLUETOOTH,
+	create:		hidp_sock_create
+};
+
+int __init hidp_init_sockets(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops)))
+		BT_ERR("Can't register HIDP socket layer (%d)", err);
+
+	return err;
+}
+
+void __exit hidp_cleanup_sockets(void)
+{
+	int err;
+
+	if ((err = bluez_sock_unregister(BTPROTO_HIDP)))
+		BT_ERR("Can't unregister HIDP socket layer (%d)", err);
+}

             reply	other threads:[~2004-07-17  8:58 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-07-17  8:58 Marcel Holtmann [this message]
2004-07-25 12:52 ` [Bluez-devel] HIDP backport for 2.4 Marcel Holtmann
2004-07-25 16:57   ` Michael Frey
2004-07-26 17:24   ` Michael Frey
2004-07-26 17:47     ` Marcel Holtmann
2004-07-26 18:05       ` Michael Frey
2004-07-26 18:16         ` Marcel Holtmann
2004-07-26 18:49           ` Michael Frey
2004-07-26 21:01             ` Marcel Holtmann
2004-07-27  0:31               ` Michael Frey
2004-07-27  8:18                 ` Marcel Holtmann
2004-07-27 14:53                   ` Michael Frey
2004-07-27 15:01                     ` Marcel Holtmann
2004-07-27 15:14                       ` Michael Frey
2004-07-27 15:24                         ` Marcel Holtmann
     [not found]                           ` <86089F56-DFE5-11D8-BE76-00039390D626@pepper.com>
2004-08-01 15:57                             ` Marcel Holtmann
2004-08-11 21:19                               ` Michael Frey
2004-08-11 22:11                                 ` Marcel Holtmann
2004-08-13 17:27                                   ` Michael Frey
2004-08-13 17:53                                     ` Marcel Holtmann
2004-08-13 18:16                                       ` Michael Frey
2004-08-13 18:21                                         ` Marcel Holtmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1090054739.4558.2.camel@pegasus \
    --to=marcel@holtmann.org \
    --cc=bluez-devel@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox