18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/sched.h> 98c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "st-nci.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define NDLC_TIMER_T1 100 148c2ecf20Sopenharmony_ci#define NDLC_TIMER_T1_WAIT 400 158c2ecf20Sopenharmony_ci#define NDLC_TIMER_T2 1200 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define PCB_TYPE_DATAFRAME 0x80 188c2ecf20Sopenharmony_ci#define PCB_TYPE_SUPERVISOR 0xc0 198c2ecf20Sopenharmony_ci#define PCB_TYPE_MASK PCB_TYPE_SUPERVISOR 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PCB_SYNC_ACK 0x20 228c2ecf20Sopenharmony_ci#define PCB_SYNC_NACK 0x10 238c2ecf20Sopenharmony_ci#define PCB_SYNC_WAIT 0x30 248c2ecf20Sopenharmony_ci#define PCB_SYNC_NOINFO 0x00 258c2ecf20Sopenharmony_ci#define PCB_SYNC_MASK PCB_SYNC_WAIT 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define PCB_DATAFRAME_RETRANSMIT_YES 0x00 288c2ecf20Sopenharmony_ci#define PCB_DATAFRAME_RETRANSMIT_NO 0x04 298c2ecf20Sopenharmony_ci#define PCB_DATAFRAME_RETRANSMIT_MASK PCB_DATAFRAME_RETRANSMIT_NO 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define PCB_SUPERVISOR_RETRANSMIT_YES 0x00 328c2ecf20Sopenharmony_ci#define PCB_SUPERVISOR_RETRANSMIT_NO 0x02 338c2ecf20Sopenharmony_ci#define PCB_SUPERVISOR_RETRANSMIT_MASK PCB_SUPERVISOR_RETRANSMIT_NO 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define PCB_FRAME_CRC_INFO_PRESENT 0x08 368c2ecf20Sopenharmony_ci#define PCB_FRAME_CRC_INFO_NOTPRESENT 0x00 378c2ecf20Sopenharmony_ci#define PCB_FRAME_CRC_INFO_MASK PCB_FRAME_CRC_INFO_PRESENT 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define NDLC_DUMP_SKB(info, skb) \ 408c2ecf20Sopenharmony_cido { \ 418c2ecf20Sopenharmony_ci pr_debug("%s:\n", info); \ 428c2ecf20Sopenharmony_ci print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \ 438c2ecf20Sopenharmony_ci 16, 1, skb->data, skb->len, 0); \ 448c2ecf20Sopenharmony_ci} while (0) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciint ndlc_open(struct llt_ndlc *ndlc) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci /* toggle reset pin */ 498c2ecf20Sopenharmony_ci ndlc->ops->enable(ndlc->phy_id); 508c2ecf20Sopenharmony_ci ndlc->powered = 1; 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ndlc_open); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_civoid ndlc_close(struct llt_ndlc *ndlc) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct nci_mode_set_cmd cmd; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci cmd.cmd_type = ST_NCI_SET_NFC_MODE; 608c2ecf20Sopenharmony_ci cmd.mode = 0; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* toggle reset pin */ 638c2ecf20Sopenharmony_ci ndlc->ops->enable(ndlc->phy_id); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP, 668c2ecf20Sopenharmony_ci sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci ndlc->powered = 0; 698c2ecf20Sopenharmony_ci ndlc->ops->disable(ndlc->phy_id); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ndlc_close); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciint ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci /* add ndlc header */ 768c2ecf20Sopenharmony_ci u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO | 778c2ecf20Sopenharmony_ci PCB_FRAME_CRC_INFO_NOTPRESENT; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci *(u8 *)skb_push(skb, 1) = pcb; 808c2ecf20Sopenharmony_ci skb_queue_tail(&ndlc->send_q, skb); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci schedule_work(&ndlc->sm_work); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ndlc_send); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void llt_ndlc_send_queue(struct llt_ndlc *ndlc) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct sk_buff *skb; 918c2ecf20Sopenharmony_ci int r; 928c2ecf20Sopenharmony_ci unsigned long time_sent; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (ndlc->send_q.qlen) 958c2ecf20Sopenharmony_ci pr_debug("sendQlen=%d unackQlen=%d\n", 968c2ecf20Sopenharmony_ci ndlc->send_q.qlen, ndlc->ack_pending_q.qlen); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci while (ndlc->send_q.qlen) { 998c2ecf20Sopenharmony_ci skb = skb_dequeue(&ndlc->send_q); 1008c2ecf20Sopenharmony_ci NDLC_DUMP_SKB("ndlc frame written", skb); 1018c2ecf20Sopenharmony_ci r = ndlc->ops->write(ndlc->phy_id, skb); 1028c2ecf20Sopenharmony_ci if (r < 0) { 1038c2ecf20Sopenharmony_ci ndlc->hard_fault = r; 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci time_sent = jiffies; 1078c2ecf20Sopenharmony_ci *(unsigned long *)skb->cb = time_sent; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci skb_queue_tail(&ndlc->ack_pending_q, skb); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* start timer t1 for ndlc aknowledge */ 1128c2ecf20Sopenharmony_ci ndlc->t1_active = true; 1138c2ecf20Sopenharmony_ci mod_timer(&ndlc->t1_timer, time_sent + 1148c2ecf20Sopenharmony_ci msecs_to_jiffies(NDLC_TIMER_T1)); 1158c2ecf20Sopenharmony_ci /* start timer t2 for chip availability */ 1168c2ecf20Sopenharmony_ci ndlc->t2_active = true; 1178c2ecf20Sopenharmony_ci mod_timer(&ndlc->t2_timer, time_sent + 1188c2ecf20Sopenharmony_ci msecs_to_jiffies(NDLC_TIMER_T2)); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct sk_buff *skb; 1258c2ecf20Sopenharmony_ci u8 pcb; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci while ((skb = skb_dequeue_tail(&ndlc->ack_pending_q))) { 1288c2ecf20Sopenharmony_ci pcb = skb->data[0]; 1298c2ecf20Sopenharmony_ci switch (pcb & PCB_TYPE_MASK) { 1308c2ecf20Sopenharmony_ci case PCB_TYPE_SUPERVISOR: 1318c2ecf20Sopenharmony_ci skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) | 1328c2ecf20Sopenharmony_ci PCB_SUPERVISOR_RETRANSMIT_YES; 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci case PCB_TYPE_DATAFRAME: 1358c2ecf20Sopenharmony_ci skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) | 1368c2ecf20Sopenharmony_ci PCB_DATAFRAME_RETRANSMIT_YES; 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci default: 1398c2ecf20Sopenharmony_ci pr_err("UNKNOWN Packet Control Byte=%d\n", pcb); 1408c2ecf20Sopenharmony_ci kfree_skb(skb); 1418c2ecf20Sopenharmony_ci continue; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci skb_queue_head(&ndlc->send_q, skb); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct sk_buff *skb; 1508c2ecf20Sopenharmony_ci u8 pcb; 1518c2ecf20Sopenharmony_ci unsigned long time_sent; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (ndlc->rcv_q.qlen) 1548c2ecf20Sopenharmony_ci pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) { 1578c2ecf20Sopenharmony_ci pcb = skb->data[0]; 1588c2ecf20Sopenharmony_ci skb_pull(skb, 1); 1598c2ecf20Sopenharmony_ci if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) { 1608c2ecf20Sopenharmony_ci switch (pcb & PCB_SYNC_MASK) { 1618c2ecf20Sopenharmony_ci case PCB_SYNC_ACK: 1628c2ecf20Sopenharmony_ci skb = skb_dequeue(&ndlc->ack_pending_q); 1638c2ecf20Sopenharmony_ci kfree_skb(skb); 1648c2ecf20Sopenharmony_ci del_timer_sync(&ndlc->t1_timer); 1658c2ecf20Sopenharmony_ci del_timer_sync(&ndlc->t2_timer); 1668c2ecf20Sopenharmony_ci ndlc->t2_active = false; 1678c2ecf20Sopenharmony_ci ndlc->t1_active = false; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case PCB_SYNC_NACK: 1708c2ecf20Sopenharmony_ci llt_ndlc_requeue_data_pending(ndlc); 1718c2ecf20Sopenharmony_ci llt_ndlc_send_queue(ndlc); 1728c2ecf20Sopenharmony_ci /* start timer t1 for ndlc aknowledge */ 1738c2ecf20Sopenharmony_ci time_sent = jiffies; 1748c2ecf20Sopenharmony_ci ndlc->t1_active = true; 1758c2ecf20Sopenharmony_ci mod_timer(&ndlc->t1_timer, time_sent + 1768c2ecf20Sopenharmony_ci msecs_to_jiffies(NDLC_TIMER_T1)); 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci case PCB_SYNC_WAIT: 1798c2ecf20Sopenharmony_ci time_sent = jiffies; 1808c2ecf20Sopenharmony_ci ndlc->t1_active = true; 1818c2ecf20Sopenharmony_ci mod_timer(&ndlc->t1_timer, time_sent + 1828c2ecf20Sopenharmony_ci msecs_to_jiffies(NDLC_TIMER_T1_WAIT)); 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci default: 1858c2ecf20Sopenharmony_ci kfree_skb(skb); 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci } else if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_DATAFRAME) { 1898c2ecf20Sopenharmony_ci nci_recv_frame(ndlc->ndev, skb); 1908c2ecf20Sopenharmony_ci } else { 1918c2ecf20Sopenharmony_ci kfree_skb(skb); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void llt_ndlc_sm_work(struct work_struct *work) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci llt_ndlc_send_queue(ndlc); 2018c2ecf20Sopenharmony_ci llt_ndlc_rcv_queue(ndlc); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (ndlc->t1_active && timer_pending(&ndlc->t1_timer) == 0) { 2048c2ecf20Sopenharmony_ci pr_debug 2058c2ecf20Sopenharmony_ci ("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n"); 2068c2ecf20Sopenharmony_ci ndlc->t1_active = false; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci llt_ndlc_requeue_data_pending(ndlc); 2098c2ecf20Sopenharmony_ci llt_ndlc_send_queue(ndlc); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (ndlc->t2_active && timer_pending(&ndlc->t2_timer) == 0) { 2138c2ecf20Sopenharmony_ci pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n"); 2148c2ecf20Sopenharmony_ci ndlc->t2_active = false; 2158c2ecf20Sopenharmony_ci ndlc->t1_active = false; 2168c2ecf20Sopenharmony_ci del_timer_sync(&ndlc->t1_timer); 2178c2ecf20Sopenharmony_ci del_timer_sync(&ndlc->t2_timer); 2188c2ecf20Sopenharmony_ci ndlc_close(ndlc); 2198c2ecf20Sopenharmony_ci ndlc->hard_fault = -EREMOTEIO; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci if (skb == NULL) { 2268c2ecf20Sopenharmony_ci pr_err("NULL Frame -> link is dead\n"); 2278c2ecf20Sopenharmony_ci ndlc->hard_fault = -EREMOTEIO; 2288c2ecf20Sopenharmony_ci ndlc_close(ndlc); 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci NDLC_DUMP_SKB("incoming frame", skb); 2318c2ecf20Sopenharmony_ci skb_queue_tail(&ndlc->rcv_q, skb); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci schedule_work(&ndlc->sm_work); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ndlc_recv); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void ndlc_t1_timeout(struct timer_list *t) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct llt_ndlc *ndlc = from_timer(ndlc, t, t1_timer); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci pr_debug("\n"); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci schedule_work(&ndlc->sm_work); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void ndlc_t2_timeout(struct timer_list *t) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct llt_ndlc *ndlc = from_timer(ndlc, t, t2_timer); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci pr_debug("\n"); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci schedule_work(&ndlc->sm_work); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ciint ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, 2578c2ecf20Sopenharmony_ci int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id, 2588c2ecf20Sopenharmony_ci struct st_nci_se_status *se_status) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct llt_ndlc *ndlc; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL); 2638c2ecf20Sopenharmony_ci if (!ndlc) 2648c2ecf20Sopenharmony_ci return -ENOMEM; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci ndlc->ops = phy_ops; 2678c2ecf20Sopenharmony_ci ndlc->phy_id = phy_id; 2688c2ecf20Sopenharmony_ci ndlc->dev = dev; 2698c2ecf20Sopenharmony_ci ndlc->powered = 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci *ndlc_id = ndlc; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* initialize timers */ 2748c2ecf20Sopenharmony_ci timer_setup(&ndlc->t1_timer, ndlc_t1_timeout, 0); 2758c2ecf20Sopenharmony_ci timer_setup(&ndlc->t2_timer, ndlc_t2_timeout, 0); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci skb_queue_head_init(&ndlc->rcv_q); 2788c2ecf20Sopenharmony_ci skb_queue_head_init(&ndlc->send_q); 2798c2ecf20Sopenharmony_ci skb_queue_head_init(&ndlc->ack_pending_q); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return st_nci_probe(ndlc, phy_headroom, phy_tailroom, se_status); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ndlc_probe); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_civoid ndlc_remove(struct llt_ndlc *ndlc) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci /* cancel timers */ 2908c2ecf20Sopenharmony_ci del_timer_sync(&ndlc->t1_timer); 2918c2ecf20Sopenharmony_ci del_timer_sync(&ndlc->t2_timer); 2928c2ecf20Sopenharmony_ci ndlc->t2_active = false; 2938c2ecf20Sopenharmony_ci ndlc->t1_active = false; 2948c2ecf20Sopenharmony_ci /* cancel work */ 2958c2ecf20Sopenharmony_ci cancel_work_sync(&ndlc->sm_work); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci st_nci_remove(ndlc->ndev); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci skb_queue_purge(&ndlc->rcv_q); 3008c2ecf20Sopenharmony_ci skb_queue_purge(&ndlc->send_q); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ndlc_remove); 303