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:
Коммит
b8f8be3f04
|
@ -1,7 +1,7 @@
|
||||||
* STMicroelectronics SAS. ST21NFCA NFC Controller
|
* STMicroelectronics SAS. ST21NFCA NFC Controller
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be "st,st21nfca_i2c".
|
- compatible: Should be "st,st21nfca-i2c".
|
||||||
- clock-frequency: I²C work frequency.
|
- clock-frequency: I²C work frequency.
|
||||||
- reg: address on the bus
|
- reg: address on the bus
|
||||||
- interrupt-parent: phandle for the interrupt gpio controller
|
- interrupt-parent: phandle for the interrupt gpio controller
|
||||||
|
@ -11,6 +11,10 @@ Required properties:
|
||||||
Optional SoC Specific Properties:
|
Optional SoC Specific Properties:
|
||||||
- pinctrl-names: Contains only one value - "default".
|
- pinctrl-names: Contains only one value - "default".
|
||||||
- pintctrl-0: Specifies the pin control groups used for this controller.
|
- 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):
|
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 {
|
st21nfca: st21nfca@1 {
|
||||||
|
|
||||||
compatible = "st,st21nfca_i2c";
|
compatible = "st,st21nfca-i2c";
|
||||||
|
|
||||||
reg = <0x01>;
|
reg = <0x01>;
|
||||||
clock-frequency = <400000>;
|
clock-frequency = <400000>;
|
||||||
|
@ -29,5 +33,8 @@ Example (for ARM-based BeagleBoard xM with ST21NFCA on I2C2):
|
||||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
|
||||||
enable-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
|
enable-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
|
||||||
|
|
||||||
|
ese-present;
|
||||||
|
uicc-present;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
* STMicroelectronics SAS. ST21NFCB NFC Controller
|
* STMicroelectronics SAS. ST21NFCB NFC Controller
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be "st,st21nfcb_i2c".
|
- compatible: Should be "st,st21nfcb-i2c".
|
||||||
- clock-frequency: I²C work frequency.
|
- clock-frequency: I²C work frequency.
|
||||||
- reg: address on the bus
|
- reg: address on the bus
|
||||||
- interrupt-parent: phandle for the interrupt gpio controller
|
- 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 {
|
st21nfcb: st21nfcb@8 {
|
||||||
|
|
||||||
compatible = "st,st21nfcb_i2c";
|
compatible = "st,st21nfcb-i2c";
|
||||||
|
|
||||||
reg = <0x08>;
|
reg = <0x08>;
|
||||||
clock-frequency = <400000>;
|
clock-frequency = <400000>;
|
||||||
|
|
|
@ -557,10 +557,11 @@ exit:
|
||||||
pr_err("Failed to handle discovered target err=%d\n", r);
|
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)
|
u8 event, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
u8 gate = hdev->pipes[pipe].gate;
|
||||||
u8 mode;
|
u8 mode;
|
||||||
|
|
||||||
pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate);
|
pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate);
|
||||||
|
|
|
@ -24,11 +24,13 @@
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/of_irq.h>
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
#include <linux/miscdevice.h>
|
#include <linux/miscdevice.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/nfc.h>
|
#include <linux/nfc.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/platform_data/pn544.h>
|
#include <linux/platform_data/pn544.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
@ -41,6 +43,11 @@
|
||||||
#define PN544_I2C_FRAME_HEADROOM 1
|
#define PN544_I2C_FRAME_HEADROOM 1
|
||||||
#define PN544_I2C_FRAME_TAILROOM 2
|
#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 */
|
/* framing in HCI mode */
|
||||||
#define PN544_HCI_I2C_LLC_LEN 1
|
#define PN544_HCI_I2C_LLC_LEN 1
|
||||||
#define PN544_HCI_I2C_LLC_CRC 2
|
#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);
|
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"
|
#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");
|
nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n");
|
||||||
|
|
||||||
/* Disable fw download */
|
/* Disable fw download */
|
||||||
gpio_set_value(phy->gpio_fw, 0);
|
gpio_set_value_cansleep(phy->gpio_fw, 0);
|
||||||
|
|
||||||
for (polarity = 0; polarity < 2; polarity++) {
|
for (polarity = 0; polarity < 2; polarity++) {
|
||||||
phy->en_polarity = polarity;
|
phy->en_polarity = polarity;
|
||||||
retry = 3;
|
retry = 3;
|
||||||
while (retry--) {
|
while (retry--) {
|
||||||
/* power off */
|
/* 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);
|
usleep_range(10000, 15000);
|
||||||
|
|
||||||
/* power on */
|
/* 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);
|
usleep_range(10000, 15000);
|
||||||
|
|
||||||
/* send reset */
|
/* 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");
|
"Could not detect nfc_en polarity, fallback to active high\n");
|
||||||
|
|
||||||
out:
|
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)
|
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_cansleep(phy->gpio_fw,
|
||||||
gpio_set_value(phy->gpio_en, phy->en_polarity);
|
run_mode == PN544_FW_MODE ? 1 : 0);
|
||||||
|
gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity);
|
||||||
usleep_range(10000, 15000);
|
usleep_range(10000, 15000);
|
||||||
|
|
||||||
phy->run_mode = run_mode;
|
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;
|
struct pn544_i2c_phy *phy = phy_id;
|
||||||
|
|
||||||
gpio_set_value(phy->gpio_fw, 0);
|
gpio_set_value_cansleep(phy->gpio_fw, 0);
|
||||||
gpio_set_value(phy->gpio_en, !phy->en_polarity);
|
gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity);
|
||||||
usleep_range(10000, 15000);
|
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);
|
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);
|
usleep_range(10000, 15000);
|
||||||
|
|
||||||
phy->powered = 0;
|
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
|
#ifdef CONFIG_OF
|
||||||
|
|
||||||
static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
|
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;
|
phy->gpio_en = ret;
|
||||||
|
|
||||||
/* Configuration of EN GPIO */
|
/* Configuration of EN GPIO */
|
||||||
ret = gpio_request(phy->gpio_en, "pn544_en");
|
ret = gpio_request(phy->gpio_en, PN544_GPIO_NAME_EN);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
nfc_err(&client->dev, "Fail EN pin\n");
|
nfc_err(&client->dev, "Fail EN pin\n");
|
||||||
goto err_dt;
|
goto err_dt;
|
||||||
|
@ -906,7 +1006,7 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
|
||||||
phy->gpio_fw = ret;
|
phy->gpio_fw = ret;
|
||||||
|
|
||||||
/* Configuration of FW GPIO */
|
/* Configuration of FW GPIO */
|
||||||
ret = gpio_request(phy->gpio_fw, "pn544_fw");
|
ret = gpio_request(phy->gpio_fw, PN544_GPIO_NAME_FW);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
nfc_err(&client->dev, "Fail FW pin\n");
|
nfc_err(&client->dev, "Fail FW pin\n");
|
||||||
goto err_gpio_en;
|
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_en = pdata->get_gpio(NFC_GPIO_ENABLE);
|
||||||
phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
|
phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
|
||||||
phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
|
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 {
|
} else {
|
||||||
nfc_err(&client->dev, "No platform data\n");
|
nfc_err(&client->dev, "No platform data\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1080,6 +1188,7 @@ static struct i2c_driver pn544_hci_i2c_driver = {
|
||||||
.name = PN544_HCI_I2C_DRIVER_NAME,
|
.name = PN544_HCI_I2C_DRIVER_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.of_match_table = of_match_ptr(of_pn544_i2c_match),
|
.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,
|
.probe = pn544_hci_i2c_probe,
|
||||||
.id_table = pn544_hci_i2c_id_table,
|
.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
|
* <= 0: driver handled the event, skb consumed
|
||||||
* 1: driver does not handle the event, please do standard processing
|
* 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 *skb)
|
||||||
{
|
{
|
||||||
struct sk_buff *rgb_skb = NULL;
|
struct sk_buff *rgb_skb = NULL;
|
||||||
|
u8 gate = hdev->pipes[pipe].gate;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
pr_debug("hci event %d\n", event);
|
pr_debug("hci event %d\n", event);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Makefile for ST21NFCA HCI based NFC driver
|
# 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
|
obj-$(CONFIG_NFC_ST21NFCA) += st21nfca_hci.o
|
||||||
|
|
||||||
st21nfca_i2c-objs = i2c.o
|
st21nfca_i2c-objs = i2c.o
|
||||||
|
|
|
@ -74,6 +74,8 @@ struct st21nfca_i2c_phy {
|
||||||
unsigned int gpio_ena;
|
unsigned int gpio_ena;
|
||||||
unsigned int irq_polarity;
|
unsigned int irq_polarity;
|
||||||
|
|
||||||
|
struct st21nfca_se_status se_status;
|
||||||
|
|
||||||
struct sk_buff *pending_skb;
|
struct sk_buff *pending_skb;
|
||||||
int current_read_len;
|
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->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;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#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;
|
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),
|
phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!phy) {
|
if (!phy)
|
||||||
nfc_err(&client->dev,
|
|
||||||
"Cannot allocate memory for st21nfca i2c phy.\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
phy->i2c_dev = client;
|
phy->i2c_dev = client;
|
||||||
phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
|
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,
|
return st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
|
||||||
ST21NFCA_FRAME_HEADROOM, ST21NFCA_FRAME_TAILROOM,
|
ST21NFCA_FRAME_HEADROOM,
|
||||||
ST21NFCA_HCI_LLC_MAX_PAYLOAD, &phy->hdev);
|
ST21NFCA_FRAME_TAILROOM,
|
||||||
|
ST21NFCA_HCI_LLC_MAX_PAYLOAD,
|
||||||
|
&phy->hdev,
|
||||||
|
&phy->se_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int st21nfca_hci_i2c_remove(struct i2c_client *client)
|
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
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id of_st21nfca_i2c_match[] = {
|
static const struct of_device_id of_st21nfca_i2c_match[] = {
|
||||||
|
{ .compatible = "st,st21nfca-i2c", },
|
||||||
{ .compatible = "st,st21nfca_i2c", },
|
{ .compatible = "st,st21nfca_i2c", },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include "st21nfca.h"
|
#include "st21nfca.h"
|
||||||
#include "st21nfca_dep.h"
|
#include "st21nfca_dep.h"
|
||||||
|
#include "st21nfca_se.h"
|
||||||
|
|
||||||
#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
|
#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 0x08
|
||||||
#define ST21NFCA_RF_CARD_F_DATARATE_212_424 0x01
|
#define ST21NFCA_RF_CARD_F_DATARATE_212_424 0x01
|
||||||
|
|
||||||
#define ST21NFCA_DEVICE_MGNT_GATE 0x01
|
|
||||||
#define ST21NFCA_DEVICE_MGNT_PIPE 0x02
|
#define ST21NFCA_DEVICE_MGNT_PIPE 0x02
|
||||||
|
|
||||||
#define ST21NFCA_DM_GETINFO 0x13
|
#define ST21NFCA_DM_GETINFO 0x13
|
||||||
|
@ -78,6 +78,11 @@
|
||||||
|
|
||||||
#define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/
|
#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 DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
|
||||||
|
|
||||||
static struct nfc_hci_gate st21nfca_gates[] = {
|
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_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
|
||||||
{ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
|
{ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
|
||||||
{ST21NFCA_RF_CARD_F_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 {
|
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
|
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
|
/* On ST21NFCA device pipes number are dynamics
|
||||||
* A maximum of 16 pipes can be created at the same time
|
* A maximum of 16 pipes can be created at the same time
|
||||||
* If pipes are already created, hci_dev_up will fail.
|
* 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.
|
* Pipe can be closed and need to be open.
|
||||||
*/
|
*/
|
||||||
r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID,
|
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)
|
if (r < 0)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
|
|
||||||
|
@ -179,17 +177,28 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
|
||||||
* - destination gid (1byte)
|
* - destination gid (1byte)
|
||||||
*/
|
*/
|
||||||
info = (struct st21nfca_pipe_info *) skb_pipe_info->data;
|
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)) &&
|
for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) &&
|
||||||
(st21nfca_gates[j].gate != info->dst_gate_id);
|
(st21nfca_gates[j].gate != info->dst_gate_id) ; j++)
|
||||||
j++)
|
|
||||||
;
|
;
|
||||||
|
|
||||||
if (j < ARRAY_SIZE(st21nfca_gates) &&
|
if (j < ARRAY_SIZE(st21nfca_gates) &&
|
||||||
st21nfca_gates[j].gate == info->dst_gate_id &&
|
st21nfca_gates[j].gate == info->dst_gate_id &&
|
||||||
ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) {
|
ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) {
|
||||||
st21nfca_gates[j].pipe = pipe_info[2];
|
st21nfca_gates[j].pipe = pipe_info[2];
|
||||||
|
|
||||||
hdev->gate2pipe[st21nfca_gates[j].gate] =
|
hdev->gate2pipe[st21nfca_gates[j].gate] =
|
||||||
st21nfca_gates[j].pipe;
|
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)) {
|
if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) {
|
||||||
for (i = skb_pipe_list->len + 3;
|
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,
|
r = nfc_hci_connect_gate(hdev,
|
||||||
NFC_HCI_HOST_CONTROLLER_ID,
|
NFC_HCI_HOST_CONTROLLER_ID,
|
||||||
st21nfca_gates[i].gate,
|
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));
|
memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
|
||||||
free_info:
|
free_info:
|
||||||
kfree_skb(skb_pipe_info);
|
kfree_skb(skb_pipe_info);
|
||||||
free_list:
|
|
||||||
kfree_skb(skb_pipe_list);
|
kfree_skb(skb_pipe_list);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -257,16 +265,33 @@ out:
|
||||||
|
|
||||||
static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
|
static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
|
||||||
{
|
{
|
||||||
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
|
||||||
u8 param;
|
u8 param;
|
||||||
|
u8 white_list[2];
|
||||||
|
int wl_size = 0;
|
||||||
int r;
|
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,
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set NFC_MODE in device management gate to enable */
|
/* Set NFC_MODE in device management gate to enable */
|
||||||
r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (skb->data[0] == 0) {
|
param = skb->data[0];
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
if (param == 0) {
|
||||||
param = 1;
|
param = 1;
|
||||||
|
|
||||||
r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
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,
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
||||||
ST21NFCA_RF_CARD_F_DATARATE,
|
ST21NFCA_RF_CARD_F_DATARATE,
|
||||||
param, 1);
|
param, 1);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
|
kfree_skb(datarate_skb);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
kfree_skb(datarate_skb);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configure sens_res
|
* Configure sens_res
|
||||||
|
@ -673,15 +702,15 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||||
struct nfc_target *target)
|
struct nfc_target *target)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
struct sk_buff *nfcid2_skb = NULL, *nfcid1_skb;
|
struct sk_buff *nfcid_skb = NULL;
|
||||||
|
|
||||||
if (gate == ST21NFCA_RF_READER_F_GATE) {
|
if (gate == ST21NFCA_RF_READER_F_GATE) {
|
||||||
r = nfc_hci_get_param(hdev, 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)
|
if (r < 0)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
if (nfcid2_skb->len > NFC_SENSF_RES_MAXSIZE) {
|
if (nfcid_skb->len > NFC_SENSF_RES_MAXSIZE) {
|
||||||
r = -EPROTO;
|
r = -EPROTO;
|
||||||
goto exit;
|
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
|
* - After the reception of SEL_RES with NFCIP-1 compliant bit
|
||||||
* set for type A frame NFCID1 will be updated
|
* set for type A frame NFCID1 will be updated
|
||||||
*/
|
*/
|
||||||
if (nfcid2_skb->len > 0) {
|
if (nfcid_skb->len > 0) {
|
||||||
/* P2P in type F */
|
/* P2P in type F */
|
||||||
memcpy(target->sensf_res, nfcid2_skb->data,
|
memcpy(target->sensf_res, nfcid_skb->data,
|
||||||
nfcid2_skb->len);
|
nfcid_skb->len);
|
||||||
target->sensf_res_len = nfcid2_skb->len;
|
target->sensf_res_len = nfcid_skb->len;
|
||||||
/* NFC Forum Digital Protocol Table 44 */
|
/* NFC Forum Digital Protocol Table 44 */
|
||||||
if (target->sensf_res[0] == 0x01 &&
|
if (target->sensf_res[0] == 0x01 &&
|
||||||
target->sensf_res[1] == 0xfe)
|
target->sensf_res[1] == 0xfe)
|
||||||
|
@ -707,27 +736,28 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
||||||
target->supported_protocols =
|
target->supported_protocols =
|
||||||
NFC_PROTO_FELICA_MASK;
|
NFC_PROTO_FELICA_MASK;
|
||||||
} else {
|
} else {
|
||||||
|
kfree_skb(nfcid_skb);
|
||||||
/* P2P in type A */
|
/* P2P in type A */
|
||||||
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
|
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
|
||||||
ST21NFCA_RF_READER_F_NFCID1,
|
ST21NFCA_RF_READER_F_NFCID1,
|
||||||
&nfcid1_skb);
|
&nfcid_skb);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
if (nfcid1_skb->len > NFC_NFCID1_MAXSIZE) {
|
if (nfcid_skb->len > NFC_NFCID1_MAXSIZE) {
|
||||||
r = -EPROTO;
|
r = -EPROTO;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
memcpy(target->sensf_res, nfcid1_skb->data,
|
memcpy(target->sensf_res, nfcid_skb->data,
|
||||||
nfcid1_skb->len);
|
nfcid_skb->len);
|
||||||
target->sensf_res_len = nfcid1_skb->len;
|
target->sensf_res_len = nfcid_skb->len;
|
||||||
target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
|
target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
|
||||||
}
|
}
|
||||||
target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE;
|
target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE;
|
||||||
}
|
}
|
||||||
r = 1;
|
r = 1;
|
||||||
exit:
|
exit:
|
||||||
kfree_skb(nfcid2_skb);
|
kfree_skb(nfcid_skb);
|
||||||
return r;
|
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:
|
* Returns:
|
||||||
* <= 0: driver handled the event, skb consumed
|
* <= 0: driver handled the event, skb consumed
|
||||||
* 1: driver does not handle the event, please do standard processing
|
* 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 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);
|
pr_debug("hci event: %d gate: %x\n", event, gate);
|
||||||
|
|
||||||
switch (gate) {
|
switch (gate) {
|
||||||
|
case NFC_HCI_ADMIN_GATE:
|
||||||
|
return st21nfca_admin_event_received(hdev, event, skb);
|
||||||
case ST21NFCA_RF_CARD_F_GATE:
|
case ST21NFCA_RF_CARD_F_GATE:
|
||||||
return st21nfca_dep_event_received(hdev, event, skb);
|
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:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
kfree_skb(skb);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nfc_hci_ops st21nfca_hci_ops = {
|
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,
|
.tm_send = st21nfca_hci_tm_send,
|
||||||
.check_presence = st21nfca_hci_check_presence,
|
.check_presence = st21nfca_hci_check_presence,
|
||||||
.event_received = st21nfca_hci_event_received,
|
.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,
|
int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
|
||||||
char *llc_name, int phy_headroom, int phy_tailroom,
|
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;
|
struct st21nfca_hci_info *info;
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
@ -929,6 +1023,8 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
|
||||||
goto err_alloc_hdev;
|
goto err_alloc_hdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info->se_status = se_status;
|
||||||
|
|
||||||
nfc_hci_set_clientdata(info->hdev, info);
|
nfc_hci_set_clientdata(info->hdev, info);
|
||||||
|
|
||||||
r = nfc_hci_register_device(info->hdev);
|
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;
|
*hdev = info->hdev;
|
||||||
st21nfca_dep_init(info->hdev);
|
st21nfca_dep_init(info->hdev);
|
||||||
|
st21nfca_se_init(info->hdev);
|
||||||
|
|
||||||
return 0;
|
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);
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
||||||
|
|
||||||
st21nfca_dep_deinit(hdev);
|
st21nfca_dep_deinit(hdev);
|
||||||
|
st21nfca_se_deinit(hdev);
|
||||||
nfc_hci_unregister_device(hdev);
|
nfc_hci_unregister_device(hdev);
|
||||||
nfc_hci_free_device(hdev);
|
nfc_hci_free_device(hdev);
|
||||||
kfree(info);
|
kfree(info);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <net/nfc/hci.h>
|
#include <net/nfc/hci.h>
|
||||||
|
|
||||||
#include "st21nfca_dep.h"
|
#include "st21nfca_dep.h"
|
||||||
|
#include "st21nfca_se.h"
|
||||||
|
|
||||||
#define HCI_MODE 0
|
#define HCI_MODE 0
|
||||||
|
|
||||||
|
@ -51,9 +52,15 @@
|
||||||
|
|
||||||
#define ST21NFCA_NUM_DEVICES 256
|
#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,
|
int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
|
||||||
char *llc_name, int phy_headroom, int phy_tailroom,
|
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);
|
void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
|
||||||
|
|
||||||
enum st21nfca_state {
|
enum st21nfca_state {
|
||||||
|
@ -66,6 +73,7 @@ struct st21nfca_hci_info {
|
||||||
void *phy_id;
|
void *phy_id;
|
||||||
|
|
||||||
struct nfc_hci_dev *hdev;
|
struct nfc_hci_dev *hdev;
|
||||||
|
struct st21nfca_se_status *se_status;
|
||||||
|
|
||||||
enum st21nfca_state state;
|
enum st21nfca_state state;
|
||||||
|
|
||||||
|
@ -76,13 +84,16 @@ struct st21nfca_hci_info {
|
||||||
void *async_cb_context;
|
void *async_cb_context;
|
||||||
|
|
||||||
struct st21nfca_dep_info dep_info;
|
struct st21nfca_dep_info dep_info;
|
||||||
|
struct st21nfca_se_info se_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Reader RF commands */
|
/* Reader RF commands */
|
||||||
#define ST21NFCA_WR_XCHG_DATA 0x10
|
#define ST21NFCA_WR_XCHG_DATA 0x10
|
||||||
|
|
||||||
|
#define ST21NFCA_DEVICE_MGNT_GATE 0x01
|
||||||
#define ST21NFCA_RF_READER_F_GATE 0x14
|
#define ST21NFCA_RF_READER_F_GATE 0x14
|
||||||
|
|
||||||
#define ST21NFCA_RF_CARD_F_GATE 0x24
|
#define ST21NFCA_RF_CARD_F_GATE 0x24
|
||||||
|
#define ST21NFCA_APDU_READER_GATE 0xf0
|
||||||
|
#define ST21NFCA_CONNECTIVITY_GATE 0x41
|
||||||
|
|
||||||
#endif /* __LOCAL_ST21NFCA_H_ */
|
#endif /* __LOCAL_ST21NFCA_H_ */
|
||||||
|
|
|
@ -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);
|
|
@ -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;
|
struct sk_buff *skb = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (!phy || irq != phy->i2c_dev->irq) {
|
if (!phy || !phy->ndlc || irq != phy->i2c_dev->irq) {
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
@ -343,18 +343,22 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client,
|
||||||
return -ENODEV;
|
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,
|
r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||||
st21nfcb_nci_irq_thread_fn,
|
st21nfcb_nci_irq_thread_fn,
|
||||||
phy->irq_polarity | IRQF_ONESHOT,
|
phy->irq_polarity | IRQF_ONESHOT,
|
||||||
ST21NFCB_NCI_DRIVER_NAME, phy);
|
ST21NFCB_NCI_DRIVER_NAME, phy);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
nfc_err(&client->dev, "Unable to register IRQ handler\n");
|
nfc_err(&client->dev, "Unable to register IRQ handler\n");
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ndlc_probe(phy, &i2c_phy_ops, &client->dev,
|
return r;
|
||||||
ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM,
|
|
||||||
&phy->ndlc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int st21nfcb_nci_i2c_remove(struct i2c_client *client)
|
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
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id of_st21nfcb_i2c_match[] = {
|
static const struct of_device_id of_st21nfcb_i2c_match[] = {
|
||||||
|
{ .compatible = "st,st21nfcb-i2c", },
|
||||||
{ .compatible = "st,st21nfcb_i2c", },
|
{ .compatible = "st,st21nfcb_i2c", },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -138,7 +138,7 @@ static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc)
|
||||||
default:
|
default:
|
||||||
pr_err("UNKNOWN Packet Control Byte=%d\n", pcb);
|
pr_err("UNKNOWN Packet Control Byte=%d\n", pcb);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
skb_queue_head(&ndlc->send_q, skb);
|
skb_queue_head(&ndlc->send_q, skb);
|
||||||
}
|
}
|
||||||
|
@ -297,6 +297,5 @@ void ndlc_remove(struct llt_ndlc *ndlc)
|
||||||
skb_queue_purge(&ndlc->send_q);
|
skb_queue_purge(&ndlc->send_q);
|
||||||
|
|
||||||
st21nfcb_nci_remove(ndlc->ndev);
|
st21nfcb_nci_remove(ndlc->ndev);
|
||||||
kfree(ndlc);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ndlc_remove);
|
EXPORT_SYMBOL(ndlc_remove);
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
struct st21nfca_nfc_platform_data {
|
struct st21nfca_nfc_platform_data {
|
||||||
unsigned int gpio_ena;
|
unsigned int gpio_ena;
|
||||||
unsigned int irq_polarity;
|
unsigned int irq_polarity;
|
||||||
|
bool is_ese_present;
|
||||||
|
bool is_uicc_present;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _ST21NFCA_HCI_H_ */
|
#endif /* _ST21NFCA_HCI_H_ */
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
#ifndef _ST21NFCB_NCI_H_
|
#ifndef _ST21NFCB_NCI_H_
|
||||||
#define _ST21NFCB_NCI_H_
|
#define _ST21NFCB_NCI_H_
|
||||||
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
|
|
||||||
#define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci"
|
#define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci"
|
||||||
|
|
||||||
struct st21nfcb_nfc_platform_data {
|
struct st21nfcb_nfc_platform_data {
|
||||||
|
@ -28,4 +26,4 @@ struct st21nfcb_nfc_platform_data {
|
||||||
unsigned int irq_polarity;
|
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 (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb);
|
||||||
int (*check_presence)(struct nfc_hci_dev *hdev,
|
int (*check_presence)(struct nfc_hci_dev *hdev,
|
||||||
struct nfc_target *target);
|
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);
|
struct sk_buff *skb);
|
||||||
int (*fw_download)(struct nfc_hci_dev *hdev, const char *firmware_name);
|
int (*fw_download)(struct nfc_hci_dev *hdev, const char *firmware_name);
|
||||||
int (*discover_se)(struct nfc_hci_dev *dev);
|
int (*discover_se)(struct nfc_hci_dev *dev);
|
||||||
|
@ -63,8 +65,10 @@ struct nfc_hci_ops {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Pipes */
|
/* Pipes */
|
||||||
#define NFC_HCI_INVALID_PIPE 0x80
|
|
||||||
#define NFC_HCI_DO_NOT_CREATE_PIPE 0x81
|
#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_LINK_MGMT_PIPE 0x00
|
||||||
#define NFC_HCI_ADMIN_PIPE 0x01
|
#define NFC_HCI_ADMIN_PIPE 0x01
|
||||||
|
|
||||||
|
@ -73,7 +77,13 @@ struct nfc_hci_gate {
|
||||||
u8 pipe;
|
u8 pipe;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct nfc_hci_pipe {
|
||||||
|
u8 gate;
|
||||||
|
u8 dest_host;
|
||||||
|
};
|
||||||
|
|
||||||
#define NFC_HCI_MAX_CUSTOM_GATES 50
|
#define NFC_HCI_MAX_CUSTOM_GATES 50
|
||||||
|
#define NFC_HCI_MAX_PIPES 127
|
||||||
struct nfc_hci_init_data {
|
struct nfc_hci_init_data {
|
||||||
u8 gate_count;
|
u8 gate_count;
|
||||||
struct nfc_hci_gate gates[NFC_HCI_MAX_CUSTOM_GATES];
|
struct nfc_hci_gate gates[NFC_HCI_MAX_CUSTOM_GATES];
|
||||||
|
@ -125,6 +135,7 @@ struct nfc_hci_dev {
|
||||||
void *clientdata;
|
void *clientdata;
|
||||||
|
|
||||||
u8 gate2pipe[NFC_HCI_MAX_GATES];
|
u8 gate2pipe[NFC_HCI_MAX_GATES];
|
||||||
|
struct nfc_hci_pipe pipes[NFC_HCI_MAX_PIPES];
|
||||||
|
|
||||||
u8 sw_romlib;
|
u8 sw_romlib;
|
||||||
u8 sw_patch;
|
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);
|
void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err);
|
||||||
|
|
||||||
int nfc_hci_result_to_errno(u8 result);
|
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 */
|
/* Host IDs */
|
||||||
#define NFC_HCI_HOST_CONTROLLER_ID 0x00
|
#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_POST_DATA 0x02
|
||||||
#define NFC_HCI_EVT_HOT_PLUG 0x03
|
#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 */
|
/* Reader RF gates events */
|
||||||
#define NFC_HCI_EVT_READER_REQUESTED 0x10
|
#define NFC_HCI_EVT_READER_REQUESTED 0x10
|
||||||
#define NFC_HCI_EVT_END_OPERATION 0x11
|
#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,
|
int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
|
||||||
const u8 *param, size_t param_len,
|
const u8 *param, size_t param_len,
|
||||||
data_exchange_cb_t cb, void *cb_context);
|
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,
|
int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
|
||||||
const u8 *param, size_t param_len);
|
const u8 *param, size_t param_len);
|
||||||
int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate);
|
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)
|
int nfc_enable_se(struct nfc_dev *dev, u32 se_idx)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct nfc_se *se;
|
struct nfc_se *se;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
@ -605,7 +604,6 @@ error:
|
||||||
|
|
||||||
int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
|
int nfc_disable_se(struct nfc_dev *dev, u32 se_idx)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct nfc_se *se;
|
struct nfc_se *se;
|
||||||
int rc;
|
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);
|
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.
|
* Execute an hci command sent to gate.
|
||||||
* skb will contain response data if success. skb can be NULL if you are not
|
* 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)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
nfc_hci_reset_pipes(hdev);
|
||||||
|
|
||||||
return 0;
|
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");
|
pr_debug("\n");
|
||||||
|
|
||||||
if (hdev->gate2pipe[dest_gate] == NFC_HCI_DO_NOT_CREATE_PIPE)
|
if (pipe == NFC_HCI_DO_NOT_CREATE_PIPE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
|
if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
|
||||||
|
@ -380,6 +363,8 @@ open_pipe:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hdev->pipes[pipe].gate = dest_gate;
|
||||||
|
hdev->pipes[pipe].dest_host = dest_host;
|
||||||
hdev->gate2pipe[dest_gate] = pipe;
|
hdev->gate2pipe[dest_gate] = pipe;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -46,6 +46,32 @@ int nfc_hci_result_to_errno(u8 result)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(nfc_hci_result_to_errno);
|
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)
|
static void nfc_hci_msg_tx_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct nfc_hci_dev *hdev = container_of(work, struct nfc_hci_dev,
|
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,
|
void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
int r = 0;
|
u8 gate = hdev->pipes[pipe].gate;
|
||||||
u8 gate = nfc_hci_pipe2gate(hdev, pipe);
|
u8 status = NFC_HCI_ANY_OK;
|
||||||
u8 local_gate, new_pipe;
|
struct hci_create_pipe_resp *create_info;
|
||||||
u8 gate_opened = 0x00;
|
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);
|
pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case NFC_HCI_ADM_NOTIFY_PIPE_CREATED:
|
case NFC_HCI_ADM_NOTIFY_PIPE_CREATED:
|
||||||
if (skb->len != 5) {
|
if (skb->len != 5) {
|
||||||
r = -EPROTO;
|
status = NFC_HCI_ANY_E_NOK;
|
||||||
break;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
create_info = (struct hci_create_pipe_resp *)skb->data;
|
||||||
|
|
||||||
local_gate = skb->data[3];
|
/* Save the new created pipe and bind with local gate,
|
||||||
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,
|
|
||||||
* the description for skb->data[3] is destination gate id
|
* the description for skb->data[3] is destination gate id
|
||||||
* but since we received this cmd from host controller, we
|
* but since we received this cmd from host controller, we
|
||||||
* are the destination and it is our local gate
|
* 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;
|
break;
|
||||||
case NFC_HCI_ANY_OPEN_PIPE:
|
case NFC_HCI_ANY_OPEN_PIPE:
|
||||||
/* if the pipe is already created, we allow remote host to
|
if (gate == NFC_HCI_INVALID_GATE) {
|
||||||
* open it
|
status = NFC_HCI_ANY_E_NOK;
|
||||||
*/
|
goto exit;
|
||||||
if (gate != 0xff)
|
}
|
||||||
nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK,
|
break;
|
||||||
&gate_opened, 1);
|
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;
|
break;
|
||||||
case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate);
|
pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate);
|
||||||
r = -EINVAL;
|
|
||||||
break;
|
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);
|
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)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
int r = 0;
|
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);
|
pr_err("Discarded event %x to unopened pipe %x\n", event, pipe);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdev->ops->event_received) {
|
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)
|
if (r <= 0)
|
||||||
goto exit_noskb;
|
goto exit_noskb;
|
||||||
}
|
}
|
||||||
|
@ -573,7 +620,7 @@ static int hci_dev_down(struct nfc_dev *nfc_dev)
|
||||||
if (hdev->ops->close)
|
if (hdev->ops->close)
|
||||||
hdev->ops->close(hdev);
|
hdev->ops->close(hdev);
|
||||||
|
|
||||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
nfc_hci_reset_pipes(hdev);
|
||||||
|
|
||||||
return 0;
|
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);
|
nfc_set_drvdata(hdev->ndev, hdev);
|
||||||
|
|
||||||
memset(hdev->gate2pipe, NFC_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
|
nfc_hci_reset_pipes(hdev);
|
||||||
|
|
||||||
hdev->quirks = quirks;
|
hdev->quirks = quirks;
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,14 @@ struct hci_create_pipe_resp {
|
||||||
u8 pipe;
|
u8 pipe;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct hci_delete_pipe_noti {
|
||||||
|
u8 pipe;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct hci_all_pipe_cleared_noti {
|
||||||
|
u8 host;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#define NFC_HCI_FRAGMENT 0x7f
|
#define NFC_HCI_FRAGMENT 0x7f
|
||||||
|
|
||||||
#define HCP_HEADER(type, instr) ((((type) & 0x03) << 6) | ((instr) & 0x3f))
|
#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,
|
data_exchange_cb_t cb, void *cb_context,
|
||||||
unsigned long completion_delay);
|
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,
|
void nfc_hci_hcp_message_rx(struct nfc_hci_dev *hdev, u8 pipe, u8 type,
|
||||||
u8 instruction, struct sk_buff *skb);
|
u8 instruction, struct sk_buff *skb);
|
||||||
|
|
||||||
|
|
|
@ -124,17 +124,6 @@ out_skb_err:
|
||||||
return 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.
|
* Receive hcp message for pipe, with type and cmd.
|
||||||
* skb contains optional message data only.
|
* skb contains optional message data only.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче