* [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, "e))) {
- while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+ while (hdev->acl_cnt && (conn = hci_low_sent_acl(hdev, "e))) {
+ 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, "e))) {
- 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