public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [Bluez-users] [PATCH] fix for kernel flow control patch mh7
@ 2006-12-05 10:59 Zsolt Barat
  2006-12-06 22:00 ` Brad Midgley
  0 siblings, 1 reply; 4+ messages in thread
From: Zsolt Barat @ 2006-12-05 10:59 UTC (permalink / raw)
  To: bluez-users

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

hi list,
attached is a trivially fixed version of the flow-control-patch mh7,
which could be found on
http://bluetooth-alsa.sf.net/bluez-sco-flowcontrol-mh7.patch
to work with gentoo-suspend2-sources and linux-2.6.18-vanilla.
i don't know which kernel-version it supposed to be apply.
would be nice to mention it on the website somewhere.
i tested till 2.6.18.3 and there is always a tiny mismatch in
hci_event.c in function hci_cc_info_param:
static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct
sk_buff *skb)
{
	struct hci_rp_read_local_features *lf;
	struct hci_rp_read_buffer_size *bs;
	struct hci_rp_read_bd_addr *ba;

	BT_DBG("%s ocf 0x%x", hdev->name, ocf);

	switch (ocf) {
	case OCF_READ_LOCAL_FEATURES:
		lf = (struct hci_rp_read_local_features *) skb->data;

		if (lf->status) {
			BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
			break;
		}

the original patch wants to rename lf->status to lv->status, which was a
bug probably but in vanilla sources it's done other ways and the struct
is renamed.
so nothing serious, just that it a applies cleanly.

regards

zsolt


[-- Attachment #2: bluez-sco-flowcontrol-mh7-gentoo-suspend2.patch --]
[-- Type: text/x-patch, Size: 39134 bytes --]

diff -urN linux-2.6.18-mh7/include/net/bluetooth/hci_core.h linux-2.6.18-mh7-sco/include/net/bluetooth/hci_core.h
--- linux-2.6.18-mh7/include/net/bluetooth/hci_core.h	2006-11-02 07:45:31.000000000 -0700
+++ linux-2.6.18-mh7-sco/include/net/bluetooth/hci_core.h	2006-11-02 07:53:44.000000000 -0700
@@ -25,6 +25,8 @@
 #ifndef __HCI_CORE_H
 #define __HCI_CORE_H
 
+#include <linux/hrtimer.h>
+
 #include <net/bluetooth/hci.h>
 
 /* HCI upper protocols */
@@ -88,12 +90,18 @@
 	unsigned long	quirks;
 
 	atomic_t	cmd_cnt;
+	/* Number of available controller buffers for ACL packets */
 	unsigned int	acl_cnt;
-	unsigned int	sco_cnt;
+	/* Number of available controller buffers for SCO packets */
+	atomic_t	sco_cnt;
 
+	/* Maximum transmition unit for ACL packets */
 	unsigned int	acl_mtu;
+	/* Maximum transmition unit for SCO packets */
 	unsigned int	sco_mtu;
+	/* Maximum number of ACL packets the controller is able to buffer */
 	unsigned int	acl_pkts;
+	/* Maximum number of SCO packets the controller is able to buffer */
 	unsigned int	sco_pkts;
 
 	unsigned long	cmd_last_tx;
@@ -145,7 +153,6 @@
 	struct list_head list;
 
 	atomic_t	 refcnt;
-	spinlock_t	 lock;
 
 	bdaddr_t	 dst;
 	__u16		 handle;
@@ -162,10 +169,19 @@
 	__u8             power_save;
 	unsigned long	 pend;
 
-	unsigned int	 sent;
-
-	struct sk_buff_head data_q;
-
+	/* sent represents the number of packets this connections
+           has "on the wire" : .... oh f.... there are no wire
+           with bluetooth. By on the wire, i mean packets that have been sent
+	   to the HCI device, and that are still in its buffers */
+	atomic_t	 sent;
+
+	struct sk_buff_head out_q;
+	/* This is only used for SCO for now */
+	void (*pkt_sent_cb)(struct hci_conn *conn);
+
+	/* tx timer : used only for SCO */
+	struct hrtimer tx_timer;
+	/* Disconnect timer */
 	struct timer_list disc_timer;
 	struct timer_list idle_timer;
 
@@ -614,7 +630,10 @@
 
 int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param);
 int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
-int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
+int hci_send_sco(struct hci_conn *conn, 
+			struct sk_buff *skb, 
+			int sndbufsize, 
+			void (*send_complete_cb)(struct hci_conn *conn));
 
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf);
 
diff -urN linux-2.6.18-mh7/include/net/bluetooth/hci_core.h~ linux-2.6.18-mh7-sco/include/net/bluetooth/hci_core.h~
--- linux-2.6.18-mh7/include/net/bluetooth/hci_core.h~	1969-12-31 17:00:00.000000000 -0700
+++ linux-2.6.18-mh7-sco/include/net/bluetooth/hci_core.h~	2006-11-02 07:51:09.000000000 -0700
@@ -0,0 +1,661 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   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 __HCI_CORE_H
+#define __HCI_CORE_H
+
+#include <linux/hrtimer.h>
+
+#include <net/bluetooth/hci.h>
+
+/* HCI upper protocols */
+#define HCI_PROTO_L2CAP	0
+#define HCI_PROTO_SCO	1
+
+/* HCI Core structures */
+struct inquiry_data {
+	bdaddr_t	bdaddr;
+	__u8		pscan_rep_mode;
+	__u8		pscan_period_mode;
+	__u8		pscan_mode;
+	__u8		dev_class[3];
+	__le16		clock_offset;
+	__s8		rssi;
+};
+
+struct inquiry_entry {
+	struct inquiry_entry 	*next;
+	__u32			timestamp;
+	struct inquiry_data	data;
+};
+
+struct inquiry_cache {
+	spinlock_t 		lock;
+	__u32			timestamp;
+	struct inquiry_entry 	*list;
+};
+
+struct hci_conn_hash {
+	struct list_head list;
+	spinlock_t       lock;
+	unsigned int     acl_num;
+	unsigned int     sco_num;
+};
+
+struct hci_dev {
+	struct list_head list;
+	spinlock_t	lock;
+	atomic_t	refcnt;
+
+	char		name[8];
+	unsigned long	flags;
+	__u16		id;
+	__u8		type;
+	bdaddr_t	bdaddr;
+	__u8		features[8];
+	__u8		hci_ver;
+	__u16		hci_rev;
+	__u16		manufacturer;
+	__u16		voice_setting;
+
+	__u16		pkt_type;
+	__u16		link_policy;
+	__u16		link_mode;
+
+	__u32		idle_timeout;
+	__u16		sniff_min_interval;
+	__u16		sniff_max_interval;
+
+	unsigned long	quirks;
+
+	atomic_t	cmd_cnt;
+	/* Number of available controller buffers for ACL packets */
+	unsigned int	acl_cnt;
+	/* Number of available controller buffers for SCO packets */
+	atomic_t	sco_cnt;
+
+	/* Maximum transmition unit for ACL packets */
+	unsigned int	acl_mtu;
+	/* Maximum transmition unit for SCO packets */
+	unsigned int	sco_mtu;
+	/* Maximum number of ACL packets the controller is able to buffer */
+	unsigned int	acl_pkts;
+	/* Maximum number of SCO packets the controller is able to buffer */
+	unsigned int	sco_pkts;
+
+	unsigned long	cmd_last_tx;
+	unsigned long	acl_last_tx;
+	unsigned long	sco_last_tx;
+
+	struct tasklet_struct	cmd_task;
+	struct tasklet_struct	rx_task;
+	struct tasklet_struct	tx_task;
+
+	struct sk_buff_head	rx_q;
+	struct sk_buff_head	raw_q;
+	struct sk_buff_head	cmd_q;
+
+	struct sk_buff		*sent_cmd;
+
+	struct semaphore	req_lock;
+	wait_queue_head_t	req_wait_q;
+	__u32			req_status;
+	__u32			req_result;
+
+	struct inquiry_cache	inq_cache;
+	struct hci_conn_hash	conn_hash;
+
+	struct hci_dev_stats	stat;
+
+	struct sk_buff_head	driver_init;
+
+	void			*driver_data;
+	void			*core_data;
+
+	atomic_t 		promisc;
+
+	struct device		*parent;
+	struct device		dev;
+
+	struct module 		*owner;
+
+	int (*open)(struct hci_dev *hdev);
+	int (*close)(struct hci_dev *hdev);
+	int (*flush)(struct hci_dev *hdev);
+	int (*send)(struct sk_buff *skb);
+	void (*destruct)(struct hci_dev *hdev);
+	void (*notify)(struct hci_dev *hdev, unsigned int evt);
+	int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
+};
+
+struct hci_conn {
+	struct list_head list;
+
+	atomic_t	 refcnt;
+
+	bdaddr_t	 dst;
+	__u16		 handle;
+	__u16		 state;
+	__u8             mode;
+	__u8		 type;
+	__u8		 out;
+	__u8		 attempt;
+	__u8		 dev_class[3];
+	__u8             features[8];
+	__u16            interval;
+	__u16            link_policy;
+	__u32		 link_mode;
+	__u8             power_save;
+	unsigned long	 pend;
+
+	struct timer_list disc_timer;
+	struct timer_list idle_timer;
+
+	struct work_struct work;
+
+	struct device	dev;
+
+	struct hci_dev	*hdev;
+	void		*l2cap_data;
+	void		*sco_data;
+	void		*priv;
+
+	struct hci_conn	*link;
+};
+
+extern struct hci_proto *hci_proto[];
+extern struct list_head hci_dev_list;
+extern struct list_head hci_cb_list;
+extern rwlock_t hci_dev_list_lock;
+extern rwlock_t hci_cb_list_lock;
+
+/* ----- Inquiry cache ----- */
+#define INQUIRY_CACHE_AGE_MAX   (HZ*30)   // 30 seconds
+#define INQUIRY_ENTRY_AGE_MAX   (HZ*60)   // 60 seconds
+
+#define inquiry_cache_lock(c)		spin_lock(&c->lock)
+#define inquiry_cache_unlock(c)		spin_unlock(&c->lock)
+#define inquiry_cache_lock_bh(c)	spin_lock_bh(&c->lock)
+#define inquiry_cache_unlock_bh(c)	spin_unlock_bh(&c->lock)
+
+static inline void inquiry_cache_init(struct hci_dev *hdev)
+{
+	struct inquiry_cache *c = &hdev->inq_cache;
+	spin_lock_init(&c->lock);
+	c->list = NULL;
+}
+
+static inline int inquiry_cache_empty(struct hci_dev *hdev)
+{
+	struct inquiry_cache *c = &hdev->inq_cache;
+	return (c->list == NULL);
+}
+
+static inline long inquiry_cache_age(struct hci_dev *hdev)
+{
+	struct inquiry_cache *c = &hdev->inq_cache;
+	return jiffies - c->timestamp;
+}
+
+static inline long inquiry_entry_age(struct inquiry_entry *e)
+{
+	return jiffies - e->timestamp;
+}
+
+struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
+void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data);
+
+/* ----- HCI Connections ----- */
+enum {
+	HCI_CONN_AUTH_PEND,
+	HCI_CONN_ENCRYPT_PEND,
+	HCI_CONN_RSWITCH_PEND,
+	HCI_CONN_MODE_CHANGE_PEND,
+};
+
+static inline void hci_conn_hash_init(struct hci_dev *hdev)
+{
+	struct hci_conn_hash *h = &hdev->conn_hash;
+	INIT_LIST_HEAD(&h->list);
+	spin_lock_init(&h->lock);
+	h->acl_num = 0;
+	h->sco_num = 0;
+}
+
+static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
+{
+	struct hci_conn_hash *h = &hdev->conn_hash;
+	list_add(&c->list, &h->list);
+	if (c->type == ACL_LINK)
+		h->acl_num++;
+	else
+		h->sco_num++;
+}
+
+static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
+{
+	struct hci_conn_hash *h = &hdev->conn_hash;
+	list_del(&c->list);
+	if (c->type == ACL_LINK)
+		h->acl_num--;
+	else
+		h->sco_num--;
+}
+
+static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
+					__u16 handle)
+{
+	struct hci_conn_hash *h = &hdev->conn_hash;
+	struct list_head *p;
+	struct hci_conn  *c;
+
+	list_for_each(p, &h->list) {
+		c = list_entry(p, struct hci_conn, list);
+		if (c->handle == handle)
+			return c;
+	}
+	return NULL;
+}
+
+static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev,
+					__u8 type, bdaddr_t *ba)
+{
+	struct hci_conn_hash *h = &hdev->conn_hash;
+	struct list_head *p;
+	struct hci_conn  *c;
+
+	list_for_each(p, &h->list) {
+		c = list_entry(p, struct hci_conn, list);
+		if (c->type == type && !bacmp(&c->dst, ba))
+			return c;
+	}
+	return NULL;
+}
+
+static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
+					__u8 type, __u16 state)
+{
+	struct hci_conn_hash *h = &hdev->conn_hash;
+	struct list_head *p;
+	struct hci_conn  *c;
+
+	list_for_each(p, &h->list) {
+		c = list_entry(p, struct hci_conn, list);
+		if (c->type == type && c->state == state)
+			return c;
+	}
+	return NULL;
+}
+
+void hci_acl_connect(struct hci_conn *conn);
+void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
+void hci_add_sco(struct hci_conn *conn, __u16 handle);
+
+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
+int    hci_conn_del(struct hci_conn *conn);
+void   hci_conn_hash_flush(struct hci_dev *hdev);
+
+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src);
+int hci_conn_auth(struct hci_conn *conn);
+int hci_conn_encrypt(struct hci_conn *conn);
+int hci_conn_change_link_key(struct hci_conn *conn);
+int hci_conn_switch_role(struct hci_conn *conn, uint8_t role);
+
+void hci_conn_enter_active_mode(struct hci_conn *conn);
+void hci_conn_enter_sniff_mode(struct hci_conn *conn);
+
+static inline void hci_conn_hold(struct hci_conn *conn)
+{
+	atomic_inc(&conn->refcnt);
+	del_timer(&conn->disc_timer);
+}
+
+static inline void hci_conn_put(struct hci_conn *conn)
+{
+	if (atomic_dec_and_test(&conn->refcnt)) {
+		unsigned long timeo;
+		if (conn->type == ACL_LINK) {
+			del_timer(&conn->idle_timer);
+			if (conn->state == BT_CONNECTED) {
+				timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT);
+				if (!conn->out)
+					timeo *= 2;
+			} else
+				timeo = msecs_to_jiffies(10);
+		} else
+			timeo = msecs_to_jiffies(10);
+		mod_timer(&conn->disc_timer, jiffies + timeo);
+	}
+}
+
+/* ----- HCI tasks ----- */
+static inline void hci_sched_cmd(struct hci_dev *hdev)
+{
+	tasklet_schedule(&hdev->cmd_task);
+}
+
+static inline void hci_sched_rx(struct hci_dev *hdev)
+{
+	tasklet_schedule(&hdev->rx_task);
+}
+
+static inline void hci_sched_tx(struct hci_dev *hdev)
+{
+	tasklet_schedule(&hdev->tx_task);
+}
+
+/* ----- HCI Devices ----- */
+static inline void __hci_dev_put(struct hci_dev *d)
+{
+	if (atomic_dec_and_test(&d->refcnt))
+		d->destruct(d);
+}
+
+static inline void hci_dev_put(struct hci_dev *d)
+{ 
+	__hci_dev_put(d);
+	module_put(d->owner);
+}
+
+static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d)
+{
+	atomic_inc(&d->refcnt);
+	return d;
+}
+
+static inline struct hci_dev *hci_dev_hold(struct hci_dev *d)
+{
+	if (try_module_get(d->owner))
+		return __hci_dev_hold(d);
+	return NULL;
+}
+
+#define hci_dev_lock(d)		spin_lock(&d->lock)
+#define hci_dev_unlock(d)	spin_unlock(&d->lock)
+#define hci_dev_lock_bh(d)	spin_lock_bh(&d->lock)
+#define hci_dev_unlock_bh(d)	spin_unlock_bh(&d->lock)
+
+struct hci_dev *hci_dev_get(int index);
+struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
+
+struct hci_dev *hci_alloc_dev(void);
+void hci_free_dev(struct hci_dev *hdev);
+int hci_register_dev(struct hci_dev *hdev);
+int hci_unregister_dev(struct hci_dev *hdev);
+int hci_suspend_dev(struct hci_dev *hdev);
+int hci_resume_dev(struct hci_dev *hdev);
+int hci_dev_open(__u16 dev);
+int hci_dev_close(__u16 dev);
+int hci_dev_reset(__u16 dev);
+int hci_dev_reset_stat(__u16 dev);
+int hci_dev_cmd(unsigned int cmd, void __user *arg);
+int hci_get_dev_list(void __user *arg);
+int hci_get_dev_info(void __user *arg);
+int hci_get_conn_list(void __user *arg);
+int hci_get_conn_info(struct hci_dev *hdev, void __user *arg);
+int hci_inquiry(void __user *arg);
+
+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
+
+/* Receive frame from HCI drivers */
+static inline int hci_recv_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	if (!hdev || (!test_bit(HCI_UP, &hdev->flags) 
+			&& !test_bit(HCI_INIT, &hdev->flags))) {
+		kfree_skb(skb);
+		return -ENXIO;
+	}
+
+	/* Incomming skb */
+	bt_cb(skb)->incoming = 1;
+
+	/* Time stamp */
+	__net_timestamp(skb);
+
+	/* Queue frame for rx task */
+	skb_queue_tail(&hdev->rx_q, skb);
+	hci_sched_rx(hdev);
+	return 0;
+}
+
+int hci_register_sysfs(struct hci_dev *hdev);
+void hci_unregister_sysfs(struct hci_dev *hdev);
+void hci_conn_add_sysfs(struct hci_conn *conn);
+void hci_conn_del_sysfs(struct hci_conn *conn);
+
+#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (pdev))
+
+/* ----- LMP capabilities ----- */
+#define lmp_rswitch_capable(dev)   ((dev)->features[0] & LMP_RSWITCH)
+#define lmp_encrypt_capable(dev)   ((dev)->features[0] & LMP_ENCRYPT)
+#define lmp_sniff_capable(dev)     ((dev)->features[0] & LMP_SNIFF)
+#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR)
+
+/* ----- HCI protocols ----- */
+struct hci_proto {
+	char 		*name;
+	unsigned int	id;
+	unsigned long	flags;
+
+	void		*priv;
+
+	int (*connect_ind) 	(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
+	int (*connect_cfm)	(struct hci_conn *conn, __u8 status);
+	int (*disconn_ind)	(struct hci_conn *conn, __u8 reason);
+	int (*recv_acldata)	(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+	int (*recv_scodata)	(struct hci_conn *conn, struct sk_buff *skb);
+	int (*auth_cfm)		(struct hci_conn *conn, __u8 status);
+	int (*encrypt_cfm)	(struct hci_conn *conn, __u8 status);
+};
+
+static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+{
+	register struct hci_proto *hp;
+	int mask = 0;
+	
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->connect_ind)
+		mask |= hp->connect_ind(hdev, bdaddr, type);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->connect_ind)
+		mask |= hp->connect_ind(hdev, bdaddr, type);
+
+	return mask;
+}
+
+static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->connect_cfm)
+		hp->connect_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->connect_cfm)
+		hp->connect_cfm(conn, status);
+}
+
+static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->disconn_ind)
+		hp->disconn_ind(conn, reason);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->disconn_ind)
+		hp->disconn_ind(conn, reason);
+}
+
+static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->auth_cfm)
+		hp->auth_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->auth_cfm)
+		hp->auth_cfm(conn, status);
+}
+
+static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->encrypt_cfm)
+		hp->encrypt_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->encrypt_cfm)
+		hp->encrypt_cfm(conn, status);
+}
+
+int hci_register_proto(struct hci_proto *hproto);
+int hci_unregister_proto(struct hci_proto *hproto);
+
+/* ----- HCI callbacks ----- */
+struct hci_cb {
+	struct list_head list;
+
+	char *name;
+
+	void (*auth_cfm)	(struct hci_conn *conn, __u8 status);
+	void (*encrypt_cfm)	(struct hci_conn *conn, __u8 status, __u8 encrypt);
+	void (*key_change_cfm)	(struct hci_conn *conn, __u8 status);
+	void (*role_switch_cfm)	(struct hci_conn *conn, __u8 status, __u8 role);
+};
+
+static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
+{
+	struct list_head *p;
+
+	hci_proto_auth_cfm(conn, status);
+
+	read_lock_bh(&hci_cb_list_lock);
+	list_for_each(p, &hci_cb_list) {
+		struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+		if (cb->auth_cfm)
+			cb->auth_cfm(conn, status);
+	}
+	read_unlock_bh(&hci_cb_list_lock);
+}
+
+static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt)
+{
+	struct list_head *p;
+
+	hci_proto_encrypt_cfm(conn, status);
+
+	read_lock_bh(&hci_cb_list_lock);
+	list_for_each(p, &hci_cb_list) {
+		struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+		if (cb->encrypt_cfm)
+			cb->encrypt_cfm(conn, status, encrypt);
+	}
+	read_unlock_bh(&hci_cb_list_lock);
+}
+
+static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
+{
+	struct list_head *p;
+
+	read_lock_bh(&hci_cb_list_lock);
+	list_for_each(p, &hci_cb_list) {
+		struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+		if (cb->key_change_cfm)
+			cb->key_change_cfm(conn, status);
+	}
+	read_unlock_bh(&hci_cb_list_lock);
+}
+
+static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, __u8 role)
+{
+	struct list_head *p;
+
+	read_lock_bh(&hci_cb_list_lock);
+	list_for_each(p, &hci_cb_list) {
+		struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+		if (cb->role_switch_cfm)
+			cb->role_switch_cfm(conn, status, role);
+	}
+	read_unlock_bh(&hci_cb_list_lock);
+}
+
+int hci_register_cb(struct hci_cb *hcb);
+int hci_unregister_cb(struct hci_cb *hcb);
+
+int hci_register_notifier(struct notifier_block *nb);
+int hci_unregister_notifier(struct notifier_block *nb);
+
+int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param);
+int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+int hci_send_sco(struct hci_conn *conn, 
+			struct sk_buff *skb, 
+			int sndbufsize, 
+			void (*send_complete_cb)(struct hci_conn *conn));
+
+void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf);
+
+void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data);
+
+/* ----- HCI Sockets ----- */
+void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
+
+/* HCI info for socket */
+#define hci_pi(sk) ((struct hci_pinfo *) sk)
+
+struct hci_pinfo {
+	struct bt_sock    bt;
+	struct hci_dev    *hdev;
+	struct hci_filter filter;
+	__u32             cmsg_mask;
+};
+
+/* HCI security filter */
+#define HCI_SFLT_MAX_OGF  5
+
+struct hci_sec_filter {
+	__u32 type_mask;
+	__u32 event_mask[2];
+	__u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
+};
+
+/* ----- HCI requests ----- */
+#define HCI_REQ_DONE	  0
+#define HCI_REQ_PEND	  1
+#define HCI_REQ_CANCELED  2
+
+#define hci_req_lock(d)		down(&d->req_lock)
+#define hci_req_unlock(d)	up(&d->req_lock)
+
+void hci_req_complete(struct hci_dev *hdev, int result);
+
+#endif /* __HCI_CORE_H */
diff -urN linux-2.6.18-mh7/include/net/bluetooth/hci.h linux-2.6.18-mh7-sco/include/net/bluetooth/hci.h
--- linux-2.6.18-mh7/include/net/bluetooth/hci.h	2006-11-02 07:45:31.000000000 -0700
+++ linux-2.6.18-mh7-sco/include/net/bluetooth/hci.h	2006-11-02 07:48:08.000000000 -0700
@@ -767,6 +767,9 @@
 	__u32 byte_tx;
 };
 
+/* Fields down there are mostly the same as hci_dev,
+   as this structure is meant to communicate info
+   to userspace */
 struct hci_dev_info {
 	__u16 dev_id;
 	char  name[8];
@@ -782,9 +785,13 @@
 	__u32 link_policy;
 	__u32 link_mode;
 
+	/* Maximum transmition unit for ACL packets */
 	__u16 acl_mtu;
+	/* Number of ACL packets the baseband is able to buffer */
 	__u16 acl_pkts;
+	/* Maximum transmition unit for SCO packets */
 	__u16 sco_mtu;
+	/* Number of SCO packets the baseband is able to buffer */
 	__u16 sco_pkts;
 
 	struct hci_dev_stats stat;
diff -urN linux-2.6.18-mh7/include/net/bluetooth/sco.h linux-2.6.18-mh7-sco/include/net/bluetooth/sco.h
--- linux-2.6.18-mh7/include/net/bluetooth/sco.h	2006-09-19 21:42:06.000000000 -0600
+++ linux-2.6.18-mh7-sco/include/net/bluetooth/sco.h	2006-11-02 07:48:08.000000000 -0700
@@ -26,12 +26,7 @@
 #define __SCO_H
 
 /* SCO defaults */
-#define SCO_DEFAULT_MTU		500
-#define SCO_DEFAULT_FLUSH_TO	0xFFFF
-
 #define SCO_CONN_TIMEOUT	(HZ * 40)
-#define SCO_DISCONN_TIMEOUT	(HZ * 2)
-#define SCO_CONN_IDLE_TIMEOUT	(HZ * 60)
 
 /* SCO socket address */
 struct sockaddr_sco {
@@ -51,6 +46,9 @@
 	__u8  dev_class[3];
 };
 
+#define SCO_TXBUFS	0x03
+#define SCO_RXBUFS	0x04
+
 /* ---- SCO connections ---- */
 struct sco_conn {
 	struct hci_conn	*hcon;
diff -urN linux-2.6.18-mh7/net/bluetooth/hci_conn.c linux-2.6.18-mh7-sco/net/bluetooth/hci_conn.c
--- linux-2.6.18-mh7/net/bluetooth/hci_conn.c	2006-11-02 07:45:31.000000000 -0700
+++ linux-2.6.18-mh7-sco/net/bluetooth/hci_conn.c	2006-11-02 07:48:08.000000000 -0700
@@ -184,7 +184,10 @@
 
 	conn->power_save = 1;
 
-	skb_queue_head_init(&conn->data_q);
+	conn->pkt_sent_cb = NULL;
+	skb_queue_head_init(&conn->out_q);
+
+	hrtimer_init(&conn->tx_timer, CLOCK_MONOTONIC, HRTIMER_NORESTART);
 
 	init_timer(&conn->disc_timer);
 	conn->disc_timer.function = hci_conn_timeout;
@@ -195,6 +198,7 @@
 	conn->idle_timer.data = (unsigned long) conn;
 
 	atomic_set(&conn->refcnt, 0);
+	atomic_set(&conn->sent, 0);
 
 	hci_dev_hold(hdev);
 
@@ -221,6 +225,8 @@
 
 	del_timer(&conn->disc_timer);
 
+	hrtimer_cancel(&conn->tx_timer);
+
 	if (conn->type == SCO_LINK) {
 		struct hci_conn *acl = conn->link;
 		if (acl) {
@@ -233,7 +239,7 @@
 			sco->link = NULL;
 
 		/* Unacked frames */
-		hdev->acl_cnt += conn->sent;
+		hdev->acl_cnt += atomic_read(&conn->sent);
 	}
 
 	tasklet_disable(&hdev->tx_task);
@@ -246,7 +252,7 @@
 
 	tasklet_enable(&hdev->tx_task);
 
-	skb_queue_purge(&conn->data_q);
+	skb_queue_purge(&conn->out_q);
 
 	hci_dev_put(hdev);
 
diff -urN linux-2.6.18-mh7/net/bluetooth/hci_core.c linux-2.6.18-mh7-sco/net/bluetooth/hci_core.c
--- linux-2.6.18-mh7/net/bluetooth/hci_core.c	2006-11-02 07:45:31.000000000 -0700
+++ linux-2.6.18-mh7-sco/net/bluetooth/hci_core.c	2006-11-02 07:48:08.000000000 -0700
@@ -619,8 +619,9 @@
 	if (hdev->flush)
 		hdev->flush(hdev);
 
-	atomic_set(&hdev->cmd_cnt, 1); 
-	hdev->acl_cnt = 0; hdev->sco_cnt = 0;
+	atomic_set(&hdev->cmd_cnt, 1);
+	atomic_set(&hdev->sco_cnt, 0); 
+	hdev->acl_cnt = 0; 
 
 	if (!test_bit(HCI_RAW, &hdev->flags))
 		ret = __hci_request(hdev, hci_reset_req, 0,
@@ -1096,7 +1097,7 @@
 		/* Non fragmented */
 		BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
 
-		skb_queue_tail(&conn->data_q, skb);
+		skb_queue_tail(&conn->out_q, skb);
 	} else {
 		/* Fragmented */
 		BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
@@ -1104,9 +1105,9 @@
 		skb_shinfo(skb)->frag_list = NULL;
 
 		/* Queue all fragments atomically */
-		spin_lock_bh(&conn->data_q.lock);
+		spin_lock_bh(&conn->out_q.lock);
 
-		__skb_queue_tail(&conn->data_q, skb);
+		__skb_queue_tail(&conn->out_q, skb);
 		do {
 			skb = list; list = list->next;
 			
@@ -1116,10 +1117,10 @@
 
 			BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
 
-			__skb_queue_tail(&conn->data_q, skb);
+			__skb_queue_tail(&conn->out_q, skb);
 		} while (list);
 
-		spin_unlock_bh(&conn->data_q.lock);
+		spin_unlock_bh(&conn->out_q.lock);
 	}
 
 	hci_sched_tx(hdev);
@@ -1127,41 +1128,49 @@
 }
 EXPORT_SYMBOL(hci_send_acl);
 
-/* Send SCO data */
-int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
+static inline void __hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
 {
-	struct hci_dev *hdev = conn->hdev;
 	struct hci_sco_hdr hdr;
 
-	BT_DBG("%s len %d", hdev->name, skb->len);
-
-	if (skb->len > hdev->sco_mtu) {
-		kfree_skb(skb);
-		return -EINVAL;
-	}
-
+	BT_DBG("skb %p len %d", skb, skb->len);
+	/* preparing frame */
 	hdr.handle = __cpu_to_le16(conn->handle);
 	hdr.dlen   = skb->len;
 
 	skb->h.raw = skb_push(skb, HCI_SCO_HDR_SIZE);
 	memcpy(skb->h.raw, &hdr, HCI_SCO_HDR_SIZE);
 
-	skb->dev = (void *) hdev;
+	skb->dev = (void *) conn->hdev;
 	bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
-	skb_queue_tail(&conn->data_q, skb);
-	hci_sched_tx(hdev);
-	return 0;
+
+	skb_queue_tail(&conn->out_q, skb);
+
+	hci_sched_tx(conn->hdev);
+}
+
+int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb, 
+		int sndbufsize, void (*send_complete_cb)(struct hci_conn *conn))
+{
+	BT_DBG("conn %p skb %p sndbufsize %d", conn, skb, sndbufsize);
+	if(skb_queue_len(&conn->out_q) < sndbufsize) {
+		__hci_send_sco(conn, skb);
+		conn->pkt_sent_cb = send_complete_cb;
+		return 0;
+	}
+	else {
+		return -EAGAIN;
+	}
 }
 EXPORT_SYMBOL(hci_send_sco);
 
 /* ---- HCI TX task (outgoing data) ---- */
 
-/* HCI Connection scheduler */
-static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
+/* HCI ACL Connection scheduler */
+static inline struct hci_conn *hci_low_sent_acl(struct hci_dev *hdev, int *quote)
 {
 	struct hci_conn_hash *h = &hdev->conn_hash;
 	struct hci_conn  *conn = NULL;
-	int num = 0, min = ~0;
+	unsigned int num = 0, min = ~0;
 	struct list_head *p;
 
 	/* We don't have to lock device here. Connections are always 
@@ -1170,20 +1179,22 @@
 		struct hci_conn *c;
 		c = list_entry(p, struct hci_conn, list);
 
-		if (c->type != type || c->state != BT_CONNECTED
-				|| skb_queue_empty(&c->data_q))
+		BT_DBG("c->type %d c->state %d len(c->out_q) %d min %d c->sent %d", 
+			c->type, c->state, skb_queue_len(&c->out_q), min, atomic_read(&c->sent));
+
+		if (c->type != ACL_LINK || c->state != BT_CONNECTED
+				|| skb_queue_empty(&c->out_q))
 			continue;
 		num++;
 
-		if (c->sent < min) {
-			min  = c->sent;
+		if (atomic_read(&c->sent) < min) {
+			min  = atomic_read(&c->sent);
 			conn = c;
 		}
 	}
 
 	if (conn) {
-		int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
-		int q = cnt / num;
+		int q = hdev->acl_cnt / num;
 		*quote = q ? q : 1;
 	} else
 		*quote = 0;
@@ -1203,7 +1214,7 @@
 	/* Kill stalled connections */
 	list_for_each(p, &h->list) {
 		c = list_entry(p, struct hci_conn, list);
-		if (c->type == ACL_LINK && c->sent) {
+		if (c->type == ACL_LINK && atomic_read(&c->sent)) {
 			BT_ERR("%s killing stalled ACL connection %s",
 				hdev->name, batostr(&c->dst));
 			hci_acl_disconn(c, 0x13);
@@ -1226,8 +1237,8 @@
 			hci_acl_tx_to(hdev);
 	}
 
-	while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
-		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+	while (hdev->acl_cnt && (conn = hci_low_sent_acl(hdev, &quote))) {
+		while (quote-- && (skb = skb_dequeue(&conn->out_q))) {
 			BT_DBG("skb %p len %d", skb, skb->len);
 
 			hci_conn_enter_active_mode(conn);
@@ -1236,28 +1247,87 @@
 			hdev->acl_last_tx = jiffies;
 
 			hdev->acl_cnt--;
-			conn->sent++;
+			atomic_inc(&conn->sent);
 		}
 	}
 }
 
-/* Schedule SCO */
-static inline void hci_sched_sco(struct hci_dev *hdev)
+/* HCI SCO tx timer */
+
+static int hci_sco_tx_timer(struct hrtimer *timer)
 {
-	struct hci_conn *conn;
-	struct sk_buff *skb;
-	int quote;
+	struct hci_conn *conn = container_of(timer, struct hci_conn, tx_timer);
+#ifdef CONFIG_BT_HCI_CORE_DEBUG
+	ktime_t now = timer->base->get_time();
+#endif
 
-	BT_DBG("%s", hdev->name);
+	BT_DBG("%s, conn %p, time %5lu.%06lu", conn->hdev->name, conn,
+		(unsigned long) now.tv64, 
+		do_div(now.tv64, NSEC_PER_SEC) / 1000);
 
-	while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
-		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
-			BT_DBG("skb %p len %d", skb, skb->len);
-			hci_send_frame(skb);
+	if(atomic_read(&conn->sent) > 0) {
+		atomic_dec(&conn->sent);
+		atomic_inc(&conn->hdev->sco_cnt);
+		hci_sched_tx(conn->hdev);
+	}
+	/* Wake up writers */
+	if(conn->pkt_sent_cb) {
+		conn->pkt_sent_cb(conn);
+	}
+	return HRTIMER_NORESTART;
+}
+ 
+/* HCI SCO Connection scheduler */
 
-			conn->sent++;
-			if (conn->sent == ~0)
-				conn->sent = 0;
+static inline void hci_sched_sco(struct hci_dev *hdev)
+{
+	struct hci_conn_hash *h = &hdev->conn_hash;
+	struct sk_buff *skb;
+	struct list_head *p;
+	struct hci_conn *c;
+	
+	BT_DBG("%s", hdev->name);
+  
+	/* We don't have to lock device here. Connections are always 
+	 * added and removed with TX task disabled. */
+	list_for_each(p, &h->list) {
+		c = list_entry(p, struct hci_conn, list);
+  
+		/* SCO scheduling algorithm makes sure there is never more than
+		   1 outstanding packet for each connection */
+		if (c->type == SCO_LINK && atomic_read(&c->sent) < 1  && c->state == BT_CONNECTED)
+		{
+			if(atomic_read(&hdev->sco_cnt) > 0) {
+				if((skb = skb_dequeue(&c->out_q)) != NULL) {
+					ktime_t now, pkt_time;
+
+					hci_send_frame(skb);
+ 
+					atomic_inc(&c->sent);			
+					atomic_dec(&hdev->sco_cnt);
+
+					c->tx_timer.function = hci_sco_tx_timer;
+					
+					pkt_time =
+						ktime_set(0, NSEC_PER_SEC / 16000 * (skb->len - HCI_SCO_HDR_SIZE));
+					now = c->tx_timer.base->get_time();
+
+					if(c->tx_timer.expires.tv64 == 0) {
+						c->tx_timer.expires = now;
+					}
+
+					c->tx_timer.expires.tv64 += pkt_time.tv64;
+					if(c->tx_timer.expires.tv64 > now.tv64) {
+						hrtimer_restart(&c->tx_timer);
+					}
+					else {
+						/* Timer is to expire in the past - this can happen if timer base
+						 precision is less than pkt_time. In this case we force timer
+						 expiration by calling its expires function */
+						c->tx_timer.function(&c->tx_timer);
+					}
+				}
+			}
 		}
 	}
 }
@@ -1269,14 +1339,14 @@
 
 	read_lock(&hci_task_lock);
 
-	BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
+	BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, atomic_read(&hdev->sco_cnt));
 
 	/* Schedule queues and send stuff to HCI driver */
 
-	hci_sched_acl(hdev);
-
 	hci_sched_sco(hdev);
 
+	hci_sched_acl(hdev);
+
 	/* Send next queued raw (unknown type) packet */
 	while ((skb = skb_dequeue(&hdev->raw_q)))
 		hci_send_frame(skb);
diff -urN linux-2.6.18-mh7/net/bluetooth/hci_event.c linux-2.6.18-mh7-sco/net/bluetooth/hci_event.c
--- linux-2.6.18-mh7/net/bluetooth/hci_event.c	2006-11-02 07:45:31.000000000 -0700
+++ linux-2.6.18-mh7-sco/net/bluetooth/hci_event.c	2006-11-02 07:48:08.000000000 -0700
@@ -372,7 +372,7 @@
 		}
 
 		hdev->acl_cnt = hdev->acl_pkts;
-		hdev->sco_cnt = hdev->sco_pkts;
+		atomic_set(&hdev->sco_cnt, hdev->sco_pkts);
 
 		BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
 			hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts);
@@ -861,15 +861,15 @@
 
 		conn = hci_conn_hash_lookup_handle(hdev, handle);
 		if (conn) {
-			conn->sent -= count;
+			atomic_sub(count, &conn->sent);
 
-			if (conn->type == SCO_LINK) {
-				if ((hdev->sco_cnt += count) > hdev->sco_pkts)
-					hdev->sco_cnt = hdev->sco_pkts;
-			} else {
+			if (conn->type == ACL_LINK) {
 				if ((hdev->acl_cnt += count) > hdev->acl_pkts)
 					hdev->acl_cnt = hdev->acl_pkts;
 			}
+			/* Note : we do not use the "number of completed packets" event
+			to increment hdev->sco_cnt, as this feature is only optionnally support
+			by bluetooth controllers. So there is no if branch for SCO_LINK packets */
 		}
 	}
 	hci_sched_tx(hdev);
diff -urN linux-2.6.18-mh7/net/bluetooth/sco.c linux-2.6.18-mh7-sco/net/bluetooth/sco.c
--- linux-2.6.18-mh7/net/bluetooth/sco.c	2006-11-02 07:45:31.000000000 -0700
+++ linux-2.6.18-mh7-sco/net/bluetooth/sco.c	2006-11-02 07:48:08.000000000 -0700
@@ -53,7 +53,13 @@
 #define BT_DBG(D...)
 #endif
 
-#define VERSION "0.5"
+#define VERSION "0.6"
+
+#define MAX_SCO_TXBUFS 200
+#define MAX_SCO_RXBUFS 200
+
+#define DEFAULT_SCO_TXBUFS 5
+#define DEFAULT_SCO_RXBUFS 5
 
 static const struct proto_ops sco_sock_ops;
 
@@ -61,6 +67,8 @@
 	.lock = RW_LOCK_UNLOCKED
 };
 
+/* Local functions declaration */
+
 static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
 static void sco_chan_del(struct sock *sk, int err);
 
@@ -69,6 +77,10 @@
 static void sco_sock_close(struct sock *sk);
 static void sco_sock_kill(struct sock *sk);
 
+static void sco_sock_rfree(struct sk_buff *skb);
+
+static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+
 /* ---- SCO timers ---- */
 static void sco_sock_timeout(unsigned long arg)
 {
@@ -230,31 +242,64 @@
 	return err;
 }
 
+static void sco_send_complete_cb(struct hci_conn *hcon)
+{
+	struct sco_conn *conn = hcon->sco_data;
+	struct sock *sk = conn->sk;
+	BT_DBG("conn %p, sock %p", conn, sk);
+
+	if(sk) {
+		bh_lock_sock(sk);
+		read_lock(&sk->sk_callback_lock);
+
+		if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
+			wake_up_interruptible(sk->sk_sleep);
+
+		sk_wake_async(sk, 2, POLL_OUT);
+
+		read_unlock(&sk->sk_callback_lock);
+		bh_unlock_sock(sk);
+	}
+}
+
 static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
 {
 	struct sco_conn *conn = sco_pi(sk)->conn;
 	struct sk_buff *skb;
-	int err, count;
+	int err;
 
 	/* Check outgoing MTU */
 	if (len > conn->mtu)
-		return -EINVAL;
+		return -EMSGSIZE;
 
 	BT_DBG("sk %p len %d", sk, len);
 
-	count = min_t(unsigned int, conn->mtu, len);
-	if (!(skb = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err)))
-		return err;
+	if (!(skb = bt_skb_alloc(len, GFP_KERNEL)))
+		return -ENOBUFS;
 
-	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
+	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
 		err = -EFAULT;
 		goto fail;
 	}
 
-	if ((err = hci_send_sco(conn->hcon, skb)) < 0)
-		return err;
+	if(msg->msg_flags & MSG_DONTWAIT) {
+		err = hci_send_sco(conn->hcon, skb, sk->sk_sndbuf, sco_send_complete_cb);
+	}
+	else {
+		err = wait_event_interruptible(*sk->sk_sleep, 
+			(sk->sk_state != BT_CONNECTED) ||
+			(hci_send_sco(conn->hcon, skb, sk->sk_sndbuf, sco_send_complete_cb) == 0)
+			);
+		/* It is possible that we have been waken up because the peer closed the SCO connection */
+		if(sk->sk_state != BT_CONNECTED) {
+			err = -ENOTCONN;
+		}
+	}
+
+	if (err < 0)
+		goto fail;
 
-	return count;
+	return len;
 
 fail:
 	kfree_skb(skb);
@@ -273,8 +318,9 @@
 	if (sk->sk_state != BT_CONNECTED)
 		goto drop;
 
-	if (!sock_queue_rcv_skb(sk, skb))
+	if (sco_sock_queue_rcv_skb(sk, skb) == 0) {
 		return;
+	}
 
 drop:
 	kfree_skb(skb);
@@ -328,7 +374,6 @@
 	BT_DBG("sk %p", sk);
 
 	skb_queue_purge(&sk->sk_receive_queue);
-	skb_queue_purge(&sk->sk_write_queue);
 }
 
 static void sco_sock_cleanup_listen(struct sock *parent)
@@ -360,6 +405,8 @@
 	/* Kill poor orphan */
 	bt_sock_unlink(&sco_sk_list, sk);
 	sock_set_flag(sk, SOCK_DEAD);
+	
+	/* release socket */
 	sock_put(sk);
 }
 
@@ -376,7 +423,7 @@
 
 	conn = sco_pi(sk)->conn;
 
-	BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket);
+	BT_DBG("sk %p state %d conn %p socket %p refcnt %d", sk, sk->sk_state, conn, sk->sk_socket, atomic_read(&sk->sk_refcnt));
 
 	switch (sk->sk_state) {
 	case BT_LISTEN:
@@ -426,6 +473,14 @@
 	INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
 
 	sk->sk_destruct = sco_sock_destruct;
+
+       /* Put sensible values for a voice link (i.e. not too big),
+          as sysctl_rmem_default & sysctl_wmem_default are
+           really not designed for that -- In our case we use sk_**buf to
+           store a count of SCO packets, not a number of bytes as most of other type of
+           sockets do */
+	sk->sk_sndbuf = DEFAULT_SCO_TXBUFS;
+	sk->sk_rcvbuf = DEFAULT_SCO_RXBUFS;
 	sk->sk_sndtimeo = SCO_CONN_TIMEOUT;
 
 	sock_reset_flag(sk, SOCK_ZAPPED);
@@ -656,6 +711,7 @@
 static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
 {
 	struct sock *sk = sock->sk;
+	u32 opt;
 	int err = 0;
 
 	BT_DBG("sk %p", sk);
@@ -663,6 +719,30 @@
 	lock_sock(sk);
 
 	switch (optname) {
+	case SCO_TXBUFS:
+		if (get_user(opt, (u32 __user *) optval)) {
+			err = -EFAULT;
+			break;
+		}
+		if(opt > MAX_SCO_TXBUFS) {
+			err = -EINVAL;
+			break;
+		}
+
+		sk->sk_sndbuf = opt;
+		break;
+	case SCO_RXBUFS:
+		if (get_user(opt, (u32 __user *) optval)) {
+			err = -EFAULT;
+			break;
+		}
+		if(opt > MAX_SCO_RXBUFS) {
+			err = -EINVAL;
+			break;
+		}
+
+		sk->sk_rcvbuf = opt;
+		break;
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -677,7 +757,8 @@
 	struct sock *sk = sock->sk;
 	struct sco_options opts;
 	struct sco_conninfo cinfo;
-	int len, err = 0; 
+	int len, err = 0;
+	int val;
 
 	BT_DBG("sk %p", sk);
 
@@ -687,6 +768,24 @@
 	lock_sock(sk);
 
 	switch (optname) {
+	case SCO_RXBUFS:
+		val = sk->sk_rcvbuf;
+
+		len = min_t(unsigned int, len, sizeof(val));
+		if (copy_to_user(optval, (char *)&val, len))
+			err = -EFAULT;
+
+		break;
+
+	case SCO_TXBUFS:
+		val = sk->sk_sndbuf;
+
+		len = min_t(unsigned int, len, sizeof(val));
+		if (copy_to_user(optval, (char *)&val, len))
+			err = -EFAULT;
+
+		break;
+
 	case SCO_OPTIONS:
 		if (sk->sk_state != BT_CONNECTED) {
 			err = -ENOTCONN;
@@ -891,6 +990,42 @@
 	return 0;
 }
 
+static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+       int err = 0;
+
+       /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+          number of warnings when compiling with -W --ANK
+        */
+       if (atomic_read(&sk->sk_rmem_alloc) + 1 >
+           (unsigned)sk->sk_rcvbuf) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       skb->dev = NULL;
+       skb->sk = sk;
+       skb->destructor = sco_sock_rfree;
+       atomic_add(1, &sk->sk_rmem_alloc);
+
+       skb_queue_tail(&sk->sk_receive_queue, skb);
+
+       if (!sock_flag(sk, SOCK_DEAD))
+               sk->sk_data_ready(sk, 1);
+out:
+       return err;
+}
+
+static void sco_sock_rfree(struct sk_buff *skb)
+{
+       struct sock *sk = skb->sk;
+
+       atomic_sub(1, &sk->sk_rmem_alloc);
+}
+
+
+/* ------- Others ------- */
+
 static ssize_t sco_sysfs_show(struct class *dev, char *buf)
 {
 	struct sock *sk;

[-- Attachment #3: Type: text/plain, Size: 347 bytes --]

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV

[-- Attachment #4: Type: text/plain, Size: 164 bytes --]

_______________________________________________
Bluez-users mailing list
Bluez-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-users

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Bluez-users] [PATCH] fix for kernel flow control patch mh7
  2006-12-05 10:59 [Bluez-users] [PATCH] fix for kernel flow control patch mh7 Zsolt Barat
@ 2006-12-06 22:00 ` Brad Midgley
  2006-12-06 23:30   ` Zsolt Barat
  0 siblings, 1 reply; 4+ messages in thread
From: Brad Midgley @ 2006-12-06 22:00 UTC (permalink / raw)
  To: BlueZ users

Zsolt

> attached is a trivially fixed version of the flow-control-patch mh7,
> which could be found on
> http://bluetooth-alsa.sf.net/bluez-sco-flowcontrol-mh7.patch
> to work with gentoo-suspend2-sources and linux-2.6.18-vanilla.
> i don't know which kernel-version it supposed to be apply.
> would be nice to mention it on the website somewhere.

I've linked to Fabien's latest submission and linked to the bluez patch 
page. Fabien's patch is against -mh7 but I will try it against -mh8 to 
se if there are any conflicts.

It would be good to know if suspend2 patches complicate things. I don't 
use suspend2 myself.

Brad



-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Bluez-users mailing list
Bluez-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-users

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Bluez-users] [PATCH] fix for kernel flow control patch mh7
  2006-12-06 22:00 ` Brad Midgley
