Commit 023033b1 authored by David S. Miller's avatar David S. Miller

Merge tag 'nfc-next-4.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/nfc-next

Samuel Ortiz says:

====================
NFC 4.2 pull request

This is the NFC pull request for 4.2.

- NCI drivers can now define their own handlers for processing
  proprietary NCI responses and notifications.

- NFC vendors can use a dedicated netlink API to send their own
  proprietary commands, like e.g. all commands needed to implement
  vendor specific manufacturing tools.

- A new generic NCI over UART driver against which any NCI chipset
  running on top of a serial interface can register.

- The st21nfcb driver is renamed to st-nci as it can and will support
  most of ST Microelectronics NCI chipsets.

- The st21nfcb driver can put its CLF in hibernate mode and save
  significant amount of power.

- A few st21nfcb minor fixes.

- The NXP NCI driver now supports ACPI enumeration.

- The Marvell NCI driver now supports both USB and serial
  physical interfaces.

- The Marvell NCI drivers also supports NCI frames being muxed
  over HCI. This is a setting that can be defined by a DT property.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents cadfaf43 d0dcad8b
* Marvell International Ltd. NCI NFC Controller
Required properties:
- compatible: Should be "mrvl,nfc-uart".
Optional SoC specific properties:
- pinctrl-names: Contains only one value - "default".
- pintctrl-0: Specifies the pin control groups used for this controller.
- reset-n-io: Output GPIO pin used to reset the chip (active low).
- hci-muxed: Specifies that the chip is muxing NCI over HCI frames.
Optional UART-based chip specific properties:
- flow-control: Specifies that the chip is using RTS/CTS.
- break-control: Specifies that the chip needs specific break management.
Example (for ARM-based BeagleBoard Black with 88W8887 on UART5):
&uart5 {
status = "okay";
nfcmrvluart: nfcmrvluart@5 {
compatible = "mrvl,nfc-uart";
reset-n-io = <&gpio3 16 0>;
hci-muxed;
flow-control;
}
};
* STMicroelectronics SAS. ST21NFCB NFC Controller
* STMicroelectronics SAS. ST NCI NFC Controller
Required properties:
- compatible: Should be "st,st21nfcb-i2c".
- compatible: Should be "st,st21nfcb-i2c" or "st,st21nfcc-i2c".
- clock-frequency: I²C work frequency.
- reg: address on the bus
- interrupt-parent: phandle for the interrupt gpio controller
......
......@@ -18,6 +18,9 @@ Optional SoC Specific Properties:
"IRQ Status Read" erratum.
- en2-rf-quirk: Specify that the trf7970a being used has the "EN2 RF"
erratum.
- t5t-rmb-extra-byte-quirk: Specify that the trf7970a has the erratum
where an extra byte is returned by Read Multiple Block commands issued
to Type 5 tags.
Example (for ARM-based BeagleBone with TRF7970A on SPI1):
......@@ -39,6 +42,7 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1):
autosuspend-delay = <30000>;
irq-status-read-quirk;
en2-rf-quirk;
t5t-rmb-extra-byte-quirk;
status = "okay";
};
};
......@@ -122,7 +122,7 @@ This must be done from a context that can sleep.
PHY Management
--------------
The physical link (i2c, ...) management is defined by the following struture:
The physical link (i2c, ...) management is defined by the following structure:
struct nfc_phy_ops {
int (*write)(void *dev_id, struct sk_buff *skb);
......
......@@ -72,6 +72,6 @@ source "drivers/nfc/pn544/Kconfig"
source "drivers/nfc/microread/Kconfig"
source "drivers/nfc/nfcmrvl/Kconfig"
source "drivers/nfc/st21nfca/Kconfig"
source "drivers/nfc/st21nfcb/Kconfig"
source "drivers/nfc/st-nci/Kconfig"
source "drivers/nfc/nxp-nci/Kconfig"
endmenu
......@@ -12,7 +12,5 @@ obj-$(CONFIG_NFC_PORT100) += port100.o
obj-$(CONFIG_NFC_MRVL) += nfcmrvl/
obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o
obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/
obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/
obj-$(CONFIG_NFC_ST_NCI) += st-nci/
obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
......@@ -211,7 +211,6 @@ static int microread_i2c_read(struct microread_i2c_phy *phy,
static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id)
{
struct microread_i2c_phy *phy = phy_id;
struct i2c_client *client;
struct sk_buff *skb = NULL;
int r;
......@@ -220,8 +219,6 @@ static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id)
return IRQ_NONE;
}
client = phy->i2c_dev;
if (phy->hard_fault != 0)
return IRQ_HANDLED;
......
......@@ -21,3 +21,14 @@ config NFC_MRVL_USB
Say Y here to compile support for Marvell NFC-over-USB driver
into the kernel or say M to compile it as module.
config NFC_MRVL_UART
tristate "Marvell NFC-over-UART driver"
depends on NFC_MRVL && NFC_NCI_UART
help
Marvell NFC-over-UART driver.
This driver provides support for Marvell NFC-over-UART devices
Say Y here to compile support for Marvell NFC-over-UART driver
into the kernel or say M to compile it as module.
......@@ -7,3 +7,6 @@ obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o
nfcmrvl_usb-y += usb.o
obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o
nfcmrvl_uart-y += uart.o
obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o
......@@ -17,6 +17,9 @@
*/
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/nfc.h>
#include <net/nfc/nci.h>
#include <net/nfc/nci_core.h>
......@@ -63,20 +66,25 @@ static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
return -EBUSY;
if (priv->config.hci_muxed) {
unsigned char *hdr;
unsigned char len = skb->len;
hdr = (char *) skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
hdr[0] = NFCMRVL_HCI_COMMAND_CODE;
hdr[1] = NFCMRVL_HCI_OGF;
hdr[2] = NFCMRVL_HCI_OCF;
hdr[3] = len;
}
return priv->if_ops->nci_send(priv, skb);
}
static int nfcmrvl_nci_setup(struct nci_dev *ndev)
{
__u8 val;
val = NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED;
nci_set_config(ndev, NFCMRVL_NOT_ALLOWED_ID, 1, &val);
val = NFCMRVL_GPIO_PIN_NFC_ACTIVE;
nci_set_config(ndev, NFCMRVL_ACTIVE_ID, 1, &val);
val = NFCMRVL_EXT_COEX_ENABLE;
nci_set_config(ndev, NFCMRVL_EXT_COEX_ID, 1, &val);
__u8 val = 1;
nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val);
return 0;
}
......@@ -89,10 +97,12 @@ static struct nci_ops nfcmrvl_nci_ops = {
struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
struct nfcmrvl_if_ops *ops,
struct device *dev)
struct device *dev,
struct nfcmrvl_platform_data *pdata)
{
struct nfcmrvl_private *priv;
int rc;
int headroom = 0;
u32 protocols;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
......@@ -103,13 +113,30 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
priv->if_ops = ops;
priv->dev = dev;
memcpy(&priv->config, pdata, sizeof(*pdata));
if (priv->config.reset_n_io) {
rc = devm_gpio_request_one(dev,
priv->config.reset_n_io,
GPIOF_OUT_INIT_LOW,
"nfcmrvl_reset_n");
if (rc < 0)
nfc_err(dev, "failed to request reset_n io\n");
}
if (priv->config.hci_muxed)
headroom = NFCMRVL_HCI_EVENT_HEADER_SIZE;
protocols = NFC_PROTO_JEWEL_MASK
| NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
| NFC_PROTO_MIFARE_MASK
| NFC_PROTO_FELICA_MASK
| NFC_PROTO_ISO14443_MASK
| NFC_PROTO_ISO14443_B_MASK
| NFC_PROTO_ISO15693_MASK
| NFC_PROTO_NFC_DEP_MASK;
priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0);
priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
headroom, 0);
if (!priv->ndev) {
nfc_err(dev, "nci_allocate_device failed\n");
rc = -ENOMEM;
......@@ -118,6 +145,8 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
nci_set_drvdata(priv->ndev, priv);
nfcmrvl_chip_reset(priv);
rc = nci_register_device(priv->ndev);
if (rc) {
nfc_err(dev, "nci_register_device failed %d\n", rc);
......@@ -144,21 +173,84 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
}
EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count)
int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
{
struct sk_buff *skb;
skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
if (priv->config.hci_muxed) {
if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE &&
skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) {
/* Data packet, let's extract NCI payload */
skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
} else {
/* Skip this packet */
kfree_skb(skb);
return 0;
}
}
memcpy(skb_put(skb, count), data, count);
if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
nci_recv_frame(priv->ndev, skb);
else {
/* Drop this packet since nobody wants it */
kfree_skb(skb);
return 0;
}
return count;
return 0;
}
EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame);
void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
{
/*
* This function does not take care if someone is using the device.
* To be improved.
*/
if (priv->config.reset_n_io) {
nfc_info(priv->dev, "reset the chip\n");
gpio_set_value(priv->config.reset_n_io, 0);
usleep_range(5000, 10000);
gpio_set_value(priv->config.reset_n_io, 1);
} else
nfc_info(priv->dev, "no reset available on this interface\n");
}
#ifdef CONFIG_OF
int nfcmrvl_parse_dt(struct device_node *node,
struct nfcmrvl_platform_data *pdata)
{
int reset_n_io;
reset_n_io = of_get_named_gpio(node, "reset-n-io", 0);
if (reset_n_io < 0) {
pr_info("no reset-n-io config\n");
reset_n_io = 0;
} else if (!gpio_is_valid(reset_n_io)) {
pr_err("invalid reset-n-io GPIO\n");
return reset_n_io;
}
pdata->reset_n_io = reset_n_io;
if (of_find_property(node, "hci-muxed", NULL))
pdata->hci_muxed = 1;
else
pdata->hci_muxed = 0;
return 0;
}
#else
int nfcmrvl_parse_dt(struct device_node *node,
struct nfcmrvl_platform_data *pdata)
{
return -ENODEV;
}
#endif
EXPORT_SYMBOL_GPL(nfcmrvl_parse_dt);
MODULE_AUTHOR("Marvell International Ltd.");
MODULE_DESCRIPTION("Marvell NFC driver ver " VERSION);
MODULE_VERSION(VERSION);
......
......@@ -16,6 +16,11 @@
* this warranty disclaimer.
**/
#ifndef _NFCMRVL_H_
#define _NFCMRVL_H_
#include <linux/platform_data/nfcmrvl.h>
/* Define private flags: */
#define NFCMRVL_NCI_RUNNING 1
......@@ -27,11 +32,49 @@
#define NFCMRVL_GPIO_PIN_NFC_ACTIVE 0xB
#define NFCMRVL_NCI_MAX_EVENT_SIZE 260
/*
** NCI FW Parmaters
*/
#define NFCMRVL_PB_BAIL_OUT 0x11
/*
** HCI defines
*/
#define NFCMRVL_HCI_EVENT_HEADER_SIZE 0x04
#define NFCMRVL_HCI_EVENT_CODE 0x04
#define NFCMRVL_HCI_NFC_EVENT_CODE 0xFF
#define NFCMRVL_HCI_COMMAND_CODE 0x01
#define NFCMRVL_HCI_OGF 0x81
#define NFCMRVL_HCI_OCF 0xFE
enum nfcmrvl_phy {
NFCMRVL_PHY_USB = 0,
NFCMRVL_PHY_UART = 1,
};
struct nfcmrvl_private {
struct nci_dev *ndev;
unsigned long flags;
/* Platform configuration */
struct nfcmrvl_platform_data config;
struct nci_dev *ndev;
/*
** PHY related information
*/
/* PHY driver context */
void *drv_data;
/* PHY device */
struct device *dev;
/* PHY type */
enum nfcmrvl_phy phy;
/* Low level driver ops */
struct nfcmrvl_if_ops *if_ops;
};
......@@ -42,7 +85,16 @@ struct nfcmrvl_if_ops {
};
void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count);
int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb);
struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
struct nfcmrvl_if_ops *ops,
struct device *dev);
struct device *dev,
struct nfcmrvl_platform_data *pdata);
void nfcmrvl_chip_reset(struct nfcmrvl_private *priv);
int nfcmrvl_parse_dt(struct device_node *node,
struct nfcmrvl_platform_data *pdata);
#endif
/**
* Marvell NFC-over-UART driver
*
* Copyright (C) 2015, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available on the worldwide web at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <net/nfc/nci.h>
#include <net/nfc/nci_core.h>
#include "nfcmrvl.h"
static unsigned int hci_muxed;
static unsigned int flow_control;
static unsigned int break_control;
static unsigned int reset_n_io;
/*
** NFCMRVL NCI OPS
*/
static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv)
{
return 0;
}
static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv)
{
return 0;
}
static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv,
struct sk_buff *skb)
{
struct nci_uart *nu = priv->drv_data;
return nu->ops.send(nu, skb);
}
static struct nfcmrvl_if_ops uart_ops = {
.nci_open = nfcmrvl_uart_nci_open,
.nci_close = nfcmrvl_uart_nci_close,
.nci_send = nfcmrvl_uart_nci_send,
};
#ifdef CONFIG_OF
static int nfcmrvl_uart_parse_dt(struct device_node *node,
struct nfcmrvl_platform_data *pdata)
{
struct device_node *matched_node;
int ret;
matched_node = of_find_compatible_node(node, NULL, "mrvl,nfc-uart");
if (!matched_node)
return -ENODEV;
ret = nfcmrvl_parse_dt(matched_node, pdata);
if (ret < 0) {
pr_err("Failed to get generic entries\n");
return ret;
}
if (of_find_property(matched_node, "flow-control", NULL))
pdata->flow_control = 1;
else
pdata->flow_control = 0;
if (of_find_property(matched_node, "break-control", NULL))
pdata->break_control = 1;
else
pdata->break_control = 0;
return 0;
}
#else
static int nfcmrvl_uart_parse_dt(struct device_node *node,
struct nfcmrvl_platform_data *pdata)
{
return -ENODEV;
}
#endif
/*
** NCI UART OPS
*/
static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
{
struct nfcmrvl_private *priv;
struct nfcmrvl_platform_data *pdata = NULL;
struct nfcmrvl_platform_data config;
/*
* Platform data cannot be used here since usually it is already used
* by low level serial driver. We can try to retrieve serial device
* and check if DT entries were added.
*/
if (nu->tty->dev->parent && nu->tty->dev->parent->of_node)
if (nfcmrvl_uart_parse_dt(nu->tty->dev->parent->of_node,
&config) == 0)
pdata = &config;
if (!pdata) {
pr_info("No platform data / DT -> fallback to module params\n");
config.hci_muxed = hci_muxed;
config.reset_n_io = reset_n_io;
config.flow_control = flow_control;
config.break_control = break_control;
pdata = &config;
}
priv = nfcmrvl_nci_register_dev(nu, &uart_ops, nu->tty->dev, pdata);
if (IS_ERR(priv))
return PTR_ERR(priv);
priv->phy = NFCMRVL_PHY_UART;
nu->drv_data = priv;
nu->ndev = priv->ndev;
/* Set BREAK */
if (priv->config.break_control && nu->tty->ops->break_ctl)
nu->tty->ops->break_ctl(nu->tty, -1);
return 0;
}
static void nfcmrvl_nci_uart_close(struct nci_uart *nu)
{
nfcmrvl_nci_unregister_dev((struct nfcmrvl_private *)nu->drv_data);
}
static int nfcmrvl_nci_uart_recv(struct nci_uart *nu, struct sk_buff *skb)
{
return nfcmrvl_nci_recv_frame((struct nfcmrvl_private *)nu->drv_data,
skb);
}
static void nfcmrvl_nci_uart_tx_start(struct nci_uart *nu)
{
struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
/* Remove BREAK to wake up the NFCC */
if (priv->config.break_control && nu->tty->ops->break_ctl) {
nu->tty->ops->break_ctl(nu->tty, 0);
usleep_range(3000, 5000);
}
}
static void nfcmrvl_nci_uart_tx_done(struct nci_uart *nu)
{
struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
/*
** To ensure that if the NFCC goes in DEEP SLEEP sate we can wake him
** up. we set BREAK. Once we will be ready to send again we will remove
** it.
*/
if (priv->config.break_control && nu->tty->ops->break_ctl)
nu->tty->ops->break_ctl(nu->tty, -1);
}
static struct nci_uart nfcmrvl_nci_uart = {
.owner = THIS_MODULE,
.name = "nfcmrvl_uart",
.driver = NCI_UART_DRIVER_MARVELL,
.ops = {
.open = nfcmrvl_nci_uart_open,
.close = nfcmrvl_nci_uart_close,
.recv = nfcmrvl_nci_uart_recv,
.tx_start = nfcmrvl_nci_uart_tx_start,
.tx_done = nfcmrvl_nci_uart_tx_done,
}
};
/*
** Module init
*/
static int nfcmrvl_uart_init_module(void)
{
return nci_uart_register(&nfcmrvl_nci_uart);
}
static void nfcmrvl_uart_exit_module(void)
{
nci_uart_unregister(&nfcmrvl_nci_uart);
}
module_init(nfcmrvl_uart_init_module);
module_exit(nfcmrvl_uart_exit_module);
MODULE_AUTHOR("Marvell International Ltd.");
MODULE_DESCRIPTION("Marvell NFC-over-UART");
MODULE_LICENSE("GPL v2");
module_param(flow_control, uint, 0);
MODULE_PARM_DESC(flow_control, "Tell if UART needs flow control at init.");
module_param(break_control, uint, 0);
MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal.");
module_param(hci_muxed, uint, 0);
MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one.");
module_param(reset_n_io, uint, 0);
MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal.");
......@@ -26,7 +26,8 @@
#define VERSION "1.0"
static struct usb_device_id nfcmrvl_table[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x1286, 0x2046, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x1286, 0x2046,
USB_CLASS_VENDOR_SPEC, 4, 1) },
{ } /* Terminating entry */
};
......@@ -69,18 +70,27 @@ static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
static void nfcmrvl_bulk_complete(struct urb *urb)
{
struct nfcmrvl_usb_drv_data *drv_data = urb->context;
struct sk_buff *skb;
int err;
dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d",
dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d\n",
urb, urb->status, urb->actual_length);
if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
return;
if (!urb->status) {
if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer,
urb->actual_length) < 0)
nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n");
skb = nci_skb_alloc(drv_data->priv->ndev, urb->actual_length,
GFP_ATOMIC);
if (!skb) {
nfc_err(&drv_data->udev->dev, "failed to alloc mem\n");
} else {
memcpy(skb_put(skb, urb->actual_length),
urb->transfer_buffer, urb->actual_length);
if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
nfc_err(&drv_data->udev->dev,
"corrupted Rx packet\n");
}
}
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
......@@ -292,6 +302,10 @@ static int nfcmrvl_probe(struct usb_interface *intf,
struct nfcmrvl_private *priv;
int i;
struct usb_device *udev = interface_to_usbdev(intf);
struct nfcmrvl_platform_data config;
/* No configuration for USB */
memset(&config, 0, sizeof(config));
nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
......@@ -329,11 +343,12 @@ static int nfcmrvl_probe(struct usb_interface *intf,
init_usb_anchor(&drv_data->deferred);
priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops,
&drv_data->udev->dev);
&drv_data->udev->dev, &config);
if (IS_ERR(priv))
return PTR_ERR(priv);
drv_data->priv = priv;
drv_data->priv->phy = NFCMRVL_PHY_USB;
priv->dev = &drv_data->udev->dev;
usb_set_intfdata(intf, drv_data);
......
......@@ -7,5 +7,3 @@ nxp-nci_i2c-objs = i2c.o
obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o
obj-$(CONFIG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
......@@ -2,8 +2,10 @@
* I2C link layer for the NXP NCI driver
*
* Copyright (C) 2014 NXP Semiconductors All rights reserved.
* Copyright (C) 2012-2015 Intel Corporation. All rights reserved.
*
* Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
* Authors: Oleg Zhurakivskyy <oleg.zhurakivskyy@intel.com>
*
* Derived from PN544 device driver:
* Copyright (C) 2012 Intel Corporation. All rights reserved.
......@@ -23,12 +25,14 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/nfc.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_data/nxp-nci.h>
......@@ -48,6 +52,7 @@ struct nxp_nci_i2c_phy {
unsigned int gpio_en;
unsigned int gpio_fw;
unsigned int gpio_irq;
int hard_fault; /*
* < 0 if hardware error occurred (e.g. i2c err)
......@@ -308,6 +313,37 @@ static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
#endif
static int nxp_nci_i2c_acpi_config(struct nxp_nci_i2c_phy *phy)
{
struct i2c_client *client = phy->i2c_dev;
struct gpio_desc *gpiod_en, *gpiod_fw, *gpiod_irq;
gpiod_en = devm_gpiod_get_index(&client->dev, NULL, 2);
gpiod_fw = devm_gpiod_get_index(&client->dev, NULL, 1);
gpiod_irq = devm_gpiod_get_index(&client->dev, NULL, 0);
if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw) || IS_ERR(gpiod_irq)) {
nfc_err(&client->dev, "No GPIOs\n");
return -EINVAL;
}
gpiod_direction_output(gpiod_en, 0);
gpiod_direction_output(gpiod_fw, 0);
gpiod_direction_input(gpiod_irq);
client->irq = gpiod_to_irq(gpiod_irq);
if (client->irq < 0) {
nfc_err(&client->dev, "No IRQ\n");
return -EINVAL;
}
phy->gpio_en = desc_to_gpio(gpiod_en);
phy->gpio_fw = desc_to_gpio(gpiod_fw);
phy->gpio_irq = desc_to_gpio(gpiod_irq);
return 0;
}
static int nxp_nci_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
......@@ -343,6 +379,11 @@ static int nxp_nci_i2c_probe(struct i2c_client *client,
phy->gpio_en = pdata->gpio_en;
phy->gpio_fw = pdata->gpio_fw;
client->irq = pdata->irq;
} else if (ACPI_HANDLE(&client->dev)) {
r = nxp_nci_i2c_acpi_config(phy);
if (r < 0)
goto probe_exit;
goto nci_probe;
} else {
nfc_err(&client->dev, "No platform data\n");
r = -EINVAL;
......@@ -359,6 +400,7 @@ static int nxp_nci_i2c_probe(struct i2c_client *client,
if (r < 0)
goto probe_exit;
nci_probe:
r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops,
NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev);
if (r < 0)
......@@ -397,10 +439,19 @@ static const struct of_device_id of_nxp_nci_i2c_match[] = {
};
MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match);
#ifdef CONFIG_ACPI
static struct acpi_device_id acpi_id[] = {
{ "NXP7471" },
{ },
};
MODULE_DEVICE_TABLE(acpi, acpi_id);
#endif
static struct i2c_driver nxp_nci_i2c_driver = {
.driver = {
.name = NXP_NCI_I2C_DRIVER_NAME,
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(acpi_id),
.of_match_table = of_match_ptr(of_nxp_nci_i2c_match),
},
.probe = nxp_nci_i2c_probe,
......@@ -413,3 +464,4 @@ module_i2c_driver(nxp_nci_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers");
MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
MODULE_AUTHOR("Oleg Zhurakivskyy <oleg.zhurakivskyy@intel.com>");
......@@ -895,56 +895,35 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
return -ENODEV;
/* Get EN GPIO from ACPI */
gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1);
gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1,
GPIOD_OUT_LOW);
if (IS_ERR(gpiod_en)) {
nfc_err(dev,
"Unable to get EN GPIO\n");
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);
gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2,
GPIOD_OUT_LOW);
if (IS_ERR(gpiod_fw)) {
nfc_err(dev,
"Unable to get FW GPIO\n");
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);
gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0,
GPIOD_IN);
if (IS_ERR(gpiod_irq)) {
nfc_err(dev,
"Unable to get IRQ GPIO\n");
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) {
......
config NFC_ST21NFCB
tristate "STMicroelectronics ST21NFCB NFC driver"
config NFC_ST_NCI
tristate "STMicroelectronics ST NCI NFC driver"
depends on NFC_NCI
default n
---help---
STMicroelectronics ST21NFCB core driver. It implements the chipset
STMicroelectronics NFC NCI chips core driver. It implements the chipset
NCI logic and hooks into the NFC kernel APIs. Physical layers will
register against it.
To compile this driver as a module, choose m here. The module will
be called st21nfcb.
be called st-nci.
Say N if unsure.
config NFC_ST21NFCB_I2C
tristate "NFC ST21NFCB i2c support"
depends on NFC_ST21NFCB && I2C
config NFC_ST_NCI_I2C
tristate "NFC ST NCI i2c support"
depends on NFC_ST_NCI && I2C
---help---
This module adds support for the STMicroelectronics st21nfcb i2c interface.
This module adds support for an I2C interface to the
STMicroelectronics NFC NCI chips familly.
Select this if your platform is using the i2c bus.
If you choose to build a module, it'll be called st21nfcb_i2c.
If you choose to build a module, it'll be called st-nci_i2c.
Say N if unsure.
#
# Makefile for ST21NFCB NCI based NFC driver
#
st-nci-objs = ndlc.o core.o st-nci_se.o
obj-$(CONFIG_NFC_ST_NCI) += st-nci.o
st-nci_i2c-objs = i2c.o
obj-$(CONFIG_NFC_ST_NCI_I2C) += st-nci_i2c.o
/*
* NCI based Driver for STMicroelectronics NFC Chip
*
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
* Copyright (C) 2014-2015 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,
......@@ -20,83 +20,118 @@
#include <linux/nfc.h>
#include <net/nfc/nci.h>
#include <net/nfc/nci_core.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "st21nfcb.h"
#include "st21nfcb_se.h"
#include "st-nci.h"
#include "st-nci_se.h"
#define DRIVER_DESC "NCI NFC driver for ST21NFCB"
#define DRIVER_DESC "NCI NFC driver for ST_NCI"
#define ST21NFCB_NCI1_X_PROPRIETARY_ISO15693 0x83
#define ST_NCI1_X_PROPRIETARY_ISO15693 0x83
static int st21nfcb_nci_open(struct nci_dev *ndev)
static int st_nci_init(struct nci_dev *ndev)
{
struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
struct nci_mode_set_cmd cmd;
cmd.cmd_type = ST_NCI_SET_NFC_MODE;
cmd.mode = 1;
return nci_prop_cmd(ndev, ST_NCI_CORE_PROP,
sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd);
}
static int st_nci_open(struct nci_dev *ndev)
{
struct st_nci_info *info = nci_get_drvdata(ndev);
int r;
if (test_and_set_bit(ST21NFCB_NCI_RUNNING, &info->flags))
if (test_and_set_bit(ST_NCI_RUNNING, &info->flags))
return 0;
r = ndlc_open(info->ndlc);
if (r)
clear_bit(ST21NFCB_NCI_RUNNING, &info->flags);
clear_bit(ST_NCI_RUNNING, &info->flags);
return r;
}
static int st21nfcb_nci_close(struct nci_dev *ndev)
static int st_nci_close(struct nci_dev *ndev)
{
struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
struct st_nci_info *info = nci_get_drvdata(ndev);
if (!test_and_clear_bit(ST21NFCB_NCI_RUNNING, &info->flags))
if (!test_bit(ST_NCI_RUNNING, &info->flags))
return 0;
ndlc_close(info->ndlc);
clear_bit(ST_NCI_RUNNING, &info->flags);
return 0;
}
static int st21nfcb_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
static int st_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
{
struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
struct st_nci_info *info = nci_get_drvdata(ndev);
skb->dev = (void *)ndev;
if (!test_bit(ST21NFCB_NCI_RUNNING, &info->flags))
if (!test_bit(ST_NCI_RUNNING, &info->flags))
return -EBUSY;
return ndlc_send(info->ndlc, skb);
}
static __u32 st21nfcb_nci_get_rfprotocol(struct nci_dev *ndev,
static __u32 st_nci_get_rfprotocol(struct nci_dev *ndev,
__u8 rf_protocol)
{
return rf_protocol == ST21NFCB_NCI1_X_PROPRIETARY_ISO15693 ?
return rf_protocol == ST_NCI1_X_PROPRIETARY_ISO15693 ?
NFC_PROTO_ISO15693_MASK : 0;
}
static struct nci_ops st21nfcb_nci_ops = {
.open = st21nfcb_nci_open,
.close = st21nfcb_nci_close,
.send = st21nfcb_nci_send,
.get_rfprotocol = st21nfcb_nci_get_rfprotocol,
.discover_se = st21nfcb_nci_discover_se,
.enable_se = st21nfcb_nci_enable_se,
.disable_se = st21nfcb_nci_disable_se,
.se_io = st21nfcb_nci_se_io,
.hci_load_session = st21nfcb_hci_load_session,
.hci_event_received = st21nfcb_hci_event_received,
.hci_cmd_received = st21nfcb_hci_cmd_received,
static int st_nci_prop_rsp_packet(struct nci_dev *ndev,
struct sk_buff *skb)
{
__u8 status = skb->data[0];
nci_req_complete(ndev, status);
return 0;
}
static struct nci_prop_ops st_nci_prop_ops[] = {
{
.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
ST_NCI_CORE_PROP),
.rsp = st_nci_prop_rsp_packet,
},
};
int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
static struct nci_ops st_nci_ops = {
.init = st_nci_init,
.open = st_nci_open,
.close = st_nci_close,
.send = st_nci_send,
.get_rfprotocol = st_nci_get_rfprotocol,
.discover_se = st_nci_discover_se,
.enable_se = st_nci_enable_se,
.disable_se = st_nci_disable_se,
.se_io = st_nci_se_io,
.hci_load_session = st_nci_hci_load_session,
.hci_event_received = st_nci_hci_event_received,
.hci_cmd_received = st_nci_hci_cmd_received,
.prop_ops = st_nci_prop_ops,
.n_prop_ops = ARRAY_SIZE(st_nci_prop_ops),
};
int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
int phy_tailroom)
{
struct st21nfcb_nci_info *info;
struct st_nci_info *info;
int r;
u32 protocols;
info = devm_kzalloc(ndlc->dev,
sizeof(struct st21nfcb_nci_info), GFP_KERNEL);
sizeof(struct st_nci_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
......@@ -108,7 +143,7 @@ int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
| NFC_PROTO_ISO15693_MASK
| NFC_PROTO_NFC_DEP_MASK;
ndlc->ndev = nci_allocate_device(&st21nfcb_nci_ops, protocols,
ndlc->ndev = nci_allocate_device(&st_nci_ops, protocols,
phy_headroom, phy_tailroom);
if (!ndlc->ndev) {
pr_err("Cannot allocate nfc ndev\n");
......@@ -125,19 +160,20 @@ int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
return r;
}
return st21nfcb_se_init(ndlc->ndev);
return st_nci_se_init(ndlc->ndev);
}
EXPORT_SYMBOL_GPL(st21nfcb_nci_probe);
EXPORT_SYMBOL_GPL(st_nci_probe);
void st21nfcb_nci_remove(struct nci_dev *ndev)
void st_nci_remove(struct nci_dev *ndev)
{
struct st21nfcb_nci_info *info = nci_get_drvdata(ndev);
struct st_nci_info *info = nci_get_drvdata(ndev);
ndlc_close(info->ndlc);
nci_unregister_device(ndev);
nci_free_device(ndev);
kfree(info);
}
EXPORT_SYMBOL_GPL(st21nfcb_nci_remove);
EXPORT_SYMBOL_GPL(st_nci_remove);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRIVER_DESC);
/*
* Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip
*
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
* Copyright (C) 2014-2015 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,
......@@ -20,7 +20,7 @@
#include <net/nfc/nci_core.h>
#include "ndlc.h"
#include "st21nfcb.h"
#include "st-nci.h"
#define NDLC_TIMER_T1 100
#define NDLC_TIMER_T1_WAIT 400
......@@ -59,13 +59,25 @@ int ndlc_open(struct llt_ndlc *ndlc)
{
/* toggle reset pin */
ndlc->ops->enable(ndlc->phy_id);
ndlc->powered = 1;
return 0;
}
EXPORT_SYMBOL(ndlc_open);
void ndlc_close(struct llt_ndlc *ndlc)
{
struct nci_mode_set_cmd cmd;
cmd.cmd_type = ST_NCI_SET_NFC_MODE;
cmd.mode = 0;
/* toggle reset pin */
ndlc->ops->enable(ndlc->phy_id);
nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP,
sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd);
ndlc->powered = 0;
ndlc->ops->disable(ndlc->phy_id);
}
EXPORT_SYMBOL(ndlc_close);
......@@ -262,6 +274,7 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
ndlc->ops = phy_ops;
ndlc->phy_id = phy_id;
ndlc->dev = dev;
ndlc->powered = 0;
*ndlc_id = ndlc;
......@@ -280,12 +293,14 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work);
return st21nfcb_nci_probe(ndlc, phy_headroom, phy_tailroom);
return st_nci_probe(ndlc, phy_headroom, phy_tailroom);
}
EXPORT_SYMBOL(ndlc_probe);
void ndlc_remove(struct llt_ndlc *ndlc)
{
st_nci_remove(ndlc->ndev);
/* cancel timers */
del_timer_sync(&ndlc->t1_timer);
del_timer_sync(&ndlc->t2_timer);
......@@ -294,7 +309,5 @@ void ndlc_remove(struct llt_ndlc *ndlc)
skb_queue_purge(&ndlc->rcv_q);
skb_queue_purge(&ndlc->send_q);
st21nfcb_nci_remove(ndlc->ndev);
}
EXPORT_SYMBOL(ndlc_remove);
/*
* NCI based Driver for STMicroelectronics NFC Chip
*
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
* Copyright (C) 2014-2015 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,
......@@ -43,10 +43,11 @@ struct llt_ndlc {
struct device *dev;
/*
* < 0 if hardware error occured
* < 0 if hardware error occurred
* and prevents normal operation.
*/
int hard_fault;
int powered;
};
int ndlc_open(struct llt_ndlc *ndlc);
......
......@@ -16,23 +16,35 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOCAL_ST21NFCB_H_
#define __LOCAL_ST21NFCB_H_
#ifndef __LOCAL_ST_NCI_H_
#define __LOCAL_ST_NCI_H_
#include "st21nfcb_se.h"
#include "st-nci_se.h"
#include "ndlc.h"
/* Define private flags: */
#define ST21NFCB_NCI_RUNNING 1
#define ST_NCI_RUNNING 1
struct st21nfcb_nci_info {
#define ST_NCI_CORE_PROP 0x01
#define ST_NCI_SET_NFC_MODE 0x02
struct nci_mode_set_cmd {
u8 cmd_type;
u8 mode;
} __packed;
struct nci_mode_set_rsp {
u8 status;
} __packed;
struct st_nci_info {
struct llt_ndlc *ndlc;
unsigned long flags;
struct st21nfcb_se_info se_info;
struct st_nci_se_info se_info;
};
void st21nfcb_nci_remove(struct nci_dev *ndev);
int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
void st_nci_remove(struct nci_dev *ndev);
int st_nci_probe(struct llt_ndlc *ndlc, int phy_headroom,
int phy_tailroom);
#endif /* __LOCAL_ST21NFCB_H_ */
#endif /* __LOCAL_ST_NCI_H_ */
/*
* NCI based Driver for STMicroelectronics NFC Chip
* Secure Element Driver for STMicroelectronics NFC NCI Chip
*
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
* Copyright (C) 2014-2015 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,
......@@ -15,18 +15,18 @@
* 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 __LOCAL_ST21NFCB_SE_H_
#define __LOCAL_ST21NFCB_SE_H_
#ifndef __LOCAL_ST_NCI_SE_H_
#define __LOCAL_ST_NCI_SE_H_
/*
* ref ISO7816-3 chap 8.1. the initial character TS is followed by a
* sequence of at most 32 characters.
*/
#define ST21NFCB_ESE_MAX_LENGTH 33
#define ST21NFCB_HCI_HOST_ID_ESE 0xc0
#define ST_NCI_ESE_MAX_LENGTH 33
#define ST_NCI_HCI_HOST_ID_ESE 0xc0
struct st21nfcb_se_info {
u8 atr[ST21NFCB_ESE_MAX_LENGTH];
struct st_nci_se_info {
u8 atr[ST_NCI_ESE_MAX_LENGTH];
struct completion req_completion;
struct timer_list bwi_timer;
......@@ -42,20 +42,20 @@ struct st21nfcb_se_info {
void *cb_context;
};
int st21nfcb_se_init(struct nci_dev *ndev);
void st21nfcb_se_deinit(struct nci_dev *ndev);
int st_nci_se_init(struct nci_dev *ndev);
void st_nci_se_deinit(struct nci_dev *ndev);
int st21nfcb_nci_discover_se(struct nci_dev *ndev);
int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx);
int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx);
int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx,
int st_nci_discover_se(struct nci_dev *ndev);
int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx);
int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx);
int st_nci_se_io(struct nci_dev *ndev, u32 se_idx,
u8 *apdu, size_t apdu_length,
se_io_cb_t cb, void *cb_context);
int st21nfcb_hci_load_session(struct nci_dev *ndev);
void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe,
int st_nci_hci_load_session(struct nci_dev *ndev);
void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
u8 event, struct sk_buff *skb);
void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
struct sk_buff *skb);
#endif /* __LOCAL_ST21NFCB_NCI_H_ */
#endif /* __LOCAL_ST_NCI_SE_H_ */
#
# Makefile for ST21NFCB NCI based NFC driver
#
st21nfcb_nci-objs = ndlc.o st21nfcb.o st21nfcb_se.o
obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb_nci.o
st21nfcb_i2c-objs = i2c.o
obj-$(CONFIG_NFC_ST21NFCB_I2C) += st21nfcb_i2c.o
......@@ -149,6 +149,7 @@
*/
#define TRF7970A_QUIRK_IRQ_STATUS_READ BIT(0)
#define TRF7970A_QUIRK_EN2_MUST_STAY_LOW BIT(1)
#define TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE BIT(2)
/* Direct commands */
#define TRF7970A_CMD_IDLE 0x00
......@@ -446,6 +447,7 @@ struct trf7970a {
u8 md_rf_tech;
u8 tx_cmd;
bool issue_eof;
bool adjust_resp_len;
int en2_gpio;
int en_gpio;
struct mutex lock;
......@@ -626,6 +628,11 @@ static void trf7970a_send_upstream(struct trf7970a *trf)
trf->aborting = false;
}
if (trf->adjust_resp_len) {
skb_trim(trf->rx_skb, trf->rx_skb->len - 1);
trf->adjust_resp_len = false;
}
trf->cb(trf->ddev, trf->cb_arg, trf->rx_skb);
trf->rx_skb = NULL;
......@@ -1429,10 +1436,15 @@ static int trf7970a_per_cmd_config(struct trf7970a *trf, struct sk_buff *skb)
trf->iso_ctrl = iso_ctrl;
}
if ((trf->framing == NFC_DIGITAL_FRAMING_ISO15693_T5T) &&
trf7970a_is_iso15693_write_or_lock(req[1]) &&
if (trf->framing == NFC_DIGITAL_FRAMING_ISO15693_T5T) {
if (trf7970a_is_iso15693_write_or_lock(req[1]) &&
(req[0] & ISO15693_REQ_FLAG_OPTION))
trf->issue_eof = true;
else if ((trf->quirks &
TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE) &&
(req[1] == ISO15693_CMD_READ_MULTIPLE_BLOCK))
trf->adjust_resp_len = true;
}
}
return 0;
......@@ -1992,6 +2004,9 @@ static int trf7970a_probe(struct spi_device *spi)
return ret;
}
if (of_property_read_bool(np, "t5t-rmb-extra-byte-quirk"))
trf->quirks |= TRF7970A_QUIRK_T5T_RMB_EXTRA_BYTE;
if (of_property_read_bool(np, "irq-status-read-quirk"))
trf->quirks |= TRF7970A_QUIRK_IRQ_STATUS_READ;
......
/*
* Copyright (C) 2015, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available on the worldwide web at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*/
#ifndef _NFCMRVL_PTF_H_
#define _NFCMRVL_PTF_H_
struct nfcmrvl_platform_data {
/*
* Generic
*/
/* GPIO that is wired to RESET_N signal */
unsigned int reset_n_io;
/* Tell if transport is muxed in HCI one */
unsigned int hci_muxed;
/*
* UART specific
*/
/* Tell if UART needs flow control at init */
unsigned int flow_control;
/* Tell if firmware supports break control for power management */
unsigned int break_control;
};
#endif /* _NFCMRVL_PTF_H_ */
/*
* Driver include for the ST21NFCB NFC chip.
* Driver include for ST NCI NFC chip family.
*
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
* Copyright (C) 2014-2015 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,
......@@ -16,14 +16,14 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ST21NFCB_NCI_H_
#define _ST21NFCB_NCI_H_
#ifndef _ST_NCI_H_
#define _ST_NCI_H_
#define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci"
#define ST_NCI_DRIVER_NAME "st_nci"
struct st21nfcb_nfc_platform_data {
struct st_nci_nfc_platform_data {
unsigned int gpio_reset;
unsigned int irq_polarity;
};
#endif /* _ST21NFCB_NCI_H_ */
#endif /* _ST_NCI_H_ */
/*
* Driver include for ST NCI NFC chip family.
*
* Copyright (C) 2014-2015 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 _ST_NCI_H_
#define _ST_NCI_H_
#define ST_NCI_DRIVER_NAME "st_nci"
struct st_nci_nfc_platform_data {
unsigned int gpio_reset;
unsigned int irq_polarity;
};
#endif /* _ST_NCI_H_ */
......@@ -179,6 +179,13 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev);
void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata);
void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev);
static inline int nfc_hci_set_vendor_cmds(struct nfc_hci_dev *hdev,
struct nfc_vendor_cmd *cmds,
int n_cmds)
{
return nfc_set_vendor_cmds(hdev->ndev, cmds, n_cmds);
}
void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err);
int nfc_hci_result_to_errno(u8 result);
......
......@@ -35,6 +35,7 @@
#define NCI_MAX_NUM_RF_CONFIGS 10
#define NCI_MAX_NUM_CONN 10
#define NCI_MAX_PARAM_LEN 251
#define NCI_MAX_PACKET_SIZE 258
/* NCI Status Codes */
#define NCI_STATUS_OK 0x00
......
......@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/tty.h>
#include <net/nfc/nfc.h>
#include <net/nfc/nci.h>
......@@ -66,7 +67,14 @@ enum nci_state {
struct nci_dev;
struct nci_prop_ops {
__u16 opcode;
int (*rsp)(struct nci_dev *dev, struct sk_buff *skb);
int (*ntf)(struct nci_dev *dev, struct sk_buff *skb);
};
struct nci_ops {
int (*init)(struct nci_dev *ndev);
int (*open)(struct nci_dev *ndev);
int (*close)(struct nci_dev *ndev);
int (*send)(struct nci_dev *ndev, struct sk_buff *skb);
......@@ -84,12 +92,16 @@ struct nci_ops {
struct sk_buff *skb);
void (*hci_cmd_received)(struct nci_dev *ndev, u8 pipe, u8 cmd,
struct sk_buff *skb);
struct nci_prop_ops *prop_ops;
size_t n_prop_ops;
};
#define NCI_MAX_SUPPORTED_RF_INTERFACES 4
#define NCI_MAX_DISCOVERED_TARGETS 10
#define NCI_MAX_NUM_NFCEE 255
#define NCI_MAX_CONN_ID 7
#define NCI_MAX_PROPRIETARY_CMD 64
struct nci_conn_info {
struct list_head list;
......@@ -264,6 +276,8 @@ int nci_request(struct nci_dev *ndev,
void (*req)(struct nci_dev *ndev,
unsigned long opt),
unsigned long opt, __u32 timeout);
int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload);
int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val);
......@@ -318,8 +332,19 @@ static inline void *nci_get_drvdata(struct nci_dev *ndev)
return ndev->driver_data;
}
static inline int nci_set_vendor_cmds(struct nci_dev *ndev,
struct nfc_vendor_cmd *cmds,
int n_cmds)
{
return nfc_set_vendor_cmds(ndev->nfc_dev, cmds, n_cmds);
}
void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb);
void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb);
int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode,
struct sk_buff *skb);
int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode,
struct sk_buff *skb);
void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb);
int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload);
int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb);
......@@ -367,4 +392,50 @@ int nci_spi_send(struct nci_spi *nspi,
struct sk_buff *skb);
struct sk_buff *nci_spi_read(struct nci_spi *nspi);
/* ----- NCI UART ---- */
/* Ioctl */
#define NCIUARTSETDRIVER _IOW('U', 0, char *)
enum nci_uart_driver {
NCI_UART_DRIVER_MARVELL = 0,
NCI_UART_DRIVER_MAX
};
struct nci_uart;
struct nci_uart_ops {
int (*open)(struct nci_uart *nci_uart);
void (*close)(struct nci_uart *nci_uart);
int (*recv)(struct nci_uart *nci_uart, struct sk_buff *skb);
int (*recv_buf)(struct nci_uart *nci_uart, const u8 *data, char *flags,
int count);
int (*send)(struct nci_uart *nci_uart, struct sk_buff *skb);
void (*tx_start)(struct nci_uart *nci_uart);
void (*tx_done)(struct nci_uart *nci_uart);
};
struct nci_uart {
struct module *owner;
struct nci_uart_ops ops;
const char *name;
enum nci_uart_driver driver;
/* Dynamic data */
struct nci_dev *ndev;
spinlock_t rx_lock;
struct work_struct write_work;
struct tty_struct *tty;
unsigned long tx_state;
struct sk_buff_head tx_q;
struct sk_buff *tx_skb;
struct sk_buff *rx_skb;
int rx_packet_len;
void *drv_data;
};
int nci_uart_register(struct nci_uart *nu);
void nci_uart_unregister(struct nci_uart *nu);
void nci_uart_set_config(struct nci_uart *nu, int baudrate, int flow_ctrl);
#endif /* __NCI_CORE_H */
......@@ -165,6 +165,12 @@ struct nfc_genl_data {
struct mutex genl_data_mutex;
};
struct nfc_vendor_cmd {
__u32 vendor_id;
__u32 subcmd;
int (*doit)(struct nfc_dev *dev, void *data, size_t data_len);
};
struct nfc_dev {
int idx;
u32 target_next_idx;
......@@ -193,6 +199,9 @@ struct nfc_dev {
struct rfkill *rfkill;
struct nfc_vendor_cmd *vendor_cmds;
int n_vendor_cmds;
struct nfc_ops *ops;
};
#define to_nfc_dev(_dev) container_of(_dev, struct nfc_dev, dev)
......@@ -296,4 +305,17 @@ struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx);
void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb,
u8 payload_type, u8 direction);
static inline int nfc_set_vendor_cmds(struct nfc_dev *dev,
struct nfc_vendor_cmd *cmds,
int n_cmds)
{
if (dev->vendor_cmds || dev->n_vendor_cmds)
return -EINVAL;
dev->vendor_cmds = cmds;
dev->n_vendor_cmds = n_cmds;
return 0;
}
#endif /* __NET_NFC_H */
......@@ -86,6 +86,8 @@
* for this event is the application ID (AID).
* @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller.
* @NFC_CMD_SE_IO: Send/Receive APDUs to/from the selected secure element.
* @NFC_CMD_VENDOR: Vendor specific command, to be implemented directly
* from the driver in order to support hardware specific operations.
*/
enum nfc_commands {
NFC_CMD_UNSPEC,
......@@ -117,6 +119,7 @@ enum nfc_commands {
NFC_CMD_GET_SE,
NFC_CMD_SE_IO,
NFC_CMD_ACTIVATE_TARGET,
NFC_CMD_VENDOR,
/* private: internal use only */
__NFC_CMD_AFTER_LAST
};
......@@ -153,6 +156,10 @@ enum nfc_commands {
* @NFC_ATTR_APDU: Secure element APDU
* @NFC_ATTR_TARGET_ISO15693_DSFID: ISO 15693 Data Storage Format Identifier
* @NFC_ATTR_TARGET_ISO15693_UID: ISO 15693 Unique Identifier
* @NFC_ATTR_VENDOR_ID: NFC manufacturer unique ID, typically an OUI
* @NFC_ATTR_VENDOR_SUBCMD: Vendor specific sub command
* @NFC_ATTR_VENDOR_DATA: Vendor specific data, to be optionally passed
* to a vendor specific command implementation
*/
enum nfc_attrs {
NFC_ATTR_UNSPEC,
......@@ -184,6 +191,9 @@ enum nfc_attrs {
NFC_ATTR_TARGET_ISO15693_DSFID,
NFC_ATTR_TARGET_ISO15693_UID,
NFC_ATTR_SE_PARAMS,
NFC_ATTR_VENDOR_ID,
NFC_ATTR_VENDOR_SUBCMD,
NFC_ATTR_VENDOR_DATA,
/* private: internal use only */
__NFC_ATTR_AFTER_LAST
};
......
......@@ -34,5 +34,6 @@
#define N_TI_WL 22 /* for TI's WL BT, FM, GPS combo chips */
#define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */
#define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */
#define N_NCI 25 /* NFC NCI UART */
#endif /* _UAPI_LINUX_TTY_H */
......@@ -19,3 +19,10 @@ config NFC_NCI_SPI
an NFC Controller (NFCC) and a Device Host (DH).
Say yes if you use an NCI driver that requires SPI link layer.
config NFC_NCI_UART
depends on NFC_NCI && TTY
tristate "NCI over UART protocol support"
default n
help
Say yes if you use an NCI driver that requires UART link layer.
......@@ -7,3 +7,6 @@ obj-$(CONFIG_NFC_NCI) += nci.o
nci-objs := core.o data.o lib.o ntf.o rsp.o hci.o
nci-$(CONFIG_NFC_NCI_SPI) += spi.o
nci_uart-y += uart.o
obj-$(CONFIG_NFC_NCI_UART) += nci_uart.o
......@@ -28,6 +28,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/completion.h>
......@@ -73,6 +74,7 @@ void nci_req_complete(struct nci_dev *ndev, int result)
complete(&ndev->req_completion);
}
}
EXPORT_SYMBOL(nci_req_complete);
static void nci_req_cancel(struct nci_dev *ndev, int err)
{
......@@ -323,6 +325,32 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
sizeof(struct nci_rf_deactivate_cmd), &cmd);
}
struct nci_prop_cmd_param {
__u16 opcode;
size_t len;
__u8 *payload;
};
static void nci_prop_cmd_req(struct nci_dev *ndev, unsigned long opt)
{
struct nci_prop_cmd_param *param = (struct nci_prop_cmd_param *)opt;
nci_send_cmd(ndev, param->opcode, param->len, param->payload);
}
int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload)
{
struct nci_prop_cmd_param param;
param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid);
param.len = len;
param.payload = payload;
return __nci_request(ndev, nci_prop_cmd_req, (unsigned long)&param,
msecs_to_jiffies(NCI_CMD_TIMEOUT));
}
EXPORT_SYMBOL(nci_prop_cmd);
static int nci_open_device(struct nci_dev *ndev)
{
int rc = 0;
......@@ -343,11 +371,17 @@ static int nci_open_device(struct nci_dev *ndev)
set_bit(NCI_INIT, &ndev->flags);
if (ndev->ops->init)
rc = ndev->ops->init(ndev);
if (!rc) {
rc = __nci_request(ndev, nci_reset_req, 0,
msecs_to_jiffies(NCI_RESET_TIMEOUT));
}
if (ndev->ops->setup)
ndev->ops->setup(ndev);
if (!rc && ndev->ops->setup) {
rc = ndev->ops->setup(ndev);
}
if (!rc) {
rc = __nci_request(ndev, nci_init_req, 0,
......@@ -407,6 +441,12 @@ static int nci_close_device(struct nci_dev *ndev)
set_bit(NCI_INIT, &ndev->flags);
__nci_request(ndev, nci_reset_req, 0,
msecs_to_jiffies(NCI_RESET_TIMEOUT));
/* After this point our queues are empty
* and no works are scheduled.
*/
ndev->ops->close(ndev);
clear_bit(NCI_INIT, &ndev->flags);
del_timer_sync(&ndev->cmd_timer);
......@@ -414,10 +454,6 @@ static int nci_close_device(struct nci_dev *ndev)
/* Flush cmd wq */
flush_workqueue(ndev->cmd_wq);
/* After this point our queues are empty
* and no works are scheduled. */
ndev->ops->close(ndev);
/* Clear flags */
ndev->flags = 0;
......@@ -762,7 +798,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
nci_request(ndev, nci_rf_deactivate_req,
NCI_DEACTIVATE_TYPE_SLEEP_MODE,
NCI_DEACTIVATE_TYPE_IDLE_MODE,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
}
}
......@@ -961,6 +997,14 @@ struct nci_dev *nci_allocate_device(struct nci_ops *ops,
return NULL;
ndev->ops = ops;
if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) {
pr_err("Too many proprietary commands: %zd\n",
ops->n_prop_ops);
ops->prop_ops = NULL;
ops->n_prop_ops = 0;
}
ndev->tx_headroom = tx_headroom;
ndev->tx_tailroom = tx_tailroom;
init_completion(&ndev->req_completion);
......@@ -1165,6 +1209,49 @@ int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
return 0;
}
/* Proprietary commands API */
static struct nci_prop_ops *prop_cmd_lookup(struct nci_dev *ndev,
__u16 opcode)
{
size_t i;
struct nci_prop_ops *prop_op;
if (!ndev->ops->prop_ops || !ndev->ops->n_prop_ops)
return NULL;
for (i = 0; i < ndev->ops->n_prop_ops; i++) {
prop_op = &ndev->ops->prop_ops[i];
if (prop_op->opcode == opcode)
return prop_op;
}
return NULL;
}
int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
struct sk_buff *skb)
{
struct nci_prop_ops *prop_op;
prop_op = prop_cmd_lookup(ndev, rsp_opcode);
if (!prop_op || !prop_op->rsp)
return -ENOTSUPP;
return prop_op->rsp(ndev, skb);
}
int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
struct sk_buff *skb)
{
struct nci_prop_ops *prop_op;
prop_op = prop_cmd_lookup(ndev, ntf_opcode);
if (!prop_op || !prop_op->ntf)
return -ENOTSUPP;
return prop_op->ntf(ndev, skb);
}
/* ---- NCI TX Data worker thread ---- */
static void nci_tx_work(struct work_struct *work)
......
......@@ -639,22 +639,19 @@ int nci_hci_dev_session_init(struct nci_dev *ndev)
ndev->hci_dev->init_data.gates[0].gate,
ndev->hci_dev->init_data.gates[0].pipe);
if (r < 0)
goto exit;
return r;
r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE,
NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, &skb);
if (r < 0)
goto exit;
return r;
if (skb->len &&
skb->len == strlen(ndev->hci_dev->init_data.session_id) &&
memcmp(ndev->hci_dev->init_data.session_id,
skb->data, skb->len) == 0 &&
!memcmp(ndev->hci_dev->init_data.session_id, skb->data, skb->len) &&
ndev->ops->hci_load_session) {
/* Restore gate<->pipe table from some proprietary location. */
r = ndev->ops->hci_load_session(ndev);
if (r < 0)
goto exit;
} else {
r = nci_hci_dev_connect_gates(ndev,
ndev->hci_dev->init_data.gate_count,
......@@ -667,8 +664,6 @@ int nci_hci_dev_session_init(struct nci_dev *ndev)
ndev->hci_dev->init_data.session_id,
strlen(ndev->hci_dev->init_data.session_id));
}
if (r == 0)
goto exit;
exit:
kfree_skb(skb);
......
......@@ -758,6 +758,15 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
/* strip the nci control header */
skb_pull(skb, NCI_CTRL_HDR_SIZE);
if (nci_opcode_gid(ntf_opcode) == NCI_GID_PROPRIETARY) {
if (nci_prop_ntf_packet(ndev, ntf_opcode, skb)) {
pr_err("unsupported ntf opcode 0x%x\n",
ntf_opcode);
}
goto end;
}
switch (ntf_opcode) {
case NCI_OP_CORE_CONN_CREDITS_NTF:
nci_core_conn_credits_ntf_packet(ndev, skb);
......@@ -796,5 +805,6 @@ void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb)
break;
}
end:
kfree_skb(skb);
}
......@@ -296,6 +296,15 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
/* strip the nci control header */
skb_pull(skb, NCI_CTRL_HDR_SIZE);
if (nci_opcode_gid(rsp_opcode) == NCI_GID_PROPRIETARY) {
if (nci_prop_rsp_packet(ndev, rsp_opcode, skb) == -ENOTSUPP) {
pr_err("unsupported rsp opcode 0x%x\n",
rsp_opcode);
}
goto end;
}
switch (rsp_opcode) {
case NCI_OP_CORE_RESET_RSP:
nci_core_reset_rsp_packet(ndev, skb);
......@@ -346,6 +355,7 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
break;
}
end:
kfree_skb(skb);
/* trigger the next cmd */
......
This diff is collapsed.
......@@ -5,6 +5,12 @@
* Lauro Ramos Venancio <lauro.venancio@openbossa.org>
* Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
*
* Vendor commands implementation based on net/wireless/nl80211.c
* which is:
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
......@@ -1489,6 +1495,50 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
}
static int nfc_genl_vendor_cmd(struct sk_buff *skb,
struct genl_info *info)
{
struct nfc_dev *dev;
struct nfc_vendor_cmd *cmd;
u32 dev_idx, vid, subcmd;
u8 *data;
size_t data_len;
int i;
if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
!info->attrs[NFC_ATTR_VENDOR_ID] ||
!info->attrs[NFC_ATTR_VENDOR_SUBCMD])
return -EINVAL;
dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
vid = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_ID]);
subcmd = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_SUBCMD]);
dev = nfc_get_device(dev_idx);
if (!dev || !dev->vendor_cmds || !dev->n_vendor_cmds)
return -ENODEV;
data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]);
if (data) {
data_len = nla_len(info->attrs[NFC_ATTR_VENDOR_DATA]);
if (data_len == 0)
return -EINVAL;
} else {
data_len = 0;
}
for (i = 0; i < dev->n_vendor_cmds; i++) {
cmd = &dev->vendor_cmds[i];
if (cmd->vendor_id != vid || cmd->subcmd != subcmd)
continue;
return cmd->doit(dev, data, data_len);
}
return -EOPNOTSUPP;
}
static const struct genl_ops nfc_genl_ops[] = {
{
.cmd = NFC_CMD_GET_DEVICE,
......@@ -1579,6 +1629,11 @@ static const struct genl_ops nfc_genl_ops[] = {
.doit = nfc_genl_activate_target,
.policy = nfc_genl_policy,
},
{
.cmd = NFC_CMD_VENDOR,
.doit = nfc_genl_vendor_cmd,
.policy = nfc_genl_policy,
},
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment