diff options
Diffstat (limited to 'drivers/nfc/mei_phy.c')
| -rw-r--r-- | drivers/nfc/mei_phy.c | 173 | 
1 files changed, 173 insertions, 0 deletions
diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c new file mode 100644 index 00000000000..11c7cbdade6 --- /dev/null +++ b/drivers/nfc/mei_phy.c @@ -0,0 +1,173 @@ +/* + * 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, see <http://www.gnu.org/licenses/>. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#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; +} __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("Could not enable device\n"); +		return r; +	} + +	r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy); +	if (r) { +		pr_err("Event cb registration failed\n"); +		mei_cl_disable_device(phy->device); +		phy->powered = 0; + +		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(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");  | 
