Bluetooth: Prioritize event packets over data packets
When there are packets available on both the event and data channels,
event packets must be read first. UART-based HCI controllers do this
prioritization before sending data over the serial connection, but
with SMD the HCI driver must serialize packets from the two channels.
CRs-fixed: 324277
Change-Id: Ic2b1ef21b2463b07eb20141f38f7cbfef1a9cd9c
Signed-off-by: Bhasker Neti <bneti@codeaurora.org>
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index 101697f..4e78152 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -2,7 +2,7 @@
* HCI_SMD (HCI Shared Memory Driver) is Qualcomm's Shared memory driver
* for the BT HCI protocol.
*
- * Copyright (c) 2000-2001, 2011 Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2000-2001, 2011-2012 Code Aurora Forum. All rights reserved.
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
*
@@ -51,8 +51,7 @@
struct wake_lock wake_lock_tx;
struct wake_lock wake_lock_rx;
struct timer_list rx_q_timer;
- struct tasklet_struct hci_event_task;
- struct tasklet_struct hci_data_task;
+ struct tasklet_struct rx_task;
};
static struct hci_smd_data hs;
@@ -129,111 +128,95 @@
kfree(hdev->driver_data);
}
-static void hci_smd_recv_data(unsigned long arg)
+static void hci_smd_recv_data(void)
{
int len = 0;
int rc = 0;
struct sk_buff *skb = NULL;
- unsigned char *buf = NULL;
struct hci_smd_data *hsmd = &hs;
wake_lock(&hs.wake_lock_rx);
len = smd_read_avail(hsmd->data_channel);
if (len > HCI_MAX_FRAME_SIZE) {
- BT_ERR("Frame larger than the allowed size");
+ BT_ERR("Frame larger than the allowed size, flushing frame");
+ smd_read(hsmd->data_channel, NULL, len);
goto out_data;
}
- while (len > 0) {
- skb = bt_skb_alloc(len, GFP_ATOMIC);
- if (!skb) {
- BT_ERR("Error in allocating socket buffer");
- goto out_data;
- }
- buf = kmalloc(len, GFP_ATOMIC);
- if (!buf) {
- BT_ERR("Error in allocating buffer");
- rc = -ENOMEM;
- goto out_data;
- }
+ if (len <= 0)
+ goto out_data;
- rc = smd_read(hsmd->data_channel, (void *)buf, len);
- if (rc < len) {
- BT_ERR("Error in reading from the channel");
- goto out_data;
- }
-
- memcpy(skb_put(skb, len), buf, len);
- skb->dev = (void *)hsmd->hdev;
- bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
-
- skb_orphan(skb);
-
- rc = hci_recv_frame(skb);
- if (rc < 0) {
- BT_ERR("Error in passing the packet to HCI Layer");
- /*
- * skb is getting freed in hci_recv_frame, making it
- * to null to avoid multiple access
- */
- skb = NULL;
- goto out_data;
- }
-
- kfree(buf);
- buf = NULL;
- len = smd_read_avail(hsmd->data_channel);
- /*
- * Start the timer to monitor whether the Rx queue is
- * empty for releasing the Rx wake lock
- */
- BT_DBG("Rx Timer is starting");
- mod_timer(&hsmd->rx_q_timer,
- jiffies + msecs_to_jiffies(RX_Q_MONITOR));
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("Error in allocating socket buffer");
+ smd_read(hsmd->data_channel, NULL, len);
+ goto out_data;
}
+
+ rc = smd_read(hsmd->data_channel, skb_put(skb, len), len);
+ if (rc < len) {
+ BT_ERR("Error in reading from the channel");
+ goto out_data;
+ }
+
+ skb->dev = (void *)hsmd->hdev;
+ bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+ skb_orphan(skb);
+
+ rc = hci_recv_frame(skb);
+ if (rc < 0) {
+ BT_ERR("Error in passing the packet to HCI Layer");
+ /*
+ * skb is getting freed in hci_recv_frame, making it
+ * to null to avoid multiple access
+ */
+ skb = NULL;
+ goto out_data;
+ }
+
+ /*
+ * Start the timer to monitor whether the Rx queue is
+ * empty for releasing the Rx wake lock
+ */
+ BT_DBG("Rx Timer is starting");
+ mod_timer(&hsmd->rx_q_timer,
+ jiffies + msecs_to_jiffies(RX_Q_MONITOR));
+
out_data:
release_lock();
- if (rc) {
- if (skb)
- kfree_skb(skb);
- kfree(buf);
- }
+ if (rc)
+ kfree_skb(skb);
}
-static void hci_smd_recv_event(unsigned long arg)
+static void hci_smd_recv_event(void)
{
int len = 0;
int rc = 0;
struct sk_buff *skb = NULL;
- unsigned char *buf = NULL;
struct hci_smd_data *hsmd = &hs;
wake_lock(&hs.wake_lock_rx);
len = smd_read_avail(hsmd->event_channel);
if (len > HCI_MAX_FRAME_SIZE) {
- BT_ERR("Frame larger than the allowed size");
+ BT_ERR("Frame larger than the allowed size, flushing frame");
+ rc = smd_read(hsmd->event_channel, NULL, len);
goto out_event;
}
while (len > 0) {
skb = bt_skb_alloc(len, GFP_ATOMIC);
if (!skb) {
- BT_ERR("Error in allocating socket buffer");
+ BT_ERR("Error in allocating socket buffer");
+ smd_read(hsmd->event_channel, NULL, len);
goto out_event;
}
- buf = kmalloc(len, GFP_ATOMIC);
- if (!buf) {
- BT_ERR("Error in allocating buffer");
- rc = -ENOMEM;
- goto out_event;
- }
- rc = smd_read(hsmd->event_channel, (void *)buf, len);
+
+ rc = smd_read(hsmd->event_channel, skb_put(skb, len), len);
if (rc < len) {
BT_ERR("Error in reading from the event channel");
goto out_event;
}
- memcpy(skb_put(skb, len), buf, len);
skb->dev = (void *)hsmd->hdev;
bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
@@ -250,8 +233,6 @@
goto out_event;
}
- kfree(buf);
- buf = NULL;
len = smd_read_avail(hsmd->event_channel);
/*
* Start the timer to monitor whether the Rx queue is
@@ -263,11 +244,8 @@
}
out_event:
release_lock();
- if (rc) {
- if (skb)
- kfree_skb(skb);
- kfree(buf);
- }
+ if (rc)
+ kfree_skb(skb);
}
static int hci_smd_send_frame(struct sk_buff *skb)
@@ -314,6 +292,16 @@
return ret;
}
+static void hci_smd_rx(unsigned long arg)
+{
+ struct hci_smd_data *hsmd = &hs;
+
+ while ((smd_read_avail(hsmd->event_channel) > 0) ||
+ (smd_read_avail(hsmd->data_channel) > 0)) {
+ hci_smd_recv_event();
+ hci_smd_recv_data();
+ }
+}
static void hci_smd_notify_event(void *data, unsigned int event)
{
@@ -330,7 +318,7 @@
case SMD_EVENT_DATA:
len = smd_read_avail(hsmd->event_channel);
if (len > 0)
- tasklet_hi_schedule(&hs.hci_event_task);
+ tasklet_hi_schedule(&hs.rx_task);
else if (len < 0)
BT_ERR("Failed to read event from smd %d", len);
@@ -355,7 +343,7 @@
int len = 0;
if (!hdev) {
- BT_ERR("HCI device (hdev=NULL)");
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
return;
}
@@ -363,7 +351,7 @@
case SMD_EVENT_DATA:
len = smd_read_avail(hsmd->data_channel);
if (len > 0)
- tasklet_hi_schedule(&hs.hci_data_task);
+ tasklet_hi_schedule(&hs.rx_task);
else if (len < 0)
BT_ERR("Failed to read data from smd %d", len);
break;
@@ -402,10 +390,9 @@
hdev->destruct = hci_smd_destruct;
hdev->owner = THIS_MODULE;
- tasklet_init(&hsmd->hci_event_task,
- hci_smd_recv_event, (unsigned long) hsmd);
- tasklet_init(&hsmd->hci_data_task,
- hci_smd_recv_data, (unsigned long) hsmd);
+
+ tasklet_init(&hsmd->rx_task,
+ hci_smd_rx, (unsigned long) hsmd);
/*
* Setup the timer to monitor whether the Rx queue is empty,
* to control the wake lock release
@@ -445,6 +432,8 @@
static void hci_smd_deregister_dev(struct hci_smd_data *hsmd)
{
+ tasklet_kill(&hs.rx_task);
+
if (hsmd->hdev) {
if (hci_unregister_dev(hsmd->hdev) < 0)
BT_ERR("Can't unregister HCI device %s",
@@ -468,9 +457,6 @@
hs.rx_q_timer.function = NULL;
hs.rx_q_timer.data = 0;
}
-
- tasklet_kill(&hs.hci_event_task);
- tasklet_kill(&hs.hci_data_task);
}
static int hcismd_set_enable(const char *val, struct kernel_param *kp)