cregit-Linux how code gets into the kernel

Release 4.14 drivers/nfc/st-nci/ndlc.c

/*
 * Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip
 *
 * Copyright (C) 2014-2015  STMicroelectronics SAS. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/sched.h>
#include <net/nfc/nci_core.h>

#include "st-nci.h"


#define NDLC_TIMER_T1		100

#define NDLC_TIMER_T1_WAIT	400

#define NDLC_TIMER_T2		1200


#define PCB_TYPE_DATAFRAME		0x80

#define PCB_TYPE_SUPERVISOR		0xc0

#define PCB_TYPE_MASK			PCB_TYPE_SUPERVISOR


#define PCB_SYNC_ACK			0x20

#define PCB_SYNC_NACK			0x10

#define PCB_SYNC_WAIT			0x30

#define PCB_SYNC_NOINFO			0x00

#define PCB_SYNC_MASK			PCB_SYNC_WAIT


#define PCB_DATAFRAME_RETRANSMIT_YES	0x00

#define PCB_DATAFRAME_RETRANSMIT_NO	0x04

#define PCB_DATAFRAME_RETRANSMIT_MASK	PCB_DATAFRAME_RETRANSMIT_NO


#define PCB_SUPERVISOR_RETRANSMIT_YES	0x00

#define PCB_SUPERVISOR_RETRANSMIT_NO	0x02

#define PCB_SUPERVISOR_RETRANSMIT_MASK	PCB_SUPERVISOR_RETRANSMIT_NO


#define PCB_FRAME_CRC_INFO_PRESENT	0x08

#define PCB_FRAME_CRC_INFO_NOTPRESENT	0x00

#define PCB_FRAME_CRC_INFO_MASK		PCB_FRAME_CRC_INFO_PRESENT


#define NDLC_DUMP_SKB(info, skb)                                 \
do {                                                             \
        pr_debug("%s:\n", info);                                 \
        print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \
                        16, 1, skb->data, skb->len, 0);          \
} while (0)


int ndlc_open(struct llt_ndlc *ndlc) { /* toggle reset pin */ ndlc->ops->enable(ndlc->phy_id); ndlc->powered = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard31100.00%2100.00%
Total31100.00%2100.00%

EXPORT_SYMBOL(ndlc_open);
void ndlc_close(struct llt_ndlc *ndlc) { struct nci_mode_set_cmd cmd; cmd.cmd_type = ST_NCI_SET_NFC_MODE; cmd.mode = 0; /* toggle reset pin */ ndlc->ops->enable(ndlc->phy_id); nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP, sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); ndlc->powered = 0; ndlc->ops->disable(ndlc->phy_id); }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard77100.00%4100.00%
Total77100.00%4100.00%

EXPORT_SYMBOL(ndlc_close);
int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb) { /* add ndlc header */ u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO | PCB_FRAME_CRC_INFO_NOTPRESENT; *(u8 *)skb_push(skb, 1) = pcb; skb_queue_tail(&ndlc->send_q, skb); schedule_work(&ndlc->sm_work); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard5693.33%150.00%
Johannes Berg46.67%150.00%
Total60100.00%2100.00%

EXPORT_SYMBOL(ndlc_send);
static void llt_ndlc_send_queue(struct llt_ndlc *ndlc) { struct sk_buff *skb; int r; unsigned long time_sent; if (ndlc->send_q.qlen) pr_debug("sendQlen=%d unackQlen=%d\n", ndlc->send_q.qlen, ndlc->ack_pending_q.qlen); while (ndlc->send_q.qlen) { skb = skb_dequeue(&ndlc->send_q); NDLC_DUMP_SKB("ndlc frame written", skb); r = ndlc->ops->write(ndlc->phy_id, skb); if (r < 0) { ndlc->hard_fault = r; break; } time_sent = jiffies; *(unsigned long *)skb->cb = time_sent; skb_queue_tail(&ndlc->ack_pending_q, skb); /* start timer t1 for ndlc aknowledge */ ndlc->t1_active = true; mod_timer(&ndlc->t1_timer, time_sent + msecs_to_jiffies(NDLC_TIMER_T1)); /* start timer t2 for chip availability */ ndlc->t2_active = true; mod_timer(&ndlc->t2_timer, time_sent + msecs_to_jiffies(NDLC_TIMER_T2)); } }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard175100.00%2100.00%
Total175100.00%2100.00%


static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc) { struct sk_buff *skb; u8 pcb; while ((skb = skb_dequeue_tail(&ndlc->ack_pending_q))) { pcb = skb->data[0]; switch (pcb & PCB_TYPE_MASK) { case PCB_TYPE_SUPERVISOR: skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) | PCB_SUPERVISOR_RETRANSMIT_YES; break; case PCB_TYPE_DATAFRAME: skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) | PCB_DATAFRAME_RETRANSMIT_YES; break; default: pr_err("UNKNOWN Packet Control Byte=%d\n", pcb); kfree_skb(skb); continue; } skb_queue_head(&ndlc->send_q, skb); } }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard116100.00%2100.00%
Total116100.00%2100.00%


