[PATCH 0/8] caif-hsi: Bug-fixes for CAIF HSI.

STALE5426d

9 messages, 1 author, 2011-10-13 · open the first message on its own page

[PATCH 0/8] caif-hsi: Bug-fixes for CAIF HSI.

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:29:55

This patch-set contains our latest pile of HSI bug fixes and
performance improvements for CAIF HSI driver.

Patches should apply cleanly on both net and net-next.

Regards,
Sjur

Daniel Martensson (4):
  caif-hsi: Making read and writes asynchronous.
  caif-hsi: HSI-Platform device register and unregisters itself
  caif-hsi: Added sanity check for length of CAIF frames
  caif-hsi: Added recovery check of CA wake status.

Dmitry Tarnyagin (3):
  caif-hsi: Fixing a race condition in the caif_hsi code
  caif-hsi: Fix for wakeup condition problem
  caif-hsi: Make inactivity timeout configurable.

Sjur Brændeland (1):
  caif-hsi: HSI Fix uninitialized data in HSI header

 drivers/net/caif/caif_hsi.c |  427 +++++++++++++++++++++++++------------------
 include/net/caif/caif_hsi.h |   37 +++-
 2 files changed, 276 insertions(+), 188 deletions(-)

[PATCH 1/8] caif-hsi: HSI Fix uninitialized data in HSI header

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:29:56

CAIF HSI header may be uninitialized and cause last message to
be repeated if transmit size is ~86 bytes long.

Signed-off-by: Sjur Brændeland <redacted>
---
 drivers/net/caif/caif_hsi.c |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 2fcabba..1937813 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -178,6 +178,9 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 	if (!skb)
 		return 0;
 
+	/* Clear offset. */
+	desc->offset = 0;
+
 	/* Check if we can embed a CAIF frame. */
 	if (skb->len < CFHSI_MAX_EMB_FRM_SZ) {
 		struct caif_payload_info *info;
@@ -206,9 +209,7 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 			consume_skb(skb);
 			skb = NULL;
 		}
-	} else
-		/* Clear offset. */
-		desc->offset = 0;
+	}
 
 	/* Create payload CAIF frames. */
 	pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
@@ -990,6 +991,8 @@ int cfhsi_probe(struct platform_device *pdev)
 	/* Set up the driver. */
 	cfhsi->drv.tx_done_cb = cfhsi_tx_done_cb;
 	cfhsi->drv.rx_done_cb = cfhsi_rx_done_cb;
+	cfhsi->drv.wake_up_cb = cfhsi_wake_up_cb;
+	cfhsi->drv.wake_down_cb = cfhsi_wake_down_cb;
 
 	/* Initialize the work queues. */
 	INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up);
@@ -1045,9 +1048,6 @@ int cfhsi_probe(struct platform_device *pdev)
 		goto err_net_reg;
 	}
 
-	cfhsi->drv.wake_up_cb = cfhsi_wake_up_cb;
-	cfhsi->drv.wake_down_cb = cfhsi_wake_down_cb;
-
 	/* Register network device. */
 	res = register_netdev(ndev);
 	if (res) {
-- 
1.7.0.4

[PATCH 2/8] caif-hsi: Fixing a race condition in the caif_hsi code

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:29:57

From: Dmitry Tarnyagin <redacted>

cfhsi->tx_state was not protected by a spin lock. TX soft-irq could interrupt
cfhsi_tx_done_work work leading to inconsistent state of the driver.

Signed-off-by: Sjur Brændeland <redacted>
---
 drivers/net/caif/caif_hsi.c |   25 ++++++++++++++++++-------
 1 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 1937813..36da27b 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -304,14 +304,22 @@ static void cfhsi_tx_done_work(struct work_struct *work)
 		spin_unlock_bh(&cfhsi->lock);
 
 		/* Create HSI frame. */
-		len = cfhsi_tx_frm(desc, cfhsi);
-		if (!len) {
-			cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
-			/* Start inactivity timer. */
-			mod_timer(&cfhsi->timer,
+		do {
+			len = cfhsi_tx_frm(desc, cfhsi);
+			if (!len) {
+				spin_lock_bh(&cfhsi->lock);
+				if (unlikely(skb_peek(&cfhsi->qhead))) {
+					spin_unlock_bh(&cfhsi->lock);
+					continue;
+				}
+				cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
+				/* Start inactivity timer. */
+				mod_timer(&cfhsi->timer,
 					jiffies + CFHSI_INACTIVITY_TOUT);
-			break;
-		}
+				spin_unlock_bh(&cfhsi->lock);
+				goto done;
+			}
+		} while (!len);
 
 		/* Set up new transfer. */
 		res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev);
@@ -320,6 +328,9 @@ static void cfhsi_tx_done_work(struct work_struct *work)
 				__func__, res);
 		}
 	} while (res < 0);
+
+done:
+	return;
 }
 
 static void cfhsi_tx_done_cb(struct cfhsi_drv *drv)
-- 
1.7.0.4

[PATCH 3/8] caif-hsi: Fix for wakeup condition problem

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:29:58

From: Dmitry Tarnyagin <redacted>

Under stressed conditions a race could happen when del_timer_sync() was called
from softirq context at the same time when mod_timer_pending() for the same
timer was called from the workqueue. This leaded to a state mismatch in the
CAIF HSI driver and following unexpected link wakeup procedure.

The fix puts del_timer_sync() and mod_timer_pending() calls under a spin lock
to protect against the race condition.

Signed-off-by: Sjur Brændeland <redacted>
---
 drivers/net/caif/caif_hsi.c |   10 +++++++---
 1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 36da27b..82c4d6c 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -551,7 +551,9 @@ static void cfhsi_rx_done_work(struct work_struct *work)
 		return;
 
 	/* Update inactivity timer if pending. */
+	spin_lock_bh(&cfhsi->lock);
 	mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT);
