Merge tag 'armsoc-dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[cascardo/linux.git] / drivers / nfc / port100.c
index 87d5099..2b2330b 100644 (file)
@@ -343,7 +343,26 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = {
        },
        [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = {
                /* nfc_digital_framing_nfcf */
-               { PORT100_IN_PROT_END, 0 },
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,     18 },
+               { PORT100_IN_PROT_ADD_CRC,                 1 },
+               { PORT100_IN_PROT_CHECK_CRC,               1 },
+               { PORT100_IN_PROT_MULTI_CARD,              0 },
+               { PORT100_IN_PROT_ADD_PARITY,              0 },
+               { PORT100_IN_PROT_CHECK_PARITY,            0 },
+               { PORT100_IN_PROT_BITWISE_AC_RECV_MODE,    0 },
+               { PORT100_IN_PROT_VALID_BIT_NUMBER,        8 },
+               { PORT100_IN_PROT_CRYPTO1,                 0 },
+               { PORT100_IN_PROT_ADD_SOF,                 0 },
+               { PORT100_IN_PROT_CHECK_SOF,               0 },
+               { PORT100_IN_PROT_ADD_EOF,                 0 },
+               { PORT100_IN_PROT_CHECK_EOF,               0 },
+               { PORT100_IN_PROT_DEAF_TIME,               4 },
+               { PORT100_IN_PROT_CRM,                     0 },
+               { PORT100_IN_PROT_CRM_MIN_LEN,             0 },
+               { PORT100_IN_PROT_T1_TAG_FRAME,            0 },
+               { PORT100_IN_PROT_RFCA,                    0 },
+               { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 },
+               { PORT100_IN_PROT_END,                     0 },
        },
        [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
                { PORT100_IN_PROT_END, 0 },
@@ -437,6 +456,12 @@ struct port100 {
        struct urb *out_urb;
        struct urb *in_urb;
 
+       /* This mutex protects the out_urb and avoids to submit a new command
+        * through port100_send_frame_async() while the previous one is being
+        * canceled through port100_abort_cmd().
+        */
+       struct mutex out_urb_lock;
+
        struct work_struct cmd_complete_work;
 
        u8 cmd_type;
@@ -445,6 +470,9 @@ struct port100 {
         * for any queuing/locking mechanism at driver level.
         */
        struct port100_cmd *cmd;
+
+       bool cmd_cancel;
+       struct completion cmd_cancel_done;
 };
 
 struct port100_cmd {
@@ -699,10 +727,27 @@ static int port100_send_ack(struct port100 *dev)
 {
        int rc;
 
+       mutex_lock(&dev->out_urb_lock);
+
+       init_completion(&dev->cmd_cancel_done);
+
+       usb_kill_urb(dev->out_urb);
+
        dev->out_urb->transfer_buffer = ack_frame;
        dev->out_urb->transfer_buffer_length = sizeof(ack_frame);
        rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
 
+       /* Set the cmd_cancel flag only if the URB has been successfully
+        * submitted. It will be reset by the out URB completion callback
+        * port100_send_complete().
+        */
+       dev->cmd_cancel = !rc;
+
+       mutex_unlock(&dev->out_urb_lock);
+
+       if (!rc)
+               wait_for_completion(&dev->cmd_cancel_done);
+
        return rc;
 }
 
@@ -711,6 +756,16 @@ static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out,
 {
        int rc;
 
+       mutex_lock(&dev->out_urb_lock);
+
+       /* A command cancel frame as been sent through dev->out_urb. Don't try
+        * to submit a new one.
+        */
+       if (dev->cmd_cancel) {
+               rc = -EAGAIN;
+               goto exit;
+       }
+
        dev->out_urb->transfer_buffer = out->data;
        dev->out_urb->transfer_buffer_length = out->len;
 
@@ -722,16 +777,15 @@ static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out,
 
        rc = usb_submit_urb(dev->out_urb, GFP_KERNEL);
        if (rc)
-               return rc;
+               goto exit;
 
        rc = port100_submit_urb_for_ack(dev, GFP_KERNEL);
        if (rc)
-               goto error;
+               usb_unlink_urb(dev->out_urb);
 
-       return 0;
+exit:
+       mutex_unlock(&dev->out_urb_lock);
 
-error:
-       usb_unlink_urb(dev->out_urb);
        return rc;
 }
 
@@ -790,6 +844,12 @@ static int port100_send_cmd_async(struct port100 *dev, u8 cmd_code,
                        PORT100_FRAME_MAX_PAYLOAD_LEN +
                        PORT100_FRAME_TAIL_LEN;
 
+       if (dev->cmd) {
+               nfc_err(&dev->interface->dev,
+                       "A command is still in process\n");
+               return -EBUSY;
+       }
+
        resp = alloc_skb(resp_len, GFP_KERNEL);
        if (!resp)
                return -ENOMEM;
@@ -867,6 +927,11 @@ static void port100_send_complete(struct urb *urb)
 {
        struct port100 *dev = urb->context;
 
+       if (dev->cmd_cancel) {
+               dev->cmd_cancel = false;
+               complete(&dev->cmd_cancel_done);
+       }
+
        switch (urb->status) {
        case 0:
                break; /* success */
@@ -985,6 +1050,10 @@ static int port100_switch_rf(struct nfc_digital_dev *ddev, bool on)
 
        *skb_put(skb, 1) = on ? 1 : 0;
 
+       /* Cancel the last command if the device is being switched off */
+       if (!on)
+               port100_abort_cmd(ddev);
+
        resp = port100_send_cmd_sync(dev, PORT100_CMD_SWITCH_RF, skb);
 
        if (IS_ERR(resp))
@@ -1430,6 +1499,7 @@ static int port100_probe(struct usb_interface *interface,
        if (!dev)
                return -ENOMEM;
 
+       mutex_init(&dev->out_urb_lock);
        dev->udev = usb_get_dev(interface_to_usbdev(interface));
        dev->interface = interface;
        usb_set_intfdata(interface, dev);