18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * NFC hardware simulation driver 48c2ecf20Sopenharmony_ci * Copyright (c) 2013, Intel Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/ctype.h> 118c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 128c2ecf20Sopenharmony_ci#include <linux/nfc.h> 138c2ecf20Sopenharmony_ci#include <net/nfc/nfc.h> 148c2ecf20Sopenharmony_ci#include <net/nfc/digital.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define NFCSIM_ERR(d, fmt, args...) nfc_err(&d->nfc_digital_dev->nfc_dev->dev, \ 178c2ecf20Sopenharmony_ci "%s: " fmt, __func__, ## args) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define NFCSIM_DBG(d, fmt, args...) dev_dbg(&d->nfc_digital_dev->nfc_dev->dev, \ 208c2ecf20Sopenharmony_ci "%s: " fmt, __func__, ## args) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define NFCSIM_VERSION "0.2" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define NFCSIM_MODE_NONE 0 258c2ecf20Sopenharmony_ci#define NFCSIM_MODE_INITIATOR 1 268c2ecf20Sopenharmony_ci#define NFCSIM_MODE_TARGET 2 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define NFCSIM_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \ 298c2ecf20Sopenharmony_ci NFC_DIGITAL_DRV_CAPS_TG_CRC) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct nfcsim { 328c2ecf20Sopenharmony_ci struct nfc_digital_dev *nfc_digital_dev; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci struct work_struct recv_work; 358c2ecf20Sopenharmony_ci struct delayed_work send_work; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci struct nfcsim_link *link_in; 388c2ecf20Sopenharmony_ci struct nfcsim_link *link_out; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci bool up; 418c2ecf20Sopenharmony_ci u8 mode; 428c2ecf20Sopenharmony_ci u8 rf_tech; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci u16 recv_timeout; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci nfc_digital_cmd_complete_t cb; 478c2ecf20Sopenharmony_ci void *arg; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci u8 dropframe; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct nfcsim_link { 538c2ecf20Sopenharmony_ci struct mutex lock; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci u8 rf_tech; 568c2ecf20Sopenharmony_ci u8 mode; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci u8 shutdown; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci struct sk_buff *skb; 618c2ecf20Sopenharmony_ci wait_queue_head_t recv_wait; 628c2ecf20Sopenharmony_ci u8 cond; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct nfcsim_link *nfcsim_link_new(void) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct nfcsim_link *link; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci link = kzalloc(sizeof(struct nfcsim_link), GFP_KERNEL); 708c2ecf20Sopenharmony_ci if (!link) 718c2ecf20Sopenharmony_ci return NULL; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci mutex_init(&link->lock); 748c2ecf20Sopenharmony_ci init_waitqueue_head(&link->recv_wait); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return link; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void nfcsim_link_free(struct nfcsim_link *link) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci dev_kfree_skb(link->skb); 828c2ecf20Sopenharmony_ci kfree(link); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void nfcsim_link_recv_wake(struct nfcsim_link *link) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci link->cond = 1; 888c2ecf20Sopenharmony_ci wake_up_interruptible(&link->recv_wait); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void nfcsim_link_set_skb(struct nfcsim_link *link, struct sk_buff *skb, 928c2ecf20Sopenharmony_ci u8 rf_tech, u8 mode) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci mutex_lock(&link->lock); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci dev_kfree_skb(link->skb); 978c2ecf20Sopenharmony_ci link->skb = skb; 988c2ecf20Sopenharmony_ci link->rf_tech = rf_tech; 998c2ecf20Sopenharmony_ci link->mode = mode; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci mutex_unlock(&link->lock); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void nfcsim_link_recv_cancel(struct nfcsim_link *link) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci mutex_lock(&link->lock); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci link->mode = NFCSIM_MODE_NONE; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci mutex_unlock(&link->lock); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci nfcsim_link_recv_wake(link); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void nfcsim_link_shutdown(struct nfcsim_link *link) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci mutex_lock(&link->lock); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci link->shutdown = 1; 1208c2ecf20Sopenharmony_ci link->mode = NFCSIM_MODE_NONE; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci mutex_unlock(&link->lock); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci nfcsim_link_recv_wake(link); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct sk_buff *nfcsim_link_recv_skb(struct nfcsim_link *link, 1288c2ecf20Sopenharmony_ci int timeout, u8 rf_tech, u8 mode) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int rc; 1318c2ecf20Sopenharmony_ci struct sk_buff *skb; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci rc = wait_event_interruptible_timeout(link->recv_wait, 1348c2ecf20Sopenharmony_ci link->cond, 1358c2ecf20Sopenharmony_ci msecs_to_jiffies(timeout)); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci mutex_lock(&link->lock); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci skb = link->skb; 1408c2ecf20Sopenharmony_ci link->skb = NULL; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (!rc) { 1438c2ecf20Sopenharmony_ci rc = -ETIMEDOUT; 1448c2ecf20Sopenharmony_ci goto done; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (!skb || link->rf_tech != rf_tech || link->mode == mode) { 1488c2ecf20Sopenharmony_ci rc = -EINVAL; 1498c2ecf20Sopenharmony_ci goto done; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (link->shutdown) { 1538c2ecf20Sopenharmony_ci rc = -ENODEV; 1548c2ecf20Sopenharmony_ci goto done; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cidone: 1588c2ecf20Sopenharmony_ci mutex_unlock(&link->lock); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (rc < 0) { 1618c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1628c2ecf20Sopenharmony_ci skb = ERR_PTR(rc); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci link->cond = 0; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return skb; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void nfcsim_send_wq(struct work_struct *work) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct nfcsim *dev = container_of(work, struct nfcsim, send_work.work); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * To effectively send data, the device just wake up its link_out which 1768c2ecf20Sopenharmony_ci * is the link_in of the peer device. The exchanged skb has already been 1778c2ecf20Sopenharmony_ci * stored in the dev->link_out through nfcsim_link_set_skb(). 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci nfcsim_link_recv_wake(dev->link_out); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void nfcsim_recv_wq(struct work_struct *work) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct nfcsim *dev = container_of(work, struct nfcsim, recv_work); 1858c2ecf20Sopenharmony_ci struct sk_buff *skb; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci skb = nfcsim_link_recv_skb(dev->link_in, dev->recv_timeout, 1888c2ecf20Sopenharmony_ci dev->rf_tech, dev->mode); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!dev->up) { 1918c2ecf20Sopenharmony_ci NFCSIM_ERR(dev, "Device is down\n"); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!IS_ERR(skb)) 1948c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci dev->cb(dev->nfc_digital_dev, dev->arg, skb); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int nfcsim_send(struct nfc_digital_dev *ddev, struct sk_buff *skb, 2028c2ecf20Sopenharmony_ci u16 timeout, nfc_digital_cmd_complete_t cb, void *arg) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct nfcsim *dev = nfc_digital_get_drvdata(ddev); 2058c2ecf20Sopenharmony_ci u8 delay; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (!dev->up) { 2088c2ecf20Sopenharmony_ci NFCSIM_ERR(dev, "Device is down\n"); 2098c2ecf20Sopenharmony_ci return -ENODEV; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci dev->recv_timeout = timeout; 2138c2ecf20Sopenharmony_ci dev->cb = cb; 2148c2ecf20Sopenharmony_ci dev->arg = arg; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci schedule_work(&dev->recv_work); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (dev->dropframe) { 2198c2ecf20Sopenharmony_ci NFCSIM_DBG(dev, "dropping frame (out of %d)\n", dev->dropframe); 2208c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2218c2ecf20Sopenharmony_ci dev->dropframe--; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (skb) { 2278c2ecf20Sopenharmony_ci nfcsim_link_set_skb(dev->link_out, skb, dev->rf_tech, 2288c2ecf20Sopenharmony_ci dev->mode); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Add random delay (between 3 and 10 ms) before sending data */ 2318c2ecf20Sopenharmony_ci get_random_bytes(&delay, 1); 2328c2ecf20Sopenharmony_ci delay = 3 + (delay & 0x07); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci schedule_delayed_work(&dev->send_work, msecs_to_jiffies(delay)); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic void nfcsim_abort_cmd(struct nfc_digital_dev *ddev) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct nfcsim *dev = nfc_digital_get_drvdata(ddev); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci nfcsim_link_recv_cancel(dev->link_in); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int nfcsim_switch_rf(struct nfc_digital_dev *ddev, bool on) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct nfcsim *dev = nfc_digital_get_drvdata(ddev); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dev->up = on; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int nfcsim_in_configure_hw(struct nfc_digital_dev *ddev, 2578c2ecf20Sopenharmony_ci int type, int param) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct nfcsim *dev = nfc_digital_get_drvdata(ddev); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci switch (type) { 2628c2ecf20Sopenharmony_ci case NFC_DIGITAL_CONFIG_RF_TECH: 2638c2ecf20Sopenharmony_ci dev->up = true; 2648c2ecf20Sopenharmony_ci dev->mode = NFCSIM_MODE_INITIATOR; 2658c2ecf20Sopenharmony_ci dev->rf_tech = param; 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci case NFC_DIGITAL_CONFIG_FRAMING: 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci default: 2728c2ecf20Sopenharmony_ci NFCSIM_ERR(dev, "Invalid configuration type: %d\n", type); 2738c2ecf20Sopenharmony_ci return -EINVAL; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int nfcsim_in_send_cmd(struct nfc_digital_dev *ddev, 2808c2ecf20Sopenharmony_ci struct sk_buff *skb, u16 timeout, 2818c2ecf20Sopenharmony_ci nfc_digital_cmd_complete_t cb, void *arg) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci return nfcsim_send(ddev, skb, timeout, cb, arg); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int nfcsim_tg_configure_hw(struct nfc_digital_dev *ddev, 2878c2ecf20Sopenharmony_ci int type, int param) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct nfcsim *dev = nfc_digital_get_drvdata(ddev); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci switch (type) { 2928c2ecf20Sopenharmony_ci case NFC_DIGITAL_CONFIG_RF_TECH: 2938c2ecf20Sopenharmony_ci dev->up = true; 2948c2ecf20Sopenharmony_ci dev->mode = NFCSIM_MODE_TARGET; 2958c2ecf20Sopenharmony_ci dev->rf_tech = param; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci case NFC_DIGITAL_CONFIG_FRAMING: 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci default: 3028c2ecf20Sopenharmony_ci NFCSIM_ERR(dev, "Invalid configuration type: %d\n", type); 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int nfcsim_tg_send_cmd(struct nfc_digital_dev *ddev, 3108c2ecf20Sopenharmony_ci struct sk_buff *skb, u16 timeout, 3118c2ecf20Sopenharmony_ci nfc_digital_cmd_complete_t cb, void *arg) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci return nfcsim_send(ddev, skb, timeout, cb, arg); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int nfcsim_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, 3178c2ecf20Sopenharmony_ci nfc_digital_cmd_complete_t cb, void *arg) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci return nfcsim_send(ddev, NULL, timeout, cb, arg); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic struct nfc_digital_ops nfcsim_digital_ops = { 3238c2ecf20Sopenharmony_ci .in_configure_hw = nfcsim_in_configure_hw, 3248c2ecf20Sopenharmony_ci .in_send_cmd = nfcsim_in_send_cmd, 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci .tg_listen = nfcsim_tg_listen, 3278c2ecf20Sopenharmony_ci .tg_configure_hw = nfcsim_tg_configure_hw, 3288c2ecf20Sopenharmony_ci .tg_send_cmd = nfcsim_tg_send_cmd, 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci .abort_cmd = nfcsim_abort_cmd, 3318c2ecf20Sopenharmony_ci .switch_rf = nfcsim_switch_rf, 3328c2ecf20Sopenharmony_ci}; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic struct dentry *nfcsim_debugfs_root; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void nfcsim_debugfs_init(void) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci nfcsim_debugfs_root = debugfs_create_dir("nfcsim", NULL); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic void nfcsim_debugfs_remove(void) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci debugfs_remove_recursive(nfcsim_debugfs_root); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic void nfcsim_debugfs_init_dev(struct nfcsim *dev) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct dentry *dev_dir; 3498c2ecf20Sopenharmony_ci char devname[5]; /* nfcX\0 */ 3508c2ecf20Sopenharmony_ci u32 idx; 3518c2ecf20Sopenharmony_ci int n; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (!nfcsim_debugfs_root) { 3548c2ecf20Sopenharmony_ci NFCSIM_ERR(dev, "nfcsim debugfs not initialized\n"); 3558c2ecf20Sopenharmony_ci return; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci idx = dev->nfc_digital_dev->nfc_dev->idx; 3598c2ecf20Sopenharmony_ci n = snprintf(devname, sizeof(devname), "nfc%d", idx); 3608c2ecf20Sopenharmony_ci if (n >= sizeof(devname)) { 3618c2ecf20Sopenharmony_ci NFCSIM_ERR(dev, "Could not compute dev name for dev %d\n", idx); 3628c2ecf20Sopenharmony_ci return; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci dev_dir = debugfs_create_dir(devname, nfcsim_debugfs_root); 3668c2ecf20Sopenharmony_ci if (!dev_dir) { 3678c2ecf20Sopenharmony_ci NFCSIM_ERR(dev, "Could not create debugfs entries for nfc%d\n", 3688c2ecf20Sopenharmony_ci idx); 3698c2ecf20Sopenharmony_ci return; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci debugfs_create_u8("dropframe", 0664, dev_dir, &dev->dropframe); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic struct nfcsim *nfcsim_device_new(struct nfcsim_link *link_in, 3768c2ecf20Sopenharmony_ci struct nfcsim_link *link_out) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct nfcsim *dev; 3798c2ecf20Sopenharmony_ci int rc; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct nfcsim), GFP_KERNEL); 3828c2ecf20Sopenharmony_ci if (!dev) 3838c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&dev->send_work, nfcsim_send_wq); 3868c2ecf20Sopenharmony_ci INIT_WORK(&dev->recv_work, nfcsim_recv_wq); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dev->nfc_digital_dev = 3898c2ecf20Sopenharmony_ci nfc_digital_allocate_device(&nfcsim_digital_ops, 3908c2ecf20Sopenharmony_ci NFC_PROTO_NFC_DEP_MASK, 3918c2ecf20Sopenharmony_ci NFCSIM_CAPABILITIES, 3928c2ecf20Sopenharmony_ci 0, 0); 3938c2ecf20Sopenharmony_ci if (!dev->nfc_digital_dev) { 3948c2ecf20Sopenharmony_ci kfree(dev); 3958c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci nfc_digital_set_drvdata(dev->nfc_digital_dev, dev); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci dev->link_in = link_in; 4018c2ecf20Sopenharmony_ci dev->link_out = link_out; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci rc = nfc_digital_register_device(dev->nfc_digital_dev); 4048c2ecf20Sopenharmony_ci if (rc) { 4058c2ecf20Sopenharmony_ci pr_err("Could not register digital device (%d)\n", rc); 4068c2ecf20Sopenharmony_ci nfc_digital_free_device(dev->nfc_digital_dev); 4078c2ecf20Sopenharmony_ci kfree(dev); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return ERR_PTR(rc); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci nfcsim_debugfs_init_dev(dev); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return dev; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic void nfcsim_device_free(struct nfcsim *dev) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci nfc_digital_unregister_device(dev->nfc_digital_dev); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci dev->up = false; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci nfcsim_link_shutdown(dev->link_in); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&dev->send_work); 4268c2ecf20Sopenharmony_ci cancel_work_sync(&dev->recv_work); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci nfc_digital_free_device(dev->nfc_digital_dev); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci kfree(dev); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic struct nfcsim *dev0; 4348c2ecf20Sopenharmony_cistatic struct nfcsim *dev1; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int __init nfcsim_init(void) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct nfcsim_link *link0, *link1; 4398c2ecf20Sopenharmony_ci int rc; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci link0 = nfcsim_link_new(); 4428c2ecf20Sopenharmony_ci link1 = nfcsim_link_new(); 4438c2ecf20Sopenharmony_ci if (!link0 || !link1) { 4448c2ecf20Sopenharmony_ci rc = -ENOMEM; 4458c2ecf20Sopenharmony_ci goto exit_err; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci nfcsim_debugfs_init(); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci dev0 = nfcsim_device_new(link0, link1); 4518c2ecf20Sopenharmony_ci if (IS_ERR(dev0)) { 4528c2ecf20Sopenharmony_ci rc = PTR_ERR(dev0); 4538c2ecf20Sopenharmony_ci goto exit_err; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci dev1 = nfcsim_device_new(link1, link0); 4578c2ecf20Sopenharmony_ci if (IS_ERR(dev1)) { 4588c2ecf20Sopenharmony_ci nfcsim_device_free(dev0); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci rc = PTR_ERR(dev1); 4618c2ecf20Sopenharmony_ci goto exit_err; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci pr_info("nfcsim " NFCSIM_VERSION " initialized\n"); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ciexit_err: 4698c2ecf20Sopenharmony_ci pr_err("Failed to initialize nfcsim driver (%d)\n", rc); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (link0) 4728c2ecf20Sopenharmony_ci nfcsim_link_free(link0); 4738c2ecf20Sopenharmony_ci if (link1) 4748c2ecf20Sopenharmony_ci nfcsim_link_free(link1); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return rc; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic void __exit nfcsim_exit(void) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct nfcsim_link *link0, *link1; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci link0 = dev0->link_in; 4848c2ecf20Sopenharmony_ci link1 = dev0->link_out; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci nfcsim_device_free(dev0); 4878c2ecf20Sopenharmony_ci nfcsim_device_free(dev1); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci nfcsim_link_free(link0); 4908c2ecf20Sopenharmony_ci nfcsim_link_free(link1); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci nfcsim_debugfs_remove(); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cimodule_init(nfcsim_init); 4968c2ecf20Sopenharmony_cimodule_exit(nfcsim_exit); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NFCSim driver ver " NFCSIM_VERSION); 4998c2ecf20Sopenharmony_ciMODULE_VERSION(NFCSIM_VERSION); 5008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 501