diff options
-rw-r--r-- | drivers/nfc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/nfc/Makefile | 1 | ||||
-rw-r--r-- | drivers/nfc/mei_phy.c | 164 | ||||
-rw-r--r-- | drivers/nfc/mei_phy.h | 30 | ||||
-rw-r--r-- | drivers/nfc/microread/Kconfig | 2 | ||||
-rw-r--r-- | drivers/nfc/microread/mei.c | 139 |
6 files changed, 215 insertions, 131 deletions
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index e57034971ccc..4775d4e61b88 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -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" diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index a189ada0926a..aa6bd657ef40 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -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 diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c new file mode 100644 index 000000000000..b8f8abc422f0 --- /dev/null +++ b/drivers/nfc/mei_phy.c @@ -0,0 +1,164 @@ +/* + * 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"); diff --git a/drivers/nfc/mei_phy.h b/drivers/nfc/mei_phy.h new file mode 100644 index 000000000000..d669900f8278 --- /dev/null +++ b/drivers/nfc/mei_phy.h @@ -0,0 +1,30 @@ +#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_ */ diff --git a/drivers/nfc/microread/Kconfig b/drivers/nfc/microread/Kconfig index 572305be6e37..951d5542f6bc 100644 --- a/drivers/nfc/microread/Kconfig +++ b/drivers/nfc/microread/Kconfig @@ -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 diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c index ca33ae193935..1ad044dce7b6 100644 --- a/drivers/nfc/microread/mei.c +++ b/drivers/nfc/microread/mei.c @@ -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; } |