+	spin_unlock_bh(&cfhsi->lock);
 
 	if (cfhsi->rx_state == CFHSI_RX_STATE_DESC) {
 		desc_pld_len = cfhsi_rx_desc(desc, cfhsi);
@@ -866,10 +868,10 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
 		start_xfer = 1;
 	}
 
-	spin_unlock_bh(&cfhsi->lock);
-
-	if (!start_xfer)
+	if (!start_xfer) {
+		spin_unlock_bh(&cfhsi->lock);
 		return 0;
+	}
 
 	/* Delete inactivity timer if started. */
 #ifdef CONFIG_SMP
@@ -878,6 +880,8 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
 	timer_active = del_timer(&cfhsi->timer);
 #endif /* CONFIG_SMP */
 
+	spin_unlock_bh(&cfhsi->lock);
+
 	if (timer_active) {
 		struct cfhsi_desc *desc = (struct cfhsi_desc *)cfhsi->tx_buf;
 		int len;
-- 
1.7.0.4

[PATCH 4/8] caif-hsi: Making read and writes asynchronous.

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:29:59

From: Daniel Martensson <redacted>

Some platforms do not allow to put HSI block into low-power
mode when FIFO is not empty. The patch flushes (by reading)
FIFO at wake down sequence. Asynchronous read and write is
implemented for that. As a side effect this will also greatly
improve performance.

Signed-off-by: Sjur Brændeland <redacted>
---
 drivers/net/caif/caif_hsi.c |  263 +++++++++++++++++++++----------------------
 include/net/caif/caif_hsi.h |   29 ++++--
 2 files changed, 147 insertions(+), 145 deletions(-)
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 82c4d6c..478b025 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -145,7 +145,7 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi)
 		}
 
 		ret = 5 * HZ;
-		wait_event_interruptible_timeout(cfhsi->flush_fifo_wait,
+		ret = wait_event_interruptible_timeout(cfhsi->flush_fifo_wait,
 			 !test_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits), ret);
 
 		if (ret < 0) {
@@ -272,16 +272,13 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 	return CFHSI_DESC_SZ + pld_len;
 }
 
-static void cfhsi_tx_done_work(struct work_struct *work)
+static void cfhsi_tx_done(struct cfhsi *cfhsi)
 {
-	struct cfhsi *cfhsi = NULL;
 	struct cfhsi_desc *desc = NULL;
 	int len = 0;
 	int res;
 
-	cfhsi = container_of(work, struct cfhsi, tx_done_work);
-	dev_dbg(&cfhsi->ndev->dev, "%s.\n",
-		__func__);
+	dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__);
 
 	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
 		return;
@@ -343,11 +340,11 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv)
 
 	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
 		return;
-
-	queue_work(cfhsi->wq, &cfhsi->tx_done_work);
+	cfhsi_tx_done(cfhsi);
 }
 
-static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
+static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
+				bool *dump)
 {
 	int xfer_sz = 0;
 	int nfrms = 0;
@@ -358,6 +355,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 			(desc->offset > CFHSI_MAX_EMB_FRM_SZ)) {
 		dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
 			__func__);
+		*dump = true;
 		return 0;
 	}
 
@@ -365,7 +363,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 	if (desc->offset) {
 		struct sk_buff *skb;
 		u8 *dst = NULL;
-		int len = 0, retries = 0;
+		int len = 0;
 		pfrm = ((u8 *)desc) + desc->offset;
 
 		/* Remove offset padding. */
@@ -378,24 +376,11 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 
 
 		/* Allocate SKB (OK even in IRQ context). */
-		skb = alloc_skb(len + 1, GFP_KERNEL);
-		while (!skb) {
-			retries++;
-			schedule_timeout(1);
-			skb = alloc_skb(len + 1, GFP_KERNEL);
-			if (skb) {
-				printk(KERN_WARNING "%s: slept for %u "
-						"before getting memory\n",
-						__func__, retries);
-				break;
-			}
-			if (retries > HZ) {
-				printk(KERN_ERR "%s: slept for 1HZ and "
-						"did not get memory\n",
-						__func__);
-				cfhsi->ndev->stats.rx_dropped++;
-				goto drop_frame;
-			}
+		skb = alloc_skb(len + 1, GFP_ATOMIC);
+		if (!skb) {
+			dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n",
+				__func__);
+			return -ENOMEM;
 		}
 		caif_assert(skb != NULL);
 
@@ -421,7 +406,6 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 		cfhsi->ndev->stats.rx_bytes += len;
 	}
 
-drop_frame:
 	/* Calculate transfer length. */
 	plen = desc->cffrm_len;
 	while (nfrms < CFHSI_MAX_PKTS && *plen) {
@@ -439,12 +423,13 @@ drop_frame:
 				"%s: Invalid payload len: %d, ignored.\n",
 			__func__, xfer_sz);
 		xfer_sz = 0;
+		*dump = true;
 	}
-
 	return xfer_sz;
 }
 
-static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
+static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
+				bool *dump)
 {
 	int rx_sz = 0;
 	int nfrms = 0;
@@ -456,21 +441,33 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 			(desc->offset > CFHSI_MAX_EMB_FRM_SZ))) {
 		dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
 			__func__);
+		*dump = true;
 		return -EINVAL;
 	}
 
 	/* Set frame pointer to start of payload. */
 	pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
 	plen = desc->cffrm_len;
+
+	/* Skip already processed frames. */
+	while (nfrms < cfhsi->rx_state.nfrms) {
+		pfrm += *plen;
+		rx_sz += *plen;
+		plen++;
+		nfrms++;
+	}
+
+	/* Parse payload. */
 	while (nfrms < CFHSI_MAX_PKTS && *plen) {
 		struct sk_buff *skb;
 		u8 *dst = NULL;
 		u8 *pcffrm = NULL;
-		int len = 0, retries = 0;
+		int len = 0;
 
 		if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) {
 			dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n",
 				__func__);