static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc) { struct sk_buff *skb; u8 pcb; unsigned long time_sent; if (ndlc->rcv_q.qlen) pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen); while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) { pcb = skb->data[0]; skb_pull(skb, 1); if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) { switch (pcb & PCB_SYNC_MASK) { case PCB_SYNC_ACK: skb = skb_dequeue(&ndlc->ack_pending_q); kfree_skb(skb); del_timer_sync(&ndlc->t1_timer); del_timer_sync(&ndlc->t2_timer); ndlc->t2_active = false; ndlc->t1_active = false; break; case PCB_SYNC_NACK: llt_ndlc_requeue_data_pending(ndlc); llt_ndlc_send_queue(ndlc); /* start timer t1 for ndlc aknowledge */ time_sent = jiffies; ndlc->t1_active = true; mod_timer(&ndlc->t1_timer, time_sent + msecs_to_jiffies(NDLC_TIMER_T1)); break; case PCB_SYNC_WAIT: time_sent = jiffies; ndlc->t1_active = true; mod_timer(&ndlc->t1_timer, time_sent + msecs_to_jiffies(NDLC_TIMER_T1_WAIT)); break; default: kfree_skb(skb); break; } } else if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_DATAFRAME) { nci_recv_frame(ndlc->ndev, skb); } else { kfree_skb(skb); } } }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard249100.00%3100.00%
Total249100.00%3100.00%


static void llt_ndlc_sm_work(struct work_struct *work) { struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work); llt_ndlc_send_queue(ndlc); llt_ndlc_rcv_queue(ndlc); if (ndlc->t1_active && timer_pending(&ndlc->t1_timer) == 0) { pr_debug ("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n"); ndlc->t1_active = false; llt_ndlc_requeue_data_pending(ndlc); llt_ndlc_send_queue(ndlc); } if (ndlc->t2_active && timer_pending(&ndlc->t2_timer) == 0) { pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n"); ndlc->t2_active = false; ndlc->t1_active = false; del_timer_sync(&ndlc->t1_timer); del_timer_sync(&ndlc->t2_timer); ndlc_close(ndlc); ndlc->hard_fault = -EREMOTEIO; } }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard138100.00%2100.00%
Total138100.00%2100.00%


void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb) { if (skb == NULL) { pr_err("NULL Frame -> link is dead\n"); ndlc->hard_fault = -EREMOTEIO; ndlc_close(ndlc); } else { NDLC_DUMP_SKB("incoming frame", skb); skb_queue_tail(&ndlc->rcv_q, skb); } schedule_work(&ndlc->sm_work); }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard68100.00%1100.00%
Total68100.00%1100.00%

EXPORT_SYMBOL(ndlc_recv);
static void ndlc_t1_timeout(unsigned long data) { struct llt_ndlc *ndlc = (struct llt_ndlc *)data; pr_debug("\n"); schedule_work(&ndlc->sm_work); }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard35100.00%1100.00%
Total35100.00%1100.00%


static void ndlc_t2_timeout(unsigned long data) { struct llt_ndlc *ndlc = (struct llt_ndlc *)data; pr_debug("\n"); schedule_work(&ndlc->sm_work); }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard35100.00%1100.00%
Total35100.00%1100.00%


int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id, struct st_nci_se_status *se_status) { struct llt_ndlc *ndlc; ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL); if (!ndlc) return -ENOMEM; ndlc->ops = phy_ops; ndlc->phy_id = phy_id; ndlc->dev = dev; ndlc->powered = 0; *ndlc_id = ndlc; /* initialize timers */ init_timer(&ndlc->t1_timer); ndlc->t1_timer.data = (unsigned long)ndlc; ndlc->t1_timer.function = ndlc_t1_timeout; init_timer(&ndlc->t2_timer); ndlc->t2_timer.data = (unsigned long)ndlc; ndlc->t2_timer.function = ndlc_t2_timeout; skb_queue_head_init(&ndlc->rcv_q); skb_queue_head_init(&ndlc->send_q); skb_queue_head_init(&ndlc->ack_pending_q); INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work); return st_nci_probe(ndlc, phy_headroom, phy_tailroom, se_status); }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard197100.00%5100.00%
Total197100.00%5100.00%

EXPORT_SYMBOL(ndlc_probe);
void ndlc_remove(struct llt_ndlc *ndlc) { st_nci_remove(ndlc->ndev); /* cancel timers */ del_timer_sync(&ndlc->t1_timer); del_timer_sync(&ndlc->t2_timer); ndlc->t2_active = false; ndlc->t1_active = false; skb_queue_purge(&ndlc->rcv_q); skb_queue_purge(&ndlc->send_q); }

Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard62100.00%3100.00%
Total62100.00%3100.00%

EXPORT_SYMBOL(ndlc_remove);

Overall Contributors

PersonTokensPropCommitsCommitProp
Christophe Ricard136999.71%1191.67%
Johannes Berg40.29%18.33%
Total1373100.00%12100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.