linux/drivers/nfc/st-nci/ndlc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip
   4 *
   5 * Copyright (C) 2014-2015  STMicroelectronics SAS. All rights reserved.
   6 */
   7
   8#include <linux/sched.h>
   9#include <net/nfc/nci_core.h>
  10
  11#include "st-nci.h"
  12
  13#define NDLC_TIMER_T1           100
  14#define NDLC_TIMER_T1_WAIT      400
  15#define NDLC_TIMER_T2           1200
  16
  17#define PCB_TYPE_DATAFRAME              0x80
  18#define PCB_TYPE_SUPERVISOR             0xc0
  19#define PCB_TYPE_MASK                   PCB_TYPE_SUPERVISOR
  20
  21#define PCB_SYNC_ACK                    0x20
  22#define PCB_SYNC_NACK                   0x10
  23#define PCB_SYNC_WAIT                   0x30
  24#define PCB_SYNC_NOINFO                 0x00
  25#define PCB_SYNC_MASK                   PCB_SYNC_WAIT
  26
  27#define PCB_DATAFRAME_RETRANSMIT_YES    0x00
  28#define PCB_DATAFRAME_RETRANSMIT_NO     0x04
  29#define PCB_DATAFRAME_RETRANSMIT_MASK   PCB_DATAFRAME_RETRANSMIT_NO
  30
  31#define PCB_SUPERVISOR_RETRANSMIT_YES   0x00
  32#define PCB_SUPERVISOR_RETRANSMIT_NO    0x02
  33#define PCB_SUPERVISOR_RETRANSMIT_MASK  PCB_SUPERVISOR_RETRANSMIT_NO
  34
  35#define PCB_FRAME_CRC_INFO_PRESENT      0x08
  36#define PCB_FRAME_CRC_INFO_NOTPRESENT   0x00
  37#define PCB_FRAME_CRC_INFO_MASK         PCB_FRAME_CRC_INFO_PRESENT
  38
  39#define NDLC_DUMP_SKB(info, skb)                                 \
  40do {                                                             \
  41        pr_debug("%s:\n", info);                                 \
  42        print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \
  43                        16, 1, skb->data, skb->len, 0);          \
  44} while (0)
  45
  46int ndlc_open(struct llt_ndlc *ndlc)
  47{
  48        /* toggle reset pin */
  49        ndlc->ops->enable(ndlc->phy_id);
  50        ndlc->powered = 1;
  51        return 0;
  52}
  53EXPORT_SYMBOL(ndlc_open);
  54
  55void ndlc_close(struct llt_ndlc *ndlc)
  56{
  57        struct nci_mode_set_cmd cmd;
  58
  59        cmd.cmd_type = ST_NCI_SET_NFC_MODE;
  60        cmd.mode = 0;
  61
  62        /* toggle reset pin */
  63        ndlc->ops->enable(ndlc->phy_id);
  64
  65        nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP,
  66                     sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd);
  67
  68        ndlc->powered = 0;
  69        ndlc->ops->disable(ndlc->phy_id);
  70}
  71EXPORT_SYMBOL(ndlc_close);
  72
  73int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb)
  74{
  75        /* add ndlc header */
  76        u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO |
  77                PCB_FRAME_CRC_INFO_NOTPRESENT;
  78
  79        *(u8 *)skb_push(skb, 1) = pcb;
  80        skb_queue_tail(&ndlc->send_q, skb);
  81
  82        schedule_work(&ndlc->sm_work);
  83
  84        return 0;
  85}
  86EXPORT_SYMBOL(ndlc_send);
  87
  88static void llt_ndlc_send_queue(struct llt_ndlc *ndlc)
  89{
  90        struct sk_buff *skb;
  91        int r;
  92        unsigned long time_sent;
  93
  94        if (ndlc->send_q.qlen)
  95                pr_debug("sendQlen=%d unackQlen=%d\n",
  96                         ndlc->send_q.qlen, ndlc->ack_pending_q.qlen);
  97
  98        while (ndlc->send_q.qlen) {
  99                skb = skb_dequeue(&ndlc->send_q);
 100                NDLC_DUMP_SKB("ndlc frame written", skb);
 101                r = ndlc->ops->write(ndlc->phy_id, skb);
 102                if (r < 0) {
 103                        ndlc->hard_fault = r;
 104                        break;
 105                }
 106                time_sent = jiffies;
 107                *(unsigned long *)skb->cb = time_sent;
 108
 109                skb_queue_tail(&ndlc->ack_pending_q, skb);
 110
 111                /* start timer t1 for ndlc aknowledge */
 112                ndlc->t1_active = true;
 113                mod_timer(&ndlc->t1_timer, time_sent +
 114                        msecs_to_jiffies(NDLC_TIMER_T1));
 115                /* start timer t2 for chip availability */
 116                ndlc->t2_active = true;
 117                mod_timer(&ndlc->t2_timer, time_sent +
 118                        msecs_to_jiffies(NDLC_TIMER_T2));
 119        }
 120}
 121
 122static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc)
 123{
 124        struct sk_buff *skb;
 125        u8 pcb;
 126
 127        while ((skb = skb_dequeue_tail(&ndlc->ack_pending_q))) {
 128                pcb = skb->data[0];
 129                switch (pcb & PCB_TYPE_MASK) {
 130                case PCB_TYPE_SUPERVISOR:
 131                        skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) |
 132                                PCB_SUPERVISOR_RETRANSMIT_YES;
 133                        break;
 134                case PCB_TYPE_DATAFRAME:
 135                        skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) |
 136                                PCB_DATAFRAME_RETRANSMIT_YES;
 137                        break;
 138                default:
 139                        pr_err("UNKNOWN Packet Control Byte=%d\n", pcb);
 140                        kfree_skb(skb);
 141                        continue;
 142                }
 143                skb_queue_head(&ndlc->send_q, skb);
 144        }
 145}
 146
 147static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc)
 148{
 149        struct sk_buff *skb;
 150        u8 pcb;
 151        unsigned long time_sent;
 152
 153        if (ndlc->rcv_q.qlen)
 154                pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen);
 155
 156        while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) {
 157                pcb = skb->data[0];
 158                skb_pull(skb, 1);
 159                if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) {
 160                        switch (pcb & PCB_SYNC_MASK) {
 161                        case PCB_SYNC_ACK:
 162                                skb = skb_dequeue(&ndlc->ack_pending_q);
 163                                kfree_skb(skb);
 164                                del_timer_sync(&ndlc->t1_timer);
 165                                del_timer_sync(&ndlc->t2_timer);
 166                                ndlc->t2_active = false;
 167                                ndlc->t1_active = false;
 168                                break;
 169                        case PCB_SYNC_NACK:
 170                                llt_ndlc_requeue_data_pending(ndlc);
 171                                llt_ndlc_send_queue(ndlc);
 172                                /* start timer t1 for ndlc aknowledge */
 173                                time_sent = jiffies;
 174                                ndlc->t1_active = true;
 175                                mod_timer(&ndlc->t1_timer, time_sent +
 176                                        msecs_to_jiffies(NDLC_TIMER_T1));
 177                                break;
 178                        case PCB_SYNC_WAIT:
 179                                time_sent = jiffies;
 180                                ndlc->t1_active = true;
 181                                mod_timer(&ndlc->t1_timer, time_sent +
 182                                          msecs_to_jiffies(NDLC_TIMER_T1_WAIT));
 183                                break;
 184                        default:
 185                                kfree_skb(skb);
 186                                break;
 187                        }
 188                } else if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_DATAFRAME) {
 189                        nci_recv_frame(ndlc->ndev, skb);
 190                } else {
 191                        kfree_skb(skb);
 192                }
 193        }
 194}
 195
 196static void llt_ndlc_sm_work(struct work_struct *work)
 197{
 198        struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work);
 199
 200        llt_ndlc_send_queue(ndlc);
 201        llt_ndlc_rcv_queue(ndlc);
 202
 203        if (ndlc->t1_active && timer_pending(&ndlc->t1_timer) == 0) {
 204                pr_debug
 205                    ("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n");
 206                ndlc->t1_active = false;
 207
 208                llt_ndlc_requeue_data_pending(ndlc);
 209                llt_ndlc_send_queue(ndlc);
 210        }
 211
 212        if (ndlc->t2_active && timer_pending(&ndlc->t2_timer) == 0) {
 213                pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n");
 214                ndlc->t2_active = false;
 215                ndlc->t1_active = false;
 216                del_timer_sync(&ndlc->t1_timer);
 217                del_timer_sync(&ndlc->t2_timer);
 218                ndlc_close(ndlc);
 219                ndlc->hard_fault = -EREMOTEIO;
 220        }
 221}
 222
 223void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb)
 224{
 225        if (skb == NULL) {
 226                pr_err("NULL Frame -> link is dead\n");
 227                ndlc->hard_fault = -EREMOTEIO;
 228                ndlc_close(ndlc);
 229        } else {
 230                NDLC_DUMP_SKB("incoming frame", skb);
 231                skb_queue_tail(&ndlc->rcv_q, skb);
 232        }
 233
 234        schedule_work(&ndlc->sm_work);
 235}
 236EXPORT_SYMBOL(ndlc_recv);
 237
 238static void ndlc_t1_timeout(struct timer_list *t)
 239{
 240        struct llt_ndlc *ndlc = from_timer(ndlc, t, t1_timer);
 241
 242        pr_debug("\n");
 243
 244        schedule_work(&ndlc->sm_work);
 245}
 246
 247static void ndlc_t2_timeout(struct timer_list *t)
 248{
 249        struct llt_ndlc *ndlc = from_timer(ndlc, t, t2_timer);
 250
 251        pr_debug("\n");
 252
 253        schedule_work(&ndlc->sm_work);
 254}
 255
 256int ndlc_probe(void *phy_id, const struct nfc_phy_ops *phy_ops,
 257               struct device *dev, int phy_headroom, int phy_tailroom,
 258               struct llt_ndlc **ndlc_id, struct st_nci_se_status *se_status)
 259{
 260        struct llt_ndlc *ndlc;
 261
 262        ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL);
 263        if (!ndlc)
 264                return -ENOMEM;
 265
 266        ndlc->ops = phy_ops;
 267        ndlc->phy_id = phy_id;
 268        ndlc->dev = dev;
 269        ndlc->powered = 0;
 270
 271        *ndlc_id = ndlc;
 272
 273        /* initialize timers */
 274        timer_setup(&ndlc->t1_timer, ndlc_t1_timeout, 0);
 275        timer_setup(&ndlc->t2_timer, ndlc_t2_timeout, 0);
 276
 277        skb_queue_head_init(&ndlc->rcv_q);
 278        skb_queue_head_init(&ndlc->send_q);
 279        skb_queue_head_init(&ndlc->ack_pending_q);
 280
 281        INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work);
 282
 283        return st_nci_probe(ndlc, phy_headroom, phy_tailroom, se_status);
 284}
 285EXPORT_SYMBOL(ndlc_probe);
 286
 287void ndlc_remove(struct llt_ndlc *ndlc)
 288{
 289        st_nci_remove(ndlc->ndev);
 290
 291        /* cancel timers */
 292        del_timer_sync(&ndlc->t1_timer);
 293        del_timer_sync(&ndlc->t2_timer);
 294        ndlc->t2_active = false;
 295        ndlc->t1_active = false;
 296
 297        skb_queue_purge(&ndlc->rcv_q);
 298        skb_queue_purge(&ndlc->send_q);
 299}
 300EXPORT_SYMBOL(ndlc_remove);
 301