+			*dump = true;
 			return -EINVAL;
 		}
 
@@ -483,24 +480,12 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 		len += 2;	/* Add FCS fields. */
 
 		/* Allocate SKB (OK even in IRQ context). */
-		skb = alloc_skb(len + 1, GFP_KERNEL);
-		while (!skb) {
-			retries++;
-			schedule_timeout(1);
-			skb = alloc_skb(len + 1, GFP_KERNEL);
-			if (skb) {
-				printk(KERN_WARNING "%s: slept for %u "
-						"before getting memory\n",
-						__func__, retries);
-				break;
-			}
-			if (retries > HZ) {
-				printk(KERN_ERR "%s: slept for 1HZ "
-						"and did not get memory\n",
-						__func__);
-				cfhsi->ndev->stats.rx_dropped++;
-				goto drop_frame;
-			}
+		skb = alloc_skb(len + 1, GFP_ATOMIC);
+		if (!skb) {
+			dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n",
+				__func__);
+			cfhsi->rx_state.nfrms = nfrms;
+			return -ENOMEM;
 		}
 		caif_assert(skb != NULL);
 
@@ -524,7 +509,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 		cfhsi->ndev->stats.rx_packets++;
 		cfhsi->ndev->stats.rx_bytes += len;
 
-drop_frame:
 		pfrm += *plen;
 		rx_sz += *plen;
 		plen++;
@@ -534,18 +518,16 @@ drop_frame:
 	return rx_sz;
 }
 
-static void cfhsi_rx_done_work(struct work_struct *work)
+static void cfhsi_rx_done(struct cfhsi *cfhsi)
 {
 	int res;
 	int desc_pld_len = 0;
-	struct cfhsi *cfhsi = NULL;
 	struct cfhsi_desc *desc = NULL;
+	bool dump = false;
 
-	cfhsi = container_of(work, struct cfhsi, rx_done_work);
 	desc = (struct cfhsi_desc *)cfhsi->rx_buf;
 
-	dev_dbg(&cfhsi->ndev->dev, "%s: Kick timer if pending.\n",
-		__func__);
+	dev_dbg(&cfhsi->ndev->dev, "%s\n", __func__);
 
 	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
 		return;
@@ -555,21 +537,33 @@ static void cfhsi_rx_done_work(struct work_struct *work)
 	mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT);
 	spin_unlock_bh(&cfhsi->lock);
 
-	if (cfhsi->rx_state == CFHSI_RX_STATE_DESC) {
-		desc_pld_len = cfhsi_rx_desc(desc, cfhsi);
+	if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
+		desc_pld_len = cfhsi_rx_desc(desc, cfhsi, &dump);
+		if (desc_pld_len == -ENOMEM)
+			goto restart;
 	} else {
 		int pld_len;
 
-		pld_len = cfhsi_rx_pld(desc, cfhsi);
+		if (!cfhsi->rx_state.piggy_desc) {
+			pld_len = cfhsi_rx_pld(desc, cfhsi, &dump);
+			if (pld_len == -ENOMEM)
+				goto restart;
+			cfhsi->rx_state.pld_len = pld_len;
+		} else {
+			pld_len = cfhsi->rx_state.pld_len;
+		}
 
 		if ((pld_len > 0) && (desc->header & CFHSI_PIGGY_DESC)) {
 			struct cfhsi_desc *piggy_desc;
 			piggy_desc = (struct cfhsi_desc *)
 				(desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ +
 						pld_len);
+			cfhsi->rx_state.piggy_desc = true;
 
 			/* Extract piggy-backed descriptor. */
-			desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi);
+			desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi, &dump);
+			if (desc_pld_len == -ENOMEM)
+				goto restart;
 
 			/*
 			 * Copy needed information from the piggy-backed
@@ -580,16 +574,24 @@ static void cfhsi_rx_done_work(struct work_struct *work)
 		}
 	}
 
+	if (unlikely(dump)) {
+		size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf;
+		dev_err(&cfhsi->ndev->dev, "%s: RX offset: %u.\n",
+			__func__, (unsigned) rx_offset);
+		print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE,
+				cfhsi->rx_buf, cfhsi->rx_len + rx_offset);
+	}
+
+	memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
 	if (desc_pld_len) {
-		cfhsi->rx_state = CFHSI_RX_STATE_PAYLOAD;
+		cfhsi->rx_state.state = CFHSI_RX_STATE_PAYLOAD;
 		cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ;
 		cfhsi->rx_len = desc_pld_len;
 	} else {
-		cfhsi->rx_state = CFHSI_RX_STATE_DESC;
+		cfhsi->rx_state.state = CFHSI_RX_STATE_DESC;
 		cfhsi->rx_ptr = cfhsi->rx_buf;
 		cfhsi->rx_len = CFHSI_DESC_SZ;
 	}
-	clear_bit(CFHSI_PENDING_RX, &cfhsi->bits);
 
 	if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) {
 		/* Set up new transfer. */
@@ -604,6 +606,26 @@ static void cfhsi_rx_done_work(struct work_struct *work)
 			cfhsi->ndev->stats.rx_dropped++;
 		}
 	}
+	return;
+
+restart:
+	if (++cfhsi->rx_state.retries > CFHSI_MAX_RX_RETRIES) {
+		dev_err(&cfhsi->ndev->dev, "%s: No memory available "
+			"in %d iterations.\n",
+			__func__, CFHSI_MAX_RX_RETRIES);
+		BUG();
+	}
+	mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1);
+}
+
+static void cfhsi_rx_slowpath(unsigned long arg)
+{
+	struct cfhsi *cfhsi = (struct cfhsi *)arg;
+
+	dev_dbg(&cfhsi->ndev->dev, "%s.\n",
+		__func__);
+
+	cfhsi_rx_done(cfhsi);
 }
 
 static void cfhsi_rx_done_cb(struct cfhsi_drv *drv)
