Commit 4912e2fe authored by Eric Lapuyade's avatar Eric Lapuyade Committed by Samuel Ortiz

NFC: mei: Add a common mei bus API for NFC drivers

This isolates the common code that is required to use an mei bus nfc
device from an NFC HCI drivers. This prepares for future drivers for
NFC chips connected behind an Intel Management Engine controller.
The microread_mei HCI driver is also modified to use that common code.
Signed-off-by: default avatarEric Lapuyade <eric.lapuyade@intel.com>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent be055b2f
......@@ -26,6 +26,16 @@ config NFC_WILINK
Say Y here to compile support for Texas Instrument's NFC WiLink driver
into the kernel or say M to compile it as module.
config NFC_MEI_PHY
tristate "MEI bus NFC device support"
depends on INTEL_MEI_BUS_NFC && NFC_HCI
help
This adds support to use an mei bus nfc device. Select this if you
will use an HCI NFC driver for an NFC chip connected behind an
Intel's Management Engine chip.
If unsure, say N.
source "drivers/nfc/pn544/Kconfig"
source "drivers/nfc/microread/Kconfig"
......
......@@ -6,5 +6,6 @@ obj-$(CONFIG_NFC_PN544) += pn544/
obj-$(CONFIG_NFC_MICROREAD) += microread/
obj-$(CONFIG_NFC_PN533) += pn533.o
obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
/*
* MEI Library for mei bus nfc device access
*
* Copyright (C) 2013 Intel Corporation. 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, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/nfc.h>
#include "mei_phy.h"
struct mei_nfc_hdr {
u8 cmd;
u8 status;
u16 req_id;
u32 reserved;
u16 data_size;
} __attribute__((packed));
#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
#define MEI_DUMP_SKB_IN(info, skb) \
do { \
pr_debug("%s:\n", info); \
print_hex_dump_debug("mei in : ", DUMP_PREFIX_OFFSET, \
16, 1, (skb)->data, (skb)->len, false); \
} while (0)
#define MEI_DUMP_SKB_OUT(info, skb) \
do { \
pr_debug("%s:\n", info); \
print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET, \
16, 1, (skb)->data, (skb)->len, false); \
} while (0)
int nfc_mei_phy_enable(void *phy_id)
{
int r;
struct nfc_mei_phy *phy = phy_id;
pr_info("%s\n", __func__);
if (phy->powered == 1)
return 0;
r = mei_cl_enable_device(phy->device);
if (r < 0) {
pr_err("MEI_PHY: Could not enable device\n");
return r;
}
phy->powered = 1;
return 0;
}
EXPORT_SYMBOL_GPL(nfc_mei_phy_enable);
void nfc_mei_phy_disable(void *phy_id)
{
struct nfc_mei_phy *phy = phy_id;
pr_info("%s\n", __func__);
mei_cl_disable_device(phy->device);
phy->powered = 0;
}
EXPORT_SYMBOL_GPL(nfc_mei_phy_disable);
/*
* Writing a frame must not return the number of written bytes.
* It must return either zero for success, or <0 for error.
* In addition, it must not alter the skb
*/
static int nfc_mei_phy_write(void *phy_id, struct sk_buff *skb)
{
struct nfc_mei_phy *phy = phy_id;
int r;
MEI_DUMP_SKB_OUT("mei frame sent", skb);
r = mei_cl_send(phy->device, skb->data, skb->len);
if (r > 0)
r = 0;
return r;
}
void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context)
{
struct nfc_mei_phy *phy = context;
if (phy->hard_fault != 0)
return;
if (events & BIT(MEI_CL_EVENT_RX)) {
struct sk_buff *skb;
int reply_size;
skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
if (!skb)
return;
reply_size = mei_cl_recv(device, skb->data, MEI_NFC_MAX_READ);
if (reply_size < MEI_NFC_HEADER_SIZE) {
kfree(skb);
return;
}
skb_put(skb, reply_size);
skb_pull(skb, MEI_NFC_HEADER_SIZE);
MEI_DUMP_SKB_IN("mei frame read", skb);
nfc_hci_recv_frame(phy->hdev, skb);
}
}
EXPORT_SYMBOL_GPL(nfc_mei_event_cb);
struct nfc_phy_ops mei_phy_ops = {
.write = nfc_mei_phy_write,
.enable = nfc_mei_phy_enable,
.disable = nfc_mei_phy_disable,
};
EXPORT_SYMBOL_GPL(mei_phy_ops);
struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *device)
{
struct nfc_mei_phy *phy;
phy = kzalloc(sizeof(struct nfc_mei_phy), GFP_KERNEL);
if (!phy)
return NULL;
phy->device = device;
mei_cl_set_drvdata(device, phy);
return phy;
}
EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc);
void nfc_mei_phy_free(struct nfc_mei_phy *phy)
{
kfree(phy);
}
EXPORT_SYMBOL_GPL(nfc_mei_phy_free);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("mei bus NFC device interface");
#ifndef __LOCAL_MEI_PHY_H_
#define __LOCAL_MEI_PHY_H_
#include <linux/mei_cl_bus.h>
#include <net/nfc/hci.h>
#define MEI_NFC_HEADER_SIZE 10
#define MEI_NFC_MAX_HCI_PAYLOAD 300
struct nfc_mei_phy {
struct mei_cl_device *device;
struct nfc_hci_dev *hdev;
int powered;
int hard_fault; /*
* < 0 if hardware error occured
* and prevents normal operation.
*/
};
extern struct nfc_phy_ops mei_phy_ops;
int nfc_mei_phy_enable(void *phy_id);
void nfc_mei_phy_disable(void *phy_id);
void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context);
struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *device);
void nfc_mei_phy_free(struct nfc_mei_phy *phy);
#endif /* __LOCAL_MEI_PHY_H_ */
......@@ -25,7 +25,7 @@ config NFC_MICROREAD_I2C
config NFC_MICROREAD_MEI
tristate "NFC Microread MEI support"
depends on NFC_MICROREAD && INTEL_MEI_BUS_NFC
depends on NFC_MICROREAD && NFC_MEI_PHY
---help---
This module adds support for the mei interface of adapters using
Inside microread chipsets. Select this if your microread chipset
......
......@@ -19,151 +19,31 @@
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/mei_cl_bus.h>
#include <linux/mod_devicetable.h>
#include <linux/nfc.h>
#include <net/nfc/hci.h>
#include <net/nfc/llc.h>
#include "../mei_phy.h"
#include "microread.h"
#define MICROREAD_DRIVER_NAME "microread"
struct mei_nfc_hdr {
u8 cmd;
u8 status;
u16 req_id;
u32 reserved;
u16 data_size;
} __attribute__((packed));
#define MEI_NFC_HEADER_SIZE 10
#define MEI_NFC_MAX_HCI_PAYLOAD 300
#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
struct microread_mei_phy {
struct mei_cl_device *device;
struct nfc_hci_dev *hdev;
int powered;
int hard_fault; /*
* < 0 if hardware error occured (e.g. i2c err)
* and prevents normal operation.
*/
};
#define MEI_DUMP_SKB_IN(info, skb) \
do { \
pr_debug("%s:\n", info); \
print_hex_dump(KERN_DEBUG, "mei in : ", DUMP_PREFIX_OFFSET, \
16, 1, (skb)->data, (skb)->len, 0); \
} while (0)
#define MEI_DUMP_SKB_OUT(info, skb) \
do { \
pr_debug("%s:\n", info); \
print_hex_dump(KERN_DEBUG, "mei out: ", DUMP_PREFIX_OFFSET, \
16, 1, (skb)->data, (skb)->len, 0); \
} while (0)
static int microread_mei_enable(void *phy_id)
{
struct microread_mei_phy *phy = phy_id;
pr_info(DRIVER_DESC ": %s\n", __func__);
phy->powered = 1;
return 0;
}
static void microread_mei_disable(void *phy_id)
{
struct microread_mei_phy *phy = phy_id;
pr_info(DRIVER_DESC ": %s\n", __func__);
phy->powered = 0;
}
/*
* Writing a frame must not return the number of written bytes.
* It must return either zero for success, or <0 for error.
* In addition, it must not alter the skb
*/
static int microread_mei_write(void *phy_id, struct sk_buff *skb)
{
struct microread_mei_phy *phy = phy_id;
int r;
MEI_DUMP_SKB_OUT("mei frame sent", skb);
r = mei_cl_send(phy->device, skb->data, skb->len);
if (r > 0)
r = 0;
return r;
}
static void microread_event_cb(struct mei_cl_device *device, u32 events,
void *context)
{
struct microread_mei_phy *phy = context;
if (phy->hard_fault != 0)
return;
if (events & BIT(MEI_CL_EVENT_RX)) {
struct sk_buff *skb;
int reply_size;
skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
if (!skb)
return;
reply_size = mei_cl_recv(device, skb->data, MEI_NFC_MAX_READ);
if (reply_size < MEI_NFC_HEADER_SIZE) {
kfree(skb);
return;
}
skb_put(skb, reply_size);
skb_pull(skb, MEI_NFC_HEADER_SIZE);
MEI_DUMP_SKB_IN("mei frame read", skb);
nfc_hci_recv_frame(phy->hdev, skb);
}
}
static struct nfc_phy_ops mei_phy_ops = {
.write = microread_mei_write,
.enable = microread_mei_enable,
.disable = microread_mei_disable,
};
static int microread_mei_probe(struct mei_cl_device *device,
const struct mei_cl_device_id *id)
{
struct microread_mei_phy *phy;
struct nfc_mei_phy *phy;
int r;
pr_info("Probing NFC microread\n");
phy = kzalloc(sizeof(struct microread_mei_phy), GFP_KERNEL);
phy = nfc_mei_phy_alloc(device);
if (!phy) {
pr_err("Cannot allocate memory for microread mei phy.\n");
return -ENOMEM;
}
phy->device = device;
mei_cl_set_drvdata(device, phy);
r = mei_cl_register_event_cb(device, microread_event_cb, phy);
r = mei_cl_register_event_cb(device, nfc_mei_event_cb, phy);
if (r) {
pr_err(MICROREAD_DRIVER_NAME ": event cb registration failed\n");
goto err_out;
......@@ -178,23 +58,22 @@ static int microread_mei_probe(struct mei_cl_device *device,
return 0;
err_out:
kfree(phy);
nfc_mei_phy_free(phy);
return r;
}
static int microread_mei_remove(struct mei_cl_device *device)
{
struct microread_mei_phy *phy = mei_cl_get_drvdata(device);
struct nfc_mei_phy *phy = mei_cl_get_drvdata(device);
pr_info("Removing microread\n");
microread_remove(phy->hdev);
if (phy->powered)
microread_mei_disable(phy);
nfc_mei_phy_disable(phy);
kfree(phy);
nfc_mei_phy_free(phy);
return 0;
}
......
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