mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
NFC: 3.20 first pull request
This is the first NFC pull request for 3.20. With this one we have: - Secure element support for the ST Micro st21nfca driver. This depends on a few HCI internal changes in order for example to support more than one secure element per controller. - ACPI support for NXP's pn544 HCI driver. This controller is found on many x86 SoCs and is typically enumerated on the ACPI bus there. - A few st21nfca and st21nfcb fixes. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUyXy7AAoJEIqAPN1PVmxKOY4P/1WJtEZfzBOFMlh9qeA8YESv cS1xbZuAVeEJ3r/sgDc87iA/4MMNZDzfhDHQlQJs/pJWAXE3Am1/dZGWHJC6pbk/ roTlbEh5OvQU8cRIdAvOcEgrBIWk+E30Mkd2OtOMpWyhbgChN7hKz0KUs7znVHBJ G3YCcZOPr7K2ra78UlYzApvGqoxiVaiEWQyj6rzx2HbVzxzICL6A5m9cRcZuGxYR sK3Y/DpKJKGwH3p1kkLIOqHy3nGhq2LttVSXF/f5xkOB8teERSkt4i8aJBiSb9ym a++iAWp2syH3sGh2rsb3G4KRYCYq2J9mJD7oBB3G/6UoNwyeSgW3GdxgH/yxt6C9 KfzHm9T8jfvQIBlf4lGeboV8zu+ysQehJFNKtxdQDlvwqPiR14clT5JFejocr9+N SegCbF+LJaZW8boOZVvhxTxASpEZ0RTFwUIkKKxtXOpK4ha0s1gEAtuZUpTYmRtJ wGqds8wszkE0wOdZJi7dsCGpp5JNI0LZZCsuKa6Ko3E7LkTAor8Bbmob0RR51ClD srtoz6kJgoozAMRLMISXBimk0geOp38iGs26GPSpRHwSV/1loN6+abaOMYlGbDCq +oVpqMmJsS/z5US5+wEkWU+O4y9TS0O74d/TxxcwUM+CLNyKI+Ms1rMOyYi0domo 2xa7MuDCrmztQbs6ROKT =SNWp -----END PGP SIGNATURE----- Merge tag 'nfc-next-3.20-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/nfc-next NFC: 3.20 first pull request This is the first NFC pull request for 3.20. With this one we have: - Secure element support for the ST Micro st21nfca driver. This depends on a few HCI internal changes in order for example to support more than one secure element per controller. - ACPI support for NXP's pn544 HCI driver. This controller is found on many x86 SoCs and is typically enumerated on the ACPI bus there. - A few st21nfca and st21nfcb fixes. Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
b8f8be3f04
@ -1,7 +1,7 @@
|
||||
* STMicroelectronics SAS. ST21NFCA NFC Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "st,st21nfca_i2c".
|
||||
- compatible: Should be "st,st21nfca-i2c".
|
||||
- clock-frequency: I²C work frequency.
|
||||
- reg: address on the bus
|
||||
- interrupt-parent: phandle for the interrupt gpio controller
|
||||
@ -11,6 +11,10 @@ Required properties:
|
||||
Optional SoC Specific Properties:
|
||||
- pinctrl-names: Contains only one value - "default".
|
||||
- pintctrl-0: Specifies the pin control groups used for this controller.
|
||||
- ese-present: Specifies that an ese is physically connected to the nfc
|
||||
controller.
|
||||
- uicc-present: Specifies that the uicc swp signal can be physically
|
||||
connected to the nfc controller.
|
||||
|
||||
Example (for ARM-based BeagleBoard xM with ST21NFCA on I2C2):
|
||||
|
||||
@ -20,7 +24,7 @@ Example (for ARM-based BeagleBoard xM with ST21NFCA on I2C2):
|
||||
|
||||
st21nfca: st21nfca@1 {
|
||||
|
||||
compatible = "st,st21nfca_i2c";
|
||||
compatible = "st,st21nfca-i2c";
|
||||
|
||||
reg = <0x01>;
|
||||
clock-frequency = <400000>;
|
||||
@ -29,5 +33,8 @@ Example (for ARM-based BeagleBoard xM with ST21NFCA on I2C2):
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
enable-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
ese-present;
|
||||
uicc-present;
|
||||
};
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
* STMicroelectronics SAS. ST21NFCB NFC Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "st,st21nfcb_i2c".
|
||||
- compatible: Should be "st,st21nfcb-i2c".
|
||||
- clock-frequency: I²C work frequency.
|
||||
- reg: address on the bus
|
||||
- interrupt-parent: phandle for the interrupt gpio controller
|
||||
@ -20,7 +20,7 @@ Example (for ARM-based BeagleBoard xM with ST21NFCB on I2C2):
|
||||
|
||||
st21nfcb: st21nfcb@8 {
|
||||
|
||||
compatible = "st,st21nfcb_i2c";
|
||||
compatible = "st,st21nfcb-i2c";
|
||||
|
||||
reg = <0x08>;
|
||||
clock-frequency = <400000>;
|
||||
|
@ -557,10 +557,11 @@ exit:
|
||||
pr_err("Failed to handle discovered target err=%d\n", r);
|
||||
}
|
||||
|
||||
static int microread_event_received(struct nfc_hci_dev *hdev, u8 gate,
|
||||
static int microread_event_received(struct nfc_hci_dev *hdev, u8 pipe,
|
||||
u8 event, struct sk_buff *skb)
|
||||
{
|
||||
int r;
|
||||
u8 gate = hdev->pipes[pipe].gate;
|
||||
u8 mode;
|
||||
|
||||
pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate);
|
||||
|
@ -24,11 +24,13 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/nfc.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_data/pn544.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
@ -41,6 +43,11 @@
|
||||
#define PN544_I2C_FRAME_HEADROOM 1
|
||||
#define PN544_I2C_FRAME_TAILROOM 2
|
||||
|
||||
/* GPIO names */
|
||||
#define PN544_GPIO_NAME_IRQ "pn544_irq"
|
||||
#define PN544_GPIO_NAME_FW "pn544_fw"
|
||||
#define PN544_GPIO_NAME_EN "pn544_en"
|
||||
|
||||
/* framing in HCI mode */
|
||||
#define PN544_HCI_I2C_LLC_LEN 1
|
||||
#define PN544_HCI_I2C_LLC_CRC 2
|
||||
@ -58,6 +65,13 @@ static struct i2c_device_id pn544_hci_i2c_id_table[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table);
|
||||
|
||||
static const struct acpi_device_id pn544_hci_i2c_acpi_match[] = {
|
||||
{"NXP5440", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, pn544_hci_i2c_acpi_match);
|
||||
|
||||
#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
|
||||
|
||||
/*
|
||||
@ -195,18 +209,19 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
|
||||
nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
|
||||
|
||||
/* Disable fw download */
|
||||
gpio_set_value(phy->gpio_fw, 0);
|
||||
gpio_set_value_cansleep(phy->gpio_fw, 0);
|
||||
|
||||
for (polarity = 0; polarity < 2; polarity++) {
|
||||
phy->en_polarity = polarity;
|
||||
retry = 3;
|
||||
while (retry--) {
|
||||
/* power off */
|
||||
gpio_set_value(phy->gpio_en, !phy->en_polarity);
|
||||
gpio_set_value_cansleep(phy->gpio_en,
|
||||
!phy->en_polarity);
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
/* power on */
|
||||
gpio_set_value(phy->gpio_en, phy->en_polarity);
|
||||
gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
/* send reset */
|
||||
@ -225,13 +240,14 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy)
|
||||
"Could not detect nfc_en polarity, fallback to active high\n");
|
||||
|
||||
out:
|
||||
gpio_set_value(phy->gpio_en, !phy->en_polarity);
|
||||
gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
|
||||
}
|
||||
|
||||
static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode)
|
||||
{
|
||||
gpio_set_value(phy->gpio_fw, run_mode == PN544_FW_MODE ? 1 : 0);
|
||||
gpio_set_value(phy->gpio_en, phy->en_polarity);
|
||||
gpio_set_value_cansleep(phy->gpio_fw,
|
||||
run_mode == PN544_FW_MODE ? 1 : 0);
|
||||
gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
phy->run_mode = run_mode;
|
||||
@ -254,14 +270,14 @@ static void pn544_hci_i2c_disable(void *phy_id)
|
||||
{
|
||||
struct pn544_i2c_phy *phy = phy_id;
|
||||
|
||||
gpio_set_value(phy->gpio_fw, 0);
|
||||
gpio_set_value(phy->gpio_en, !phy->en_polarity);
|
||||
gpio_set_value_cansleep(phy->gpio_fw, 0);
|
||||
gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
gpio_set_value(phy->gpio_en, phy->en_polarity);
|
||||
gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
gpio_set_value(phy->gpio_en, !phy->en_polarity);
|
||||
gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
phy->powered = 0;
|
||||
@ -859,6 +875,90 @@ exit_state_wait_secure_write_answer:
|
||||
}
|
||||
}
|
||||
|
||||
static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
|
||||
{
|
||||
struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
|
||||
const struct acpi_device_id *id;
|
||||
struct gpio_desc *gpiod_en, *gpiod_irq, *gpiod_fw;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* Match the struct device against a given list of ACPI IDs */
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
/* Get EN GPIO from ACPI */
|
||||
gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1);
|
||||
if (IS_ERR(gpiod_en)) {
|
||||
nfc_err(dev,
|
||||
"Unable to get EN GPIO\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy->gpio_en = desc_to_gpio(gpiod_en);
|
||||
|
||||
/* Configuration EN GPIO */
|
||||
ret = gpiod_direction_output(gpiod_en, 0);
|
||||
if (ret) {
|
||||
nfc_err(dev, "Fail EN pin direction\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get FW GPIO from ACPI */
|
||||
gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2);
|
||||
if (IS_ERR(gpiod_fw)) {
|
||||
nfc_err(dev,
|
||||
"Unable to get FW GPIO\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy->gpio_fw = desc_to_gpio(gpiod_fw);
|
||||
|
||||
/* Configuration FW GPIO */
|
||||
ret = gpiod_direction_output(gpiod_fw, 0);
|
||||
if (ret) {
|
||||
nfc_err(dev, "Fail FW pin direction\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get IRQ GPIO */
|
||||
gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0);
|
||||
if (IS_ERR(gpiod_irq)) {
|
||||
nfc_err(dev,
|
||||
"Unable to get IRQ GPIO\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy->gpio_irq = desc_to_gpio(gpiod_irq);
|
||||
|
||||
/* Configure IRQ GPIO */
|
||||
ret = gpiod_direction_input(gpiod_irq);
|
||||
if (ret) {
|
||||
nfc_err(dev, "Fail IRQ pin direction\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Map the pin to an IRQ */
|
||||
ret = gpiod_to_irq(gpiod_irq);
|
||||
if (ret < 0) {
|
||||
nfc_err(dev, "Fail pin IRQ mapping\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
nfc_info(dev, "GPIO resource, no:%d irq:%d\n",
|
||||
desc_to_gpio(gpiod_irq), ret);
|
||||
client->irq = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
|
||||
@ -884,7 +984,7 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
|
||||
phy->gpio_en = ret;
|
||||
|
||||
/* Configuration of EN GPIO */
|
||||
ret = gpio_request(phy->gpio_en, "pn544_en");
|
||||
ret = gpio_request(phy->gpio_en, PN544_GPIO_NAME_EN);
|
||||
if (ret) {
|
||||
nfc_err(&client->dev, "Fail EN pin\n");
|
||||
goto err_dt;
|
||||
@ -906,7 +1006,7 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
|
||||
phy->gpio_fw = ret;
|
||||
|
||||
/* Configuration of FW GPIO */
|
||||
ret = gpio_request(phy->gpio_fw, "pn544_fw");
|
||||
ret = gpio_request(phy->gpio_fw, PN544_GPIO_NAME_FW);
|
||||
if (ret) {
|
||||
nfc_err(&client->dev, "Fail FW pin\n");
|
||||
goto err_gpio_en;
|
||||
@ -1001,6 +1101,14 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
|
||||
phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
|
||||
phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
|
||||
phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
|
||||
/* Using ACPI */
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
r = pn544_hci_i2c_acpi_request_resources(client);
|
||||
if (r) {
|
||||
nfc_err(&client->dev,
|
||||
"Cannot get ACPI data\n");
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
nfc_err(&client->dev, "No platform data\n");
|
||||
return -EINVAL;
|
||||
@ -1080,6 +1188,7 @@ static struct i2c_driver pn544_hci_i2c_driver = {
|
||||
.name = PN544_HCI_I2C_DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(of_pn544_i2c_match),
|
||||
.acpi_match_table = ACPI_PTR(pn544_hci_i2c_acpi_match),
|
||||
},
|
||||
.probe = pn544_hci_i2c_probe,
|
||||
.id_table = pn544_hci_i2c_id_table,
|
||||
|
@ -724,10 +724,11 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev,
|
||||
* <= 0: driver handled the event, skb consumed
|
||||
* 1: driver does not handle the event, please do standard processing
|
||||
*/
|
||||
static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
||||
static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *rgb_skb = NULL;
|
||||
u8 gate = hdev->pipes[pipe].gate;
|
||||
int r;
|
||||
|
||||
pr_debug("hci event %d\n", event);
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Makefile for ST21NFCA HCI based NFC driver
|
||||
#
|
||||
|
||||
st21nfca_hci-objs = st21nfca.o st21nfca_dep.o
|
||||
st21nfca_hci-objs = st21nfca.o st21nfca_dep.o st21nfca_se.o
|
||||
obj-$(CONFIG_NFC_ST21NFCA) += st21nfca_hci.o
|
||||
|
||||
st21nfca_i2c-objs = i2c.o
|
||||
|
@ -74,6 +74,8 @@ struct st21nfca_i2c_phy {
|
||||
unsigned int gpio_ena;
|
||||
unsigned int irq_polarity;
|
||||
|
||||
struct st21nfca_se_status se_status;
|
||||
|
||||
struct sk_buff *pending_skb;
|
||||
int current_read_len;
|
||||
/*
|
||||
@ -537,6 +539,11 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
|
||||
|
||||
phy->irq_polarity = irq_get_trigger_type(client->irq);
|
||||
|
||||
phy->se_status.is_ese_present =
|
||||
of_property_read_bool(pp, "ese-present");
|
||||
phy->se_status.is_uicc_present =
|
||||
of_property_read_bool(pp, "uicc-present");
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
@ -571,6 +578,9 @@ static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
|
||||
}
|
||||
}
|
||||
|
||||
phy->se_status.is_ese_present = pdata->is_ese_present;
|
||||
phy->se_status.is_uicc_present = pdata->is_uicc_present;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -591,11 +601,8 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client,
|
||||
|
||||
phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy),
|
||||
GFP_KERNEL);
|
||||
if (!phy) {
|
||||
nfc_err(&client->dev,
|
||||
"Cannot allocate memory for st21nfca i2c phy.\n");
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
phy->i2c_dev = client;
|
||||
phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
|
||||
@ -641,8 +648,11 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
return st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
|
||||
ST21NFCA_FRAME_HEADROOM, ST21NFCA_FRAME_TAILROOM,
|
||||
ST21NFCA_HCI_LLC_MAX_PAYLOAD, &phy->hdev);
|
||||
ST21NFCA_FRAME_HEADROOM,
|
||||
ST21NFCA_FRAME_TAILROOM,
|
||||
ST21NFCA_HCI_LLC_MAX_PAYLOAD,
|
||||
&phy->hdev,
|
||||
&phy->se_status);
|
||||
}
|
||||
|
||||
static int st21nfca_hci_i2c_remove(struct i2c_client *client)
|
||||
@ -661,6 +671,7 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client)
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_st21nfca_i2c_match[] = {
|
||||
{ .compatible = "st,st21nfca-i2c", },
|
||||
{ .compatible = "st,st21nfca_i2c", },
|
||||
{}
|
||||
};
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "st21nfca.h"
|
||||
#include "st21nfca_dep.h"
|
||||
#include "st21nfca_se.h"
|
||||
|
||||
#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
|
||||
|
||||
@ -62,7 +63,6 @@
|
||||
#define ST21NFCA_RF_CARD_F_DATARATE 0x08
|
||||
#define ST21NFCA_RF_CARD_F_DATARATE_212_424 0x01
|
||||
|
||||
#define ST21NFCA_DEVICE_MGNT_GATE 0x01
|
||||
#define ST21NFCA_DEVICE_MGNT_PIPE 0x02
|
||||
|
||||
#define ST21NFCA_DM_GETINFO 0x13
|
||||
@ -78,6 +78,11 @@
|
||||
|
||||
#define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/
|
||||
|
||||
#define ST21NFCA_EVT_HOT_PLUG 0x03
|
||||
#define ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80)
|
||||
|
||||
#define ST21NFCA_SE_TO_PIPES 2000
|
||||
|
||||
static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
|
||||
|
||||
static struct nfc_hci_gate st21nfca_gates[] = {
|
||||
@ -92,6 +97,10 @@ static struct nfc_hci_gate st21nfca_gates[] = {
|
||||
{ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
|
||||
{ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
|
||||
{ST21NFCA_RF_CARD_F_GATE, NFC_HCI_INVALID_PIPE},
|
||||
|
||||
/* Secure element pipes are created by secure element host */
|
||||
{ST21NFCA_CONNECTIVITY_GATE, NFC_HCI_DO_NOT_CREATE_PIPE},
|
||||
{ST21NFCA_APDU_READER_GATE, NFC_HCI_DO_NOT_CREATE_PIPE},
|
||||
};
|
||||
|
||||
struct st21nfca_pipe_info {
|
||||
@ -118,18 +127,6 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
|
||||
NFC_HCI_TERMINAL_HOST_ID, 0
|
||||
};
|
||||
|
||||
skb_pipe_list = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL);
|
||||
if (!skb_pipe_list) {
|
||||
r = -ENOMEM;
|
||||
goto free_list;
|
||||
}
|
||||
|
||||
skb_pipe_info = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL);
|
||||
if (!skb_pipe_info) {
|
||||
r = -ENOMEM;
|
||||
goto free_info;
|
||||
}
|
||||
|
||||
/* On ST21NFCA device pipes number are dynamics
|
||||
* A maximum of 16 pipes can be created at the same time
|
||||
* If pipes are already created, hci_dev_up will fail.
|
||||
@ -148,7 +145,8 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
|
||||
* Pipe can be closed and need to be open.
|
||||
*/
|
||||
r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID,
|
||||
ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE);
|
||||
ST21NFCA_DEVICE_MGNT_GATE,
|
||||
ST21NFCA_DEVICE_MGNT_PIPE);
|
||||
if (r < 0)
|
||||
goto free_info;
|
||||
|
||||
@ -179,17 +177,28 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
|
||||
* - destination gid (1byte)
|
||||
*/
|
||||
info = (struct st21nfca_pipe_info *) skb_pipe_info->data;
|
||||
if (info->dst_gate_id == ST21NFCA_APDU_READER_GATE &&
|
||||
info->src_host_id != ST21NFCA_ESE_HOST_ID) {
|
||||
pr_err("Unexpected apdu_reader pipe on host %x\n",
|
||||
info->src_host_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) &&
|
||||
(st21nfca_gates[j].gate != info->dst_gate_id);
|
||||
j++)
|
||||
(st21nfca_gates[j].gate != info->dst_gate_id) ; j++)
|
||||
;
|
||||
|
||||
if (j < ARRAY_SIZE(st21nfca_gates) &&
|
||||
st21nfca_gates[j].gate == info->dst_gate_id &&
|
||||
ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) {
|
||||
st21nfca_gates[j].pipe = pipe_info[2];
|
||||
|
||||
hdev->gate2pipe[st21nfca_gates[j].gate] =
|
||||
st21nfca_gates[j].pipe;
|
||||
hdev->pipes[st21nfca_gates[j].pipe].gate =
|
||||
st21nfca_gates[j].gate;
|
||||
hdev->pipes[st21nfca_gates[j].pipe].dest_host =
|
||||
info->src_host_id;
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +208,7 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
|
||||
*/
|
||||
if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) {
|
||||
for (i = skb_pipe_list->len + 3;
|
||||
i < ARRAY_SIZE(st21nfca_gates); i++) {
|
||||
i < ARRAY_SIZE(st21nfca_gates) - 2; i++) {
|
||||
r = nfc_hci_connect_gate(hdev,
|
||||
NFC_HCI_HOST_CONTROLLER_ID,
|
||||
st21nfca_gates[i].gate,
|
||||
@ -212,7 +221,6 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
|
||||
memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
|
||||
free_info:
|
||||
kfree_skb(skb_pipe_info);
|
||||
free_list:
|
||||
kfree_skb(skb_pipe_list);
|
||||
return r;
|
||||
}
|
||||
@ -257,16 +265,33 @@ out:
|
||||
|
||||
static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||
struct sk_buff *skb;
|
||||
|
||||
u8 param;
|
||||
u8 white_list[2];
|
||||
int wl_size = 0;
|
||||
int r;
|
||||
|
||||
param = NFC_HCI_UICC_HOST_ID;
|
||||
if (info->se_status->is_ese_present &&
|
||||
info->se_status->is_uicc_present) {
|
||||
white_list[wl_size++] = NFC_HCI_UICC_HOST_ID;
|
||||
white_list[wl_size++] = ST21NFCA_ESE_HOST_ID;
|
||||
} else if (!info->se_status->is_ese_present &&
|
||||
info->se_status->is_uicc_present) {
|
||||
white_list[wl_size++] = NFC_HCI_UICC_HOST_ID;
|
||||
} else if (info->se_status->is_ese_present &&
|
||||
!info->se_status->is_uicc_present) {
|
||||
white_list[wl_size++] = ST21NFCA_ESE_HOST_ID;
|
||||
}
|
||||
|
||||
if (wl_size) {
|
||||
r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
|
||||
NFC_HCI_ADMIN_WHITELIST, ¶m, 1);
|
||||
NFC_HCI_ADMIN_WHITELIST,
|
||||
(u8 *) &white_list, wl_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Set NFC_MODE in device management gate to enable */
|
||||
r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
||||
@ -274,8 +299,9 @@ static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (skb->data[0] == 0) {
|
||||
param = skb->data[0];
|
||||
kfree_skb(skb);
|
||||
if (param == 0) {
|
||||
param = 1;
|
||||
|
||||
r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
||||
@ -417,9 +443,12 @@ static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
|
||||
r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
||||
ST21NFCA_RF_CARD_F_DATARATE,
|
||||
param, 1);
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
kfree_skb(datarate_skb);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
kfree_skb(datarate_skb);
|
||||
|
||||
/*
|
||||
* Configure sens_res
|
||||
@ -673,15 +702,15 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||
struct nfc_target *target)
|
||||
{
|
||||
int r;
|
||||
struct sk_buff *nfcid2_skb = NULL, *nfcid1_skb;
|
||||
struct sk_buff *nfcid_skb = NULL;
|
||||
|
||||
if (gate == ST21NFCA_RF_READER_F_GATE) {
|
||||
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
|
||||
ST21NFCA_RF_READER_F_NFCID2, &nfcid2_skb);
|
||||
ST21NFCA_RF_READER_F_NFCID2, &nfcid_skb);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
|
||||
if (nfcid2_skb->len > NFC_SENSF_RES_MAXSIZE) {
|
||||
if (nfcid_skb->len > NFC_SENSF_RES_MAXSIZE) {
|
||||
r = -EPROTO;
|
||||
goto exit;
|
||||
}
|
||||
@ -693,11 +722,11 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||
* - After the reception of SEL_RES with NFCIP-1 compliant bit
|
||||
* set for type A frame NFCID1 will be updated
|
||||
*/
|
||||
if (nfcid2_skb->len > 0) {
|
||||
if (nfcid_skb->len > 0) {
|
||||
/* P2P in type F */
|
||||
memcpy(target->sensf_res, nfcid2_skb->data,
|
||||
nfcid2_skb->len);
|
||||
target->sensf_res_len = nfcid2_skb->len;
|
||||
memcpy(target->sensf_res, nfcid_skb->data,
|
||||
nfcid_skb->len);
|
||||
target->sensf_res_len = nfcid_skb->len;
|
||||
/* NFC Forum Digital Protocol Table 44 */
|
||||
if (target->sensf_res[0] == 0x01 &&
|
||||
target->sensf_res[1] == 0xfe)
|
||||
@ -707,27 +736,28 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||
target->supported_protocols =
|
||||
NFC_PROTO_FELICA_MASK;
|
||||
} else {
|
||||
kfree_skb(nfcid_skb);
|
||||
/* P2P in type A */
|
||||
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
|
||||
ST21NFCA_RF_READER_F_NFCID1,
|
||||
&nfcid1_skb);
|
||||
&nfcid_skb);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
|
||||
if (nfcid1_skb->len > NFC_NFCID1_MAXSIZE) {
|
||||
if (nfcid_skb->len > NFC_NFCID1_MAXSIZE) {
|
||||
r = -EPROTO;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(target->sensf_res, nfcid1_skb->data,
|
||||
nfcid1_skb->len);
|
||||
target->sensf_res_len = nfcid1_skb->len;
|
||||
memcpy(target->sensf_res, nfcid_skb->data,
|
||||
nfcid_skb->len);
|
||||
target->sensf_res_len = nfcid_skb->len;
|
||||
target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
|
||||
}
|
||||
target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE;
|
||||
}
|
||||
r = 1;
|
||||
exit:
|
||||
kfree_skb(nfcid2_skb);
|
||||
kfree_skb(nfcid_skb);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -829,24 +859,82 @@ static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev,
|
||||
}
|
||||
}
|
||||
|
||||
static void st21nfca_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||
u8 gate = hdev->pipes[pipe].gate;
|
||||
|
||||
pr_debug("cmd: %x\n", cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case NFC_HCI_ANY_OPEN_PIPE:
|
||||
if (gate != ST21NFCA_APDU_READER_GATE &&
|
||||
hdev->pipes[pipe].dest_host != NFC_HCI_UICC_HOST_ID)
|
||||
info->se_info.count_pipes++;
|
||||
|
||||
if (info->se_info.count_pipes == info->se_info.expected_pipes) {
|
||||
del_timer_sync(&info->se_info.se_active_timer);
|
||||
info->se_info.se_active = false;
|
||||
info->se_info.count_pipes = 0;
|
||||
complete(&info->se_info.req_completion);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int st21nfca_admin_event_received(struct nfc_hci_dev *hdev, u8 event,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
pr_debug("admin event: %x\n", event);
|
||||
|
||||
switch (event) {
|
||||
case ST21NFCA_EVT_HOT_PLUG:
|
||||
if (info->se_info.se_active) {
|
||||
if (!ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(skb)) {
|
||||
del_timer_sync(&info->se_info.se_active_timer);
|
||||
info->se_info.se_active = false;
|
||||
complete(&info->se_info.req_completion);
|
||||
} else {
|
||||
mod_timer(&info->se_info.se_active_timer,
|
||||
jiffies +
|
||||
msecs_to_jiffies(ST21NFCA_SE_TO_PIPES));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* <= 0: driver handled the event, skb consumed
|
||||
* 1: driver does not handle the event, please do standard processing
|
||||
*/
|
||||
static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
|
||||
static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe,
|
||||
u8 event, struct sk_buff *skb)
|
||||
{
|
||||
u8 gate = hdev->pipes[pipe].gate;
|
||||
u8 host = hdev->pipes[pipe].dest_host;
|
||||
|
||||
pr_debug("hci event: %d gate: %x\n", event, gate);
|
||||
|
||||
switch (gate) {
|
||||
case NFC_HCI_ADMIN_GATE:
|
||||
return st21nfca_admin_event_received(hdev, event, skb);
|
||||
case ST21NFCA_RF_CARD_F_GATE:
|
||||
return st21nfca_dep_event_received(hdev, event, skb);
|
||||
case ST21NFCA_CONNECTIVITY_GATE:
|
||||
return st21nfca_connectivity_event_received(hdev, host,
|
||||
event, skb);
|
||||
case ST21NFCA_APDU_READER_GATE:
|
||||
return st21nfca_apdu_reader_event_received(hdev, event, skb);
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nfc_hci_ops st21nfca_hci_ops = {
|
||||
@ -865,11 +953,17 @@ static struct nfc_hci_ops st21nfca_hci_ops = {
|
||||
.tm_send = st21nfca_hci_tm_send,
|
||||
.check_presence = st21nfca_hci_check_presence,
|
||||
.event_received = st21nfca_hci_event_received,
|
||||
.cmd_received = st21nfca_hci_cmd_received,
|
||||
.discover_se = st21nfca_hci_discover_se,
|
||||
.enable_se = st21nfca_hci_enable_se,
|
||||
.disable_se = st21nfca_hci_disable_se,
|
||||
.se_io = st21nfca_hci_se_io,
|
||||
};
|
||||
|
||||
int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
|
||||
char *llc_name, int phy_headroom, int phy_tailroom,
|
||||
int phy_payload, struct nfc_hci_dev **hdev)
|
||||
int phy_payload, struct nfc_hci_dev **hdev,
|
||||
struct st21nfca_se_status *se_status)
|
||||
{
|
||||
struct st21nfca_hci_info *info;
|
||||
int r = 0;
|
||||
@ -929,6 +1023,8 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
|
||||
goto err_alloc_hdev;
|
||||
}
|
||||
|
||||
info->se_status = se_status;
|
||||
|
||||
nfc_hci_set_clientdata(info->hdev, info);
|
||||
|
||||
r = nfc_hci_register_device(info->hdev);
|
||||
@ -937,6 +1033,7 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
|
||||
|
||||
*hdev = info->hdev;
|
||||
st21nfca_dep_init(info->hdev);
|
||||
st21nfca_se_init(info->hdev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -955,6 +1052,7 @@ void st21nfca_hci_remove(struct nfc_hci_dev *hdev)
|
||||
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||
|
||||
st21nfca_dep_deinit(hdev);
|
||||
st21nfca_se_deinit(hdev);
|
||||
nfc_hci_unregister_device(hdev);
|
||||
nfc_hci_free_device(hdev);
|
||||
kfree(info);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <net/nfc/hci.h>
|
||||
|
||||
#include "st21nfca_dep.h"
|
||||
#include "st21nfca_se.h"
|
||||
|
||||
#define HCI_MODE 0
|
||||
|
||||
@ -51,9 +52,15 @@
|
||||
|
||||
#define ST21NFCA_NUM_DEVICES 256
|
||||
|
||||
struct st21nfca_se_status {
|
||||
bool is_ese_present;
|
||||
bool is_uicc_present;
|
||||
};
|
||||
|
||||
int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
|
||||
char *llc_name, int phy_headroom, int phy_tailroom,
|
||||
int phy_payload, struct nfc_hci_dev **hdev);
|
||||
int phy_payload, struct nfc_hci_dev **hdev,
|
||||
struct st21nfca_se_status *se_status);
|
||||
void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
|
||||
|
||||
enum st21nfca_state {
|
||||
@ -66,6 +73,7 @@ struct st21nfca_hci_info {
|
||||
void *phy_id;
|
||||
|
||||
struct nfc_hci_dev *hdev;
|
||||
struct st21nfca_se_status *se_status;
|
||||
|
||||
enum st21nfca_state state;
|
||||
|
||||
@ -76,13 +84,16 @@ struct st21nfca_hci_info {
|
||||
void *async_cb_context;
|
||||
|
||||
struct st21nfca_dep_info dep_info;
|
||||
struct st21nfca_se_info se_info;
|
||||
};
|
||||
|
||||
/* 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
|
||||
|
||||
#endif /* __LOCAL_ST21NFCA_H_ */
|
||||
|
390
drivers/nfc/st21nfca/st21nfca_se.c
Normal file
390
drivers/nfc/st21nfca/st21nfca_se.c
Normal file
@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
|
||||
*
|
||||
* 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 that 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <net/nfc/hci.h>
|
||||
|
||||
#include "st21nfca.h"
|
||||
#include "st21nfca_se.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_ESE_HOST_ID 0xc0
|
||||
|
||||
#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;
|
||||
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;
|
||||
|
||||
host_id = sk_host_list->data[sk_host_list->len - 1];
|
||||
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 (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:
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(st21nfca_hci_se_io);
|
||||
|
||||
static void st21nfca_se_wt_timeout(unsigned long data)
|
||||
{
|
||||
/*
|
||||
* 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 = (struct st21nfca_hci_info *) data;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
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, ¶m, 1);
|
||||
}
|
||||
info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME);
|
||||
}
|
||||
|
||||
static void st21nfca_se_activation_timeout(unsigned long data)
|
||||
{
|
||||
struct st21nfca_hci_info *info = (struct st21nfca_hci_info *) data;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
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;
|
||||
|
||||
pr_debug("connectivity gate event: %x\n", event);
|
||||
|
||||
switch (event) {
|
||||
case ST21NFCA_EVT_CONNECTIVITY:
|
||||
break;
|
||||
case ST21NFCA_EVT_TRANSACTION:
|
||||
break;
|
||||
default:
|
||||
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:
|
||||
del_timer_sync(&info->se_info.bwi_timer);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
/* initialize timers */
|
||||
init_timer(&info->se_info.bwi_timer);
|
||||
info->se_info.bwi_timer.data = (unsigned long)info;
|
||||
info->se_info.bwi_timer.function = st21nfca_se_wt_timeout;
|
||||
info->se_info.bwi_active = false;
|
||||
|
||||
init_timer(&info->se_info.se_active_timer);
|
||||
info->se_info.se_active_timer.data = (unsigned long)info;
|
||||
info->se_info.se_active_timer.function = st21nfca_se_activation_timeout;
|
||||
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)
|
||||
del_timer_sync(&info->se_info.bwi_timer);
|
||||
if (info->se_info.se_active)
|
||||
del_timer_sync(&info->se_info.se_active_timer);
|
||||
|
||||
info->se_info.bwi_active = false;
|
||||
info->se_info.se_active = false;
|
||||
}
|
||||
EXPORT_SYMBOL(st21nfca_se_deinit);
|
63
drivers/nfc/st21nfca/st21nfca_se.h
Normal file
63
drivers/nfc/st21nfca/st21nfca_se.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
|
||||
*
|
||||
* 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 that 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __ST21NFCA_SE_H
|
||||
#define __ST21NFCA_SE_H
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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);
|
||||
#endif /* __ST21NFCA_SE_H */
|
@ -199,7 +199,7 @@ static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id)
|
||||
struct sk_buff *skb = NULL;
|
||||
int r;
|
||||
|
||||
if (!phy || irq != phy->i2c_dev->irq) {
|
||||
if (!phy || !phy->ndlc || irq != phy->i2c_dev->irq) {
|
||||
WARN_ON_ONCE(1);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
@ -343,18 +343,22 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
r = ndlc_probe(phy, &i2c_phy_ops, &client->dev,
|
||||
ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM,
|
||||
&phy->ndlc);
|
||||
if (r < 0) {
|
||||
nfc_err(&client->dev, "Unable to register ndlc layer\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||
st21nfcb_nci_irq_thread_fn,
|
||||
phy->irq_polarity | IRQF_ONESHOT,
|
||||
ST21NFCB_NCI_DRIVER_NAME, phy);
|
||||
if (r < 0) {
|
||||
if (r < 0)
|
||||
nfc_err(&client->dev, "Unable to register IRQ handler\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
return ndlc_probe(phy, &i2c_phy_ops, &client->dev,
|
||||
ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM,
|
||||
&phy->ndlc);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int st21nfcb_nci_i2c_remove(struct i2c_client *client)
|
||||
@ -373,6 +377,7 @@ static int st21nfcb_nci_i2c_remove(struct i2c_client *client)
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_st21nfcb_i2c_match[] = {
|
||||
{ .compatible = "st,st21nfcb-i2c", },
|
||||
{ .compatible = "st,st21nfcb_i2c", },
|
||||
{}
|
||||
};
|
||||
|
@ -138,7 +138,7 @@ static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc)
|
||||
default:
|
||||
pr_err("UNKNOWN Packet Control Byte=%d\n", pcb);
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
skb_queue_head(&ndlc->send_q, skb);
|
||||
}
|
||||
@ -297,6 +297,5 @@ void ndlc_remove(struct llt_ndlc *ndlc)
|
||||
skb_queue_purge(&ndlc->send_q);
|
||||
|
||||
st21nfcb_nci_remove(ndlc->ndev);
|
||||
kfree(ndlc);
|
||||
}
|
||||
EXPORT_SYMBOL(ndlc_remove);
|
||||
|
@ -26,6 +26,8 @@
|
||||
struct st21nfca_nfc_platform_data {
|
||||
unsigned int gpio_ena;
|
||||
unsigned int irq_polarity;
|
||||
bool is_ese_present;
|
||||
bool is_uicc_present;
|
||||
};
|
||||
|
||||
#endif /* _ST21NFCA_HCI_H_ */
|
||||
|
@ -19,8 +19,6 @@
|
||||
#ifndef _ST21NFCB_NCI_H_
|
||||
#define _ST21NFCB_NCI_H_
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci"
|
||||
|
||||
struct st21nfcb_nfc_platform_data {
|
||||
@ -28,4 +26,4 @@ struct st21nfcb_nfc_platform_data {
|
||||
unsigned int irq_polarity;
|
||||
};
|
||||
|
||||
#endif /* _ST21NFCA_HCI_H_ */
|
||||
#endif /* _ST21NFCB_NCI_H_ */
|
||||
|
@ -51,7 +51,9 @@ struct nfc_hci_ops {
|
||||
int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
|
||||
int (*check_presence)(struct nfc_hci_dev *hdev,
|
||||
struct nfc_target *target);
|
||||
int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
||||
int (*event_received)(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
|
||||
struct sk_buff *skb);
|
||||
void (*cmd_received)(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
|
||||
struct sk_buff *skb);
|
||||
int (*fw_download)(struct nfc_hci_dev *hdev, const char *firmware_name);
|
||||
int (*discover_se)(struct nfc_hci_dev *dev);
|
||||
@ -63,8 +65,10 @@ struct nfc_hci_ops {
|
||||
};
|
||||
|
||||
/* Pipes */
|
||||
#define NFC_HCI_INVALID_PIPE 0x80
|
||||
#define NFC_HCI_DO_NOT_CREATE_PIPE 0x81
|
||||
#define NFC_HCI_INVALID_PIPE 0x80
|
||||
#define NFC_HCI_INVALID_GATE 0xFF
|
||||
#define NFC_HCI_INVALID_HOST 0x80
|
||||
#define NFC_HCI_LINK_MGMT_PIPE 0x00
|
||||
#define NFC_HCI_ADMIN_PIPE 0x01
|
||||
|
||||
@ -73,7 +77,13 @@ struct nfc_hci_gate {
|
||||
u8 pipe;
|
||||
};
|
||||
|
||||
struct nfc_hci_pipe {
|
||||
u8 gate;
|
||||
u8 dest_host;
|
||||
};
|
||||
|
||||
#define NFC_HCI_MAX_CUSTOM_GATES 50
|
||||
#define NFC_HCI_MAX_PIPES 127
|
||||
struct nfc_hci_init_data {
|
||||
u8 gate_count;
|
||||
struct nfc_hci_gate gates[NFC_HCI_MAX_CUSTOM_GATES];
|
||||
@ -125,6 +135,7 @@ struct nfc_hci_dev {
|
||||
void *clientdata;
|
||||
|
||||
u8 gate2pipe[NFC_HCI_MAX_GATES];
|
||||
struct nfc_hci_pipe pipes[NFC_HCI_MAX_PIPES];
|
||||
|
||||
u8 sw_romlib;
|
||||
u8 sw_patch;
|
||||
@ -167,6 +178,8 @@ void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev);
|
||||
void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err);
|
||||
|
||||
int nfc_hci_result_to_errno(u8 result);
|
||||
void nfc_hci_reset_pipes(struct nfc_hci_dev *dev);
|
||||
void nfc_hci_reset_pipes_per_host(struct nfc_hci_dev *hdev, u8 host);
|
||||
|
||||
/* Host IDs */
|
||||
#define NFC_HCI_HOST_CONTROLLER_ID 0x00
|
||||
@ -219,6 +232,12 @@ int nfc_hci_result_to_errno(u8 result);
|
||||
#define NFC_HCI_EVT_POST_DATA 0x02
|
||||
#define NFC_HCI_EVT_HOT_PLUG 0x03
|
||||
|
||||
/* Generic commands */
|
||||
#define NFC_HCI_ANY_SET_PARAMETER 0x01
|
||||
#define NFC_HCI_ANY_GET_PARAMETER 0x02
|
||||
#define NFC_HCI_ANY_OPEN_PIPE 0x03
|
||||
#define NFC_HCI_ANY_CLOSE_PIPE 0x04
|
||||
|
||||
/* Reader RF gates events */
|
||||
#define NFC_HCI_EVT_READER_REQUESTED 0x10
|
||||
#define NFC_HCI_EVT_END_OPERATION 0x11
|
||||
@ -249,8 +268,6 @@ int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
|
||||
int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
|
||||
const u8 *param, size_t param_len,
|
||||
data_exchange_cb_t cb, void *cb_context);
|
||||
int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
|
||||
const u8 *param, size_t param_len);
|
||||
int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
||||
const u8 *param, size_t param_len);
|
||||
int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate);
|
||||
|
@ -555,7 +555,6 @@ EXPORT_SYMBOL(nfc_find_se);
|
||||
|
||||
int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
|
||||
{
|
||||
|
||||
struct nfc_se *se;
|
||||
int rc;
|
||||
|
||||
@ -605,7 +604,6 @@ error:
|
||||
|
||||
int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
|
||||
{
|
||||
|
||||
struct nfc_se *se;
|
||||
int rc;
|
||||
|
||||
|
@ -116,23 +116,6 @@ int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_send_event);
|
||||
|
||||
int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
|
||||
const u8 *param, size_t param_len)
|
||||
{
|
||||
u8 pipe;
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
pipe = hdev->gate2pipe[gate];
|
||||
if (pipe == NFC_HCI_INVALID_PIPE)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_RESPONSE,
|
||||
response, param, param_len, NULL, NULL,
|
||||
0);
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_send_response);
|
||||
|
||||
/*
|
||||
* Execute an hci command sent to gate.
|
||||
* skb will contain response data if success. skb can be NULL if you are not
|
||||
@ -331,7 +314,7 @@ int nfc_hci_disconnect_all_gates(struct nfc_hci_dev *hdev)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
||||
nfc_hci_reset_pipes(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -345,7 +328,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
if (hdev->gate2pipe[dest_gate] == NFC_HCI_DO_NOT_CREATE_PIPE)
|
||||
if (pipe == NFC_HCI_DO_NOT_CREATE_PIPE)
|
||||
return 0;
|
||||
|
||||
if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
|
||||
@ -380,6 +363,8 @@ open_pipe:
|
||||
return r;
|
||||
}
|
||||
|
||||
hdev->pipes[pipe].gate = dest_gate;
|
||||
hdev->pipes[pipe].dest_host = dest_host;
|
||||
hdev->gate2pipe[dest_gate] = pipe;
|
||||
|
||||
return 0;
|
||||
|
@ -46,6 +46,32 @@ int nfc_hci_result_to_errno(u8 result)
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_result_to_errno);
|
||||
|
||||
void nfc_hci_reset_pipes(struct nfc_hci_dev *hdev)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < NFC_HCI_MAX_PIPES; i++) {
|
||||
hdev->pipes[i].gate = NFC_HCI_INVALID_GATE;
|
||||
hdev->pipes[i].dest_host = NFC_HCI_INVALID_HOST;
|
||||
}
|
||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_reset_pipes);
|
||||
|
||||
void nfc_hci_reset_pipes_per_host(struct nfc_hci_dev *hdev, u8 host)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < NFC_HCI_MAX_PIPES; i++) {
|
||||
if (hdev->pipes[i].dest_host != host)
|
||||
continue;
|
||||
|
||||
hdev->pipes[i].gate = NFC_HCI_INVALID_GATE;
|
||||
hdev->pipes[i].dest_host = NFC_HCI_INVALID_HOST;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(nfc_hci_reset_pipes_per_host);
|
||||
|
||||
static void nfc_hci_msg_tx_work(struct work_struct *work)
|
||||
{
|
||||
struct nfc_hci_dev *hdev = container_of(work, struct nfc_hci_dev,
|
||||
@ -167,48 +193,69 @@ exit:
|
||||
void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
int r = 0;
|
||||
u8 gate = nfc_hci_pipe2gate(hdev, pipe);
|
||||
u8 local_gate, new_pipe;
|
||||
u8 gate_opened = 0x00;
|
||||
u8 gate = hdev->pipes[pipe].gate;
|
||||
u8 status = NFC_HCI_ANY_OK;
|
||||
struct hci_create_pipe_resp *create_info;
|
||||
struct hci_delete_pipe_noti *delete_info;
|
||||
struct hci_all_pipe_cleared_noti *cleared_info;
|
||||
|
||||
pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case NFC_HCI_ADM_NOTIFY_PIPE_CREATED:
|
||||
if (skb->len != 5) {
|
||||
r = -EPROTO;
|
||||
break;
|
||||
status = NFC_HCI_ANY_E_NOK;
|
||||
goto exit;
|
||||
}
|
||||
create_info = (struct hci_create_pipe_resp *)skb->data;
|
||||
|
||||
local_gate = skb->data[3];
|
||||
new_pipe = skb->data[4];
|
||||
nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
|
||||
|
||||
/* save the new created pipe and bind with local gate,
|
||||
/* Save the new created pipe and bind with local gate,
|
||||
* the description for skb->data[3] is destination gate id
|
||||
* but since we received this cmd from host controller, we
|
||||
* are the destination and it is our local gate
|
||||
*/
|
||||
hdev->gate2pipe[local_gate] = new_pipe;
|
||||
hdev->gate2pipe[create_info->dest_gate] = create_info->pipe;
|
||||
hdev->pipes[create_info->pipe].gate = create_info->dest_gate;
|
||||
hdev->pipes[create_info->pipe].dest_host =
|
||||
create_info->src_host;
|
||||
break;
|
||||
case NFC_HCI_ANY_OPEN_PIPE:
|
||||
/* if the pipe is already created, we allow remote host to
|
||||
* open it
|
||||
*/
|
||||
if (gate != 0xff)
|
||||
nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK,
|
||||
&gate_opened, 1);
|
||||
if (gate == NFC_HCI_INVALID_GATE) {
|
||||
status = NFC_HCI_ANY_E_NOK;
|
||||
goto exit;
|
||||
}
|
||||
break;
|
||||
case NFC_HCI_ADM_NOTIFY_PIPE_DELETED:
|
||||
if (skb->len != 1) {
|
||||
status = NFC_HCI_ANY_E_NOK;
|
||||
goto exit;
|
||||
}
|
||||
delete_info = (struct hci_delete_pipe_noti *)skb->data;
|
||||
|
||||
hdev->pipes[delete_info->pipe].gate = NFC_HCI_INVALID_GATE;
|
||||
hdev->pipes[delete_info->pipe].dest_host = NFC_HCI_INVALID_HOST;
|
||||
break;
|
||||
case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
|
||||
nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
|
||||
if (skb->len != 1) {
|
||||
status = NFC_HCI_ANY_E_NOK;
|
||||
goto exit;
|
||||
}
|
||||
cleared_info = (struct hci_all_pipe_cleared_noti *)skb->data;
|
||||
|
||||
nfc_hci_reset_pipes_per_host(hdev, cleared_info->host);
|
||||
break;
|
||||
default:
|
||||
pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate);
|
||||
r = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdev->ops->cmd_received)
|
||||
hdev->ops->cmd_received(hdev, pipe, cmd, skb);
|
||||
|
||||
exit:
|
||||
nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_RESPONSE,
|
||||
status, NULL, 0, NULL, NULL, 0);
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
@ -330,15 +377,15 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
int r = 0;
|
||||
u8 gate = nfc_hci_pipe2gate(hdev, pipe);
|
||||
u8 gate = hdev->pipes[pipe].gate;
|
||||
|
||||
if (gate == 0xff) {
|
||||
if (gate == NFC_HCI_INVALID_GATE) {
|
||||
pr_err("Discarded event %x to unopened pipe %x\n", event, pipe);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (hdev->ops->event_received) {
|
||||
r = hdev->ops->event_received(hdev, gate, event, skb);
|
||||
r = hdev->ops->event_received(hdev, pipe, event, skb);
|
||||
if (r <= 0)
|
||||
goto exit_noskb;
|
||||
}
|
||||
@ -573,7 +620,7 @@ static int hci_dev_down(struct nfc_dev *nfc_dev)
|
||||
if (hdev->ops->close)
|
||||
hdev->ops->close(hdev);
|
||||
|
||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
||||
nfc_hci_reset_pipes(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -932,7 +979,7 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
|
||||
|
||||
nfc_set_drvdata(hdev->ndev, hdev);
|
||||
|
||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
||||
nfc_hci_reset_pipes(hdev);
|
||||
|
||||
hdev->quirks = quirks;
|
||||
|
||||
|
@ -65,6 +65,14 @@ struct hci_create_pipe_resp {
|
||||
u8 pipe;
|
||||
} __packed;
|
||||
|
||||
struct hci_delete_pipe_noti {
|
||||
u8 pipe;
|
||||
} __packed;
|
||||
|
||||
struct hci_all_pipe_cleared_noti {
|
||||
u8 host;
|
||||
} __packed;
|
||||
|
||||
#define NFC_HCI_FRAGMENT 0x7f
|
||||
|
||||
#define HCP_HEADER(type, instr) ((((type) & 0x03) << 6) | ((instr) & 0x3f))
|
||||
@ -77,8 +85,6 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
|
||||
data_exchange_cb_t cb, void *cb_context,
|
||||
unsigned long completion_delay);
|
||||
|
||||
u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe);
|
||||
|
||||
void nfc_hci_hcp_message_rx(struct nfc_hci_dev *hdev, u8 pipe, u8 type,
|
||||
u8 instruction, struct sk_buff *skb);
|
||||
|
||||
|
@ -124,17 +124,6 @@ out_skb_err:
|
||||
return err;
|
||||
}
|
||||
|
||||
u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe)
|
||||
{
|
||||
int gate;
|
||||
|
||||
for (gate = 0; gate < NFC_HCI_MAX_GATES; gate++)
|
||||
if (hdev->gate2pipe[gate] == pipe)
|
||||
return gate;
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive hcp message for pipe, with type and cmd.
|
||||
* skb contains optional message data only.
|
||||
|
Loading…
Reference in New Issue
Block a user