@@ -617,12 +639,10 @@ static void cfhsi_rx_done_cb(struct cfhsi_drv *drv)
 	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
 		return;
 
-	set_bit(CFHSI_PENDING_RX, &cfhsi->bits);
-
 	if (test_and_clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits))
 		wake_up_interruptible(&cfhsi->flush_fifo_wait);
 	else
-		queue_work(cfhsi->wq, &cfhsi->rx_done_work);
+		cfhsi_rx_done(cfhsi);
 }
 
 static void cfhsi_wake_up(struct work_struct *work)
@@ -651,9 +671,9 @@ static void cfhsi_wake_up(struct work_struct *work)
 		__func__);
 
 	/* Wait for acknowledge. */
-	ret = CFHSI_WAKEUP_TOUT;
-	wait_event_interruptible_timeout(cfhsi->wake_up_wait,
-					test_bit(CFHSI_WAKE_UP_ACK,
+	ret = CFHSI_WAKE_TOUT;
+	ret = wait_event_interruptible_timeout(cfhsi->wake_up_wait,
+					test_and_clear_bit(CFHSI_WAKE_UP_ACK,
 							&cfhsi->bits), ret);
 	if (unlikely(ret < 0)) {
 		/* Interrupted by signal. */
@@ -678,16 +698,11 @@ static void cfhsi_wake_up(struct work_struct *work)
 	clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
 
 	/* Resume read operation. */
-	if (!test_bit(CFHSI_PENDING_RX, &cfhsi->bits)) {
-		dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n",
-			__func__);
-		res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr,
-				cfhsi->rx_len, cfhsi->dev);
-		if (WARN_ON(res < 0)) {
-			dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n",
-				__func__, res);
-		}
-	}
+	dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", __func__);
+	res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->dev);
+
+	if (WARN_ON(res < 0))
+		dev_err(&cfhsi->ndev->dev, "%s: RX err %d.\n", __func__, res);
 
 	/* Clear power up acknowledment. */
 	clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
@@ -726,50 +741,29 @@ static void cfhsi_wake_up(struct work_struct *work)
 				"%s: Failed to create HSI frame: %d.\n",
 				__func__, len);
 	}
-
 }
 
 static void cfhsi_wake_down(struct work_struct *work)
 {
 	long ret;
 	struct cfhsi *cfhsi = NULL;
-	size_t fifo_occupancy;
+	size_t fifo_occupancy = 0;
+	int retry = CFHSI_WAKE_TOUT;
 
 	cfhsi = container_of(work, struct cfhsi, wake_down_work);
-	dev_dbg(&cfhsi->ndev->dev, "%s.\n",
-		__func__);
+	dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__);
 
 	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
 		return;
 
-	/* Check if there is something in FIFO. */
-	if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
-							&fifo_occupancy)))
-		fifo_occupancy = 0;
-
-	if (fifo_occupancy) {
-		dev_dbg(&cfhsi->ndev->dev,
-				"%s: %u words in RX FIFO, restart timer.\n",
-				__func__, (unsigned) fifo_occupancy);
-		spin_lock_bh(&cfhsi->lock);
-		mod_timer(&cfhsi->timer,
-				jiffies + CFHSI_INACTIVITY_TOUT);
-		spin_unlock_bh(&cfhsi->lock);
-		return;
-	}
-
-	/* Cancel pending RX requests */
-	cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
-
 	/* Deactivate wake line. */
 	cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
 
 	/* Wait for acknowledge. */
-	ret = CFHSI_WAKEUP_TOUT;
+	ret = CFHSI_WAKE_TOUT;
 	ret = wait_event_interruptible_timeout(cfhsi->wake_down_wait,
-					test_bit(CFHSI_WAKE_DOWN_ACK,
-							&cfhsi->bits),
-					ret);
+					test_and_clear_bit(CFHSI_WAKE_DOWN_ACK,
+							&cfhsi->bits), ret);
 	if (ret < 0) {
 		/* Interrupted by signal. */
 		dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
@@ -777,28 +771,31 @@ static void cfhsi_wake_down(struct work_struct *work)
 		return;
 	} else if (!ret) {
 		/* Timeout */
-		dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n",
-			__func__);
+		dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__);
 	}
 
-	/* Clear power down acknowledment. */
-	clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits);
+	/* Check FIFO occupancy. */
+	while (retry) {
+		WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
+							&fifo_occupancy));
+
+		if (!fifo_occupancy)
+			break;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		retry--;
+	}
+
+	if (!retry)
+		dev_err(&cfhsi->ndev->dev, "%s: FIFO Timeout.\n", __func__);
+
+	/* Clear AWAKE condition. */
 	clear_bit(CFHSI_AWAKE, &cfhsi->bits);
 
-	/* Check if there is something in FIFO. */
-	if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
-							&fifo_occupancy)))
-		fifo_occupancy = 0;
+	/* Cancel pending RX requests. */
+	cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
 
-	if (fifo_occupancy) {
-		dev_dbg(&cfhsi->ndev->dev,
-				"%s: %u words in RX FIFO, wakeup forced.\n",
-				__func__, (unsigned) fifo_occupancy);
-		if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits))
-			queue_work(cfhsi->wq, &cfhsi->wake_up_work);
-	} else
-		dev_dbg(&cfhsi->ndev->dev, "%s: Done.\n",
-			__func__);
 }
 
 static void cfhsi_wake_up_cb(struct cfhsi_drv *drv)
@@ -874,11 +871,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
 	}
 
 	/* Delete inactivity timer if started. */
-#ifdef CONFIG_SMP
 	timer_active = del_timer_sync(&cfhsi->timer);
-#else
-	timer_active = del_timer(&cfhsi->timer);
-#endif /* CONFIG_SMP */
 
 	spin_unlock_bh(&cfhsi->lock);
 
