123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- /*
- * NFC hardware simulation driver
- * Copyright (c) 2013, Intel Corporation.
- *
- * 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 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.
- *
- */
- #include <linux/device.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/nfc.h>
- #include <net/nfc/nfc.h>
- #define DEV_ERR(_dev, fmt, args...) nfc_err(&_dev->nfc_dev->dev, \
- "%s: " fmt, __func__, ## args)
- #define DEV_DBG(_dev, fmt, args...) dev_dbg(&_dev->nfc_dev->dev, \
- "%s: " fmt, __func__, ## args)
- #define NFCSIM_VERSION "0.1"
- #define NFCSIM_POLL_NONE 0
- #define NFCSIM_POLL_INITIATOR 1
- #define NFCSIM_POLL_TARGET 2
- #define NFCSIM_POLL_DUAL (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET)
- struct nfcsim {
- struct nfc_dev *nfc_dev;
- struct mutex lock;
- struct delayed_work recv_work;
- struct sk_buff *clone_skb;
- struct delayed_work poll_work;
- u8 polling_mode;
- u8 curr_polling_mode;
- u8 shutting_down;
- u8 up;
- u8 initiator;
- data_exchange_cb_t cb;
- void *cb_context;
- struct nfcsim *peer_dev;
- };
- static struct nfcsim *dev0;
- static struct nfcsim *dev1;
- static struct workqueue_struct *wq;
- static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
- {
- DEV_DBG(dev, "shutdown=%d\n", shutdown);
- mutex_lock(&dev->lock);
- dev->polling_mode = NFCSIM_POLL_NONE;
- dev->shutting_down = shutdown;
- dev->cb = NULL;
- dev_kfree_skb(dev->clone_skb);
- dev->clone_skb = NULL;
- mutex_unlock(&dev->lock);
- cancel_delayed_work_sync(&dev->poll_work);
- cancel_delayed_work_sync(&dev->recv_work);
- }
- static int nfcsim_target_found(struct nfcsim *dev)
- {
- struct nfc_target nfc_tgt;
- DEV_DBG(dev, "\n");
- memset(&nfc_tgt, 0, sizeof(struct nfc_target));
- nfc_tgt.supported_protocols = NFC_PROTO_NFC_DEP_MASK;
- nfc_targets_found(dev->nfc_dev, &nfc_tgt, 1);
- return 0;
- }
- static int nfcsim_dev_up(struct nfc_dev *nfc_dev)
- {
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- DEV_DBG(dev, "\n");
- mutex_lock(&dev->lock);
- dev->up = 1;
- mutex_unlock(&dev->lock);
- return 0;
- }
- static int nfcsim_dev_down(struct nfc_dev *nfc_dev)
- {
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- DEV_DBG(dev, "\n");
- mutex_lock(&dev->lock);
- dev->up = 0;
- mutex_unlock(&dev->lock);
- return 0;
- }
- static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev,
- struct nfc_target *target,
- u8 comm_mode, u8 *gb, size_t gb_len)
- {
- int rc;
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- struct nfcsim *peer = dev->peer_dev;
- u8 *remote_gb;
- size_t remote_gb_len;
- DEV_DBG(dev, "target_idx: %d, comm_mode: %d\n", target->idx, comm_mode);
- mutex_lock(&peer->lock);
- nfc_tm_activated(peer->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
- NFC_COMM_ACTIVE, gb, gb_len);
- remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len);
- if (!remote_gb) {
- DEV_ERR(peer, "Can't get remote general bytes\n");
- mutex_unlock(&peer->lock);
- return -EINVAL;
- }
- mutex_unlock(&peer->lock);
- mutex_lock(&dev->lock);
- rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len);
- if (rc) {
- DEV_ERR(dev, "Can't set remote general bytes\n");
- mutex_unlock(&dev->lock);
- return rc;
- }
- rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_ACTIVE,
- NFC_RF_INITIATOR);
- mutex_unlock(&dev->lock);
- return rc;
- }
- static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev)
- {
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- DEV_DBG(dev, "\n");
- nfcsim_cleanup_dev(dev, 0);
- return 0;
- }
- static int nfcsim_start_poll(struct nfc_dev *nfc_dev,
- u32 im_protocols, u32 tm_protocols)
- {
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- int rc;
- mutex_lock(&dev->lock);
- if (dev->polling_mode != NFCSIM_POLL_NONE) {
- DEV_ERR(dev, "Already in polling mode\n");
- rc = -EBUSY;
- goto exit;
- }
- if (im_protocols & NFC_PROTO_NFC_DEP_MASK)
- dev->polling_mode |= NFCSIM_POLL_INITIATOR;
- if (tm_protocols & NFC_PROTO_NFC_DEP_MASK)
- dev->polling_mode |= NFCSIM_POLL_TARGET;
- if (dev->polling_mode == NFCSIM_POLL_NONE) {
- DEV_ERR(dev, "Unsupported polling mode\n");
- rc = -EINVAL;
- goto exit;
- }
- dev->initiator = 0;
- dev->curr_polling_mode = NFCSIM_POLL_NONE;
- queue_delayed_work(wq, &dev->poll_work, 0);
- DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X\n", im_protocols,
- tm_protocols);
- rc = 0;
- exit:
- mutex_unlock(&dev->lock);
- return rc;
- }
- static void nfcsim_stop_poll(struct nfc_dev *nfc_dev)
- {
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- DEV_DBG(dev, "Stop poll\n");
- mutex_lock(&dev->lock);
- dev->polling_mode = NFCSIM_POLL_NONE;
- mutex_unlock(&dev->lock);
- cancel_delayed_work_sync(&dev->poll_work);
- }
- static int nfcsim_activate_target(struct nfc_dev *nfc_dev,
- struct nfc_target *target, u32 protocol)
- {
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- DEV_DBG(dev, "\n");
- return -ENOTSUPP;
- }
- static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev,
- struct nfc_target *target, u8 mode)
- {
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- DEV_DBG(dev, "\n");
- }
- static void nfcsim_wq_recv(struct work_struct *work)
- {
- struct nfcsim *dev = container_of(work, struct nfcsim,
- recv_work.work);
- mutex_lock(&dev->lock);
- if (dev->shutting_down || !dev->up || !dev->clone_skb) {
- dev_kfree_skb(dev->clone_skb);
- goto exit;
- }
- if (dev->initiator) {
- if (!dev->cb) {
- DEV_ERR(dev, "Null recv callback\n");
- dev_kfree_skb(dev->clone_skb);
- goto exit;
- }
- dev->cb(dev->cb_context, dev->clone_skb, 0);
- dev->cb = NULL;
- } else {
- nfc_tm_data_received(dev->nfc_dev, dev->clone_skb);
- }
- exit:
- dev->clone_skb = NULL;
- mutex_unlock(&dev->lock);
- }
- static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
- struct sk_buff *skb, data_exchange_cb_t cb,
- void *cb_context)
- {
- struct nfcsim *dev = nfc_get_drvdata(nfc_dev);
- struct nfcsim *peer = dev->peer_dev;
- int err;
- mutex_lock(&dev->lock);
- if (dev->shutting_down || !dev->up) {
- mutex_unlock(&dev->lock);
- err = -ENODEV;
- goto exit;
- }
- dev->cb = cb;
- dev->cb_context = cb_context;
- mutex_unlock(&dev->lock);
- mutex_lock(&peer->lock);
- peer->clone_skb = skb_clone(skb, GFP_KERNEL);
- if (!peer->clone_skb) {
- DEV_ERR(dev, "skb_clone failed\n");
- mutex_unlock(&peer->lock);
- err = -ENOMEM;
- goto exit;
- }
- /* This simulates an arbitrary transmission delay between the 2 devices.
- * If packet transmission occurs immediately between them, we have a
- * non-stop flow of several tens of thousands SYMM packets per second
- * and a burning cpu.
- *
- * TODO: Add support for a sysfs entry to control this delay.
- */
- queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5));
- mutex_unlock(&peer->lock);
- err = 0;
- exit:
- dev_kfree_skb(skb);
- return err;
- }
- static int nfcsim_im_transceive(struct nfc_dev *nfc_dev,
- struct nfc_target *target, struct sk_buff *skb,
- data_exchange_cb_t cb, void *cb_context)
- {
- return nfcsim_tx(nfc_dev, target, skb, cb, cb_context);
- }
- static int nfcsim_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
- {
- return nfcsim_tx(nfc_dev, NULL, skb, NULL, NULL);
- }
- static struct nfc_ops nfcsim_nfc_ops = {
- .dev_up = nfcsim_dev_up,
- .dev_down = nfcsim_dev_down,
- .dep_link_up = nfcsim_dep_link_up,
- .dep_link_down = nfcsim_dep_link_down,
- .start_poll = nfcsim_start_poll,
- .stop_poll = nfcsim_stop_poll,
- .activate_target = nfcsim_activate_target,
- .deactivate_target = nfcsim_deactivate_target,
- .im_transceive = nfcsim_im_transceive,
- .tm_send = nfcsim_tm_send,
- };
- static void nfcsim_set_polling_mode(struct nfcsim *dev)
- {
- if (dev->polling_mode == NFCSIM_POLL_NONE) {
- dev->curr_polling_mode = NFCSIM_POLL_NONE;
- return;
- }
- if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
- if (dev->polling_mode & NFCSIM_POLL_INITIATOR)
- dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
- else
- dev->curr_polling_mode = NFCSIM_POLL_TARGET;
- return;
- }
- if (dev->polling_mode == NFCSIM_POLL_DUAL) {
- if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
- dev->curr_polling_mode = NFCSIM_POLL_INITIATOR;
- else
- dev->curr_polling_mode = NFCSIM_POLL_TARGET;
- }
- }
- static void nfcsim_wq_poll(struct work_struct *work)
- {
- struct nfcsim *dev = container_of(work, struct nfcsim, poll_work.work);
- struct nfcsim *peer = dev->peer_dev;
- /* These work items run on an ordered workqueue and are therefore
- * serialized. So we can take both mutexes without being dead locked.
- */
- mutex_lock(&dev->lock);
- mutex_lock(&peer->lock);
- nfcsim_set_polling_mode(dev);
- if (dev->curr_polling_mode == NFCSIM_POLL_NONE) {
- DEV_DBG(dev, "Not polling\n");
- goto unlock;
- }
- DEV_DBG(dev, "Polling as %s",
- dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ?
- "initiator\n" : "target\n");
- if (dev->curr_polling_mode == NFCSIM_POLL_TARGET)
- goto sched_work;
- if (peer->curr_polling_mode == NFCSIM_POLL_TARGET) {
- peer->polling_mode = NFCSIM_POLL_NONE;
- dev->polling_mode = NFCSIM_POLL_NONE;
- dev->initiator = 1;
- nfcsim_target_found(dev);
- goto unlock;
- }
- sched_work:
- /* This defines the delay for an initiator to check if the other device
- * is polling in target mode.
- * If the device starts in dual mode polling, it switches between
- * initiator and target at every round.
- * Because the wq is ordered and only 1 work item is executed at a time,
- * we'll always have one device polling as initiator and the other as
- * target at some point, even if both are started in dual mode.
- */
- queue_delayed_work(wq, &dev->poll_work, msecs_to_jiffies(200));
- unlock:
- mutex_unlock(&peer->lock);
- mutex_unlock(&dev->lock);
- }
- static struct nfcsim *nfcsim_init_dev(void)
- {
- struct nfcsim *dev;
- int rc = -ENOMEM;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL)
- return ERR_PTR(-ENOMEM);
- mutex_init(&dev->lock);
- INIT_DELAYED_WORK(&dev->recv_work, nfcsim_wq_recv);
- INIT_DELAYED_WORK(&dev->poll_work, nfcsim_wq_poll);
- dev->nfc_dev = nfc_allocate_device(&nfcsim_nfc_ops,
- NFC_PROTO_NFC_DEP_MASK,
- 0, 0);
- if (!dev->nfc_dev)
- goto error;
- nfc_set_drvdata(dev->nfc_dev, dev);
- rc = nfc_register_device(dev->nfc_dev);
- if (rc)
- goto free_nfc_dev;
- return dev;
- free_nfc_dev:
- nfc_free_device(dev->nfc_dev);
- error:
- kfree(dev);
- return ERR_PTR(rc);
- }
- static void nfcsim_free_device(struct nfcsim *dev)
- {
- nfc_unregister_device(dev->nfc_dev);
- nfc_free_device(dev->nfc_dev);
- kfree(dev);
- }
- static int __init nfcsim_init(void)
- {
- int rc;
- /* We need an ordered wq to ensure that poll_work items are executed
- * one at a time.
- */
- wq = alloc_ordered_workqueue("nfcsim", 0);
- if (!wq) {
- rc = -ENOMEM;
- goto exit;
- }
- dev0 = nfcsim_init_dev();
- if (IS_ERR(dev0)) {
- rc = PTR_ERR(dev0);
- goto exit;
- }
- dev1 = nfcsim_init_dev();
- if (IS_ERR(dev1)) {
- kfree(dev0);
- rc = PTR_ERR(dev1);
- goto exit;
- }
- dev0->peer_dev = dev1;
- dev1->peer_dev = dev0;
- pr_debug("NFCsim " NFCSIM_VERSION " initialized\n");
- rc = 0;
- exit:
- if (rc)
- pr_err("Failed to initialize nfcsim driver (%d)\n",
- rc);
- return rc;
- }
- static void __exit nfcsim_exit(void)
- {
- nfcsim_cleanup_dev(dev0, 1);
- nfcsim_cleanup_dev(dev1, 1);
- nfcsim_free_device(dev0);
- nfcsim_free_device(dev1);
- destroy_workqueue(wq);
- }
- module_init(nfcsim_init);
- module_exit(nfcsim_exit);
- MODULE_DESCRIPTION("NFCSim driver ver " NFCSIM_VERSION);
- MODULE_VERSION(NFCSIM_VERSION);
- MODULE_LICENSE("GPL");
|