Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
config NFC_ST21NFCA
tristate
select CRC_CCITT
help
STMicroelectronics ST21NFCA core driver. It implements the chipset
HCI logic and hooks into the NFC kernel APIs. Physical layers will
register against it.
config NFC_ST21NFCA_I2C
tristate "STMicroelectronics ST21NFCA NFC driver (I2C)"
depends on NFC_HCI && I2C && NFC_SHDLC
select NFC_ST21NFCA
help
This module adds support for the STMicroelectronics st21nfca i2c interface.
Select this if your platform is using the i2c bus.
If you choose to build a module, it'll be called st21nfca_i2c.
Say N if unsure.
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ST21NFCA HCI based NFC driver
#
st21nfca_hci-objs = core.o dep.o se.o vendor_cmds.o
obj-$(CONFIG_NFC_ST21NFCA) += st21nfca_hci.o
st21nfca_i2c-objs = i2c.o
obj-$(CONFIG_NFC_ST21NFCA_I2C) += st21nfca_i2c.o
File diff suppressed because it is too large Load Diff
+660
View File
@@ -0,0 +1,660 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
*/
#include <net/nfc/hci.h>
#include "st21nfca.h"
#define ST21NFCA_NFCIP1_INITIATOR 0x00
#define ST21NFCA_NFCIP1_REQ 0xd4
#define ST21NFCA_NFCIP1_RES 0xd5
#define ST21NFCA_NFCIP1_ATR_REQ 0x00
#define ST21NFCA_NFCIP1_ATR_RES 0x01
#define ST21NFCA_NFCIP1_PSL_REQ 0x04
#define ST21NFCA_NFCIP1_PSL_RES 0x05
#define ST21NFCA_NFCIP1_DEP_REQ 0x06
#define ST21NFCA_NFCIP1_DEP_RES 0x07
#define ST21NFCA_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03)
#define ST21NFCA_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
#define ST21NFCA_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
((pfb) & ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT)
#define ST21NFCA_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
#define ST21NFCA_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
#define ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT 0x10
#define ST21NFCA_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
((pfb) & ST21NFCA_NFC_DEP_PFB_TIMEOUT_BIT)
#define ST21NFCA_NFC_DEP_PFB_I_PDU 0x00
#define ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU 0x40
#define ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU 0x80
#define ST21NFCA_ATR_REQ_MIN_SIZE 17
#define ST21NFCA_ATR_REQ_MAX_SIZE 65
#define ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B 0x30
#define ST21NFCA_GB_BIT 0x02
#define ST21NFCA_EVT_SEND_DATA 0x10
#define ST21NFCA_EVT_FIELD_ON 0x11
#define ST21NFCA_EVT_CARD_DEACTIVATED 0x12
#define ST21NFCA_EVT_CARD_ACTIVATED 0x13
#define ST21NFCA_EVT_FIELD_OFF 0x14
#define ST21NFCA_EVT_CARD_F_BITRATE 0x16
#define ST21NFCA_EVT_READER_F_BITRATE 0x13
#define ST21NFCA_PSL_REQ_SEND_SPEED(brs) (brs & 0x38)
#define ST21NFCA_PSL_REQ_RECV_SPEED(brs) (brs & 0x07)
#define ST21NFCA_PP2LRI(pp) ((pp & 0x30) >> 4)
#define ST21NFCA_CARD_BITRATE_212 0x01
#define ST21NFCA_CARD_BITRATE_424 0x02
#define ST21NFCA_DEFAULT_TIMEOUT 0x0a
#define PROTOCOL_ERR(req) pr_err("%d: ST21NFCA Protocol error: %s\n", \
__LINE__, req)
struct st21nfca_atr_req {
u8 length;
u8 cmd0;
u8 cmd1;
u8 nfcid3[NFC_NFCID3_MAXSIZE];
u8 did;
u8 bsi;
u8 bri;
u8 ppi;
u8 gbi[];
} __packed;
struct st21nfca_atr_res {
u8 length;
u8 cmd0;
u8 cmd1;
u8 nfcid3[NFC_NFCID3_MAXSIZE];
u8 did;
u8 bsi;
u8 bri;
u8 to;
u8 ppi;
u8 gbi[];
} __packed;
struct st21nfca_psl_req {
u8 length;
u8 cmd0;
u8 cmd1;
u8 did;
u8 brs;
u8 fsl;
} __packed;
struct st21nfca_psl_res {
u8 length;
u8 cmd0;
u8 cmd1;
u8 did;
} __packed;
struct st21nfca_dep_req_res {
u8 length;
u8 cmd0;
u8 cmd1;
u8 pfb;
u8 did;
u8 nad;
} __packed;
static void st21nfca_tx_work(struct work_struct *work)
{
struct st21nfca_hci_info *info = container_of(work,
struct st21nfca_hci_info,
dep_info.tx_work);
struct nfc_dev *dev;
struct sk_buff *skb;
dev = info->hdev->ndev;
skb = info->dep_info.tx_pending;
device_lock(&dev->dev);
nfc_hci_send_cmd_async(info->hdev, ST21NFCA_RF_READER_F_GATE,
ST21NFCA_WR_XCHG_DATA, skb->data, skb->len,
info->async_cb, info);
device_unlock(&dev->dev);
kfree_skb(skb);
}
static void st21nfca_im_send_pdu(struct st21nfca_hci_info *info,
struct sk_buff *skb)
{
info->dep_info.tx_pending = skb;
schedule_work(&info->dep_info.tx_work);
}
static int st21nfca_tm_send_atr_res(struct nfc_hci_dev *hdev,
struct st21nfca_atr_req *atr_req)
{
struct st21nfca_atr_res *atr_res;
struct sk_buff *skb;
size_t gb_len;
int r;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
gb_len = atr_req->length - sizeof(struct st21nfca_atr_req);
skb = alloc_skb(atr_req->length + 1, GFP_KERNEL);
if (!skb)
return -ENOMEM;
skb_put(skb, sizeof(struct st21nfca_atr_res));
atr_res = (struct st21nfca_atr_res *)skb->data;
memset(atr_res, 0, sizeof(struct st21nfca_atr_res));
atr_res->length = atr_req->length + 1;
atr_res->cmd0 = ST21NFCA_NFCIP1_RES;
atr_res->cmd1 = ST21NFCA_NFCIP1_ATR_RES;
memcpy(atr_res->nfcid3, atr_req->nfcid3, 6);
atr_res->bsi = 0x00;
atr_res->bri = 0x00;
atr_res->to = ST21NFCA_DEFAULT_TIMEOUT;
atr_res->ppi = ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B;
if (gb_len) {
skb_put(skb, gb_len);
atr_res->ppi |= ST21NFCA_GB_BIT;
memcpy(atr_res->gbi, atr_req->gbi, gb_len);
r = nfc_set_remote_general_bytes(hdev->ndev, atr_res->gbi,
gb_len);
if (r < 0) {
kfree_skb(skb);
return r;
}
}
info->dep_info.curr_nfc_dep_pni = 0;
r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
kfree_skb(skb);
return r;
}
static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev,
struct sk_buff *skb)
{
struct st21nfca_atr_req *atr_req;
size_t gb_len;
int r;
skb_trim(skb, skb->len - 1);
if (!skb->len)
return -EIO;
if (skb->len < ST21NFCA_ATR_REQ_MIN_SIZE)
return -EPROTO;
atr_req = (struct st21nfca_atr_req *)skb->data;
if (atr_req->length < sizeof(struct st21nfca_atr_req))
return -EPROTO;
if (atr_req->length > skb->len)
return -EPROTO;
r = st21nfca_tm_send_atr_res(hdev, atr_req);
if (r)
return r;
gb_len = skb->len - sizeof(struct st21nfca_atr_req);
r = nfc_tm_activated(hdev->ndev, NFC_PROTO_NFC_DEP_MASK,
NFC_COMM_PASSIVE, atr_req->gbi, gb_len);
if (r)
return r;
return 0;
}
static int st21nfca_tm_send_psl_res(struct nfc_hci_dev *hdev,
struct st21nfca_psl_req *psl_req)
{
struct st21nfca_psl_res *psl_res;
struct sk_buff *skb;
u8 bitrate[2] = {0, 0};
int r;
skb = alloc_skb(sizeof(struct st21nfca_psl_res), GFP_KERNEL);
if (!skb)
return -ENOMEM;
skb_put(skb, sizeof(struct st21nfca_psl_res));
psl_res = (struct st21nfca_psl_res *)skb->data;
psl_res->length = sizeof(struct st21nfca_psl_res);
psl_res->cmd0 = ST21NFCA_NFCIP1_RES;
psl_res->cmd1 = ST21NFCA_NFCIP1_PSL_RES;
psl_res->did = psl_req->did;
r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
if (r < 0)
goto error;
/*
* ST21NFCA only support P2P passive.
* PSL_REQ BRS value != 0 has only a meaning to
* change technology to type F.
* We change to BITRATE 424Kbits.
* In other case switch to BITRATE 106Kbits.
*/
if (ST21NFCA_PSL_REQ_SEND_SPEED(psl_req->brs) &&
ST21NFCA_PSL_REQ_RECV_SPEED(psl_req->brs)) {
bitrate[0] = ST21NFCA_CARD_BITRATE_424;
bitrate[1] = ST21NFCA_CARD_BITRATE_424;
}
/* Send an event to change bitrate change event to card f */
r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
ST21NFCA_EVT_CARD_F_BITRATE, bitrate, 2);
error:
kfree_skb(skb);
return r;
}
static int st21nfca_tm_recv_psl_req(struct nfc_hci_dev *hdev,
struct sk_buff *skb)
{
struct st21nfca_psl_req *psl_req;
skb_trim(skb, skb->len - 1);
if (!skb->len)
return -EIO;
psl_req = (struct st21nfca_psl_req *)skb->data;
if (skb->len < sizeof(struct st21nfca_psl_req))
return -EIO;
return st21nfca_tm_send_psl_res(hdev, psl_req);
}
int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb)
{
int r;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
*(u8 *)skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni;
*(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_RES;
*(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_RES;
*(u8 *)skb_push(skb, 1) = skb->len;
r = nfc_hci_send_event(hdev, ST21NFCA_RF_CARD_F_GATE,
ST21NFCA_EVT_SEND_DATA, skb->data, skb->len);
kfree_skb(skb);
return r;
}
EXPORT_SYMBOL(st21nfca_tm_send_dep_res);
static int st21nfca_tm_recv_dep_req(struct nfc_hci_dev *hdev,
struct sk_buff *skb)
{
struct st21nfca_dep_req_res *dep_req;
u8 size;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
skb_trim(skb, skb->len - 1);
size = 4;
dep_req = (struct st21nfca_dep_req_res *)skb->data;
if (skb->len < size)
return -EIO;
if (ST21NFCA_NFC_DEP_DID_BIT_SET(dep_req->pfb))
size++;
if (ST21NFCA_NFC_DEP_NAD_BIT_SET(dep_req->pfb))
size++;
if (skb->len < size)
return -EIO;
/* Receiving DEP_REQ - Decoding */
switch (ST21NFCA_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
case ST21NFCA_NFC_DEP_PFB_I_PDU:
info->dep_info.curr_nfc_dep_pni =
ST21NFCA_NFC_DEP_PFB_PNI(dep_req->pfb);
break;
case ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU:
pr_err("Received a ACK/NACK PDU\n");
break;
case ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU:
pr_err("Received a SUPERVISOR PDU\n");
break;
}
skb_pull(skb, size);
return nfc_tm_data_received(hdev->ndev, skb);
}
static int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev,
struct sk_buff *skb)
{
u8 cmd0, cmd1;
int r;
cmd0 = skb->data[1];
switch (cmd0) {
case ST21NFCA_NFCIP1_REQ:
cmd1 = skb->data[2];
switch (cmd1) {
case ST21NFCA_NFCIP1_ATR_REQ:
r = st21nfca_tm_recv_atr_req(hdev, skb);
break;
case ST21NFCA_NFCIP1_PSL_REQ:
r = st21nfca_tm_recv_psl_req(hdev, skb);
break;
case ST21NFCA_NFCIP1_DEP_REQ:
r = st21nfca_tm_recv_dep_req(hdev, skb);
break;
default:
return 1;
}
break;
default:
return 1;
}
return r;
}
/*
* Returns:
* <= 0: driver handled the event, skb consumed
* 1: driver does not handle the event, please do standard processing
*/
int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
u8 event, struct sk_buff *skb)
{
int r = 0;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
pr_debug("dep event: %d\n", event);
switch (event) {
case ST21NFCA_EVT_CARD_ACTIVATED:
info->dep_info.curr_nfc_dep_pni = 0;
break;
case ST21NFCA_EVT_CARD_DEACTIVATED:
break;
case ST21NFCA_EVT_FIELD_ON:
break;
case ST21NFCA_EVT_FIELD_OFF:
break;
case ST21NFCA_EVT_SEND_DATA:
r = st21nfca_tm_event_send_data(hdev, skb);
if (r < 0)
return r;
return 0;
default:
nfc_err(&hdev->ndev->dev, "Unexpected event on card f gate\n");
return 1;
}
kfree_skb(skb);
return r;
}
EXPORT_SYMBOL(st21nfca_dep_event_received);
static void st21nfca_im_send_psl_req(struct nfc_hci_dev *hdev, u8 did, u8 bsi,
u8 bri, u8 lri)
{
struct sk_buff *skb;
struct st21nfca_psl_req *psl_req;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
skb =
alloc_skb(sizeof(struct st21nfca_psl_req) + 1, GFP_KERNEL);
if (!skb)
return;
skb_reserve(skb, 1);
skb_put(skb, sizeof(struct st21nfca_psl_req));
psl_req = (struct st21nfca_psl_req *) skb->data;
psl_req->length = sizeof(struct st21nfca_psl_req);
psl_req->cmd0 = ST21NFCA_NFCIP1_REQ;
psl_req->cmd1 = ST21NFCA_NFCIP1_PSL_REQ;
psl_req->did = did;
psl_req->brs = (0x30 & bsi << 4) | (bri & 0x03);
psl_req->fsl = lri;
*(u8 *)skb_push(skb, 1) = info->dep_info.to | 0x10;
st21nfca_im_send_pdu(info, skb);
}
#define ST21NFCA_CB_TYPE_READER_F 1
static void st21nfca_im_recv_atr_res_cb(void *context, struct sk_buff *skb,
int err)
{
struct st21nfca_hci_info *info = context;
struct st21nfca_atr_res *atr_res;
int r;
if (err != 0)
return;
if (!skb)
return;
switch (info->async_cb_type) {
case ST21NFCA_CB_TYPE_READER_F:
skb_trim(skb, skb->len - 1);
atr_res = (struct st21nfca_atr_res *)skb->data;
r = nfc_set_remote_general_bytes(info->hdev->ndev,
atr_res->gbi,
skb->len - sizeof(struct st21nfca_atr_res));
if (r < 0)
return;
if (atr_res->to >= 0x0e)
info->dep_info.to = 0x0e;
else
info->dep_info.to = atr_res->to + 1;
info->dep_info.to |= 0x10;
r = nfc_dep_link_is_up(info->hdev->ndev, info->dep_info.idx,
NFC_COMM_PASSIVE, NFC_RF_INITIATOR);
if (r < 0)
return;
info->dep_info.curr_nfc_dep_pni = 0;
if (ST21NFCA_PP2LRI(atr_res->ppi) != info->dep_info.lri)
st21nfca_im_send_psl_req(info->hdev, atr_res->did,
atr_res->bsi, atr_res->bri,
ST21NFCA_PP2LRI(atr_res->ppi));
break;
default:
kfree_skb(skb);
break;
}
}
int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len)
{
struct sk_buff *skb;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
struct st21nfca_atr_req *atr_req;
struct nfc_target *target;
uint size;
info->dep_info.to = ST21NFCA_DEFAULT_TIMEOUT;
size = ST21NFCA_ATR_REQ_MIN_SIZE + gb_len;
if (size > ST21NFCA_ATR_REQ_MAX_SIZE) {
PROTOCOL_ERR("14.6.1.1");
return -EINVAL;
}
skb =
alloc_skb(sizeof(struct st21nfca_atr_req) + gb_len + 1, GFP_KERNEL);
if (!skb)
return -ENOMEM;
skb_reserve(skb, 1);
skb_put(skb, sizeof(struct st21nfca_atr_req));
atr_req = (struct st21nfca_atr_req *)skb->data;
memset(atr_req, 0, sizeof(struct st21nfca_atr_req));
atr_req->cmd0 = ST21NFCA_NFCIP1_REQ;
atr_req->cmd1 = ST21NFCA_NFCIP1_ATR_REQ;
memset(atr_req->nfcid3, 0, NFC_NFCID3_MAXSIZE);
target = hdev->ndev->targets;
if (target->sensf_res_len > 0)
memcpy(atr_req->nfcid3, target->sensf_res,
target->sensf_res_len);
else
get_random_bytes(atr_req->nfcid3, NFC_NFCID3_MAXSIZE);
atr_req->did = 0x0;
atr_req->bsi = 0x00;
atr_req->bri = 0x00;
atr_req->ppi = ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B;
if (gb_len) {
atr_req->ppi |= ST21NFCA_GB_BIT;
skb_put_data(skb, gb, gb_len);
}
atr_req->length = sizeof(struct st21nfca_atr_req) + hdev->gb_len;
*(u8 *)skb_push(skb, 1) = info->dep_info.to | 0x10; /* timeout */
info->async_cb_type = ST21NFCA_CB_TYPE_READER_F;
info->async_cb_context = info;
info->async_cb = st21nfca_im_recv_atr_res_cb;
info->dep_info.bri = atr_req->bri;
info->dep_info.bsi = atr_req->bsi;
info->dep_info.lri = ST21NFCA_PP2LRI(atr_req->ppi);
return nfc_hci_send_cmd_async(hdev, ST21NFCA_RF_READER_F_GATE,
ST21NFCA_WR_XCHG_DATA, skb->data,
skb->len, info->async_cb, info);
}
EXPORT_SYMBOL(st21nfca_im_send_atr_req);
static void st21nfca_im_recv_dep_res_cb(void *context, struct sk_buff *skb,
int err)
{
struct st21nfca_hci_info *info = context;
struct st21nfca_dep_req_res *dep_res;
int size;
if (err != 0)
return;
if (!skb)
return;
switch (info->async_cb_type) {
case ST21NFCA_CB_TYPE_READER_F:
dep_res = (struct st21nfca_dep_req_res *)skb->data;
size = 3;
if (skb->len < size)
goto exit;
if (ST21NFCA_NFC_DEP_DID_BIT_SET(dep_res->pfb))
size++;
if (ST21NFCA_NFC_DEP_NAD_BIT_SET(dep_res->pfb))
size++;
if (skb->len < size)
goto exit;
skb_trim(skb, skb->len - 1);
/* Receiving DEP_REQ - Decoding */
switch (ST21NFCA_NFC_DEP_PFB_TYPE(dep_res->pfb)) {
case ST21NFCA_NFC_DEP_PFB_ACK_NACK_PDU:
pr_err("Received a ACK/NACK PDU\n");
fallthrough;
case ST21NFCA_NFC_DEP_PFB_I_PDU:
info->dep_info.curr_nfc_dep_pni =
ST21NFCA_NFC_DEP_PFB_PNI(dep_res->pfb + 1);
size++;
skb_pull(skb, size);
nfc_tm_data_received(info->hdev->ndev, skb);
break;
case ST21NFCA_NFC_DEP_PFB_SUPERVISOR_PDU:
pr_err("Received a SUPERVISOR PDU\n");
skb_pull(skb, size);
*(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ;
*(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ;
*(u8 *)skb_push(skb, 1) = skb->len;
*(u8 *)skb_push(skb, 1) = info->dep_info.to | 0x10;
st21nfca_im_send_pdu(info, skb);
break;
}
return;
default:
break;
}
exit:
kfree_skb(skb);
}
int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
info->async_cb_type = ST21NFCA_CB_TYPE_READER_F;
info->async_cb_context = info;
info->async_cb = st21nfca_im_recv_dep_res_cb;
*(u8 *)skb_push(skb, 1) = info->dep_info.curr_nfc_dep_pni;
*(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_DEP_REQ;
*(u8 *)skb_push(skb, 1) = ST21NFCA_NFCIP1_REQ;
*(u8 *)skb_push(skb, 1) = skb->len;
*(u8 *)skb_push(skb, 1) = info->dep_info.to | 0x10;
return nfc_hci_send_cmd_async(hdev, ST21NFCA_RF_READER_F_GATE,
ST21NFCA_WR_XCHG_DATA,
skb->data, skb->len,
info->async_cb, info);
}
EXPORT_SYMBOL(st21nfca_im_send_dep_req);
void st21nfca_dep_init(struct nfc_hci_dev *hdev)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
INIT_WORK(&info->dep_info.tx_work, st21nfca_tx_work);
info->dep_info.curr_nfc_dep_pni = 0;
info->dep_info.idx = 0;
info->dep_info.to = ST21NFCA_DEFAULT_TIMEOUT;
}
EXPORT_SYMBOL(st21nfca_dep_init);
void st21nfca_dep_deinit(struct nfc_hci_dev *hdev)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
cancel_work_sync(&info->dep_info.tx_work);
}
EXPORT_SYMBOL(st21nfca_dep_deinit);
+606
View File
@@ -0,0 +1,606 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* I2C Link Layer for ST21NFCA HCI based Driver
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/crc-ccitt.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/nfc.h>
#include <linux/firmware.h>
#include <net/nfc/hci.h>
#include <net/nfc/llc.h>
#include <net/nfc/nfc.h>
#include "st21nfca.h"
/*
* Every frame starts with ST21NFCA_SOF_EOF and ends with ST21NFCA_SOF_EOF.
* Because ST21NFCA_SOF_EOF is a possible data value, there is a mecanism
* called byte stuffing has been introduced.
*
* if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING
* - insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte)
* - xor byte with ST21NFCA_BYTE_STUFFING_MASK
*/
#define ST21NFCA_SOF_EOF 0x7e
#define ST21NFCA_BYTE_STUFFING_MASK 0x20
#define ST21NFCA_ESCAPE_BYTE_STUFFING 0x7d
/* SOF + 00 */
#define ST21NFCA_FRAME_HEADROOM 2
/* 2 bytes crc + EOF */
#define ST21NFCA_FRAME_TAILROOM 3
#define IS_START_OF_FRAME(buf) (buf[0] == ST21NFCA_SOF_EOF && \
buf[1] == 0)
#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci"
#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c"
struct st21nfca_i2c_phy {
struct i2c_client *i2c_dev;
struct nfc_hci_dev *hdev;
struct gpio_desc *gpiod_ena;
struct st21nfca_se_status se_status;
struct sk_buff *pending_skb;
int current_read_len;
/*
* crc might have fail because i2c macro
* is disable due to other interface activity
*/
int crc_trials;
int powered;
int run_mode;
/*
* < 0 if hardware error occured (e.g. i2c err)
* and prevents normal operation.
*/
int hard_fault;
struct mutex phy_lock;
};
static const u8 len_seq[] = { 16, 24, 12, 29 };
static const u16 wait_tab[] = { 2, 3, 5, 15, 20, 40};
#define I2C_DUMP_SKB(info, skb) \
do { \
pr_debug("%s:\n", info); \
print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
16, 1, (skb)->data, (skb)->len, 0); \
} while (0)
/*
* In order to get the CLF in a known state we generate an internal reboot
* using a proprietary command.
* Once the reboot is completed, we expect to receive a ST21NFCA_SOF_EOF
* fill buffer.
*/
static int st21nfca_hci_platform_init(struct st21nfca_i2c_phy *phy)
{
u16 wait_reboot[] = { 50, 300, 1000 };
char reboot_cmd[] = { 0x7E, 0x66, 0x48, 0xF6, 0x7E };
u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE];
int i, r = -1;
for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) {
r = i2c_master_send(phy->i2c_dev, reboot_cmd,
sizeof(reboot_cmd));
if (r < 0)
msleep(wait_reboot[i]);
}
if (r < 0)
return r;
/* CLF is spending about 20ms to do an internal reboot */
msleep(20);
r = -1;
for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) {
r = i2c_master_recv(phy->i2c_dev, tmp,
ST21NFCA_HCI_LLC_MAX_SIZE);
if (r < 0)
msleep(wait_reboot[i]);
}
if (r < 0)
return r;
for (i = 0; i < ST21NFCA_HCI_LLC_MAX_SIZE &&
tmp[i] == ST21NFCA_SOF_EOF; i++)
;
if (r != ST21NFCA_HCI_LLC_MAX_SIZE)
return -ENODEV;
usleep_range(1000, 1500);
return 0;
}
static int st21nfca_hci_i2c_enable(void *phy_id)
{
struct st21nfca_i2c_phy *phy = phy_id;
gpiod_set_value(phy->gpiod_ena, 1);
phy->powered = 1;
phy->run_mode = ST21NFCA_HCI_MODE;
usleep_range(10000, 15000);
return 0;
}
static void st21nfca_hci_i2c_disable(void *phy_id)
{
struct st21nfca_i2c_phy *phy = phy_id;
gpiod_set_value(phy->gpiod_ena, 0);
phy->powered = 0;
}
static void st21nfca_hci_add_len_crc(struct sk_buff *skb)
{
u16 crc;
u8 tmp;
*(u8 *)skb_push(skb, 1) = 0;
crc = crc_ccitt(0xffff, skb->data, skb->len);
crc = ~crc;
tmp = crc & 0x00ff;
skb_put_u8(skb, tmp);
tmp = (crc >> 8) & 0x00ff;
skb_put_u8(skb, tmp);
}
static void st21nfca_hci_remove_len_crc(struct sk_buff *skb)
{
skb_pull(skb, ST21NFCA_FRAME_HEADROOM);
skb_trim(skb, skb->len - ST21NFCA_FRAME_TAILROOM);
}
/*
* Writing a frame must not return the number of written bytes.
* It must return either zero for success, or <0 for error.
* In addition, it must not alter the skb
*/
static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb)
{
int r = -1, i, j;
struct st21nfca_i2c_phy *phy = phy_id;
struct i2c_client *client = phy->i2c_dev;
u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE * 2];
I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb);
if (phy->hard_fault != 0)
return phy->hard_fault;
/*
* Compute CRC before byte stuffing computation on frame
* Note st21nfca_hci_add_len_crc is doing a byte stuffing
* on its own value
*/
st21nfca_hci_add_len_crc(skb);
/* add ST21NFCA_SOF_EOF on tail */
skb_put_u8(skb, ST21NFCA_SOF_EOF);
/* add ST21NFCA_SOF_EOF on head */
*(u8 *)skb_push(skb, 1) = ST21NFCA_SOF_EOF;
/*
* Compute byte stuffing
* if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING
* insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte)
* xor byte with ST21NFCA_BYTE_STUFFING_MASK
*/
tmp[0] = skb->data[0];
for (i = 1, j = 1; i < skb->len - 1; i++, j++) {
if (skb->data[i] == ST21NFCA_SOF_EOF
|| skb->data[i] == ST21NFCA_ESCAPE_BYTE_STUFFING) {
tmp[j] = ST21NFCA_ESCAPE_BYTE_STUFFING;
j++;
tmp[j] = skb->data[i] ^ ST21NFCA_BYTE_STUFFING_MASK;
} else {
tmp[j] = skb->data[i];
}
}
tmp[j] = skb->data[i];
j++;
/*
* Manage sleep mode
* Try 3 times to send data with delay between each
*/
mutex_lock(&phy->phy_lock);
for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) {
r = i2c_master_send(client, tmp, j);
if (r < 0)
msleep(wait_tab[i]);
}
mutex_unlock(&phy->phy_lock);
if (r >= 0) {
if (r != j)
r = -EREMOTEIO;
else
r = 0;
}
st21nfca_hci_remove_len_crc(skb);
return r;
}
static int get_frame_size(u8 *buf, int buflen)
{
int len = 0;
if (buf[len + 1] == ST21NFCA_SOF_EOF)
return 0;
for (len = 1; len < buflen && buf[len] != ST21NFCA_SOF_EOF; len++)
;
return len;
}
static int check_crc(u8 *buf, int buflen)
{
u16 crc;
crc = crc_ccitt(0xffff, buf, buflen - 2);
crc = ~crc;
if (buf[buflen - 2] != (crc & 0xff) || buf[buflen - 1] != (crc >> 8)) {
pr_err(ST21NFCA_HCI_DRIVER_NAME
": CRC error 0x%x != 0x%x 0x%x\n", crc, buf[buflen - 1],
buf[buflen - 2]);
pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
16, 2, buf, buflen, false);
return -EPERM;
}
return 0;
}
/*
* Prepare received data for upper layer.
* Received data include byte stuffing, crc and sof/eof
* which is not usable by hci part.
* returns:
* frame size without sof/eof, header and byte stuffing
* -EBADMSG : frame was incorrect and discarded
*/
static int st21nfca_hci_i2c_repack(struct sk_buff *skb)
{
int i, j, r, size;
if (skb->len < 1 || (skb->len > 1 && skb->data[1] != 0))
return -EBADMSG;
size = get_frame_size(skb->data, skb->len);
if (size > 0) {
skb_trim(skb, size);
/* remove ST21NFCA byte stuffing for upper layer */
for (i = 1, j = 0; i < skb->len; i++) {
if (skb->data[i + j] ==
(u8) ST21NFCA_ESCAPE_BYTE_STUFFING) {
skb->data[i] = skb->data[i + j + 1]
| ST21NFCA_BYTE_STUFFING_MASK;
i++;
j++;
}
skb->data[i] = skb->data[i + j];
}
/* remove byte stuffing useless byte */
skb_trim(skb, i - j);
/* remove ST21NFCA_SOF_EOF from head */
skb_pull(skb, 1);
r = check_crc(skb->data, skb->len);
if (r != 0)
return -EBADMSG;
/* remove headbyte */
skb_pull(skb, 1);
/* remove crc. Byte Stuffing is already removed here */
skb_trim(skb, skb->len - 2);
return skb->len;
}
return 0;
}
/*
* Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees
* that i2c bus will be flushed and that next read will start on a new frame.
* returned skb contains only LLC header and payload.
* returns:
* frame size : if received frame is complete (find ST21NFCA_SOF_EOF at
* end of read)
* -EAGAIN : if received frame is incomplete (not find ST21NFCA_SOF_EOF
* at end of read)
* -EREMOTEIO : i2c read error (fatal)
* -EBADMSG : frame was incorrect and discarded
* (value returned from st21nfca_hci_i2c_repack)
* -EIO : if no ST21NFCA_SOF_EOF is found after reaching
* the read length end sequence
*/
static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy,
struct sk_buff *skb)
{
int r, i;
u8 len;
u8 buf[ST21NFCA_HCI_LLC_MAX_PAYLOAD];
struct i2c_client *client = phy->i2c_dev;
if (phy->current_read_len < ARRAY_SIZE(len_seq)) {
len = len_seq[phy->current_read_len];
/*
* Add retry mecanism
* Operation on I2C interface may fail in case of operation on
* RF or SWP interface
*/
r = 0;
mutex_lock(&phy->phy_lock);
for (i = 0; i < ARRAY_SIZE(wait_tab) && r <= 0; i++) {
r = i2c_master_recv(client, buf, len);
if (r < 0)
msleep(wait_tab[i]);
}
mutex_unlock(&phy->phy_lock);
if (r != len) {
phy->current_read_len = 0;
return -EREMOTEIO;
}
/*
* The first read sequence does not start with SOF.
* Data is corrupeted so we drop it.
*/
if (!phy->current_read_len && !IS_START_OF_FRAME(buf)) {
skb_trim(skb, 0);
phy->current_read_len = 0;
return -EIO;
} else if (phy->current_read_len && IS_START_OF_FRAME(buf)) {
/*
* Previous frame transmission was interrupted and
* the frame got repeated.
* Received frame start with ST21NFCA_SOF_EOF + 00.
*/
skb_trim(skb, 0);
phy->current_read_len = 0;
}
skb_put_data(skb, buf, len);
if (skb->data[skb->len - 1] == ST21NFCA_SOF_EOF) {
phy->current_read_len = 0;
return st21nfca_hci_i2c_repack(skb);
}
phy->current_read_len++;
return -EAGAIN;
}
return -EIO;
}
/*
* Reads an shdlc frame from the chip. This is not as straightforward as it
* seems. The frame format is data-crc, and corruption can occur anywhere
* while transiting on i2c bus, such that we could read an invalid data.
* The tricky case is when we read a corrupted data or crc. We must detect
* this here in order to determine that data can be transmitted to the hci
* core. This is the reason why we check the crc here.
* The CLF will repeat a frame until we send a RR on that frame.
*
* On ST21NFCA, IRQ goes in idle when read starts. As no size information are
* available in the incoming data, other IRQ might come. Every IRQ will trigger
* a read sequence with different length and will fill the current frame.
* The reception is complete once we reach a ST21NFCA_SOF_EOF.
*/
static irqreturn_t st21nfca_hci_irq_thread_fn(int irq, void *phy_id)
{
struct st21nfca_i2c_phy *phy = phy_id;
int r;
if (!phy || irq != phy->i2c_dev->irq) {
WARN_ON_ONCE(1);
return IRQ_NONE;
}
if (phy->hard_fault != 0)
return IRQ_HANDLED;
r = st21nfca_hci_i2c_read(phy, phy->pending_skb);
if (r == -EREMOTEIO) {
phy->hard_fault = r;
nfc_hci_recv_frame(phy->hdev, NULL);
return IRQ_HANDLED;
} else if (r == -EAGAIN || r == -EIO) {
return IRQ_HANDLED;
} else if (r == -EBADMSG && phy->crc_trials < ARRAY_SIZE(wait_tab)) {
/*
* With ST21NFCA, only one interface (I2C, RF or SWP)
* may be active at a time.
* Having incorrect crc is usually due to i2c macrocell
* deactivation in the middle of a transmission.
* It may generate corrupted data on i2c.
* We give sometime to get i2c back.
* The complete frame will be repeated.
*/
msleep(wait_tab[phy->crc_trials]);
phy->crc_trials++;
phy->current_read_len = 0;
kfree_skb(phy->pending_skb);
} else if (r > 0) {
/*
* We succeeded to read data from the CLF and
* data is valid.
* Reset counter.
*/
nfc_hci_recv_frame(phy->hdev, phy->pending_skb);
phy->crc_trials = 0;
} else {
kfree_skb(phy->pending_skb);
}
phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
if (phy->pending_skb == NULL) {
phy->hard_fault = -ENOMEM;
nfc_hci_recv_frame(phy->hdev, NULL);
}
return IRQ_HANDLED;
}
static const struct nfc_phy_ops i2c_phy_ops = {
.write = st21nfca_hci_i2c_write,
.enable = st21nfca_hci_i2c_enable,
.disable = st21nfca_hci_i2c_disable,
};
static const struct acpi_gpio_params enable_gpios = { 1, 0, false };
static const struct acpi_gpio_mapping acpi_st21nfca_gpios[] = {
{ "enable-gpios", &enable_gpios, 1 },
{},
};
static int st21nfca_hci_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct st21nfca_i2c_phy *phy;
int r;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
return -ENODEV;
}
phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy),
GFP_KERNEL);
if (!phy)
return -ENOMEM;
phy->i2c_dev = client;
phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
if (phy->pending_skb == NULL)
return -ENOMEM;
phy->current_read_len = 0;
phy->crc_trials = 0;
mutex_init(&phy->phy_lock);
i2c_set_clientdata(client, phy);
r = devm_acpi_dev_add_driver_gpios(dev, acpi_st21nfca_gpios);
if (r)
dev_dbg(dev, "Unable to add GPIO mapping table\n");
/* Get EN GPIO from resource provider */
phy->gpiod_ena = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(phy->gpiod_ena)) {
nfc_err(dev, "Unable to get ENABLE GPIO\n");
r = PTR_ERR(phy->gpiod_ena);
goto out_free;
}
phy->se_status.is_ese_present =
device_property_read_bool(&client->dev, "ese-present");
phy->se_status.is_uicc_present =
device_property_read_bool(&client->dev, "uicc-present");
r = st21nfca_hci_platform_init(phy);
if (r < 0) {
nfc_err(&client->dev, "Unable to reboot st21nfca\n");
goto out_free;
}
r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
st21nfca_hci_irq_thread_fn,
IRQF_ONESHOT,
ST21NFCA_HCI_DRIVER_NAME, phy);
if (r < 0) {
nfc_err(&client->dev, "Unable to register IRQ handler\n");
goto out_free;
}
r = st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
ST21NFCA_FRAME_HEADROOM,
ST21NFCA_FRAME_TAILROOM,
ST21NFCA_HCI_LLC_MAX_PAYLOAD,
&phy->hdev,
&phy->se_status);
if (r)
goto out_free;
return 0;
out_free:
kfree_skb(phy->pending_skb);
return r;
}
static void st21nfca_hci_i2c_remove(struct i2c_client *client)
{
struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
st21nfca_hci_remove(phy->hdev);
if (phy->powered)
st21nfca_hci_i2c_disable(phy);
kfree_skb(phy->pending_skb);
}
static const struct i2c_device_id st21nfca_hci_i2c_id_table[] = {
{ .name = ST21NFCA_HCI_DRIVER_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table);
static const struct acpi_device_id st21nfca_hci_i2c_acpi_match[] __maybe_unused = {
{"SMO2100", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, st21nfca_hci_i2c_acpi_match);
static const struct of_device_id of_st21nfca_i2c_match[] __maybe_unused = {
{ .compatible = "st,st21nfca-i2c", },
{ .compatible = "st,st21nfca_i2c", },
{}
};
MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match);
static struct i2c_driver st21nfca_hci_i2c_driver = {
.driver = {
.name = ST21NFCA_HCI_I2C_DRIVER_NAME,
.of_match_table = of_match_ptr(of_st21nfca_i2c_match),
.acpi_match_table = ACPI_PTR(st21nfca_hci_i2c_acpi_match),
},
.probe = st21nfca_hci_i2c_probe,
.id_table = st21nfca_hci_i2c_id_table,
.remove = st21nfca_hci_i2c_remove,
};
module_i2c_driver(st21nfca_hci_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRIVER_DESC);
+447
View File
@@ -0,0 +1,447 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
*/
#include <net/nfc/hci.h>
#include "st21nfca.h"
#define ST21NFCA_EVT_UICC_ACTIVATE 0x10
#define ST21NFCA_EVT_UICC_DEACTIVATE 0x13
#define ST21NFCA_EVT_SE_HARD_RESET 0x20
#define ST21NFCA_EVT_SE_SOFT_RESET 0x11
#define ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER 0x21
#define ST21NFCA_EVT_SE_ACTIVATE 0x22
#define ST21NFCA_EVT_SE_DEACTIVATE 0x23
#define ST21NFCA_EVT_TRANSMIT_DATA 0x10
#define ST21NFCA_EVT_WTX_REQUEST 0x11
#define ST21NFCA_EVT_CONNECTIVITY 0x10
#define ST21NFCA_EVT_TRANSACTION 0x12
#define ST21NFCA_SE_TO_HOT_PLUG 1000
/* Connectivity pipe only */
#define ST21NFCA_SE_COUNT_PIPE_UICC 0x01
/* Connectivity + APDU Reader pipe */
#define ST21NFCA_SE_COUNT_PIPE_EMBEDDED 0x02
#define ST21NFCA_SE_MODE_OFF 0x00
#define ST21NFCA_SE_MODE_ON 0x01
#define ST21NFCA_PARAM_ATR 0x01
#define ST21NFCA_ATR_DEFAULT_BWI 0x04
/*
* WT = 2^BWI/10[s], convert into msecs and add a secure
* room by increasing by 2 this timeout
*/
#define ST21NFCA_BWI_TO_TIMEOUT(x) ((1 << x) * 200)
#define ST21NFCA_ATR_GET_Y_FROM_TD(x) (x >> 4)
/* If TA is present bit 0 is set */
#define ST21NFCA_ATR_TA_PRESENT(x) (x & 0x01)
/* If TB is present bit 1 is set */
#define ST21NFCA_ATR_TB_PRESENT(x) (x & 0x02)
static u8 st21nfca_se_get_bwi(struct nfc_hci_dev *hdev)
{
int i;
u8 td;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
/* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */
for (i = 1; i < ST21NFCA_ESE_MAX_LENGTH; i++) {
td = ST21NFCA_ATR_GET_Y_FROM_TD(info->se_info.atr[i]);
if (ST21NFCA_ATR_TA_PRESENT(td))
i++;
if (ST21NFCA_ATR_TB_PRESENT(td)) {
i++;
return info->se_info.atr[i] >> 4;
}
}
return ST21NFCA_ATR_DEFAULT_BWI;
}
static void st21nfca_se_get_atr(struct nfc_hci_dev *hdev)
{
int r;
struct sk_buff *skb;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
r = nfc_hci_get_param(hdev, ST21NFCA_APDU_READER_GATE,
ST21NFCA_PARAM_ATR, &skb);
if (r < 0)
return;
if (skb->len <= ST21NFCA_ESE_MAX_LENGTH) {
memcpy(info->se_info.atr, skb->data, skb->len);
info->se_info.wt_timeout =
ST21NFCA_BWI_TO_TIMEOUT(st21nfca_se_get_bwi(hdev));
}
kfree_skb(skb);
}
static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx,
u8 state)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
int r, i;
struct sk_buff *sk_host_list;
u8 se_event, host_id;
switch (se_idx) {
case NFC_HCI_UICC_HOST_ID:
se_event = (state == ST21NFCA_SE_MODE_ON ?
ST21NFCA_EVT_UICC_ACTIVATE :
ST21NFCA_EVT_UICC_DEACTIVATE);
info->se_info.count_pipes = 0;
info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_UICC;
break;
case ST21NFCA_ESE_HOST_ID:
se_event = (state == ST21NFCA_SE_MODE_ON ?
ST21NFCA_EVT_SE_ACTIVATE :
ST21NFCA_EVT_SE_DEACTIVATE);
info->se_info.count_pipes = 0;
info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_EMBEDDED;
break;
default:
return -EINVAL;
}
/*
* Wait for an EVT_HOT_PLUG in order to
* retrieve a relevant host list.
*/
reinit_completion(&info->se_info.req_completion);
r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE, se_event,
NULL, 0);
if (r < 0)
return r;
mod_timer(&info->se_info.se_active_timer, jiffies +
msecs_to_jiffies(ST21NFCA_SE_TO_HOT_PLUG));
info->se_info.se_active = true;
/* Ignore return value and check in any case the host_list */
wait_for_completion_interruptible(&info->se_info.req_completion);
r = nfc_hci_get_param(hdev, NFC_HCI_ADMIN_GATE,
NFC_HCI_ADMIN_HOST_LIST,
&sk_host_list);
if (r < 0)
return r;
for (i = 0; i < sk_host_list->len &&
sk_host_list->data[i] != se_idx; i++)
;
host_id = sk_host_list->data[i];
kfree_skb(sk_host_list);
if (state == ST21NFCA_SE_MODE_ON && host_id == se_idx)
return se_idx;
else if (state == ST21NFCA_SE_MODE_OFF && host_id != se_idx)
return se_idx;
return -1;
}
int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
int se_count = 0;
if (test_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks))
return 0;
if (info->se_status->is_uicc_present) {
nfc_add_se(hdev->ndev, NFC_HCI_UICC_HOST_ID, NFC_SE_UICC);
se_count++;
}
if (info->se_status->is_ese_present) {
nfc_add_se(hdev->ndev, ST21NFCA_ESE_HOST_ID, NFC_SE_EMBEDDED);
se_count++;
}
return !se_count;
}
EXPORT_SYMBOL(st21nfca_hci_discover_se);
int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx)
{
int r;
/*
* According to upper layer, se_idx == NFC_SE_UICC when
* info->se_status->is_uicc_enable is true should never happen.
* Same for eSE.
*/
r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_ON);
if (r == ST21NFCA_ESE_HOST_ID) {
st21nfca_se_get_atr(hdev);
r = nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE,
ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0);
if (r < 0)
return r;
} else if (r < 0) {
/*
* The activation tentative failed, the secure element
* is not connected. Remove from the list.
*/
nfc_remove_se(hdev->ndev, se_idx);
return r;
}
return 0;
}
EXPORT_SYMBOL(st21nfca_hci_enable_se);
int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx)
{
int r;
/*
* According to upper layer, se_idx == NFC_SE_UICC when
* info->se_status->is_uicc_enable is true should never happen
* Same for eSE.
*/
r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_OFF);
if (r < 0)
return r;
return 0;
}
EXPORT_SYMBOL(st21nfca_hci_disable_se);
int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx,
u8 *apdu, size_t apdu_length,
se_io_cb_t cb, void *cb_context)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
pr_debug("se_io %x\n", se_idx);
switch (se_idx) {
case ST21NFCA_ESE_HOST_ID:
info->se_info.cb = cb;
info->se_info.cb_context = cb_context;
mod_timer(&info->se_info.bwi_timer, jiffies +
msecs_to_jiffies(info->se_info.wt_timeout));
info->se_info.bwi_active = true;
return nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE,
ST21NFCA_EVT_TRANSMIT_DATA,
apdu, apdu_length);
default:
/* Need to free cb_context here as at the moment we can't
* clearly indicate to the caller if the callback function
* would be called (and free it) or not. In both cases a
* negative value may be returned to the caller.
*/
kfree(cb_context);
return -ENODEV;
}
}
EXPORT_SYMBOL(st21nfca_hci_se_io);
static void st21nfca_se_wt_work(struct work_struct *work)
{
/*
* No answer from the secure element
* within the defined timeout.
* Let's send a reset request as recovery procedure.
* According to the situation, we first try to send a software reset
* to the secure element. If the next command is still not
* answering in time, we send to the CLF a secure element hardware
* reset request.
*/
/* hardware reset managed through VCC_UICC_OUT power supply */
u8 param = 0x01;
struct st21nfca_hci_info *info = container_of(work,
struct st21nfca_hci_info,
se_info.timeout_work);
info->se_info.bwi_active = false;
if (!info->se_info.xch_error) {
info->se_info.xch_error = true;
nfc_hci_send_event(info->hdev, ST21NFCA_APDU_READER_GATE,
ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0);
} else {
info->se_info.xch_error = false;
nfc_hci_send_event(info->hdev, ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_EVT_SE_HARD_RESET, &param, 1);
}
info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME);
}
static void st21nfca_se_wt_timeout(struct timer_list *t)
{
struct st21nfca_hci_info *info = timer_container_of(info, t,
se_info.bwi_timer);
schedule_work(&info->se_info.timeout_work);
}
static void st21nfca_se_activation_timeout(struct timer_list *t)
{
struct st21nfca_hci_info *info = timer_container_of(info, t,
se_info.se_active_timer);
info->se_info.se_active = false;
complete(&info->se_info.req_completion);
}
/*
* Returns:
* <= 0: driver handled the event, skb consumed
* 1: driver does not handle the event, please do standard processing
*/
int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
u8 event, struct sk_buff *skb)
{
int r = 0;
struct device *dev = &hdev->ndev->dev;
struct nfc_evt_transaction *transaction;
u32 aid_len;
u8 params_len;
pr_debug("connectivity gate event: %x\n", event);
switch (event) {
case ST21NFCA_EVT_CONNECTIVITY:
r = nfc_se_connectivity(hdev->ndev, host);
break;
case ST21NFCA_EVT_TRANSACTION:
/* According to specification etsi 102 622
* 11.2.2.4 EVT_TRANSACTION Table 52
* Description Tag Length
* AID 81 5 to 16
* PARAMETERS 82 0 to 255
*
* The key differences are aid storage length is variably sized
* in the packet, but fixed in nfc_evt_transaction, and that the aid_len
* is u8 in the packet, but u32 in the structure, and the tags in
* the packet are not included in nfc_evt_transaction.
*
* size in bytes: 1 1 5-16 1 1 0-255
* offset: 0 1 2 aid_len + 2 aid_len + 3 aid_len + 4
* member name: aid_tag(M) aid_len aid params_tag(M) params_len params
* example: 0x81 5-16 X 0x82 0-255 X
*/
if (skb->len < 2 || skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
return -EPROTO;
aid_len = skb->data[1];
if (skb->len < aid_len + 4 || aid_len > sizeof(transaction->aid))
return -EPROTO;
params_len = skb->data[aid_len + 3];
/* Verify PARAMETERS tag is (82), and final check that there is enough
* space in the packet to read everything.
*/
if ((skb->data[aid_len + 2] != NFC_EVT_TRANSACTION_PARAMS_TAG) ||
(skb->len < aid_len + 4 + params_len))
return -EPROTO;
transaction = devm_kzalloc(dev, sizeof(*transaction) + params_len, GFP_KERNEL);
if (!transaction)
return -ENOMEM;
transaction->aid_len = aid_len;
transaction->params_len = params_len;
memcpy(transaction->aid, &skb->data[2], aid_len);
memcpy(transaction->params, &skb->data[aid_len + 4], params_len);
r = nfc_se_transaction(hdev->ndev, host, transaction);
break;
default:
nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n");
return 1;
}
kfree_skb(skb);
return r;
}
EXPORT_SYMBOL(st21nfca_connectivity_event_received);
int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
u8 event, struct sk_buff *skb)
{
int r = 0;
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
pr_debug("apdu reader gate event: %x\n", event);
switch (event) {
case ST21NFCA_EVT_TRANSMIT_DATA:
timer_delete_sync(&info->se_info.bwi_timer);
cancel_work_sync(&info->se_info.timeout_work);
info->se_info.bwi_active = false;
r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0);
if (r < 0)
goto exit;
info->se_info.cb(info->se_info.cb_context,
skb->data, skb->len, 0);
break;
case ST21NFCA_EVT_WTX_REQUEST:
mod_timer(&info->se_info.bwi_timer, jiffies +
msecs_to_jiffies(info->se_info.wt_timeout));
break;
default:
nfc_err(&hdev->ndev->dev, "Unexpected event on apdu reader gate\n");
return 1;
}
exit:
kfree_skb(skb);
return r;
}
EXPORT_SYMBOL(st21nfca_apdu_reader_event_received);
void st21nfca_se_init(struct nfc_hci_dev *hdev)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
init_completion(&info->se_info.req_completion);
INIT_WORK(&info->se_info.timeout_work, st21nfca_se_wt_work);
/* initialize timers */
timer_setup(&info->se_info.bwi_timer, st21nfca_se_wt_timeout, 0);
info->se_info.bwi_active = false;
timer_setup(&info->se_info.se_active_timer,
st21nfca_se_activation_timeout, 0);
info->se_info.se_active = false;
info->se_info.count_pipes = 0;
info->se_info.expected_pipes = 0;
info->se_info.xch_error = false;
info->se_info.wt_timeout =
ST21NFCA_BWI_TO_TIMEOUT(ST21NFCA_ATR_DEFAULT_BWI);
}
EXPORT_SYMBOL(st21nfca_se_init);
void st21nfca_se_deinit(struct nfc_hci_dev *hdev)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
if (info->se_info.bwi_active)
timer_delete_sync(&info->se_info.bwi_timer);
if (info->se_info.se_active)
timer_delete_sync(&info->se_info.se_active_timer);
cancel_work_sync(&info->se_info.timeout_work);
info->se_info.bwi_active = false;
info->se_info.se_active = false;
}
EXPORT_SYMBOL(st21nfca_se_deinit);
+201
View File
@@ -0,0 +1,201 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
*/
#ifndef __LOCAL_ST21NFCA_H_
#define __LOCAL_ST21NFCA_H_
#include <net/nfc/hci.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#define HCI_MODE 0
/* framing in HCI mode */
#define ST21NFCA_SOF_EOF_LEN 2
/* Almost every time value is 0 */
#define ST21NFCA_HCI_LLC_LEN 1
/* Size in worst case :
* In normal case CRC len = 2 but byte stuffing
* may appear in case one CRC byte = ST21NFCA_SOF_EOF
*/
#define ST21NFCA_HCI_LLC_CRC 4
#define ST21NFCA_HCI_LLC_LEN_CRC (ST21NFCA_SOF_EOF_LEN + \
ST21NFCA_HCI_LLC_LEN + \
ST21NFCA_HCI_LLC_CRC)
#define ST21NFCA_HCI_LLC_MIN_SIZE (1 + ST21NFCA_HCI_LLC_LEN_CRC)
/* Worst case when adding byte stuffing between each byte */
#define ST21NFCA_HCI_LLC_MAX_PAYLOAD 29
#define ST21NFCA_HCI_LLC_MAX_SIZE (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \
ST21NFCA_HCI_LLC_MAX_PAYLOAD)
/* Reader RF commands */
#define ST21NFCA_WR_XCHG_DATA 0x10
#define ST21NFCA_DEVICE_MGNT_GATE 0x01
#define ST21NFCA_RF_READER_F_GATE 0x14
#define ST21NFCA_RF_CARD_F_GATE 0x24
#define ST21NFCA_APDU_READER_GATE 0xf0
#define ST21NFCA_CONNECTIVITY_GATE 0x41
/*
* ref ISO7816-3 chap 8.1. the initial character TS is followed by a
* sequence of at most 32 characters.
*/
#define ST21NFCA_ESE_MAX_LENGTH 33
#define ST21NFCA_ESE_HOST_ID 0xc0
#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
#define ST21NFCA_HCI_MODE 0
#define ST21NFCA_NUM_DEVICES 256
#define ST21NFCA_VENDOR_OUI 0x0080E1 /* STMicroelectronics */
#define ST21NFCA_FACTORY_MODE 2
struct st21nfca_se_status {
bool is_ese_present;
bool is_uicc_present;
};
enum st21nfca_state {
ST21NFCA_ST_COLD,
ST21NFCA_ST_READY,
};
/**
* enum nfc_vendor_cmds - supported nfc vendor commands
*
* @FACTORY_MODE: Allow to set the driver into a mode where no secure element
* are activated. It does not consider any NFC_ATTR_VENDOR_DATA.
* @HCI_CLEAR_ALL_PIPES: Allow to execute a HCI clear all pipes command.
* It does not consider any NFC_ATTR_VENDOR_DATA.
* @HCI_DM_PUT_DATA: Allow to configure specific CLF registry as for example
* RF trimmings or low level drivers configurations (I2C, SPI, SWP).
* @HCI_DM_UPDATE_AID: Allow to configure an AID routing into the CLF routing
* table following RF technology, CLF mode or protocol.
* @HCI_DM_GET_INFO: Allow to retrieve CLF information.
* @HCI_DM_GET_DATA: Allow to retrieve CLF configurable data such as low
* level drivers configurations or RF trimmings.
* @HCI_DM_LOAD: Allow to load a firmware into the CLF. A complete
* packet can be more than 8KB.
* @HCI_DM_RESET: Allow to run a CLF reset in order to "commit" CLF
* configuration changes without CLF power off.
* @HCI_GET_PARAM: Allow to retrieve an HCI CLF parameter (for example the
* white list).
* @HCI_DM_FIELD_GENERATOR: Allow to generate different kind of RF
* technology. When using this command to anti-collision is done.
* @HCI_LOOPBACK: Allow to echo a command and test the Dh to CLF
* connectivity.
*/
enum nfc_vendor_cmds {
FACTORY_MODE,
HCI_CLEAR_ALL_PIPES,
HCI_DM_PUT_DATA,
HCI_DM_UPDATE_AID,
HCI_DM_GET_INFO,
HCI_DM_GET_DATA,
HCI_DM_LOAD,
HCI_DM_RESET,
HCI_GET_PARAM,
HCI_DM_FIELD_GENERATOR,
HCI_LOOPBACK,
};
struct st21nfca_vendor_info {
struct completion req_completion;
struct sk_buff *rx_skb;
};
struct st21nfca_dep_info {
struct sk_buff *tx_pending;
struct work_struct tx_work;
u8 curr_nfc_dep_pni;
u32 idx;
u8 to;
u8 did;
u8 bsi;
u8 bri;
u8 lri;
} __packed;
struct st21nfca_se_info {
u8 atr[ST21NFCA_ESE_MAX_LENGTH];
struct completion req_completion;
struct timer_list bwi_timer;
int wt_timeout; /* in msecs */
bool bwi_active;
struct timer_list se_active_timer;
bool se_active;
int expected_pipes;
int count_pipes;
bool xch_error;
se_io_cb_t cb;
void *cb_context;
struct work_struct timeout_work;
};
struct st21nfca_hci_info {
const struct nfc_phy_ops *phy_ops;
void *phy_id;
struct nfc_hci_dev *hdev;
struct st21nfca_se_status *se_status;
enum st21nfca_state state;
struct mutex info_lock;
int async_cb_type;
data_exchange_cb_t async_cb;
void *async_cb_context;
struct st21nfca_dep_info dep_info;
struct st21nfca_se_info se_info;
struct st21nfca_vendor_info vendor_info;
};
int st21nfca_hci_probe(void *phy_id, const struct nfc_phy_ops *phy_ops,
char *llc_name, int phy_headroom, int phy_tailroom,
int phy_payload, struct nfc_hci_dev **hdev,
struct st21nfca_se_status *se_status);
void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
u8 event, struct sk_buff *skb);
int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb);
int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len);
int st21nfca_im_send_dep_req(struct nfc_hci_dev *hdev, struct sk_buff *skb);
void st21nfca_dep_init(struct nfc_hci_dev *hdev);
void st21nfca_dep_deinit(struct nfc_hci_dev *hdev);
int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
u8 event, struct sk_buff *skb);
int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev,
u8 event, struct sk_buff *skb);
int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev);
int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx);
int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx);
int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx,
u8 *apdu, size_t apdu_length,
se_io_cb_t cb, void *cb_context);
void st21nfca_se_init(struct nfc_hci_dev *hdev);
void st21nfca_se_deinit(struct nfc_hci_dev *hdev);
int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *ndev, u8 event,
struct sk_buff *skb);
int st21nfca_vendor_cmds_init(struct nfc_hci_dev *ndev);
#endif /* __LOCAL_ST21NFCA_H_ */
@@ -0,0 +1,364 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Proprietary commands extension for STMicroelectronics NFC Chip
*
* Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
*/
#include <net/genetlink.h>
#include <linux/module.h>
#include <linux/nfc.h>
#include <net/nfc/hci.h>
#include <net/nfc/llc.h>
#include "st21nfca.h"
#define ST21NFCA_HCI_DM_GETDATA 0x10
#define ST21NFCA_HCI_DM_PUTDATA 0x11
#define ST21NFCA_HCI_DM_LOAD 0x12
#define ST21NFCA_HCI_DM_GETINFO 0x13
#define ST21NFCA_HCI_DM_UPDATE_AID 0x20
#define ST21NFCA_HCI_DM_RESET 0x3e
#define ST21NFCA_HCI_DM_FIELD_GENERATOR 0x32
#define ST21NFCA_FACTORY_MODE_ON 1
#define ST21NFCA_FACTORY_MODE_OFF 0
#define ST21NFCA_EVT_POST_DATA 0x02
struct get_param_data {
u8 gate;
u8 data;
} __packed;
static int st21nfca_factory_mode(struct nfc_dev *dev, void *data,
size_t data_len)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
if (data_len != 1)
return -EINVAL;
pr_debug("factory mode: %x\n", ((u8 *)data)[0]);
switch (((u8 *)data)[0]) {
case ST21NFCA_FACTORY_MODE_ON:
test_and_set_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
break;
case ST21NFCA_FACTORY_MODE_OFF:
clear_bit(ST21NFCA_FACTORY_MODE, &hdev->quirks);
break;
default:
return -EINVAL;
}
return 0;
}
static int st21nfca_hci_clear_all_pipes(struct nfc_dev *dev, void *data,
size_t data_len)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
return nfc_hci_disconnect_all_gates(hdev);
}
static int st21nfca_hci_dm_put_data(struct nfc_dev *dev, void *data,
size_t data_len)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_HCI_DM_PUTDATA, data,
data_len, NULL);
}
static int st21nfca_hci_dm_update_aid(struct nfc_dev *dev, void *data,
size_t data_len)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_HCI_DM_UPDATE_AID, data, data_len, NULL);
}
static int st21nfca_hci_dm_get_info(struct nfc_dev *dev, void *data,
size_t data_len)
{
int r;
struct sk_buff *msg, *skb;
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
r = nfc_hci_send_cmd(hdev,
ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_HCI_DM_GETINFO,
data, data_len, &skb);
if (r)
goto exit;
msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
HCI_DM_GET_INFO, skb->len);
if (!msg) {
r = -ENOMEM;
goto free_skb;
}
if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
kfree_skb(msg);
r = -ENOBUFS;
goto free_skb;
}
r = nfc_vendor_cmd_reply(msg);
free_skb:
kfree_skb(skb);
exit:
return r;
}
static int st21nfca_hci_dm_get_data(struct nfc_dev *dev, void *data,
size_t data_len)
{
int r;
struct sk_buff *msg, *skb;
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
r = nfc_hci_send_cmd(hdev,
ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_HCI_DM_GETDATA,
data, data_len, &skb);
if (r)
goto exit;
msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
HCI_DM_GET_DATA, skb->len);
if (!msg) {
r = -ENOMEM;
goto free_skb;
}
if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
kfree_skb(msg);
r = -ENOBUFS;
goto free_skb;
}
r = nfc_vendor_cmd_reply(msg);
free_skb:
kfree_skb(skb);
exit:
return r;
}
static int st21nfca_hci_dm_load(struct nfc_dev *dev, void *data,
size_t data_len)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_HCI_DM_LOAD, data, data_len, NULL);
}
static int st21nfca_hci_dm_reset(struct nfc_dev *dev, void *data,
size_t data_len)
{
int r;
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
r = nfc_hci_send_cmd_async(hdev, ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_HCI_DM_RESET, data, data_len, NULL, NULL);
if (r < 0)
return r;
r = nfc_llc_stop(hdev->llc);
if (r < 0)
return r;
return nfc_llc_start(hdev->llc);
}
static int st21nfca_hci_get_param(struct nfc_dev *dev, void *data,
size_t data_len)
{
int r;
struct sk_buff *msg, *skb;
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
struct get_param_data *param = (struct get_param_data *)data;
if (data_len < sizeof(struct get_param_data))
return -EPROTO;
r = nfc_hci_get_param(hdev, param->gate, param->data, &skb);
if (r)
goto exit;
msg = nfc_vendor_cmd_alloc_reply_skb(dev, ST21NFCA_VENDOR_OUI,
HCI_GET_PARAM, skb->len);
if (!msg) {
r = -ENOMEM;
goto free_skb;
}
if (nla_put(msg, NFC_ATTR_VENDOR_DATA, skb->len, skb->data)) {
kfree_skb(msg);
r = -ENOBUFS;
goto free_skb;
}
r = nfc_vendor_cmd_reply(msg);
free_skb:
kfree_skb(skb);
exit:
return r;
}
static int st21nfca_hci_dm_field_generator(struct nfc_dev *dev, void *data,
size_t data_len)
{
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
return nfc_hci_send_cmd(hdev,
ST21NFCA_DEVICE_MGNT_GATE,
ST21NFCA_HCI_DM_FIELD_GENERATOR,
data, data_len, NULL);
}
int st21nfca_hci_loopback_event_received(struct nfc_hci_dev *hdev, u8 event,
struct sk_buff *skb)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
switch (event) {
case ST21NFCA_EVT_POST_DATA:
info->vendor_info.rx_skb = skb;
break;
default:
nfc_err(&hdev->ndev->dev, "Unexpected event on loopback gate\n");
}
complete(&info->vendor_info.req_completion);
return 0;
}
EXPORT_SYMBOL(st21nfca_hci_loopback_event_received);
static int st21nfca_hci_loopback(struct nfc_dev *dev, void *data,
size_t data_len)
{
int r;
struct sk_buff *msg;
struct nfc_hci_dev *hdev = nfc_get_drvdata(dev);
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
if (data_len <= 0)
return -EPROTO;
reinit_completion(&info->vendor_info.req_completion);
info->vendor_info.rx_skb = NULL;
r = nfc_hci_send_event(hdev, NFC_HCI_LOOPBACK_GATE,
ST21NFCA_EVT_POST_DATA, data, data_len);
if (r < 0) {
r = -EPROTO;
goto exit;
}
wait_for_completion_interruptible(&info->vendor_info.req_completion);
if (!info->vendor_info.rx_skb ||
info->vendor_info.rx_skb->len != data_len) {
r = -EPROTO;
goto exit;
}
msg = nfc_vendor_cmd_alloc_reply_skb(hdev->ndev,
ST21NFCA_VENDOR_OUI,
HCI_LOOPBACK,
info->vendor_info.rx_skb->len);
if (!msg) {
r = -ENOMEM;
goto free_skb;
}
if (nla_put(msg, NFC_ATTR_VENDOR_DATA, info->vendor_info.rx_skb->len,
info->vendor_info.rx_skb->data)) {
kfree_skb(msg);
r = -ENOBUFS;
goto free_skb;
}
r = nfc_vendor_cmd_reply(msg);
free_skb:
kfree_skb(info->vendor_info.rx_skb);
exit:
return r;
}
static const struct nfc_vendor_cmd st21nfca_vendor_cmds[] = {
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = FACTORY_MODE,
.doit = st21nfca_factory_mode,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_CLEAR_ALL_PIPES,
.doit = st21nfca_hci_clear_all_pipes,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_DM_PUT_DATA,
.doit = st21nfca_hci_dm_put_data,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_DM_UPDATE_AID,
.doit = st21nfca_hci_dm_update_aid,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_DM_GET_INFO,
.doit = st21nfca_hci_dm_get_info,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_DM_GET_DATA,
.doit = st21nfca_hci_dm_get_data,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_DM_LOAD,
.doit = st21nfca_hci_dm_load,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_DM_RESET,
.doit = st21nfca_hci_dm_reset,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_GET_PARAM,
.doit = st21nfca_hci_get_param,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_DM_FIELD_GENERATOR,
.doit = st21nfca_hci_dm_field_generator,
},
{
.vendor_id = ST21NFCA_VENDOR_OUI,
.subcmd = HCI_LOOPBACK,
.doit = st21nfca_hci_loopback,
},
};
int st21nfca_vendor_cmds_init(struct nfc_hci_dev *hdev)
{
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
init_completion(&info->vendor_info.req_completion);
return nfc_hci_set_vendor_cmds(hdev, st21nfca_vendor_cmds,
sizeof(st21nfca_vendor_cmds));
}
EXPORT_SYMBOL(st21nfca_vendor_cmds_init);