@@ -962,7 +955,7 @@ int cfhsi_probe(struct platform_device *pdev)
 
 	/* Initialize state vaiables. */
 	cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
-	cfhsi->rx_state = CFHSI_RX_STATE_DESC;
+	cfhsi->rx_state.state = CFHSI_RX_STATE_DESC;
 
 	/* Set flow info */
 	cfhsi->flow_off_sent = 0;
@@ -1012,15 +1005,12 @@ int cfhsi_probe(struct platform_device *pdev)
 	/* Initialize the work queues. */
 	INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up);
 	INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down);
-	INIT_WORK(&cfhsi->rx_done_work, cfhsi_rx_done_work);
-	INIT_WORK(&cfhsi->tx_done_work, cfhsi_tx_done_work);
 
 	/* Clear all bit fields. */
 	clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
 	clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits);
 	clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
 	clear_bit(CFHSI_AWAKE, &cfhsi->bits);
-	clear_bit(CFHSI_PENDING_RX, &cfhsi->bits);
 
 	/* Create work thread. */
 	cfhsi->wq = create_singlethread_workqueue(pdev->name);
@@ -1040,6 +1030,10 @@ int cfhsi_probe(struct platform_device *pdev)
 	init_timer(&cfhsi->timer);
 	cfhsi->timer.data = (unsigned long)cfhsi;
 	cfhsi->timer.function = cfhsi_inactivity_tout;
+	/* Setup the slowpath RX timer. */
+	init_timer(&cfhsi->rx_slowpath_timer);
+	cfhsi->rx_slowpath_timer.data = (unsigned long)cfhsi;
+	cfhsi->rx_slowpath_timer.function = cfhsi_rx_slowpath;
 
 	/* Add CAIF HSI device to list. */
 	spin_lock(&cfhsi_list_lock);
@@ -1110,12 +1104,9 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev)
 	/* Flush workqueue */
 	flush_workqueue(cfhsi->wq);
 
-	/* Delete timer if pending */
-#ifdef CONFIG_SMP
+	/* Delete timers if pending */
 	del_timer_sync(&cfhsi->timer);
-#else
-	del_timer(&cfhsi->timer);
-#endif /* CONFIG_SMP */
+	del_timer_sync(&cfhsi->rx_slowpath_timer);
 
 	/* Cancel pending RX request (if any) */
 	cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h
index c5dedd8..17dff45 100644
--- a/include/net/caif/caif_hsi.h
+++ b/include/net/caif/caif_hsi.h
@@ -75,18 +75,21 @@ struct cfhsi_desc {
 #define CFHSI_WAKE_UP_ACK			1
 #define CFHSI_WAKE_DOWN_ACK			2
 #define CFHSI_AWAKE				3
-#define CFHSI_PENDING_RX			4
-#define CFHSI_SHUTDOWN				6
-#define CFHSI_FLUSH_FIFO			7
+#define CFHSI_WAKELOCK_HELD			4
+#define CFHSI_SHUTDOWN				5
+#define CFHSI_FLUSH_FIFO			6
 
 #ifndef CFHSI_INACTIVITY_TOUT
 #define CFHSI_INACTIVITY_TOUT			(1 * HZ)
 #endif /* CFHSI_INACTIVITY_TOUT */
 
-#ifndef CFHSI_WAKEUP_TOUT
-#define CFHSI_WAKEUP_TOUT			(3 * HZ)
-#endif /* CFHSI_WAKEUP_TOUT */
+#ifndef CFHSI_WAKE_TOUT
+#define CFHSI_WAKE_TOUT			(3 * HZ)
+#endif /* CFHSI_WAKE_TOUT */
 
+#ifndef CFHSI_MAX_RX_RETRIES
+#define CFHSI_MAX_RX_RETRIES		(10 * HZ)
+#endif
 
 /* Structure implemented by the CAIF HSI driver. */
 struct cfhsi_drv {
@@ -109,6 +112,15 @@ struct cfhsi_dev {
 	struct cfhsi_drv *drv;
 };
 
+/* Structure holds status of received CAIF frames processing */
+struct cfhsi_rx_state {
+	int state;
+	int nfrms;
+	int pld_len;
+	int retries;
+	bool piggy_desc;
+};
+
 /* Structure implemented by CAIF HSI drivers. */
 struct cfhsi {
 	struct caif_dev_common cfdev;
@@ -118,7 +130,7 @@ struct cfhsi {
 	struct cfhsi_drv drv;
 	struct cfhsi_dev *dev;
 	int tx_state;
-	int rx_state;
+	struct cfhsi_rx_state rx_state;
 	int rx_len;
 	u8 *rx_ptr;
 	u8 *tx_buf;
@@ -130,13 +142,12 @@ struct cfhsi {
 	struct list_head list;
 	struct work_struct wake_up_work;
 	struct work_struct wake_down_work;
-	struct work_struct rx_done_work;
-	struct work_struct tx_done_work;
 	struct workqueue_struct *wq;
 	wait_queue_head_t wake_up_wait;
 	wait_queue_head_t wake_down_wait;
 	wait_queue_head_t flush_fifo_wait;
 	struct timer_list timer;
+	struct timer_list rx_slowpath_timer;
 	unsigned long bits;
 };
 
-- 
1.7.0.4

[PATCH 6/8] caif-hsi: Make inactivity timeout configurable.

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:30:00

From: Dmitry Tarnyagin <redacted>

CAIF HSI uses a timer for inactivity. Upon timeout HSI-wake signaling
is initiated to allow power-down of the HSI block.

Signed-off-by: Sjur Brændeland <redacted>
---
 drivers/net/caif/caif_hsi.c |   28 +++++++++++++++++++++++-----
 include/net/caif/caif_hsi.h |    1 +
 2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index f46ab4d..1e1f0a3 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -29,6 +29,10 @@ MODULE_DESCRIPTION("CAIF HSI driver");
 #define PAD_POW2(x, pow) ((((x)&((pow)-1)) == 0) ? 0 :\
 				(((pow)-((x)&((pow)-1)))))
 
+static int inactivity_timeout = 1000;
+module_param(inactivity_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(inactivity_timeout, "Inactivity timeout on HSI, ms.");
+
 /*
  * HSI padding options.
  * Warning: must be a base of 2 (& operation used) and can not be zero !
@@ -98,7 +102,8 @@ static void cfhsi_abort_tx(struct cfhsi *cfhsi)
 	}
 	cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
 	if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
-		mod_timer(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT);
+		mod_timer(&cfhsi->timer,
+			jiffies + cfhsi->inactivity_timeout);
 	spin_unlock_bh(&cfhsi->lock);
 }
 
@@ -312,7 +317,7 @@ static void cfhsi_tx_done(struct cfhsi *cfhsi)
 				cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
 				/* Start inactivity timer. */
 				mod_timer(&cfhsi->timer,
-					jiffies + CFHSI_INACTIVITY_TOUT);
+					jiffies + cfhsi->inactivity_timeout);
 				spin_unlock_bh(&cfhsi->lock);
 				goto done;
 			}
@@ -534,7 +539,8 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
 
 	/* Update inactivity timer if pending. */
 	spin_lock_bh(&cfhsi->lock);
-	mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT);
+	mod_timer_pending(&cfhsi->timer,
+			jiffies + cfhsi->inactivity_timeout);
 	spin_unlock_bh(&cfhsi->lock);
 
 	if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
@@ -715,7 +721,7 @@ static void cfhsi_wake_up(struct work_struct *work)
 			__func__);
 		/* Start inactivity timer. */
 		mod_timer(&cfhsi->timer,
-				jiffies + CFHSI_INACTIVITY_TOUT);
+				jiffies + cfhsi->inactivity_timeout);
 		spin_unlock_bh(&cfhsi->lock);
 		return;
 	}
