NFC: st21nfca: Improved i2c Rx data correctness check
authorChristophe Ricard <christophe.ricard@gmail.com>
Thu, 24 Apr 2014 21:19:33 +0000 (23:19 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Sun, 4 May 2014 23:00:34 +0000 (01:00 +0200)
A frame starts with ST21NFCA_SOF_EOF(0x7e) + 0x00.
A frame ends with ST21NFCA_SOF_EOF(0x7e).
It is possible that the i2c macrocell is stopped for other
communication interfaces with highest priority(RF or SWP).
This can be seen with some 0xFF data at the end of a received shdlc buffer.
If this happen we need to discard the frame because the CLF will repeat it.

In order to push accurate data to hci layer, we add the following fix:
- Instead of looking for the first 0x7e in the frame, check that the last
received byte is 0x7e.
- Check that the first frame reception block start with start of
frame(0x7e 0x00). If not, clear the buffer.
- Check that the next frame reception block do not start with start of
frame(0x7e). If so, clear the buffer.

Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
drivers/nfc/st21nfca/i2c.c

index e29351ca7dd18b53534c2017c5096be3410648fb..48f8e23fc3210b1f6166c214b9839d3fc801d2b0 100644 (file)
@@ -53,6 +53,8 @@
 
 /* 2 bytes crc + EOF */
 #define ST21NFCA_FRAME_TAILROOM 3
+#define IS_START_OF_FRAME(buf) (buf[0] == ST21NFCA_SOF_EOF && \
+                               buf[1] == 0)
 
 #define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c"
 
@@ -361,6 +363,7 @@ static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy,
 {
        int r, i;
        u8 len;
+       u8 buf[ST21NFCA_HCI_LLC_MAX_PAYLOAD];
        struct i2c_client *client = phy->i2c_dev;
 
        if (phy->current_read_len < ARRAY_SIZE(len_seq)) {
@@ -373,7 +376,7 @@ static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy,
                 */
                r = 0;
                for (i = 0; i < ARRAY_SIZE(wait_tab) && r <= 0; i++) {
-                       r = i2c_master_recv(client, skb_put(skb, len), len);
+                       r = i2c_master_recv(client, buf, len);
                        if (r < 0)
                                msleep(wait_tab[i]);
                }
@@ -383,8 +386,28 @@ static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy,
                        return -EREMOTEIO;
                }
 
-               if (memchr(skb->data + 2, ST21NFCA_SOF_EOF,
-                               skb->len - 2) != NULL) {
+               /*
+                * The first read sequence does not start with SOF.
+                * Data is corrupeted so we drop it.
+                */
+               if (!phy->current_read_len && buf[0] != ST21NFCA_SOF_EOF) {
+                       skb_trim(skb, 0);
+                       phy->current_read_len = 0;
+                       return -EIO;
+               } else if (phy->current_read_len &&
+                       IS_START_OF_FRAME(buf)) {
+                       /*
+                        * Previous frame transmission was interrupted and
+                        * the frame got repeated.
+                        * Received frame start with ST21NFCA_SOF_EOF + 00.
+                        */
+                       skb_trim(skb, 0);
+                       phy->current_read_len = 0;
+               }
+
+               memcpy(skb_put(skb, len), buf, len);
+
+               if (skb->data[skb->len - 1] == ST21NFCA_SOF_EOF) {
                        phy->current_read_len = 0;
                        return st21nfca_hci_i2c_repack(skb);
                }