162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/sched.h> 962306a36Sopenharmony_ci#include <net/nfc/nci_core.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "st-nci.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define NDLC_TIMER_T1 100 1462306a36Sopenharmony_ci#define NDLC_TIMER_T1_WAIT 400 1562306a36Sopenharmony_ci#define NDLC_TIMER_T2 1200 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define PCB_TYPE_DATAFRAME 0x80 1862306a36Sopenharmony_ci#define PCB_TYPE_SUPERVISOR 0xc0 1962306a36Sopenharmony_ci#define PCB_TYPE_MASK PCB_TYPE_SUPERVISOR 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PCB_SYNC_ACK 0x20 2262306a36Sopenharmony_ci#define PCB_SYNC_NACK 0x10 2362306a36Sopenharmony_ci#define PCB_SYNC_WAIT 0x30 2462306a36Sopenharmony_ci#define PCB_SYNC_NOINFO 0x00 2562306a36Sopenharmony_ci#define PCB_SYNC_MASK PCB_SYNC_WAIT 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define PCB_DATAFRAME_RETRANSMIT_YES 0x00 2862306a36Sopenharmony_ci#define PCB_DATAFRAME_RETRANSMIT_NO 0x04 2962306a36Sopenharmony_ci#define PCB_DATAFRAME_RETRANSMIT_MASK PCB_DATAFRAME_RETRANSMIT_NO 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define PCB_SUPERVISOR_RETRANSMIT_YES 0x00 3262306a36Sopenharmony_ci#define PCB_SUPERVISOR_RETRANSMIT_NO 0x02 3362306a36Sopenharmony_ci#define PCB_SUPERVISOR_RETRANSMIT_MASK PCB_SUPERVISOR_RETRANSMIT_NO 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define PCB_FRAME_CRC_INFO_PRESENT 0x08 3662306a36Sopenharmony_ci#define PCB_FRAME_CRC_INFO_NOTPRESENT 0x00 3762306a36Sopenharmony_ci#define PCB_FRAME_CRC_INFO_MASK PCB_FRAME_CRC_INFO_PRESENT 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define NDLC_DUMP_SKB(info, skb) \ 4062306a36Sopenharmony_cido { \ 4162306a36Sopenharmony_ci pr_debug("%s:\n", info); \ 4262306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \ 4362306a36Sopenharmony_ci 16, 1, skb->data, skb->len, 0); \ 4462306a36Sopenharmony_ci} while (0) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciint ndlc_open(struct llt_ndlc *ndlc) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci /* toggle reset pin */ 4962306a36Sopenharmony_ci ndlc->ops->enable(ndlc->phy_id); 5062306a36Sopenharmony_ci ndlc->powered = 1; 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ciEXPORT_SYMBOL(ndlc_open); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_civoid ndlc_close(struct llt_ndlc *ndlc) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct nci_mode_set_cmd cmd; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci cmd.cmd_type = ST_NCI_SET_NFC_MODE; 6062306a36Sopenharmony_ci cmd.mode = 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* toggle reset pin */ 6362306a36Sopenharmony_ci ndlc->ops->enable(ndlc->phy_id); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP, 6662306a36Sopenharmony_ci sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ndlc->powered = 0; 6962306a36Sopenharmony_ci ndlc->ops->disable(ndlc->phy_id); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ciEXPORT_SYMBOL(ndlc_close); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciint ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci /* add ndlc header */ 7662306a36Sopenharmony_ci u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO | 7762306a36Sopenharmony_ci PCB_FRAME_CRC_INFO_NOTPRESENT; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci *(u8 *)skb_push(skb, 1) = pcb; 8062306a36Sopenharmony_ci skb_queue_tail(&ndlc->send_q, skb); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci schedule_work(&ndlc->sm_work); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ciEXPORT_SYMBOL(ndlc_send); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void llt_ndlc_send_queue(struct llt_ndlc *ndlc) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct sk_buff *skb; 9162306a36Sopenharmony_ci int r; 9262306a36Sopenharmony_ci unsigned long time_sent; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (ndlc->send_q.qlen) 9562306a36Sopenharmony_ci pr_debug("sendQlen=%d unackQlen=%d\n", 9662306a36Sopenharmony_ci ndlc->send_q.qlen, ndlc->ack_pending_q.qlen); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci while (ndlc->send_q.qlen) { 9962306a36Sopenharmony_ci skb = skb_dequeue(&ndlc->send_q); 10062306a36Sopenharmony_ci NDLC_DUMP_SKB("ndlc frame written", skb); 10162306a36Sopenharmony_ci r = ndlc->ops->write(ndlc->phy_id, skb); 10262306a36Sopenharmony_ci if (r < 0) { 10362306a36Sopenharmony_ci ndlc->hard_fault = r; 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci time_sent = jiffies; 10762306a36Sopenharmony_ci *(unsigned long *)skb->cb = time_sent; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci skb_queue_tail(&ndlc->ack_pending_q, skb); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* start timer t1 for ndlc aknowledge */ 11262306a36Sopenharmony_ci ndlc->t1_active = true; 11362306a36Sopenharmony_ci mod_timer(&ndlc->t1_timer, time_sent + 11462306a36Sopenharmony_ci msecs_to_jiffies(NDLC_TIMER_T1)); 11562306a36Sopenharmony_ci /* start timer t2 for chip availability */ 11662306a36Sopenharmony_ci ndlc->t2_active = true; 11762306a36Sopenharmony_ci mod_timer(&ndlc->t2_timer, time_sent + 11862306a36Sopenharmony_ci msecs_to_jiffies(NDLC_TIMER_T2)); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct sk_buff *skb; 12562306a36Sopenharmony_ci u8 pcb; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci while ((skb = skb_dequeue_tail(&ndlc->ack_pending_q))) { 12862306a36Sopenharmony_ci pcb = skb->data[0]; 12962306a36Sopenharmony_ci switch (pcb & PCB_TYPE_MASK) { 13062306a36Sopenharmony_ci case PCB_TYPE_SUPERVISOR: 13162306a36Sopenharmony_ci skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) | 13262306a36Sopenharmony_ci PCB_SUPERVISOR_RETRANSMIT_YES; 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci case PCB_TYPE_DATAFRAME: 13562306a36Sopenharmony_ci skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) | 13662306a36Sopenharmony_ci PCB_DATAFRAME_RETRANSMIT_YES; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci default: 13962306a36Sopenharmony_ci pr_err("UNKNOWN Packet Control Byte=%d\n", pcb); 14062306a36Sopenharmony_ci kfree_skb(skb); 14162306a36Sopenharmony_ci continue; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci skb_queue_head(&ndlc->send_q, skb); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct sk_buff *skb; 15062306a36Sopenharmony_ci u8 pcb; 15162306a36Sopenharmony_ci unsigned long time_sent; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (ndlc->rcv_q.qlen) 15462306a36Sopenharmony_ci pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) { 15762306a36Sopenharmony_ci pcb = skb->data[0]; 15862306a36Sopenharmony_ci skb_pull(skb, 1); 15962306a36Sopenharmony_ci if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) { 16062306a36Sopenharmony_ci switch (pcb & PCB_SYNC_MASK) { 16162306a36Sopenharmony_ci case PCB_SYNC_ACK: 16262306a36Sopenharmony_ci skb = skb_dequeue(&ndlc->ack_pending_q); 16362306a36Sopenharmony_ci kfree_skb(skb); 16462306a36Sopenharmony_ci del_timer_sync(&ndlc->t1_timer); 16562306a36Sopenharmony_ci del_timer_sync(&ndlc->t2_timer); 16662306a36Sopenharmony_ci ndlc->t2_active = false; 16762306a36Sopenharmony_ci ndlc->t1_active = false; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case PCB_SYNC_NACK: 17062306a36Sopenharmony_ci llt_ndlc_requeue_data_pending(ndlc); 17162306a36Sopenharmony_ci llt_ndlc_send_queue(ndlc); 17262306a36Sopenharmony_ci /* start timer t1 for ndlc aknowledge */ 17362306a36Sopenharmony_ci time_sent = jiffies; 17462306a36Sopenharmony_ci ndlc->t1_active = true; 17562306a36Sopenharmony_ci mod_timer(&ndlc->t1_timer, time_sent + 17662306a36Sopenharmony_ci msecs_to_jiffies(NDLC_TIMER_T1)); 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci case PCB_SYNC_WAIT: 17962306a36Sopenharmony_ci time_sent = jiffies; 18062306a36Sopenharmony_ci ndlc->t1_active = true; 18162306a36Sopenharmony_ci mod_timer(&ndlc->t1_timer, time_sent + 18262306a36Sopenharmony_ci msecs_to_jiffies(NDLC_TIMER_T1_WAIT)); 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci default: 18562306a36Sopenharmony_ci kfree_skb(skb); 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci } else if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_DATAFRAME) { 18962306a36Sopenharmony_ci nci_recv_frame(ndlc->ndev, skb); 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci kfree_skb(skb); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void llt_ndlc_sm_work(struct work_struct *work) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci llt_ndlc_send_queue(ndlc); 20162306a36Sopenharmony_ci llt_ndlc_rcv_queue(ndlc); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (ndlc->t1_active && timer_pending(&ndlc->t1_timer) == 0) { 20462306a36Sopenharmony_ci pr_debug 20562306a36Sopenharmony_ci ("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n"); 20662306a36Sopenharmony_ci ndlc->t1_active = false; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci llt_ndlc_requeue_data_pending(ndlc); 20962306a36Sopenharmony_ci llt_ndlc_send_queue(ndlc); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (ndlc->t2_active && timer_pending(&ndlc->t2_timer) == 0) { 21362306a36Sopenharmony_ci pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n"); 21462306a36Sopenharmony_ci ndlc->t2_active = false; 21562306a36Sopenharmony_ci ndlc->t1_active = false; 21662306a36Sopenharmony_ci del_timer_sync(&ndlc->t1_timer); 21762306a36Sopenharmony_ci del_timer_sync(&ndlc->t2_timer); 21862306a36Sopenharmony_ci ndlc_close(ndlc); 21962306a36Sopenharmony_ci ndlc->hard_fault = -EREMOTEIO; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_civoid ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci if (skb == NULL) { 22662306a36Sopenharmony_ci pr_err("NULL Frame -> link is dead\n"); 22762306a36Sopenharmony_ci ndlc->hard_fault = -EREMOTEIO; 22862306a36Sopenharmony_ci ndlc_close(ndlc); 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci NDLC_DUMP_SKB("incoming frame", skb); 23162306a36Sopenharmony_ci skb_queue_tail(&ndlc->rcv_q, skb); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci schedule_work(&ndlc->sm_work); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ciEXPORT_SYMBOL(ndlc_recv); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic void ndlc_t1_timeout(struct timer_list *t) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct llt_ndlc *ndlc = from_timer(ndlc, t, t1_timer); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci schedule_work(&ndlc->sm_work); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void ndlc_t2_timeout(struct timer_list *t) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct llt_ndlc *ndlc = from_timer(ndlc, t, t2_timer); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci schedule_work(&ndlc->sm_work); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciint ndlc_probe(void *phy_id, const struct nfc_phy_ops *phy_ops, 25362306a36Sopenharmony_ci struct device *dev, int phy_headroom, int phy_tailroom, 25462306a36Sopenharmony_ci struct llt_ndlc **ndlc_id, struct st_nci_se_status *se_status) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct llt_ndlc *ndlc; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL); 25962306a36Sopenharmony_ci if (!ndlc) 26062306a36Sopenharmony_ci return -ENOMEM; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ndlc->ops = phy_ops; 26362306a36Sopenharmony_ci ndlc->phy_id = phy_id; 26462306a36Sopenharmony_ci ndlc->dev = dev; 26562306a36Sopenharmony_ci ndlc->powered = 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci *ndlc_id = ndlc; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* initialize timers */ 27062306a36Sopenharmony_ci timer_setup(&ndlc->t1_timer, ndlc_t1_timeout, 0); 27162306a36Sopenharmony_ci timer_setup(&ndlc->t2_timer, ndlc_t2_timeout, 0); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci skb_queue_head_init(&ndlc->rcv_q); 27462306a36Sopenharmony_ci skb_queue_head_init(&ndlc->send_q); 27562306a36Sopenharmony_ci skb_queue_head_init(&ndlc->ack_pending_q); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return st_nci_probe(ndlc, phy_headroom, phy_tailroom, se_status); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ciEXPORT_SYMBOL(ndlc_probe); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_civoid ndlc_remove(struct llt_ndlc *ndlc) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci /* cancel timers */ 28662306a36Sopenharmony_ci del_timer_sync(&ndlc->t1_timer); 28762306a36Sopenharmony_ci del_timer_sync(&ndlc->t2_timer); 28862306a36Sopenharmony_ci ndlc->t2_active = false; 28962306a36Sopenharmony_ci ndlc->t1_active = false; 29062306a36Sopenharmony_ci /* cancel work */ 29162306a36Sopenharmony_ci cancel_work_sync(&ndlc->sm_work); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci st_nci_remove(ndlc->ndev); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci skb_queue_purge(&ndlc->rcv_q); 29662306a36Sopenharmony_ci skb_queue_purge(&ndlc->send_q); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ciEXPORT_SYMBOL(ndlc_remove); 299