@@ -989,7 +995,19 @@ int cfhsi_probe(struct platform_device *pdev)
 		goto err_alloc_rx;
 	}
 
-	/* Initialize receive variables. */
+	/* Pre-calculate inactivity timeout. */
+	if (inactivity_timeout != -1) {
+		cfhsi->inactivity_timeout =
+				inactivity_timeout * HZ / 1000;
+		if (!cfhsi->inactivity_timeout)
+			cfhsi->inactivity_timeout = 1;
+		else if (cfhsi->inactivity_timeout > NEXT_TIMER_MAX_DELTA)
+			cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA;
+	} else {
+		cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA;
+	}
+
+	/* Initialize recieve vaiables. */
 	cfhsi->rx_ptr = cfhsi->rx_buf;
 	cfhsi->rx_len = CFHSI_DESC_SZ;
 
diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h
index 17dff45..9b69d15 100644
--- a/include/net/caif/caif_hsi.h
+++ b/include/net/caif/caif_hsi.h
@@ -131,6 +131,7 @@ struct cfhsi {
 	struct cfhsi_dev *dev;
 	int tx_state;
 	struct cfhsi_rx_state rx_state;
+	unsigned long inactivity_timeout;
 	int rx_len;
 	u8 *rx_ptr;
 	u8 *tx_buf;
-- 
1.7.0.4

[PATCH 5/8] caif-hsi: HSI-Platform device register and unregisters itself

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:30:00

From: Daniel Martensson <redacted>

Platform device is no longer removed from caif_hsi at shutdown.
The HSI-platform device must do it's own registration and unregistration.

Signed-off-by: Sjur Brændeland <redacted>
---
 drivers/net/caif/caif_hsi.c |   16 ++++------------
 1 files changed, 4 insertions(+), 12 deletions(-)
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 478b025..f46ab4d 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -1083,7 +1083,7 @@ int cfhsi_probe(struct platform_device *pdev)
 	return res;
 }
 
-static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev)
+static void cfhsi_shutdown(struct cfhsi *cfhsi)
 {
 	u8 *tx_buf, *rx_buf;
 
@@ -1093,14 +1093,6 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev)
 	/* going to shutdown driver */
 	set_bit(CFHSI_SHUTDOWN, &cfhsi->bits);
 
-	if (remove_platform_dev) {
-		/* Flush workqueue */
-		flush_workqueue(cfhsi->wq);
-
-		/* Notify device. */
-		platform_device_unregister(cfhsi->pdev);
-	}
-
 	/* Flush workqueue */
 	flush_workqueue(cfhsi->wq);
 
@@ -1111,7 +1103,7 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev)
 	/* Cancel pending RX request (if any) */
 	cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
 
-	/* Flush again and destroy workqueue */
+	/* Destroy workqueue */
 	destroy_workqueue(cfhsi->wq);
 
 	/* Store bufferes: will be freed later. */
@@ -1150,7 +1142,7 @@ int cfhsi_remove(struct platform_device *pdev)
 			spin_unlock(&cfhsi_list_lock);
 
 			/* Shutdown driver. */
-			cfhsi_shutdown(cfhsi, false);
+			cfhsi_shutdown(cfhsi);
 
 			return 0;
 		}
@@ -1183,7 +1175,7 @@ static void __exit cfhsi_exit_module(void)
 		spin_unlock(&cfhsi_list_lock);
 
 		/* Shutdown driver. */
-		cfhsi_shutdown(cfhsi, true);
+		cfhsi_shutdown(cfhsi);
 
 		spin_lock(&cfhsi_list_lock);
 	}
-- 
1.7.0.4

[PATCH 7/8] caif-hsi: Added sanity check for length of CAIF frames

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:30:01

From: Daniel Martensson <redacted>

Added sanity check for length of CAIF frames, and tear down of
CAIF link-layer device upon protocol error.

