#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>
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;
(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. */
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);
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;
(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. */
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;
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) {
int res;
int desc_pld_len = 0;
struct cfhsi_desc *desc = NULL;
- bool dump = false;
desc = (struct cfhsi_desc *)cfhsi->rx_buf;
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;
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;
*/
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));
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)
}
+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;
/* 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);