summaryrefslogtreecommitdiffstats
path: root/drivers/nfc/pn544_hci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nfc/pn544_hci.c')
-rw-r--r--drivers/nfc/pn544_hci.c177
1 files changed, 120 insertions, 57 deletions
diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c
index aa71807..c9c8570 100644
--- a/drivers/nfc/pn544_hci.c
+++ b/drivers/nfc/pn544_hci.c
@@ -29,7 +29,7 @@
#include <linux/nfc.h>
#include <net/nfc/hci.h>
-#include <net/nfc/shdlc.h>
+#include <net/nfc/llc.h>
#include <linux/nfc/pn544.h>
@@ -128,10 +128,12 @@ static struct nfc_hci_gate pn544_gates[] = {
/* Largest headroom needed for outgoing custom commands */
#define PN544_CMDS_HEADROOM 2
+#define PN544_FRAME_HEADROOM 1
+#define PN544_FRAME_TAILROOM 2
struct pn544_hci_info {
struct i2c_client *i2c_dev;
- struct nfc_shdlc *shdlc;
+ struct nfc_hci_dev *hdev;
enum pn544_state state;
@@ -146,6 +148,9 @@ struct pn544_hci_info {
* < 0 if hardware error occured (e.g. i2c err)
* and prevents normal operation.
*/
+ int async_cb_type;
+ data_exchange_cb_t async_cb;
+ void *async_cb_context;
};
static void pn544_hci_platform_init(struct pn544_hci_info *info)
@@ -230,8 +235,12 @@ static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len)
r = i2c_master_send(client, buf, len);
}
- if (r >= 0 && r != len)
- r = -EREMOTEIO;
+ if (r >= 0) {
+ if (r != len)
+ return -EREMOTEIO;
+ else
+ return 0;
+ }
return r;
}
@@ -341,13 +350,16 @@ flush:
static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)
{
struct pn544_hci_info *info = dev_id;
- struct i2c_client *client = info->i2c_dev;
+ struct i2c_client *client;
struct sk_buff *skb = NULL;
int r;
- BUG_ON(!info);
- BUG_ON(irq != info->i2c_dev->irq);
+ if (!info || irq != info->i2c_dev->irq) {
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+ }
+ client = info->i2c_dev;
dev_dbg(&client->dev, "IRQ\n");
if (info->hard_fault != 0)
@@ -357,21 +369,21 @@ static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id)
if (r == -EREMOTEIO) {
info->hard_fault = r;
- nfc_shdlc_recv_frame(info->shdlc, NULL);
+ nfc_hci_recv_frame(info->hdev, NULL);
return IRQ_HANDLED;
} else if ((r == -ENOMEM) || (r == -EBADMSG)) {
return IRQ_HANDLED;
}
- nfc_shdlc_recv_frame(info->shdlc, skb);
+ nfc_hci_recv_frame(info->hdev, skb);
return IRQ_HANDLED;
}
-static int pn544_hci_open(struct nfc_shdlc *shdlc)
+static int pn544_hci_open(struct nfc_hci_dev *hdev)
{
- struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
int r = 0;
mutex_lock(&info->info_lock);
@@ -391,9 +403,9 @@ out:
return r;
}
-static void pn544_hci_close(struct nfc_shdlc *shdlc)
+static void pn544_hci_close(struct nfc_hci_dev *hdev)
{
- struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
mutex_lock(&info->info_lock);
@@ -408,9 +420,8 @@ out:
mutex_unlock(&info->info_lock);
}
-static int pn544_hci_ready(struct nfc_shdlc *shdlc)
+static int pn544_hci_ready(struct nfc_hci_dev *hdev)
{
- struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
struct sk_buff *skb;
static struct hw_config {
u8 adr[2];
@@ -576,21 +587,45 @@ static int pn544_hci_ready(struct nfc_shdlc *shdlc)
return 0;
}
-static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb)
+static void pn544_hci_add_len_crc(struct sk_buff *skb)
{
- struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc);
+ u16 crc;
+ int len;
+
+ len = skb->len + 2;
+ *skb_push(skb, 1) = len;
+
+ crc = crc_ccitt(0xffff, skb->data, skb->len);
+ crc = ~crc;
+ *skb_put(skb, 1) = crc & 0xff;
+ *skb_put(skb, 1) = crc >> 8;
+}
+
+static void pn544_hci_remove_len_crc(struct sk_buff *skb)
+{
+ skb_pull(skb, PN544_FRAME_HEADROOM);
+ skb_trim(skb, PN544_FRAME_TAILROOM);
+}
+
+static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
struct i2c_client *client = info->i2c_dev;
+ int r;
if (info->hard_fault != 0)
return info->hard_fault;
- return pn544_hci_i2c_write(client, skb->data, skb->len);
+ pn544_hci_add_len_crc(skb);
+ r = pn544_hci_i2c_write(client, skb->data, skb->len);
+ pn544_hci_remove_len_crc(skb);
+
+ return r;
}
-static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,
+static int pn544_hci_start_poll(struct nfc_hci_dev *hdev,
u32 im_protocols, u32 tm_protocols)
{
- struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
u8 phases = 0;
int r;
u8 duration[2];
@@ -641,7 +676,7 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,
return r;
}
-static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,
+static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
struct nfc_target *target)
{
switch (gate) {
@@ -659,11 +694,10 @@ static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate,
return 0;
}
-static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
+static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
u8 gate,
struct nfc_target *target)
{
- struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
struct sk_buff *uid_skb;
int r = 0;
@@ -704,6 +738,26 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
return r;
}
+#define PN544_CB_TYPE_READER_F 1
+
+static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb,
+ int err)
+{
+ struct pn544_hci_info *info = context;
+
+ switch (info->async_cb_type) {
+ case PN544_CB_TYPE_READER_F:
+ if (err == 0)
+ skb_pull(skb, 1);
+ info->async_cb(info->async_cb_context, skb, err);
+ break;
+ default:
+ if (err == 0)
+ kfree_skb(skb);
+ break;
+ }
+}
+
#define MIFARE_CMD_AUTH_KEY_A 0x60
#define MIFARE_CMD_AUTH_KEY_B 0x61
#define MIFARE_CMD_HEADER 2
@@ -715,13 +769,12 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc,
* <= 0: driver handled the data exchange
* 1: driver doesn't especially handle, please do standard processing
*/
-static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
+static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev,
struct nfc_target *target,
- struct sk_buff *skb,
- struct sk_buff **res_skb)
+ struct sk_buff *skb, data_exchange_cb_t cb,
+ void *cb_context)
{
- struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
- int r;
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__,
target->hci_reader_gate);
@@ -746,41 +799,43 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc,
memcpy(data, uid, MIFARE_UID_LEN);
}
- return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
- PN544_MIFARE_CMD,
- skb->data, skb->len, res_skb);
+ return nfc_hci_send_cmd_async(hdev,
+ target->hci_reader_gate,
+ PN544_MIFARE_CMD,
+ skb->data, skb->len,
+ cb, cb_context);
} else
return 1;
case PN544_RF_READER_F_GATE:
*skb_push(skb, 1) = 0;
*skb_push(skb, 1) = 0;
- r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
- PN544_FELICA_RAW,
- skb->data, skb->len, res_skb);
- if (r == 0)
- skb_pull(*res_skb, 1);
- return r;
+ info->async_cb_type = PN544_CB_TYPE_READER_F;
+ info->async_cb = cb;
+ info->async_cb_context = cb_context;
+
+ return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ PN544_FELICA_RAW, skb->data,
+ skb->len,
+ pn544_hci_data_exchange_cb, info);
case PN544_RF_READER_JEWEL_GATE:
- return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
- PN544_JEWEL_RAW_CMD,
- skb->data, skb->len, res_skb);
+ return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+ PN544_JEWEL_RAW_CMD, skb->data,
+ skb->len, cb, cb_context);
default:
return 1;
}
}
-static int pn544_hci_check_presence(struct nfc_shdlc *shdlc,
+static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
struct nfc_target *target)
{
- struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
-
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
PN544_RF_READER_CMD_PRESENCE_CHECK,
NULL, 0, NULL);
}
-static struct nfc_shdlc_ops pn544_shdlc_ops = {
+static struct nfc_hci_ops pn544_hci_ops = {
.open = pn544_hci_open,
.close = pn544_hci_close,
.hci_ready = pn544_hci_ready,
@@ -848,8 +903,8 @@ static int __devinit pn544_hci_probe(struct i2c_client *client,
pn544_hci_platform_init(info);
r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn,
- IRQF_TRIGGER_RISING, PN544_HCI_DRIVER_NAME,
- info);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ PN544_HCI_DRIVER_NAME, info);
if (r < 0) {
dev_err(&client->dev, "Unable to register IRQ handler\n");
goto err_rti;
@@ -872,22 +927,30 @@ static int __devinit pn544_hci_probe(struct i2c_client *client,
NFC_PROTO_ISO14443_B_MASK |
NFC_PROTO_NFC_DEP_MASK;
- info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops,
- &init_data, protocols,
- PN544_CMDS_HEADROOM, 0,
- PN544_HCI_LLC_MAX_PAYLOAD,
- dev_name(&client->dev));
- if (!info->shdlc) {
- dev_err(&client->dev, "Cannot allocate nfc shdlc.\n");
+ info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data,
+ protocols, LLC_SHDLC_NAME,
+ PN544_FRAME_HEADROOM +
+ PN544_CMDS_HEADROOM,
+ PN544_FRAME_TAILROOM,
+ PN544_HCI_LLC_MAX_PAYLOAD);
+ if (!info->hdev) {
+ dev_err(&client->dev, "Cannot allocate nfc hdev.\n");
r = -ENOMEM;
- goto err_allocshdlc;
+ goto err_alloc_hdev;
}
- nfc_shdlc_set_clientdata(info->shdlc, info);
+ nfc_hci_set_clientdata(info->hdev, info);
+
+ r = nfc_hci_register_device(info->hdev);
+ if (r)
+ goto err_regdev;
return 0;
-err_allocshdlc:
+err_regdev:
+ nfc_hci_free_device(info->hdev);
+
+err_alloc_hdev:
free_irq(client->irq, info);
err_rti:
@@ -908,7 +971,7 @@ static __devexit int pn544_hci_remove(struct i2c_client *client)
dev_dbg(&client->dev, "%s\n", __func__);
- nfc_shdlc_free(info->shdlc);
+ nfc_hci_free_device(info->hdev);
if (info->state != PN544_ST_COLD) {
if (pdata->disable)
OpenPOWER on IntegriCloud