Signed-off-by: Sjur Brændeland <redacted>
---
 drivers/net/caif/caif_hsi.c |   79 ++++++++++++++++++++++++++----------------
 include/net/caif/caif_hsi.h |    6 ++-
 2 files changed, 53 insertions(+), 32 deletions(-)
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index 1e1f0a3..e9e7cbf 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -18,6 +18,7 @@
 #include <linux/sched.h>
 #include <linux/if_arp.h>
 #include <linux/timer.h>
+#include <linux/rtnetlink.h>
 #include <net/caif/caif_layer.h>
 #include <net/caif/caif_hsi.h>
 
@@ -348,8 +349,7 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv)
 	cfhsi_tx_done(cfhsi);
 }
 
-static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
-				bool *dump)
+static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 {
 	int xfer_sz = 0;
 	int nfrms = 0;
@@ -360,8 +360,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
 			(desc->offset > CFHSI_MAX_EMB_FRM_SZ)) {
 		dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
 			__func__);
-		*dump = true;
-		return 0;
+		return -EPROTO;
 	}
 
 	/* Check for embedded CAIF frame. */
@@ -379,6 +378,12 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
 		len |= ((*(pfrm+1)) << 8) & 0xFF00;
 		len += 2;	/* Add FCS fields. */
 
+		/* Sanity check length of CAIF frame. */
+		if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) {
+			dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n",
+				__func__);
+			return -EPROTO;
+		}
 
 		/* Allocate SKB (OK even in IRQ context). */
 		skb = alloc_skb(len + 1, GFP_ATOMIC);
@@ -423,18 +428,16 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
 	if (desc->header & CFHSI_PIGGY_DESC)
 		xfer_sz += CFHSI_DESC_SZ;
 
-	if (xfer_sz % 4) {
+	if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX - CFHSI_DESC_SZ))) {
 		dev_err(&cfhsi->ndev->dev,
 				"%s: Invalid payload len: %d, ignored.\n",
 			__func__, xfer_sz);
-		xfer_sz = 0;
-		*dump = true;
+		return -EPROTO;
 	}
 	return xfer_sz;
 }
 
-static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
-				bool *dump)
+static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 {
 	int rx_sz = 0;
 	int nfrms = 0;
@@ -446,8 +449,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
 			(desc->offset > CFHSI_MAX_EMB_FRM_SZ))) {
 		dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
 			__func__);
-		*dump = true;
-		return -EINVAL;
+		return -EPROTO;
 	}
 
 	/* Set frame pointer to start of payload. */
@@ -469,13 +471,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
 		u8 *pcffrm = NULL;
 		int len = 0;
 
-		if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) {
-			dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n",
-				__func__);
-			*dump = true;
-			return -EINVAL;
-		}
-
 		/* CAIF frame starts after head padding. */
 		pcffrm = pfrm + *pfrm + 1;
 
@@ -484,6 +479,13 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
 		len |= ((*(pcffrm + 1)) << 8) & 0xFF00;
 		len += 2;	/* Add FCS fields. */
 
+		/* Sanity check length of CAIF frames. */
+		if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) {
+			dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n",
+				__func__);
+			return -EPROTO;
+		}
+
 		/* Allocate SKB (OK even in IRQ context). */
 		skb = alloc_skb(len + 1, GFP_ATOMIC);
 		if (!skb) {
@@ -528,7 +530,6 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
 	int res;
 	int desc_pld_len = 0;
 	struct cfhsi_desc *desc = NULL;
-	bool dump = false;
 
 	desc = (struct cfhsi_desc *)cfhsi->rx_buf;
 
@@ -544,16 +545,20 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
 	spin_unlock_bh(&cfhsi->lock);
 
 	if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
-		desc_pld_len = cfhsi_rx_desc(desc, cfhsi, &dump);
+		desc_pld_len = cfhsi_rx_desc(desc, cfhsi);
 		if (desc_pld_len == -ENOMEM)
 			goto restart;
+		if (desc_pld_len == -EPROTO)
+			goto out_of_sync;
 	} else {
 		int pld_len;
 
 		if (!cfhsi->rx_state.piggy_desc) {
-			pld_len = cfhsi_rx_pld(desc, cfhsi, &dump);
+			pld_len = cfhsi_rx_pld(desc, cfhsi);
 			if (pld_len == -ENOMEM)
 				goto restart;
+			if (pld_len == -EPROTO)
+				goto out_of_sync;
 			cfhsi->rx_state.pld_len = pld_len;
 		} else {
 			pld_len = cfhsi->rx_state.pld_len;
@@ -567,7 +572,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
 			cfhsi->rx_state.piggy_desc = true;
 
 			/* Extract piggy-backed descriptor. */
-			desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi, &dump);
+			desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi);
 			if (desc_pld_len == -ENOMEM)
 				goto restart;
 
@@ -577,15 +582,10 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
 			 */
 			memcpy((u8 *)desc, (u8 *)piggy_desc,
 					CFHSI_DESC_SHORT_SZ);
-		}
-	}
 
-	if (unlikely(dump)) {
-		size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf;
-		dev_err(&cfhsi->ndev->dev, "%s: RX offset: %u.\n",
-			__func__, (unsigned) rx_offset);
-		print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE,
-				cfhsi->rx_buf, cfhsi->rx_len + rx_offset);
+			if (desc_pld_len == -EPROTO)
+				goto out_of_sync;
+		}
 	}
 
 	memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
@@ -622,6 +622,13 @@ restart:
 		BUG();
 	}
 	mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1);
+	return;
+
+out_of_sync:
+	dev_err(&cfhsi->ndev->dev, "%s: Out of sync.\n", __func__);
+	print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE,
+			cfhsi->rx_buf, CFHSI_DESC_SZ);
+	schedule_work(&cfhsi->out_of_sync_work);
 }
 
 static void cfhsi_rx_slowpath(unsigned long arg)
