* [Bluez-devel] [PATCH] SCO flow control
@ 2006-05-29 19:46 Fabien Chevalier
2006-05-29 20:33 ` Marcel Holtmann
0 siblings, 1 reply; 14+ messages in thread
From: Fabien Chevalier @ 2006-05-29 19:46 UTC (permalink / raw)
To: BlueZ development
[-- Attachment #1: Type: text/plain, Size: 3579 bytes --]
Hi all,
I just finished a patch to bluez that implements flow control for SCO.
If you are interested in the subject, take a deep breath a read on... :-)
The goal of the "flow control" feature is the following :
- improve robustness : using this patch, user space software is not
able to crash your Linux box anymore by flooding it with SCO packets.
- ease of programmation for user space software. Without this feature
user space software has to use one of the following tricks:
1°) Send a sco packet to the peer only when it receives a sco
packet from the peer. This is the trick used by hstest software shipped
with bluez-utils for instance.
2°) Throttle itself using some kind of sleeping routines. This
is the approach i used when writing bluez-headset (See one of my
precedent posts for more information on that).
- This patch has also the side effect to be able to know how many
packets are "in the pipe", i.e. in the socket queue, but not yet sent
over the air.
This is the kind of information expected by traditionnal PCM
applications (e.g. this is called the "buffer size" in ALSA language).
Thus it makes PCM emulation over bluetooth *much* simpler. :-)
How it works
- SCO socket layer has been further extented, and now uses the
sk_sndbuf and sk_rcvbuf socket variables to count the number of packets
that are "in the pipe". When the sk_sndbuf has reached its maximum value
and the application tries to send more, it goes to sleep (or the send
call returns with -EAGAIN if non blocking mode was selected). The
maximum values can be changed using two new SCO socket options:
SCO_RXBUFS and SCO_TXBUFS.
- After a SCO packet has been sent to HCI, a timer is started. This
timer expires when the SCO packet has been sent. Timer expired event
then triggers the tx tasklet, that will dequeue one packet from the SCO
socket queue, send it to the HCI, and eventually wake up the
corresponding user process.
- Scheduling algorithm has been changed for SCO, so that there is a
maximum of 1 hardware SCO buffer used per SCO connection. This may seem
stupid to have only *one* packet. I tried to use more SCO buffers,
however it really didn't improve sound quality. So supposedly one is big
enough. :-)
Other nice features:
- I extended the sco modules with two new parameters:
"tx_quality_tune", and "rx_quality_tune". These parameters, when set to
1, will make the sco modules print a warning message when the SCO stream
gets interrupted for whatever reason (aka 'audio cut'), respectively in
tx an rx. This feature is to be used by application developpers to fine
tune their SCO_TXBUFS and SCO_RXBUFS parameters.
So, (we're close to the end...)
I ran the following tests on this patch:
- Play a a sound using hstest
- Play/Record using asound/arecord + bluez-headset
I would very much like feedback from people, using bluetooth devices
other than USB though, as i only have USB dongles to do my testing.
To sum-up, this is non-trivial patch.
I'm would be very much interested in a review from kernel hackers, and
would be quite happy to see this patch merged at some point in the future.
Now a question for Marcel : Could you have a look ? Can you tell what
you would do with it ? (e.g: drop it - it's crap. OR cool! but you
should change x and y and z...)
Cheers,
Fabien
PS: I'm not yet used to linux kernel processes, but will be happy to
improve the patch/reformat things so that it fits into best practices. :-)
[-- Attachment #2: patch-sco-flowcontrol-v1.diff --]
[-- Type: text/plain, Size: 28692 bytes --]
diff -rU 6 --exclude-from=kernelexcludes.txt /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci.h /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci.h
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci.h 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci.h 2006-05-21 15:55:23.000000000 +0200
@@ -712,12 +712,15 @@
__u32 sco_tx;
__u32 sco_rx;
__u32 byte_rx;
__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];
bdaddr_t bdaddr;
@@ -727,15 +730,19 @@
__u8 features[8];
__u32 pkt_type;
__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;
};
struct hci_conn_info {
diff -rU 6 --exclude-from=kernelexcludes.txt /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci_core.h /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci_core.h
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci_core.h 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci_core.h 2006-05-25 17:47:11.000000000 +0200
@@ -81,18 +81,24 @@
__u16 link_policy;
__u16 link_mode;
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 */
unsigned int 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;
@@ -142,26 +148,39 @@
atomic_t refcnt;
spinlock_t lock;
bdaddr_t dst;
__u16 handle;
__u16 state;
+ /* type : ACL or SCO */
__u8 type;
__u8 out;
__u8 dev_class[3];
__u32 link_mode;
unsigned long pend;
+ /* 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 */
unsigned int sent;
+ /* Queued packets for this connection
+ Starting with bluez core version 2.9, SCO packets are not in this queue anymore,
+ but remain in the sockets send queue until they are forwared to the driver by
+ the hci scheduler */
struct sk_buff_head data_q;
- struct timer_list timer;
+ /* Data timer : used only for SCO */
+ struct timer_list data_timer;
+ /* Disconnect timer */
+ struct timer_list disc_timer;
struct hci_dev *hdev;
void *l2cap_data;
+ /* private use for sco */
void *sco_data;
void *priv;
struct hci_conn *link;
};
@@ -283,37 +302,37 @@
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);
-static inline void hci_conn_set_timer(struct hci_conn *conn, unsigned long timeout)
+static inline void hci_conn_set_disc_timer(struct hci_conn *conn, unsigned long timeout)
{
- mod_timer(&conn->timer, jiffies + timeout);
+ mod_timer(&conn->disc_timer, jiffies + timeout);
}
-static inline void hci_conn_del_timer(struct hci_conn *conn)
+static inline void hci_conn_del_disc_timer(struct hci_conn *conn)
{
- del_timer(&conn->timer);
+ del_timer(&conn->disc_timer);
}
static inline void hci_conn_hold(struct hci_conn *conn)
{
atomic_inc(&conn->refcnt);
- hci_conn_del_timer(conn);
+ hci_conn_del_disc_timer(conn);
}
static inline void hci_conn_put(struct hci_conn *conn)
{
if (atomic_dec_and_test(&conn->refcnt)) {
if (conn->type == ACL_LINK) {
unsigned long timeo = (conn->out) ?
HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
- hci_conn_set_timer(conn, timeo);
+ hci_conn_set_disc_timer(conn, timeo);
} else
- hci_conn_set_timer(conn, HZ / 100);
+ hci_conn_set_disc_timer(conn, HZ / 100);
}
}
/* ----- HCI tasks ----- */
static inline void hci_sched_cmd(struct hci_dev *hdev)
{
@@ -419,19 +438,20 @@
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);
+ 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);
+ struct sk_buff* (*get_scodata) (struct hci_conn *conn);
+ 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;
@@ -575,13 +595,13 @@
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);
+void hci_stream_sco(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 ----- */
diff -rU 6 --exclude-from=kernelexcludes.txt /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/sco.h /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/sco.h
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/sco.h 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/sco.h 2006-05-28 17:36:49.000000000 +0200
@@ -23,18 +23,13 @@
*/
#ifndef __SCO_H
#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 {
sa_family_t sco_family;
bdaddr_t sco_bdaddr;
};
@@ -48,12 +43,15 @@
#define SCO_CONNINFO 0x02
struct sco_conninfo {
__u16 hci_handle;
__u8 dev_class[3];
};
+#define SCO_TXBUFS 0x03
+#define SCO_RXBUFS 0x04
+
/* ---- SCO connections ---- */
struct sco_conn {
struct hci_conn *hcon;
bdaddr_t *dst;
bdaddr_t *src;
diff -rU 6 --exclude-from=kernelexcludes.txt /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/af_bluetooth.c /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/af_bluetooth.c
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/af_bluetooth.c 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/af_bluetooth.c 2006-05-21 17:38:28.000000000 +0200
@@ -46,13 +46,13 @@
#ifndef CONFIG_BT_SOCK_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
-#define VERSION "2.8"
+#define VERSION "2.9"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
static struct net_proto_family *bt_proto[BT_MAX_PROTO];
int bt_sock_register(int proto, struct net_proto_family *ops)
diff -rU 6 --exclude-from=kernelexcludes.txt /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/hci_conn.c /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/hci_conn.c
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/hci_conn.c 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/hci_conn.c 2006-05-25 18:09:19.000000000 +0200
@@ -130,17 +130,17 @@
else
conn->state = BT_CLOSED;
hci_dev_unlock(hdev);
return;
}
-static void hci_conn_init_timer(struct hci_conn *conn)
+static void hci_conn_init_disc_timer(struct hci_conn *conn)
{
- init_timer(&conn->timer);
- conn->timer.function = hci_conn_timeout;
- conn->timer.data = (unsigned long)conn;
+ init_timer(&conn->disc_timer);
+ conn->disc_timer.function = hci_conn_timeout;
+ conn->disc_timer.data = (unsigned long)conn;
}
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
@@ -153,13 +153,14 @@
bacpy(&conn->dst, dst);
conn->type = type;
conn->hdev = hdev;
conn->state = BT_OPEN;
skb_queue_head_init(&conn->data_q);
- hci_conn_init_timer(conn);
+ hci_conn_init_disc_timer(conn);
+ init_timer(&conn->data_timer);
atomic_set(&conn->refcnt, 0);
hci_dev_hold(hdev);
tasklet_disable(&hdev->tx_task);
@@ -176,13 +177,14 @@
int hci_conn_del(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
- hci_conn_del_timer(conn);
+ hci_conn_del_disc_timer(conn);
+ del_timer_sync(&conn->data_timer);
if (conn->type == SCO_LINK) {
struct hci_conn *acl = conn->link;
if (acl) {
acl->link = NULL;
hci_conn_put(acl);
diff -rU 6 --exclude-from=kernelexcludes.txt /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/hci_core.c /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/hci_core.c
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/hci_core.c 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/hci_core.c 2006-05-29 17:45:48.000000000 +0200
@@ -1116,69 +1116,49 @@
hci_sched_tx(hdev);
return 0;
}
EXPORT_SYMBOL(hci_send_acl);
-/* Send SCO data */
-int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
+/* Push sco data if not already in progress */
+void hci_stream_sco(struct hci_conn *conn)
{
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;
- }
-
- 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;
- bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
- skb_queue_tail(&conn->data_q, skb);
hci_sched_tx(hdev);
- return 0;
}
-EXPORT_SYMBOL(hci_send_sco);
+EXPORT_SYMBOL(hci_stream_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;
struct list_head *p;
/* We don't have to lock device here. Connections are always
* added and removed with TX task disabled. */
list_for_each(p, &h->list) {
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
- if (c->type != type || c->state != BT_CONNECTED
+ if (c->type != ACL_LINK || c->state != BT_CONNECTED
|| skb_queue_empty(&c->data_q))
continue;
num++;
if (c->sent < min) {
min = 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;
BT_DBG("conn %p quote %d", conn, *quote);
return conn;
@@ -1215,41 +1195,95 @@
/* ACL tx timeout must be longer than maximum
* link supervision timeout (40.9 seconds) */
if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
hci_acl_tx_to(hdev);
}
- while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) {
+ while (hdev->acl_cnt && (conn = hci_low_sent_acl(hdev, "e))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(skb);
hdev->acl_last_tx = jiffies;
hdev->acl_cnt--;
conn->sent++;
}
}
}
-/* Schedule SCO */
+/* HCI SCO Connection scheduler */
+
+static void hci_send_sco(struct hci_dev *hdev, struct hci_conn *conn, struct sk_buff *skb)
+{
+ struct hci_sco_hdr hdr;
+
+ 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;
+ bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
+
+ hci_send_frame(skb);
+}
+
+static void hci_sco_tx_timer(unsigned long data)
+{
+ struct hci_conn *conn = (struct hci_conn *) data;
+ BT_DBG("%s, conn %p", conn->hdev->name, conn);
+ if(conn->sent > 0) {
+ conn->sent--;
+ conn->hdev->sco_cnt++;
+ hci_sched_tx(conn->hdev);
+ }
+}
+
static inline void hci_sched_sco(struct hci_dev *hdev)
{
- struct hci_conn *conn;
+ struct hci_conn_hash *h = &hdev->conn_hash;
struct sk_buff *skb;
- int quote;
-
+ struct hci_proto *hp = hci_proto[HCI_PROTO_SCO];
+ struct list_head *p;
+ struct hci_conn *c;
+
BT_DBG("%s", hdev->name);
- 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);
+ /* 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);
- conn->sent++;
- if (conn->sent == ~0)
- conn->sent = 0;
+ /* SCO scheduling algorithm makes sure there is never more than
+ 1 outstanding packet for each connection -- I tried with
+ 2 and more, however there voice quality was not significantly
+ increased. So let it be 1.*/
+ if (c->sent < 1 && c->type == SCO_LINK && c->state == BT_CONNECTED)
+ {
+ /* Found one !! */
+ if(hdev->sco_cnt > 0) {
+ if((skb = hp->get_scodata(c)) != NULL) {
+ hci_send_sco(hdev, c, skb);
+
+ c->sent++;
+ hdev->sco_cnt--;
+
+ /* premare data transmit_done timer */
+ c->data_timer.data = (unsigned long)c;
+ c->data_timer.function = hci_sco_tx_timer;
+ mod_timer(&c->data_timer, jiffies + 1000 * (skb->len % 16) / HZ );
+ }
+ }
+ else {
+ /* exit from loop, no free HCI SCO buffer */
+ break;
+ }
}
}
}
static void hci_tx_task(unsigned long arg)
{
@@ -1259,16 +1293,16 @@
read_lock(&hci_task_lock);
BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, 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);
read_unlock(&hci_task_lock);
}
diff -rU 6 --exclude-from=kernelexcludes.txt /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/sco.c /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/sco.c
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/sco.c 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/sco.c 2006-05-29 18:07:21.000000000 +0200
@@ -51,28 +51,48 @@
#ifndef CONFIG_BT_SCO_DEBUG
#undef BT_DBG
#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 unsigned int tx_quality_tune;
+static unsigned int rx_quality_tune;
static const struct proto_ops sco_sock_ops;
static struct bt_sock_list sco_sk_list = {
.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);
static int sco_conn_del(struct hci_conn *conn, int err);
static void sco_sock_close(struct sock *sk);
static void sco_sock_kill(struct sock *sk);
+static void sco_sock_wfree(struct sk_buff *skb);
+static void sco_sock_rfree(struct sk_buff *skb);
+static long sock_wait_for_wmem(struct sock * sk, long timeo);
+
+static struct sk_buff *sco_skb_send_alloc(struct sock *sk, unsigned long len,
+ int nb, int *errcode);
+
+static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+
/* ---- SCO timers ---- */
static void sco_sock_timeout(unsigned long arg)
{
struct sock *sk = (struct sock *) arg;
BT_DBG("sock %p state %d", sk, sk->sk_state);
@@ -230,58 +250,30 @@
done:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
}
-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;
-
- /* Check outgoing MTU */
- if (len > conn->mtu)
- return -EINVAL;
-
- 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 (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
- err = -EFAULT;
- goto fail;
- }
-
- if ((err = hci_send_sco(conn->hcon, skb)) < 0)
- goto fail;
-
- return count;
-
-fail:
- kfree_skb(skb);
- return err;
-}
-
static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
{
struct sock *sk = sco_chan_get(conn);
if (!sk)
goto drop;
BT_DBG("sk %p len %d", sk, skb->len);
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;
-
+ }
+ else if(rx_quality_tune) {
+ printk(KERN_INFO "rx_quality_tune: audio cut !\n");
+ }
drop:
kfree_skb(skb);
return;
}
/* -------- Socket interface ---------- */
@@ -328,13 +320,30 @@
static void sco_sock_destruct(struct sock *sk)
{
BT_DBG("sk %p", sk);
skb_queue_purge(&sk->sk_receive_queue);
- skb_queue_purge(&sk->sk_write_queue);
+}
+
+static void sco_sock_write_space(struct sock *sk)
+{
+ read_lock(&sk->sk_callback_lock);
+
+ /* Unlock writers ASAP
+ */
+ if((atomic_read(&sk->sk_wmem_alloc)) < sk->sk_sndbuf) {
+ if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
+ wake_up_interruptible(sk->sk_sleep);
+
+ /* Should agree with poll, otherwise some programs break */
+ if (sock_writeable(sk))
+ sk_wake_async(sk, 2, POLL_OUT);
+ }
+
+ read_unlock(&sk->sk_callback_lock);
}
static void sco_sock_cleanup_listen(struct sock *parent)
{
struct sock *sk;
@@ -360,12 +369,19 @@
BT_DBG("sk %p state %d", sk, sk->sk_state);
/* Kill poor orphan */
bt_sock_unlink(&sco_sk_list, sk);
sock_set_flag(sk, SOCK_DEAD);
+
+ /* We cannot purge write queue in socket destructor as
+ it was done before, as skb in this queue hold a reference
+ to the socket itself */
+ skb_queue_purge(&sk->sk_write_queue);
+
+ /* release socket */
sock_put(sk);
}
/* Close socket.
* Must be called on unlocked socket.
*/
@@ -376,13 +392,13 @@
sco_sock_clear_timer(sk);
lock_sock(sk);
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:
sco_sock_cleanup_listen(sk);
break;
@@ -426,12 +442,20 @@
return NULL;
sock_init_data(sock, sk);
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
sk->sk_destruct = sco_sock_destruct;
+ sk->sk_write_space = sco_sock_write_space;
+
+ /* 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 ro
+ store a count of SCO packets, not a number of bytes */
+ sk->sk_sndbuf = DEFAULT_SCO_TXBUFS;
+ sk->sk_rcvbuf = DEFAULT_SCO_RXBUFS;
sk->sk_sndtimeo = SCO_CONN_TIMEOUT;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
@@ -631,43 +655,94 @@
static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
int err = 0;
+ struct sk_buff * skb;
+ struct sco_conn *conn = sco_pi(sk)->conn;
- BT_DBG("sock %p, sk %p", sock, sk);
+ BT_DBG("sock %p, sk %p, len %d", sock, sk, len);
err = sock_error(sk);
if (err)
return err;
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
+ /* Check outgoing MTU and that this is a 16 bytes multiple
+ 16 bytes multiple is required by SCO scheduler algorithm */
+ if (len > conn->mtu || (len % 16) != 0)
+ return -EINVAL;
+
lock_sock(sk);
- if (sk->sk_state == BT_CONNECTED)
- err = sco_send_frame(sk, msg, len);
- else
+ if (sk->sk_state == BT_CONNECTED) {
+ /* This is the call that will put us to sleep if socket send queue is full */
+ skb = sco_skb_send_alloc(sk, len,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+
+ if (skb) {
+ err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ if (err == 0) {
+ BT_DBG("skb %p, size %d", skb, len);
+
+ skb_queue_tail(&sk->sk_write_queue, skb);
+ hci_stream_sco(conn->hcon);
+ err = len;
+ }
+ else {
+ kfree_skb(skb);
+ }
+ }
+ }
+ else {
err = -ENOTCONN;
+ }
release_sock(sk);
+
return err;
}
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);
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;
}
release_sock(sk);
@@ -676,22 +751,41 @@
static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
{
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);
if (get_user(len, optlen))
return -EFAULT;
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;
break;
}
@@ -890,12 +984,169 @@
drop:
kfree_skb(skb);
return 0;
}
+struct sk_buff* sco_get_scodata(struct hci_conn *hcon)
+{
+ struct sco_conn *conn = hcon->sco_data;
+ struct sk_buff* skb = NULL;
+
+ BT_DBG("conn %p", conn);
+
+ if (conn) {
+ struct sock *sk = sco_chan_get(conn);
+ if(sk) {
+ skb = skb_dequeue(&sk->sk_write_queue);
+ }
+ }
+
+ if(skb == NULL && tx_quality_tune) {
+ printk(KERN_INFO "tx_quality_tune: audio cut !\n");
+ }
+
+ return skb;
+}
+
+/* Generic socket manipulations functions, adapted for SCO */
+
+static void sco_sock_wfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+
+ atomic_sub(1, &sk->sk_wmem_alloc);
+ if (!sock_flag(sk, SOCK_USE_WRITE_QUEUE))
+ sk->sk_write_space(sk);
+ sock_put(sk);
+}
+
+static void sco_sock_rfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+
+ atomic_sub(1, &sk->sk_rmem_alloc);
+}
+
+
+static long sock_wait_for_wmem(struct sock * sk, long timeo)
+{
+ DEFINE_WAIT(wait);
+
+ clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ for (;;) {
+ if (!timeo)
+ break;
+ if (signal_pending(current))
+ break;
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf)
+ break;
+ if (sk->sk_shutdown & SEND_SHUTDOWN)
+ break;
+ if (sk->sk_err)
+ break;
+ timeo = schedule_timeout(timeo);
+ }
+ finish_wait(sk->sk_sleep, &wait);
+ return timeo;
+}
+
+static struct sk_buff *sco_skb_send_alloc(struct sock *sk, unsigned long len,
+ int nb, int *errcode)
+{
+ struct sk_buff *skb;
+ gfp_t gfp_mask;
+ int err;
+ long timeo = nb ? 0 : MAX_SCHEDULE_TIMEOUT;
+
+ gfp_mask = sk->sk_allocation;
+ if (gfp_mask & __GFP_WAIT)
+ gfp_mask |= __GFP_REPEAT;
+
+ while (1) {
+ err = sock_error(sk);
+ if (err != 0)
+ goto failure;
+
+ err = -EPIPE;
+ if (sk->sk_shutdown & SEND_SHUTDOWN)
+ goto failure;
+
+ if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
+ skb = alloc_skb(len + BT_SKB_RESERVE, sk->sk_allocation);
+ if (skb) {
+ /* Full success... */
+ break;
+ }
+ err = -ENOBUFS;
+ goto failure;
+ }
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ err = -EAGAIN;
+ if (!timeo)
+ goto failure;
+ if (signal_pending(current))
+ goto interrupted;
+ timeo = sock_wait_for_wmem(sk, timeo);
+ }
+
+ sock_hold(sk);
+ skb->sk = sk;
+ skb->destructor = sco_sock_wfree;
+ atomic_add(1, &sk->sk_wmem_alloc);
+ skb_reserve(skb, BT_SKB_RESERVE);
+ bt_cb(skb)->incoming = 0;
+
+ return skb;
+
+interrupted:
+ err = sock_intr_errno(timeo);
+failure:
+ *errcode = err;
+ return NULL;
+}
+
+static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ int err = 0;
+ int skb_len;
+
+ /* 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) >=
+ (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);
+
+ /* Cache the SKB length before we tack it onto the receive
+ * queue. Once it is added it no longer belongs to us and
+ * may be freed by other threads of control pulling packets
+ * from the queue.
+ */
+ skb_len = skb->len;
+
+ skb_queue_tail(&sk->sk_receive_queue, skb);
+
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_data_ready(sk, skb_len);
+out:
+ return err;
+}
+
+/* ------- Others ------- */
+
static ssize_t sco_sysfs_show(struct class *dev, char *buf)
{
struct sock *sk;
struct hlist_node *node;
char *str = buf;
@@ -943,13 +1194,14 @@
static struct hci_proto sco_hci_proto = {
.name = "SCO",
.id = HCI_PROTO_SCO,
.connect_ind = sco_connect_ind,
.connect_cfm = sco_connect_cfm,
.disconn_ind = sco_disconn_ind,
- .recv_scodata = sco_recv_scodata
+ .recv_scodata = sco_recv_scodata,
+ .get_scodata = sco_get_scodata
};
static int __init sco_init(void)
{
int err;
@@ -995,11 +1247,17 @@
proto_unregister(&sco_proto);
}
module_init(sco_init);
module_exit(sco_exit);
+module_param(tx_quality_tune, bool, 0644);
+MODULE_PARM_DESC(tx_quality_tune, "Print warning message when an audio cut will happen in tx");
+
+module_param(rx_quality_tune, bool, 0644);
+MODULE_PARM_DESC(rx_quality_tune, "Print warning message when an audio cut will happen in rx");
+
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("bt-proto-2");
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
[-- Attachment #4: Type: text/plain, Size: 164 bytes --]
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-05-29 19:46 [Bluez-devel] [PATCH] SCO flow control Fabien Chevalier
@ 2006-05-29 20:33 ` Marcel Holtmann
2006-05-29 21:39 ` Pieter Poorthuis
2006-05-30 13:28 ` Fabien Chevalier
0 siblings, 2 replies; 14+ messages in thread
From: Marcel Holtmann @ 2006-05-29 20:33 UTC (permalink / raw)
To: BlueZ development
Hi Fabien,
> I just finished a patch to bluez that implements flow control for SCO.
please split the changes to Bluetooth core and SCO module. Your changes
to the core will conflict with the automatic sniff mode support and I
need to sort that out.
And for the core part, please be brief with any additional comments. My
rule for comments is to put them where the code is not clear by itself.
What you have done is actually over-commenting and that makes the code
harder to read and understand.
Regards
Marcel
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-05-29 20:33 ` Marcel Holtmann
@ 2006-05-29 21:39 ` Pieter Poorthuis
2006-05-29 22:21 ` Marcel Holtmann
2006-05-30 13:28 ` Fabien Chevalier
1 sibling, 1 reply; 14+ messages in thread
From: Pieter Poorthuis @ 2006-05-29 21:39 UTC (permalink / raw)
To: BlueZ development
Marcel Holtmann wrote:
> And for the core part, please be brief with any additional comments. My
> rule for comments is to put them where the code is not clear by itself.
> What you have done is actually over-commenting and that makes the code
> harder to read and understand.
>
>
I've read the code and for me the comments are very welcome. As well as
your enthousiasm & code, Fabian.
Cheers, Pieter
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-05-29 21:39 ` Pieter Poorthuis
@ 2006-05-29 22:21 ` Marcel Holtmann
0 siblings, 0 replies; 14+ messages in thread
From: Marcel Holtmann @ 2006-05-29 22:21 UTC (permalink / raw)
To: BlueZ development
Hi Pieter,
> > And for the core part, please be brief with any additional comments. My
> > rule for comments is to put them where the code is not clear by itself.
> > What you have done is actually over-commenting and that makes the code
> > harder to read and understand.
> >
> I've read the code and for me the comments are very welcome. As well as
> your enthousiasm & code, Fabian.
the right amount of comments and the coding style has always been
something people disagree on. However the kernel follows the policy to
have clean and easy code. You should understand the code and there
should be no need for comments. The comments are used to explain parts
that are not easy to guess.
Example for these are the reasons why to put a memory barrier here or
why not using some kind of locking or the reason why this isn't a race
conditions even if it looks like one etc.
Regards
Marcel
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-05-29 20:33 ` Marcel Holtmann
2006-05-29 21:39 ` Pieter Poorthuis
@ 2006-05-30 13:28 ` Fabien Chevalier
2006-06-01 19:15 ` Marcel Holtmann
1 sibling, 1 reply; 14+ messages in thread
From: Fabien Chevalier @ 2006-05-30 13:28 UTC (permalink / raw)
To: BlueZ development
[-- Attachment #1: Type: text/plain, Size: 1446 bytes --]
Hello Marcel&All,
> please split the changes to Bluetooth core and SCO module. Your changes
> to the core will conflict with the automatic sniff mode support and I
> need to sort that out.
Ok, i've just done that, i've split the patch into three smaller bits.
- Patch part 1 are changes to hci core required for SCO flow control
itself.
- Patch part 2 are the changes to the SCO socket layer.
- Patch part 3 are additionnal comments on hci core code.
Please note however that part 1 and part 2 can not be applied seperately.
>
> And for the core part, please be brief with any additional comments. My
> rule for comments is to put them where the code is not clear by itself.
I would tend to agree with the general philosophy of the thing, however
for my point of view bluez kernel code lacks proper fields structure
explanation. Comparing for skbuff.h & usb.h to hci_core.h for instance
is just night and day... skbuff.h & usb.h are just well commented files,
while hci.h and hci_core.h have hardly any comment. :-(
> What you have done is actually over-commenting and that makes the code
> harder to read and understand.
Could you give me an example ? I don't see which part is not lined up
with kernel coding style :-(
anyway,
All this is really *cosmetic* compared to the feature itself. :-)
On another level, does anyone here have anything to add on the feature
itself, or the way i designed it ??
Cheers,
Fabien
[-- Attachment #2: bluez-sco-flowcontrol-1of3.diff --]
[-- Type: text/plain, Size: 13176 bytes --]
diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='sco.*' /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci_core.h /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci_core.h
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci_core.h 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci_core.h 2006-05-25 17:47:11.000000000 +0200
@@ -142,26 +148,39 @@
atomic_t refcnt;
spinlock_t lock;
bdaddr_t dst;
__u16 handle;
__u16 state;
+ /* type : ACL or SCO */
__u8 type;
__u8 out;
__u8 dev_class[3];
__u32 link_mode;
unsigned long pend;
+ /* 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 */
unsigned int sent;
+ /* Queued packets for this connection
+ Starting with bluez core version 2.9, SCO packets are not in this queue anymore,
+ but remain in the sockets send queue until they are forwared to the driver by
+ the hci scheduler */
struct sk_buff_head data_q;
- struct timer_list timer;
+ /* Data timer : used only for SCO */
+ struct timer_list data_timer;
+ /* Disconnect timer */
+ struct timer_list disc_timer;
struct hci_dev *hdev;
void *l2cap_data;
+ /* private use for sco */
void *sco_data;
void *priv;
struct hci_conn *link;
};
@@ -283,37 +302,37 @@
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);
-static inline void hci_conn_set_timer(struct hci_conn *conn, unsigned long timeout)
+static inline void hci_conn_set_disc_timer(struct hci_conn *conn, unsigned long timeout)
{
- mod_timer(&conn->timer, jiffies + timeout);
+ mod_timer(&conn->disc_timer, jiffies + timeout);
}
-static inline void hci_conn_del_timer(struct hci_conn *conn)
+static inline void hci_conn_del_disc_timer(struct hci_conn *conn)
{
- del_timer(&conn->timer);
+ del_timer(&conn->disc_timer);
}
static inline void hci_conn_hold(struct hci_conn *conn)
{
atomic_inc(&conn->refcnt);
- hci_conn_del_timer(conn);
+ hci_conn_del_disc_timer(conn);
}
static inline void hci_conn_put(struct hci_conn *conn)
{
if (atomic_dec_and_test(&conn->refcnt)) {
if (conn->type == ACL_LINK) {
unsigned long timeo = (conn->out) ?
HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
- hci_conn_set_timer(conn, timeo);
+ hci_conn_set_disc_timer(conn, timeo);
} else
- hci_conn_set_timer(conn, HZ / 100);
+ hci_conn_set_disc_timer(conn, HZ / 100);
}
}
/* ----- HCI tasks ----- */
static inline void hci_sched_cmd(struct hci_dev *hdev)
{
@@ -419,19 +438,20 @@
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);
+ 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);
+ struct sk_buff* (*get_scodata) (struct hci_conn *conn);
+ 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;
@@ -575,13 +595,13 @@
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);
+void hci_stream_sco(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 ----- */
diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='sco.*' /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/af_bluetooth.c /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/af_bluetooth.c
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/af_bluetooth.c 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/af_bluetooth.c 2006-05-21 17:38:28.000000000 +0200
@@ -46,13 +46,13 @@
#ifndef CONFIG_BT_SOCK_DEBUG
#undef BT_DBG
#define BT_DBG(D...)
#endif
-#define VERSION "2.8"
+#define VERSION "2.9"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
static struct net_proto_family *bt_proto[BT_MAX_PROTO];
int bt_sock_register(int proto, struct net_proto_family *ops)
diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='sco.*' /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/hci_conn.c /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/hci_conn.c
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/hci_conn.c 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/hci_conn.c 2006-05-25 18:09:19.000000000 +0200
@@ -130,17 +130,17 @@
else
conn->state = BT_CLOSED;
hci_dev_unlock(hdev);
return;
}
-static void hci_conn_init_timer(struct hci_conn *conn)
+static void hci_conn_init_disc_timer(struct hci_conn *conn)
{
- init_timer(&conn->timer);
- conn->timer.function = hci_conn_timeout;
- conn->timer.data = (unsigned long)conn;
+ init_timer(&conn->disc_timer);
+ conn->disc_timer.function = hci_conn_timeout;
+ conn->disc_timer.data = (unsigned long)conn;
}
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
@@ -153,13 +153,14 @@
bacpy(&conn->dst, dst);
conn->type = type;
conn->hdev = hdev;
conn->state = BT_OPEN;
skb_queue_head_init(&conn->data_q);
- hci_conn_init_timer(conn);
+ hci_conn_init_disc_timer(conn);
+ init_timer(&conn->data_timer);
atomic_set(&conn->refcnt, 0);
hci_dev_hold(hdev);
tasklet_disable(&hdev->tx_task);
@@ -176,13 +177,14 @@
int hci_conn_del(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
- hci_conn_del_timer(conn);
+ hci_conn_del_disc_timer(conn);
+ del_timer_sync(&conn->data_timer);
if (conn->type == SCO_LINK) {
struct hci_conn *acl = conn->link;
if (acl) {
acl->link = NULL;
hci_conn_put(acl);
diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='sco.*' /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/hci_core.c /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/hci_core.c
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/hci_core.c 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/hci_core.c 2006-05-30 12:56:00.000000000 +0200
@@ -1116,69 +1116,49 @@
hci_sched_tx(hdev);
return 0;
}
EXPORT_SYMBOL(hci_send_acl);
-/* Send SCO data */
-int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
+/* Push sco data if not already in progress */
+void hci_stream_sco(struct hci_conn *conn)
{
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;
- }
-
- 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;
- bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
- skb_queue_tail(&conn->data_q, skb);
hci_sched_tx(hdev);
- return 0;
}
-EXPORT_SYMBOL(hci_send_sco);
+EXPORT_SYMBOL(hci_stream_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;
struct list_head *p;
/* We don't have to lock device here. Connections are always
* added and removed with TX task disabled. */
list_for_each(p, &h->list) {
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
- if (c->type != type || c->state != BT_CONNECTED
+ if (c->type != ACL_LINK || c->state != BT_CONNECTED
|| skb_queue_empty(&c->data_q))
continue;
num++;
if (c->sent < min) {
min = 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;
BT_DBG("conn %p quote %d", conn, *quote);
return conn;
@@ -1215,41 +1195,95 @@
/* ACL tx timeout must be longer than maximum
* link supervision timeout (40.9 seconds) */
if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
hci_acl_tx_to(hdev);
}
- while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) {
+ while (hdev->acl_cnt && (conn = hci_low_sent_acl(hdev, "e))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(skb);
hdev->acl_last_tx = jiffies;
hdev->acl_cnt--;
conn->sent++;
}
}
}
-/* Schedule SCO */
+/* HCI SCO Connection scheduler */
+
+static void hci_send_sco(struct hci_dev *hdev, struct hci_conn *conn, struct sk_buff *skb)
+{
+ struct hci_sco_hdr hdr;
+
+ 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;
+ bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
+
+ hci_send_frame(skb);
+}
+
+static void hci_sco_tx_timer(unsigned long data)
+{
+ struct hci_conn *conn = (struct hci_conn *) data;
+ BT_DBG("%s, conn %p", conn->hdev->name, conn);
+ if(conn->sent > 0) {
+ conn->sent--;
+ conn->hdev->sco_cnt++;
+ hci_sched_tx(conn->hdev);
+ }
+}
+
static inline void hci_sched_sco(struct hci_dev *hdev)
{
- struct hci_conn *conn;
+ struct hci_conn_hash *h = &hdev->conn_hash;
struct sk_buff *skb;
- int quote;
-
+ struct hci_proto *hp = hci_proto[HCI_PROTO_SCO];
+ struct list_head *p;
+ struct hci_conn *c;
+
BT_DBG("%s", hdev->name);
- 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);
+ /* 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);
- conn->sent++;
- if (conn->sent == ~0)
- conn->sent = 0;
+ /* SCO scheduling algorithm makes sure there is never more than
+ 1 outstanding packet for each connection -- I tried with
+ 2 and more, however there voice quality was not significantly
+ increased. So let it be 1.*/
+ if (c->sent < 1 && c->type == SCO_LINK && c->state == BT_CONNECTED)
+ {
+ if(hdev->sco_cnt > 0) {
+ if((skb = hp->get_scodata(c)) != NULL) {
+ hci_send_sco(hdev, c, skb);
+
+ c->sent++;
+ hdev->sco_cnt--;
+
+ c->data_timer.data = (unsigned long)c;
+ c->data_timer.function = hci_sco_tx_timer;
+ /* At this point we are already supposed to have skb->len a multiple of 16 --
+ this is supposed to be enforced by SCO socket layer */
+ mod_timer(&c->data_timer, jiffies + 1000 * (skb->len % 16) / HZ );
+ }
+ }
+ else {
+ /* exit from loop, as there is no free HCI SCO buffer */
+ break;
+ }
}
}
}
static void hci_tx_task(unsigned long arg)
{
@@ -1259,16 +1293,16 @@
read_lock(&hci_task_lock);
BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, 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);
read_unlock(&hci_task_lock);
}
[-- Attachment #3: bluez-sco-flowcontrol-2of3.diff --]
[-- Type: text/plain, Size: 14042 bytes --]
diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='*hci*' --exclude='af_bluetooth.*' /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/sco.h /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/sco.h
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/sco.h 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/sco.h 2006-05-28 17:36:49.000000000 +0200
@@ -23,18 +23,13 @@
*/
#ifndef __SCO_H
#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 {
sa_family_t sco_family;
bdaddr_t sco_bdaddr;
};
@@ -48,12 +43,15 @@
#define SCO_CONNINFO 0x02
struct sco_conninfo {
__u16 hci_handle;
__u8 dev_class[3];
};
+#define SCO_TXBUFS 0x03
+#define SCO_RXBUFS 0x04
+
/* ---- SCO connections ---- */
struct sco_conn {
struct hci_conn *hcon;
bdaddr_t *dst;
bdaddr_t *src;
diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='*hci*' --exclude='af_bluetooth.*' /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/sco.c /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/sco.c
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/net/bluetooth/sco.c 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/net/bluetooth/sco.c 2006-05-30 13:58:51.000000000 +0200
@@ -51,28 +51,48 @@
#ifndef CONFIG_BT_SCO_DEBUG
#undef BT_DBG
#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 unsigned int tx_quality_tune;
+static unsigned int rx_quality_tune;
static const struct proto_ops sco_sock_ops;
static struct bt_sock_list sco_sk_list = {
.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);
static int sco_conn_del(struct hci_conn *conn, int err);
static void sco_sock_close(struct sock *sk);
static void sco_sock_kill(struct sock *sk);
+static void sco_sock_wfree(struct sk_buff *skb);
+static void sco_sock_rfree(struct sk_buff *skb);
+static long sock_wait_for_wmem(struct sock * sk, long timeo);
+
+static struct sk_buff *sco_skb_send_alloc(struct sock *sk, unsigned long len,
+ int nb, int *errcode);
+
+static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
+
/* ---- SCO timers ---- */
static void sco_sock_timeout(unsigned long arg)
{
struct sock *sk = (struct sock *) arg;
BT_DBG("sock %p state %d", sk, sk->sk_state);
@@ -230,58 +250,30 @@
done:
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
}
-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;
-
- /* Check outgoing MTU */
- if (len > conn->mtu)
- return -EINVAL;
-
- 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 (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
- err = -EFAULT;
- goto fail;
- }
-
- if ((err = hci_send_sco(conn->hcon, skb)) < 0)
- goto fail;
-
- return count;
-
-fail:
- kfree_skb(skb);
- return err;
-}
-
static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
{
struct sock *sk = sco_chan_get(conn);
if (!sk)
goto drop;
BT_DBG("sk %p len %d", sk, skb->len);
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;
-
+ }
+ else if(rx_quality_tune) {
+ printk(KERN_INFO "rx_quality_tune: audio cut !\n");
+ }
drop:
kfree_skb(skb);
return;
}
/* -------- Socket interface ---------- */
@@ -328,13 +320,30 @@
static void sco_sock_destruct(struct sock *sk)
{
BT_DBG("sk %p", sk);
skb_queue_purge(&sk->sk_receive_queue);
- skb_queue_purge(&sk->sk_write_queue);
+}
+
+static void sco_sock_write_space(struct sock *sk)
+{
+ read_lock(&sk->sk_callback_lock);
+
+ /* Unlock writers ASAP
+ */
+ if((atomic_read(&sk->sk_wmem_alloc)) < sk->sk_sndbuf) {
+ if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
+ wake_up_interruptible(sk->sk_sleep);
+
+ /* Should agree with poll, otherwise some programs break */
+ if (sock_writeable(sk))
+ sk_wake_async(sk, 2, POLL_OUT);
+ }
+
+ read_unlock(&sk->sk_callback_lock);
}
static void sco_sock_cleanup_listen(struct sock *parent)
{
struct sock *sk;
@@ -360,12 +369,19 @@
BT_DBG("sk %p state %d", sk, sk->sk_state);
/* Kill poor orphan */
bt_sock_unlink(&sco_sk_list, sk);
sock_set_flag(sk, SOCK_DEAD);
+
+ /* We cannot purge write queue in socket destructor as
+ it was done before, as skb in this queue hold a reference
+ to the socket itself */
+ skb_queue_purge(&sk->sk_write_queue);
+
+ /* release socket */
sock_put(sk);
}
/* Close socket.
* Must be called on unlocked socket.
*/
@@ -376,13 +392,13 @@
sco_sock_clear_timer(sk);
lock_sock(sk);
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:
sco_sock_cleanup_listen(sk);
break;
@@ -426,12 +442,21 @@
return NULL;
sock_init_data(sock, sk);
INIT_LIST_HEAD(&bt_sk(sk)->accept_q);
sk->sk_destruct = sco_sock_destruct;
+ sk->sk_write_space = sco_sock_write_space;
+
+ /* 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);
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
@@ -631,43 +656,100 @@
static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
int err = 0;
+ struct sk_buff * skb;
+ struct sco_conn *conn = 0;
- BT_DBG("sock %p, sk %p", sock, sk);
+ BT_DBG("sock %p, sk %p, len %d", sock, sk, len);
err = sock_error(sk);
if (err)
return err;
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
+
lock_sock(sk);
- if (sk->sk_state == BT_CONNECTED)
- err = sco_send_frame(sk, msg, len);
- else
- err = -ENOTCONN;
+ conn = sco_pi(sk)->conn;
+
+ /* Check outgoing MTU and that this is a 16 bytes multiple .
+ 16 bytes multiple is required by the new SCO scheduler algorithm */
+ if (len <= conn->mtu && (len % 16) == 0) {
+ if (sk->sk_state == BT_CONNECTED) {
+ /* This is the call that will put us to sleep if socket send queue is full */
+ skb = sco_skb_send_alloc(sk, len,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+
+ if (skb) {
+ err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ if (err == 0) {
+
+ BT_DBG("skb %p, size %d", skb, len);
+
+ skb_queue_tail(&sk->sk_write_queue, skb);
+ hci_stream_sco(conn->hcon);
+ err = len;
+ }
+ else {
+ kfree_skb(skb);
+ }
+ }
+ }
+ else {
+ err = -ENOTCONN;
+ }
+ }
+ else {
+ err = -EINVAL;
+ }
release_sock(sk);
+
return err;
}
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);
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;
}
release_sock(sk);
@@ -676,22 +758,41 @@
static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
{
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);
if (get_user(len, optlen))
return -EFAULT;
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;
break;
}
@@ -890,12 +991,169 @@
drop:
kfree_skb(skb);
return 0;
}
+struct sk_buff* sco_get_scodata(struct hci_conn *hcon)
+{
+ struct sco_conn *conn = hcon->sco_data;
+ struct sk_buff* skb = NULL;
+
+ BT_DBG("conn %p", conn);
+
+ if (conn) {
+ struct sock *sk = sco_chan_get(conn);
+ if(sk) {
+ skb = skb_dequeue(&sk->sk_write_queue);
+ }
+ }
+
+ if(skb == NULL && tx_quality_tune) {
+ printk(KERN_INFO "tx_quality_tune: audio cut !\n");
+ }
+
+ return skb;
+}
+
+/* Generic socket manipulations functions, adapted for SCO */
+
+static void sco_sock_wfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+
+ atomic_sub(1, &sk->sk_wmem_alloc);
+ if (!sock_flag(sk, SOCK_USE_WRITE_QUEUE))
+ sk->sk_write_space(sk);
+ sock_put(sk);
+}
+
+static void sco_sock_rfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+
+ atomic_sub(1, &sk->sk_rmem_alloc);
+}
+
+
+static long sock_wait_for_wmem(struct sock * sk, long timeo)
+{
+ DEFINE_WAIT(wait);
+
+ clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ for (;;) {
+ if (!timeo)
+ break;
+ if (signal_pending(current))
+ break;
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf)
+ break;
+ if (sk->sk_shutdown & SEND_SHUTDOWN)
+ break;
+ if (sk->sk_err)
+ break;
+ timeo = schedule_timeout(timeo);
+ }
+ finish_wait(sk->sk_sleep, &wait);
+ return timeo;
+}
+
+static struct sk_buff *sco_skb_send_alloc(struct sock *sk, unsigned long len,
+ int nb, int *errcode)
+{
+ struct sk_buff *skb;
+ gfp_t gfp_mask;
+ int err;
+ long timeo = nb ? 0 : MAX_SCHEDULE_TIMEOUT;
+
+ gfp_mask = sk->sk_allocation;
+ if (gfp_mask & __GFP_WAIT)
+ gfp_mask |= __GFP_REPEAT;
+
+ while (1) {
+ err = sock_error(sk);
+ if (err != 0)
+ goto failure;
+
+ err = -EPIPE;
+ if (sk->sk_shutdown & SEND_SHUTDOWN)
+ goto failure;
+
+ if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) {
+ skb = alloc_skb(len + BT_SKB_RESERVE, sk->sk_allocation);
+ if (skb) {
+ /* Full success... */
+ break;
+ }
+ err = -ENOBUFS;
+ goto failure;
+ }
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ err = -EAGAIN;
+ if (!timeo)
+ goto failure;
+ if (signal_pending(current))
+ goto interrupted;
+ timeo = sock_wait_for_wmem(sk, timeo);
+ }
+
+ sock_hold(sk);
+ skb->sk = sk;
+ skb->destructor = sco_sock_wfree;
+ atomic_add(1, &sk->sk_wmem_alloc);
+ skb_reserve(skb, BT_SKB_RESERVE);
+ bt_cb(skb)->incoming = 0;
+
+ return skb;
+
+interrupted:
+ err = sock_intr_errno(timeo);
+failure:
+ *errcode = err;
+ return NULL;
+}
+
+static int sco_sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ int err = 0;
+ int skb_len;
+
+ /* 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) >=
+ (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);
+
+ /* Cache the SKB length before we tack it onto the receive
+ * queue. Once it is added it no longer belongs to us and
+ * may be freed by other threads of control pulling packets
+ * from the queue.
+ */
+ skb_len = skb->len;
+
+ skb_queue_tail(&sk->sk_receive_queue, skb);
+
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_data_ready(sk, skb_len);
+out:
+ return err;
+}
+
+/* ------- Others ------- */
+
static ssize_t sco_sysfs_show(struct class *dev, char *buf)
{
struct sock *sk;
struct hlist_node *node;
char *str = buf;
@@ -943,13 +1201,14 @@
static struct hci_proto sco_hci_proto = {
.name = "SCO",
.id = HCI_PROTO_SCO,
.connect_ind = sco_connect_ind,
.connect_cfm = sco_connect_cfm,
.disconn_ind = sco_disconn_ind,
- .recv_scodata = sco_recv_scodata
+ .recv_scodata = sco_recv_scodata,
+ .get_scodata = sco_get_scodata
};
static int __init sco_init(void)
{
int err;
@@ -995,11 +1254,17 @@
proto_unregister(&sco_proto);
}
module_init(sco_init);
module_exit(sco_exit);
+module_param(tx_quality_tune, bool, 0644);
+MODULE_PARM_DESC(tx_quality_tune, "Print warning message when an audio cut will happen in tx");
+
+module_param(rx_quality_tune, bool, 0644);
+MODULE_PARM_DESC(rx_quality_tune, "Print warning message when an audio cut will happen in rx");
+
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("bt-proto-2");
[-- Attachment #4: bluez-sco-flowcontrol-3of3.diff --]
[-- Type: text/plain, Size: 2315 bytes --]
diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='sco.*' /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci.h /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci.h
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci.h 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci.h 2006-05-21 15:55:23.000000000 +0200
@@ -712,12 +712,15 @@
__u32 sco_tx;
__u32 sco_rx;
__u32 byte_rx;
__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];
bdaddr_t bdaddr;
@@ -727,15 +730,19 @@
__u8 features[8];
__u32 pkt_type;
__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;
};
struct hci_conn_info {
diff -rU 6 --exclude-from=kernelexcludes.txt --exclude='sco.*' /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci_core.h /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci_core.h
--- /home/fchevalier/tmp/linux-2.6.16.16-orig/include/net/bluetooth/hci_core.h 2006-05-11 03:56:24.000000000 +0200
+++ /home/fchevalier/tmp/linux-2.6.16.16/include/net/bluetooth/hci_core.h 2006-05-25 17:47:11.000000000 +0200
@@ -81,18 +81,24 @@
__u16 link_policy;
__u16 link_mode;
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 */
unsigned int 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;
[-- Attachment #5: Type: text/plain, Size: 0 bytes --]
[-- Attachment #6: Type: text/plain, Size: 164 bytes --]
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-05-30 13:28 ` Fabien Chevalier
@ 2006-06-01 19:15 ` Marcel Holtmann
2006-06-07 20:15 ` Fabien Chevalier
0 siblings, 1 reply; 14+ messages in thread
From: Marcel Holtmann @ 2006-06-01 19:15 UTC (permalink / raw)
To: BlueZ development
Hi Fabien,
> > please split the changes to Bluetooth core and SCO module. Your changes
> > to the core will conflict with the automatic sniff mode support and I
> > need to sort that out.
>
> Ok, i've just done that, i've split the patch into three smaller bits.
> - Patch part 1 are changes to hci core required for SCO flow control
> itself.
> - Patch part 2 are the changes to the SCO socket layer.
> - Patch part 3 are additionnal comments on hci core code.
>
> Please note however that part 1 and part 2 can not be applied seperately.
so far I only looked at the changes to the HCI core. Please don't rename
the disconnect timer. This will collide with the changes for automatic
sniff mode support.
Can we change the core and add a hci_stream_sco() without breaking the
API. This would make it easier for me to apply the changes.
Another question is if we really should queue the data in sk_write_queue
and not directly in the core. I would prefer to send the data to the
core and then stream it from there.
Regards
Marcel
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-06-01 19:15 ` Marcel Holtmann
@ 2006-06-07 20:15 ` Fabien Chevalier
2006-06-07 20:30 ` Marcel Holtmann
0 siblings, 1 reply; 14+ messages in thread
From: Fabien Chevalier @ 2006-06-07 20:15 UTC (permalink / raw)
To: BlueZ development
Hi Marcel,
Sorry to be a little late on this answer, however a busy work schedule
and a sunny Sunday prevented me to answer my e-mails for a few days :-)
> so far I only looked at the changes to the HCI core. Please don't rename
> the disconnect timer. This will collide with the changes for automatic
> sniff mode support.
Ok, however as now sniff mode is out, i would suggest i build on top of
it :-)
>
> Can we change the core and add a hci_stream_sco() without breaking the
> API. This would make it easier for me to apply the changes.
I see your point, however i don't see how to bring this feature in
without breaking the hci core API for SCO sockets... :-(
>
> Another question is if we really should queue the data in sk_write_queue
> and not directly in the core. I would prefer to send the data to the
> core and then stream it from there.
That's an interesting question... :-)
I asked it myself when i started to design this feature.
In fact having outgoing packets stored in a socket queue has some
advantages:
* it makes use of the BSD socket send queue, and thus can reuse some
generic socket queue management related functions, which rely on some
well known sk_buff variable (sk_sndbuff, sk_wmem_alloc, sk_rcvbuff,
sk_rmem_alloc ...)
* it makes the thing symetrical, both uplink and downlink path make
use socket queues to buffer data.
As such I thought there were not any valid reason to put in the core...
however if you have suggestions i would be very happy to hear them. :-)
For now i would suggest we do the following:
- for you: have a look at the "whole picture", including the SCO part
patch. This way you would be able to better understand the interaction
between SCO sockets & hci layer. This way you will be able to send in
other comments too. :-)
- for me: * port the patch on top of the 2.6.16-mh1 :-)
* have it to work also when HZ % 333 != 0 ( i've just seen
it wouldn't work properly in this case... )
What do you think of the idea ?
Cheers,
Fabien
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-06-07 20:15 ` Fabien Chevalier
@ 2006-06-07 20:30 ` Marcel Holtmann
2006-06-09 17:05 ` Fabien Chevalier
0 siblings, 1 reply; 14+ messages in thread
From: Marcel Holtmann @ 2006-06-07 20:30 UTC (permalink / raw)
To: BlueZ development
Hi Fabien,
> Sorry to be a little late on this answer, however a busy work schedule
> and a sunny Sunday prevented me to answer my e-mails for a few days :-)
no big deal. I should do this more often by myself ;)
> > Can we change the core and add a hci_stream_sco() without breaking the
> > API. This would make it easier for me to apply the changes.
>
> I see your point, however i don't see how to bring this feature in
> without breaking the hci core API for SCO sockets... :-(
No problem. We can break the API, but at least I need some good reason
for it.
> > Another question is if we really should queue the data in sk_write_queue
> > and not directly in the core. I would prefer to send the data to the
> > core and then stream it from there.
>
> That's an interesting question... :-)
> I asked it myself when i started to design this feature.
> In fact having outgoing packets stored in a socket queue has some
> advantages:
> * it makes use of the BSD socket send queue, and thus can reuse some
> generic socket queue management related functions, which rely on some
> well known sk_buff variable (sk_sndbuff, sk_wmem_alloc, sk_rcvbuff,
> sk_rmem_alloc ...)
> * it makes the thing symetrical, both uplink and downlink path make
> use socket queues to buffer data.
>
> As such I thought there were not any valid reason to put in the core...
> however if you have suggestions i would be very happy to hear them. :-)
Actually I never thought about SCO as socket is the best interface. So
we might wanna move away from it sometime in the future. And in this
case it would be great if it uses simple SKB queues or maybe kfifo for
it. Even if this sounds like more work right now, I might be worth it.
> For now i would suggest we do the following:
> - for you: have a look at the "whole picture", including the SCO part
> patch. This way you would be able to better understand the interaction
> between SCO sockets & hci layer. This way you will be able to send in
> other comments too. :-)
> - for me: * port the patch on top of the 2.6.16-mh1 :-)
> * have it to work also when HZ % 333 != 0 ( i've just seen
> it wouldn't work properly in this case... )
Let's have another patch on top of 2.6.16-mh1 and maybe you also wanna
adapt scotest for testing it.
Regards
Marcel
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-06-07 20:30 ` Marcel Holtmann
@ 2006-06-09 17:05 ` Fabien Chevalier
2006-06-09 20:17 ` Marcel Holtmann
0 siblings, 1 reply; 14+ messages in thread
From: Fabien Chevalier @ 2006-06-09 17:05 UTC (permalink / raw)
To: BlueZ development
Hi Marcel,
>>> Another question is if we really should queue the data in sk_write_queue
>>> and not directly in the core. I would prefer to send the data to the
>>> core and then stream it from there.
>> That's an interesting question... :-)
>> I asked it myself when i started to design this feature.
>> In fact having outgoing packets stored in a socket queue has some
>> advantages:
>> * it makes use of the BSD socket send queue, and thus can reuse some
>> generic socket queue management related functions, which rely on some
>> well known sk_buff variable (sk_sndbuff, sk_wmem_alloc, sk_rcvbuff,
>> sk_rmem_alloc ...)
>> * it makes the thing symetrical, both uplink and downlink path make
>> use socket queues to buffer data.
>>
>> As such I thought there were not any valid reason to put in the core...
>> however if you have suggestions i would be very happy to hear them. :-)
>
> Actually I never thought about SCO as socket is the best interface. So
> we might wanna move away from it sometime in the future. And in this
> case it would be great if it uses simple SKB queues or maybe kfifo for
> it. Even if this sounds like more work right now, I might be worth it.
It might make sense: however we must take into account the following
constraints:
- we must be aple to setup fifo lengths from userspace, so that to
simulate a variable size ring buffer as found on sound card hardware.
- we must be able to put the sending task to sleep, and wake it up
later, when the fifo gets 'full'.
I'm not against this, even if it means more work...
However i don't have any precise idea on which communication mechanism
to use between userspace/kernelspace. Any ideas ??
>
> Let's have another patch on top of 2.6.16-mh1 and maybe you also wanna
> adapt scotest for testing it.
>
Ok, i'm gonna work on that then...
Cheers,
Fabien
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-06-09 17:05 ` Fabien Chevalier
@ 2006-06-09 20:17 ` Marcel Holtmann
2006-06-10 9:24 ` Fabien Chevalier
2006-06-10 9:59 ` Fabien Chevalier
0 siblings, 2 replies; 14+ messages in thread
From: Marcel Holtmann @ 2006-06-09 20:17 UTC (permalink / raw)
To: BlueZ development
Hi Fabien,
> >>> Another question is if we really should queue the data in sk_write_queue
> >>> and not directly in the core. I would prefer to send the data to the
> >>> core and then stream it from there.
> >> That's an interesting question... :-)
> >> I asked it myself when i started to design this feature.
> >> In fact having outgoing packets stored in a socket queue has some
> >> advantages:
> >> * it makes use of the BSD socket send queue, and thus can reuse some
> >> generic socket queue management related functions, which rely on some
> >> well known sk_buff variable (sk_sndbuff, sk_wmem_alloc, sk_rcvbuff,
> >> sk_rmem_alloc ...)
> >> * it makes the thing symetrical, both uplink and downlink path make
> >> use socket queues to buffer data.
> >>
> >> As such I thought there were not any valid reason to put in the core...
> >> however if you have suggestions i would be very happy to hear them. :-)
> >
> > Actually I never thought about SCO as socket is the best interface. So
> > we might wanna move away from it sometime in the future. And in this
> > case it would be great if it uses simple SKB queues or maybe kfifo for
> > it. Even if this sounds like more work right now, I might be worth it.
>
> It might make sense: however we must take into account the following
> constraints:
> - we must be aple to setup fifo lengths from userspace, so that to
> simulate a variable size ring buffer as found on sound card hardware.
> - we must be able to put the sending task to sleep, and wake it up
> later, when the fifo gets 'full'.
I don't see any problems with both. We can use workqueues or tasklets
for the second one.
> I'm not against this, even if it means more work...
I think it will be worth it.
> However i don't have any precise idea on which communication mechanism
> to use between userspace/kernelspace. Any ideas ??
We might leave the socket for now. The alternative is a character device
or directly an ALSA sound card. Haven't fully thought about it either.
> > Let's have another patch on top of 2.6.16-mh1 and maybe you also wanna
> > adapt scotest for testing it.
> >
> Ok, i'm gonna work on that then...
The 2.6.16-mh2 is missing the automatic sniff mode patch, but only for
testing. It is coming back for sure.
Regards
Marcel
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-06-09 20:17 ` Marcel Holtmann
@ 2006-06-10 9:24 ` Fabien Chevalier
2006-06-10 22:22 ` Marcel Holtmann
2006-06-10 9:59 ` Fabien Chevalier
1 sibling, 1 reply; 14+ messages in thread
From: Fabien Chevalier @ 2006-06-10 9:24 UTC (permalink / raw)
To: BlueZ development
Hi Marcel,
>> I'm not against this, even if it means more work...
>
> I think it will be worth it.
>
>> However i don't have any precise idea on which communication mechanism
>> to use between userspace/kernelspace. Any ideas ??
>
> We might leave the socket for now. The alternative is a character device
> or directly an ALSA sound card. Haven't fully thought about it either.
For the ALSA sound card, this is basically what snd-bt-sco does. The
issue with this alternative, is that the headset&handsfree profile
mandates the use of an RFCOMM channel before to use SCO. Doing this at
kernel level will mean that SCO layer will be dependant on RFCOMM
sockets, or that user space will have to handle RFCOMM channel before to
open ALSA device. This is roughly the way btsco works today. The result
is not very pretty :-(
>
>>> Let's have another patch on top of 2.6.16-mh1 and maybe you also wanna
>>> adapt scotest for testing it.
>>>
>> Ok, i'm gonna work on that then...
>
> The 2.6.16-mh2 is missing the automatic sniff mode patch, but only for
> testing. It is coming back for sure.
Ok, i will stick with mh1 then....
Cheers,
Fabien
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-06-09 20:17 ` Marcel Holtmann
2006-06-10 9:24 ` Fabien Chevalier
@ 2006-06-10 9:59 ` Fabien Chevalier
2006-06-10 23:14 ` Marcel Holtmann
1 sibling, 1 reply; 14+ messages in thread
From: Fabien Chevalier @ 2006-06-10 9:59 UTC (permalink / raw)
To: BlueZ development
Hi Marcel,
A just thought to a few more things related to a sco socket.
I just though to a real advantage of using socket for SCO (vs ALSA
device or character device).
Using a character or block device usually means that the device is
connected. You can plug your USB mass storage device, and the /dev/sda
will be usefull.
Sockets are usually used to connect to devices that the OS does not know
if they exist or not (think to a distant IP host: the kernel has no way
of knowing if it is valid are not. The connect call will just ask the
kernel to 'give a try', and bail out if the device does not exist).
Having said that, this is really quite similar to the bluetooth
technology, where before you have tryed to connect, you don't know if
the device is on or off...)
And for kfifo use: it make sense, however the bad point is that all
bluez stack is coded with sk_buffs all over the place. Using kfifo
today will mean having to do the following convertion:
(hci core) kfifo --> (hci device) sk_buff
I think for now i'm gonna stick with sk_buffs :-)
However i will have a look to see how to queue sk_buffs at hci core
layer instead of socket layer :-)
Cheers,
Fabien
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-06-10 9:24 ` Fabien Chevalier
@ 2006-06-10 22:22 ` Marcel Holtmann
0 siblings, 0 replies; 14+ messages in thread
From: Marcel Holtmann @ 2006-06-10 22:22 UTC (permalink / raw)
To: BlueZ development
Hi Fabien,
> > We might leave the socket for now. The alternative is a character device
> > or directly an ALSA sound card. Haven't fully thought about it either.
>
> For the ALSA sound card, this is basically what snd-bt-sco does. The
> issue with this alternative, is that the headset&handsfree profile
> mandates the use of an RFCOMM channel before to use SCO. Doing this at
> kernel level will mean that SCO layer will be dependant on RFCOMM
> sockets, or that user space will have to handle RFCOMM channel before to
> open ALSA device. This is roughly the way btsco works today. The result
> is not very pretty :-(
except that we don't would use the SCO socket interface in the kernel
like the current snd-bt-sco is doing. And yes, we would need an extra
configuration channel, because using AT commands the kernel is not an
option at all.
Regards
Marcel
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Bluez-devel] [PATCH] SCO flow control
2006-06-10 9:59 ` Fabien Chevalier
@ 2006-06-10 23:14 ` Marcel Holtmann
0 siblings, 0 replies; 14+ messages in thread
From: Marcel Holtmann @ 2006-06-10 23:14 UTC (permalink / raw)
To: BlueZ development
Hi Fabien,
> A just thought to a few more things related to a sco socket.
> I just though to a real advantage of using socket for SCO (vs ALSA
> device or character device).
> Using a character or block device usually means that the device is
> connected. You can plug your USB mass storage device, and the /dev/sda
> will be usefull.
> Sockets are usually used to connect to devices that the OS does not know
> if they exist or not (think to a distant IP host: the kernel has no way
> of knowing if it is valid are not. The connect call will just ask the
> kernel to 'give a try', and bail out if the device does not exist).
> Having said that, this is really quite similar to the bluetooth
> technology, where before you have tryed to connect, you don't know if
> the device is on or off...)
lets stay with the sockets for now.
> And for kfifo use: it make sense, however the bad point is that all
> bluez stack is coded with sk_buffs all over the place. Using kfifo
> today will mean having to do the following convertion:
>
> (hci core) kfifo --> (hci device) sk_buff
>
> I think for now i'm gonna stick with sk_buffs :-)
> However i will have a look to see how to queue sk_buffs at hci core
> layer instead of socket layer :-)
Sounds like a good plan.
Regards
Marcel
_______________________________________________
Bluez-devel mailing list
Bluez-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2006-06-10 23:14 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-05-29 19:46 [Bluez-devel] [PATCH] SCO flow control Fabien Chevalier
2006-05-29 20:33 ` Marcel Holtmann
2006-05-29 21:39 ` Pieter Poorthuis
2006-05-29 22:21 ` Marcel Holtmann
2006-05-30 13:28 ` Fabien Chevalier
2006-06-01 19:15 ` Marcel Holtmann
2006-06-07 20:15 ` Fabien Chevalier
2006-06-07 20:30 ` Marcel Holtmann
2006-06-09 17:05 ` Fabien Chevalier
2006-06-09 20:17 ` Marcel Holtmann
2006-06-10 9:24 ` Fabien Chevalier
2006-06-10 22:22 ` Marcel Holtmann
2006-06-10 9:59 ` Fabien Chevalier
2006-06-10 23:14 ` Marcel Holtmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).