@ 2006-12-06 23:30   ` Zsolt Barat
  2006-12-07 12:13     ` Zsolt Barat
  0 siblings, 1 reply; 4+ messages in thread
From: Zsolt Barat @ 2006-12-06 23:30 UTC (permalink / raw)
  To: BlueZ users

Brad Midgley schrieb:
> Zsolt
> 
>> attached is a trivially fixed version of the flow-control-patch mh7,
>> which could be found on
>> http://bluetooth-alsa.sf.net/bluez-sco-flowcontrol-mh7.patch
>> to work with gentoo-suspend2-sources and linux-2.6.18-vanilla.
>> i don't know which kernel-version it supposed to be apply.
>> would be nice to mention it on the website somewhere.
> 
> I've linked to Fabien's latest submission and linked to the bluez patch 
> page. Fabien's patch is against -mh7 but I will try it against -mh8 to 
> se if there are any conflicts.
ah, ok now i understand. mh is an extra patch set. i thought mh is
already fabians private patch set.
then all is probably wrong.
nevertheless with the small fix above the flow-control patch applies
also against 2.6.18 with suspend2-patches and even to 2.6.18-vanilla.
if it has any use it's another question.
what i can say for sure is that with the flow-control-patch sco and the
alsa-plugin layer works. i had to figure out to do resampling and
channel-routing right to get it work. but it was after i applied the patch.
btw. the setup with the kernel module is much easier in this regard.