@@ -804,6 +811,17 @@ static void cfhsi_wake_down(struct work_struct *work)
 
 }
 
+static void cfhsi_out_of_sync(struct work_struct *work)
+{
+	struct cfhsi *cfhsi = NULL;
+
+	cfhsi = container_of(work, struct cfhsi, out_of_sync_work);
+
+	rtnl_lock();
+	dev_close(cfhsi->ndev);
+	rtnl_unlock();
+}
+
 static void cfhsi_wake_up_cb(struct cfhsi_drv *drv)
 {
 	struct cfhsi *cfhsi = NULL;
@@ -1023,6 +1041,7 @@ int cfhsi_probe(struct platform_device *pdev)
 	/* Initialize the work queues. */
 	INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up);
 	INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down);
+	INIT_WORK(&cfhsi->out_of_sync_work, cfhsi_out_of_sync);
 
 	/* Clear all bit fields. */
 	clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h
index 9b69d15..3356769 100644
--- a/include/net/caif/caif_hsi.h
+++ b/include/net/caif/caif_hsi.h
@@ -52,8 +52,9 @@ struct cfhsi_desc {
 /*
  * Maximum bytes transferred in one transfer.
  */
-/* TODO: 4096 is temporary... */
-#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * 4096)
+#define CFHSI_MAX_CAIF_FRAME_SZ 4096
+
+#define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * CFHSI_MAX_CAIF_FRAME_SZ)
 
 /* Size of the complete HSI TX buffer. */
 #define CFHSI_BUF_SZ_TX (CFHSI_DESC_SZ + CFHSI_MAX_PAYLOAD_SZ)
@@ -143,6 +144,7 @@ struct cfhsi {
 	struct list_head list;
 	struct work_struct wake_up_work;
 	struct work_struct wake_down_work;
+	struct work_struct out_of_sync_work;
 	struct workqueue_struct *wq;
 	wait_queue_head_t wake_up_wait;
 	wait_queue_head_t wake_down_wait;
-- 
1.7.0.4

[PATCH 8/8] caif-hsi: Added recovery check of CA wake status.

From: Sjur Brændeland <hidden>
Date: 2011-10-13 22:30:03

From: Daniel Martensson <redacted>

Added recovery check of CA wake status in case of wake up timeout.
Added check of CA wake status in case of wake down timeout.

Signed-off-by: Sjur Brændeland <redacted>
---
 drivers/net/caif/caif_hsi.c |   42 ++++++++++++++++++++++++++++++++++++++++--
 include/net/caif/caif_hsi.h |    1 +
 2 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c
index e9e7cbf..0733525 100644
--- a/drivers/net/caif/caif_hsi.c
+++ b/drivers/net/caif/caif_hsi.c
@@ -674,6 +674,7 @@ static void cfhsi_wake_up(struct work_struct *work)
 		/* It happenes when wakeup is requested by
 		 * both ends at the same time. */
 		clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
+		clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
 		return;
 	}
 
@@ -690,19 +691,47 @@ static void cfhsi_wake_up(struct work_struct *work)
 							&cfhsi->bits), ret);
 	if (unlikely(ret < 0)) {
 		/* Interrupted by signal. */
-		dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
+		dev_err(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
 			__func__, ret);
+
 		clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
 		cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
 		return;
 	} else if (!ret) {
+		bool ca_wake = false;
+		size_t fifo_occupancy = 0;
+
 		/* Wakeup timeout */
 		dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n",
 			__func__);
+
+		/* Check FIFO to check if modem has sent something. */
+		WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
+					&fifo_occupancy));
+
+		dev_err(&cfhsi->ndev->dev, "%s: Bytes in FIFO: %u.\n",
+				__func__, (unsigned) fifo_occupancy);
+
+		/* Check if we misssed the interrupt. */
+		WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev,
+							&ca_wake));
+
+		if (ca_wake) {
+			dev_err(&cfhsi->ndev->dev, "%s: CA Wake missed !.\n",
+				__func__);
+
+			/* Clear the CFHSI_WAKE_UP_ACK bit to prevent race. */
+			clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
+
+			/* Continue execution. */
+			goto wake_ack;
+		}
+
 		clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
 		cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
 		return;
 	}
+wake_ack:
 	dev_dbg(&cfhsi->ndev->dev, "%s: Woken.\n",
 		__func__);
 
@@ -779,12 +808,21 @@ static void cfhsi_wake_down(struct work_struct *work)
 							&cfhsi->bits), ret);
 	if (ret < 0) {
 		/* Interrupted by signal. */
-		dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
+		dev_err(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
 			__func__, ret);
 		return;
 	} else if (!ret) {
+		bool ca_wake = true;
+
 		/* Timeout */
 		dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__);
+
+		/* Check if we misssed the interrupt. */
+		WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev,
+							&ca_wake));
+		if (!ca_wake)
+			dev_err(&cfhsi->ndev->dev, "%s: CA Wake missed !.\n",
+				__func__);
 	}
 
 	/* Check FIFO occupancy. */
diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h
index 3356769..8d55251 100644
--- a/include/net/caif/caif_hsi.h
+++ b/include/net/caif/caif_hsi.h
@@ -108,6 +108,7 @@ struct cfhsi_dev {
 	int (*cfhsi_rx) (u8 *ptr, int len, struct cfhsi_dev *dev);
 	int (*cfhsi_wake_up) (struct cfhsi_dev *dev);
 	int (*cfhsi_wake_down) (struct cfhsi_dev *dev);
+	int (*cfhsi_get_peer_wake) (struct cfhsi_dev *dev, bool *status);
 	int (*cfhsi_fifo_occupancy)(struct cfhsi_dev *dev, size_t *occupancy);
 	int (*cfhsi_rx_cancel)(struct cfhsi_dev *dev);
 	struct cfhsi_drv *drv;
-- 
1.7.0.4
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help