> 
> It would be good to know if suspend2 patches complicate things. I don't 
> use suspend2 myself.
no, no complications so far. i think suspend2 doesn't touch this areas
where flow-control is involved. i have the kernel running on my notebook
since i posted the patch. suspend+resume works fine.

regards

zsolt


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Bluez-users mailing list
Bluez-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-users

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [Bluez-users] [PATCH] fix for kernel flow control patch mh7
  2006-12-06 23:30   ` Zsolt Barat
@ 2006-12-07 12:13     ` Zsolt Barat
  0 siblings, 0 replies; 4+ messages in thread
From: Zsolt Barat @ 2006-12-07 12:13 UTC (permalink / raw)
  To: BlueZ users

i tried also 2.6.18-mh8 now. it apllies cleanly and works. the order i
applied the patches where:
for gentoo:
4410-suspend2-2.2.8-for-2.6.18.patch
4411-x86_64-nosave.patch

patch-2.6.18-mh8
bluez-sco-flowcontrol-mh7.patch (without fix)

the two gentoo patches forms the suspend2-source. if someone wants to
build a non-suspen2/non-gentoo kernel he has to apply the two latest
ones naturally.
this would be the safest way to apply the flow-controll patch i think,
since it implies the mh-patch-set.

regards

zsolt

Zsolt Barat schrieb:
> Brad Midgley schrieb:
>> Zsolt
>>
>>> attached is a trivially fixed version of the flow-control-patch mh7,
>>> which could be found on
>>> http://bluetooth-alsa.sf.net/bluez-sco-flowcontrol-mh7.patch
>>> to work with gentoo-suspend2-sources and linux-2.6.18-vanilla.
>>> i don't know which kernel-version it supposed to be apply.
>>> would be nice to mention it on the website somewhere.
>> I've linked to Fabien's latest submission and linked to the bluez patch 
>> page. Fabien's patch is against -mh7 but I will try it against -mh8 to 
>> se if there are any conflicts.
> ah, ok now i understand. mh is an extra patch set. i thought mh is
> already fabians private patch set.
> then all is probably wrong.
> nevertheless with the small fix above the flow-control patch applies
> also against 2.6.18 with suspend2-patches and even to 2.6.18-vanilla.
> if it has any use it's another question.
> what i can say for sure is that with the flow-control-patch sco and the
> alsa-plugin layer works. i had to figure out to do resampling and
> channel-routing right to get it work. but it was after i applied the patch.
> btw. the setup with the kernel module is much easier in this regard.
> 
>> It would be good to know if suspend2 patches complicate things. I don't 
>> use suspend2 myself.
> no, no complications so far. i think suspend2 doesn't touch this areas
> where flow-control is involved. i have the kernel running on my notebook
> since i posted the patch. suspend+resume works fine.
> 
> regards
> 
> zsolt
> 
> 
> -------------------------------------------------------------------------
> Take Surveys. Earn Cash. Influence the Future of IT
> Join SourceForge.net's Techsay panel and you'll get the chance to share your
> opinions on IT & business topics through brief surveys - and earn cash
> http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
> _______________________________________________
> Bluez-users mailing list
> Bluez-users@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/bluez-users


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Bluez-users mailing list
Bluez-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-users

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2006-12-07 12:13 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-12-05 10:59 [Bluez-users] [PATCH] fix for kernel flow control patch mh7 Zsolt Barat
2006-12-06 22:00 ` Brad Midgley
2006-12-06 23:30   ` Zsolt Barat
2006-12-07 12:13     ` Zsolt Barat

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox