Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NFC: microread: Add MEI physical layer

On some peculiar worlds, microreads are found hidden behind MEIs and needs
to be accessed through the ME bus.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

+254
+11
drivers/nfc/microread/Kconfig
··· 22 22 23 23 If you choose to build a module, it'll be called microread_i2c. 24 24 Say N if unsure. 25 + 26 + config NFC_MICROREAD_MEI 27 + tristate "NFC Microread MEI support" 28 + depends on NFC_MICROREAD && INTEL_MEI_BUS_NFC 29 + ---help--- 30 + This module adds support for the mei interface of adapters using 31 + Inside microread chipsets. Select this if your microread chipset 32 + is handled by Intel's Management Engine Interface on your platform. 33 + 34 + If you choose to build a module, it'll be called microread_mei. 35 + Say N if unsure.
+2
drivers/nfc/microread/Makefile
··· 3 3 # 4 4 5 5 microread_i2c-objs = i2c.o 6 + microread_mei-objs = mei.o 6 7 7 8 obj-$(CONFIG_NFC_MICROREAD) += microread.o 8 9 obj-$(CONFIG_NFC_MICROREAD_I2C) += microread_i2c.o 10 + obj-$(CONFIG_NFC_MICROREAD_MEI) += microread_mei.o
+241
drivers/nfc/microread/mei.c
··· 1 + /* 2 + * HCI based Driver for Inside Secure microread NFC Chip 3 + * 4 + * Copyright (C) 2013 Intel Corporation. All rights reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms and conditions of the GNU General Public License, 8 + * version 2, as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program; if not, write to the 17 + * Free Software Foundation, Inc., 18 + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 + */ 20 + 21 + #include <linux/module.h> 22 + #include <linux/slab.h> 23 + #include <linux/interrupt.h> 24 + #include <linux/gpio.h> 25 + #include <linux/mei_bus.h> 26 + 27 + #include <linux/nfc.h> 28 + #include <net/nfc/hci.h> 29 + #include <net/nfc/llc.h> 30 + 31 + #include "microread.h" 32 + 33 + #define MICROREAD_DRIVER_NAME "microread" 34 + 35 + #define MICROREAD_UUID UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, 0x94, \ 36 + 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) 37 + 38 + struct mei_nfc_hdr { 39 + u8 cmd; 40 + u8 status; 41 + u16 req_id; 42 + u32 reserved; 43 + u16 data_size; 44 + } __attribute__((packed)); 45 + 46 + #define MEI_NFC_HEADER_SIZE 10 47 + #define MEI_NFC_MAX_HCI_PAYLOAD 300 48 + #define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD) 49 + 50 + struct microread_mei_phy { 51 + struct mei_bus_client *client; 52 + struct nfc_hci_dev *hdev; 53 + 54 + int powered; 55 + 56 + int hard_fault; /* 57 + * < 0 if hardware error occured (e.g. i2c err) 58 + * and prevents normal operation. 59 + */ 60 + }; 61 + 62 + #define MEI_DUMP_SKB_IN(info, skb) \ 63 + do { \ 64 + pr_debug("%s:\n", info); \ 65 + print_hex_dump(KERN_DEBUG, "mei in : ", DUMP_PREFIX_OFFSET, \ 66 + 16, 1, (skb)->data, (skb)->len, 0); \ 67 + } while (0) 68 + 69 + #define MEI_DUMP_SKB_OUT(info, skb) \ 70 + do { \ 71 + pr_debug("%s:\n", info); \ 72 + print_hex_dump(KERN_DEBUG, "mei out: ", DUMP_PREFIX_OFFSET, \ 73 + 16, 1, (skb)->data, (skb)->len, 0); \ 74 + } while (0) 75 + 76 + static int microread_mei_enable(void *phy_id) 77 + { 78 + struct microread_mei_phy *phy = phy_id; 79 + 80 + pr_info(DRIVER_DESC ": %s\n", __func__); 81 + 82 + phy->powered = 1; 83 + 84 + return 0; 85 + } 86 + 87 + static void microread_mei_disable(void *phy_id) 88 + { 89 + struct microread_mei_phy *phy = phy_id; 90 + 91 + pr_info(DRIVER_DESC ": %s\n", __func__); 92 + 93 + phy->powered = 0; 94 + } 95 + 96 + /* 97 + * Writing a frame must not return the number of written bytes. 98 + * It must return either zero for success, or <0 for error. 99 + * In addition, it must not alter the skb 100 + */ 101 + static int microread_mei_write(void *phy_id, struct sk_buff *skb) 102 + { 103 + struct microread_mei_phy *phy = phy_id; 104 + int r; 105 + 106 + MEI_DUMP_SKB_OUT("mei frame sent", skb); 107 + 108 + r = mei_bus_send(phy->client, skb->data, skb->len); 109 + if (r > 0) 110 + r = 0; 111 + 112 + return r; 113 + } 114 + 115 + static void microread_event_cb(struct mei_bus_client *client, u32 events, 116 + void *context) 117 + { 118 + struct microread_mei_phy *phy = context; 119 + 120 + if (phy->hard_fault != 0) 121 + return; 122 + 123 + if (events & BIT(MEI_BUS_EVENT_RX)) { 124 + struct sk_buff *skb; 125 + int reply_size; 126 + 127 + skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL); 128 + if (!skb) 129 + return; 130 + 131 + reply_size = mei_bus_recv(client, skb->data, MEI_NFC_MAX_READ); 132 + if (reply_size < MEI_NFC_HEADER_SIZE) { 133 + kfree(skb); 134 + return; 135 + } 136 + 137 + skb_put(skb, reply_size); 138 + skb_pull(skb, MEI_NFC_HEADER_SIZE); 139 + 140 + MEI_DUMP_SKB_IN("mei frame read", skb); 141 + 142 + nfc_hci_recv_frame(phy->hdev, skb); 143 + } 144 + } 145 + 146 + static struct nfc_phy_ops mei_phy_ops = { 147 + .write = microread_mei_write, 148 + .enable = microread_mei_enable, 149 + .disable = microread_mei_disable, 150 + }; 151 + 152 + static int microread_mei_probe(struct mei_bus_client *client) 153 + { 154 + struct microread_mei_phy *phy; 155 + int r; 156 + 157 + pr_info("Probing NFC microread\n"); 158 + 159 + phy = kzalloc(sizeof(struct microread_mei_phy), GFP_KERNEL); 160 + if (!phy) { 161 + pr_err("Cannot allocate memory for microread mei phy.\n"); 162 + return -ENOMEM; 163 + } 164 + 165 + phy->client = client; 166 + mei_bus_set_clientdata(client, phy); 167 + 168 + r = mei_bus_register_event_cb(client, microread_event_cb, phy); 169 + if (r) { 170 + pr_err(MICROREAD_DRIVER_NAME ": event cb registration failed\n"); 171 + goto err_out; 172 + } 173 + 174 + r = microread_probe(phy, &mei_phy_ops, LLC_NOP_NAME, 175 + MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD, 176 + &phy->hdev); 177 + if (r < 0) 178 + goto err_out; 179 + 180 + return 0; 181 + 182 + err_out: 183 + kfree(phy); 184 + 185 + return r; 186 + } 187 + 188 + static int microread_mei_remove(struct mei_bus_client *client) 189 + { 190 + struct microread_mei_phy *phy = mei_bus_get_clientdata(client); 191 + 192 + pr_info("Removing microread\n"); 193 + 194 + microread_remove(phy->hdev); 195 + 196 + if (phy->powered) 197 + microread_mei_disable(phy); 198 + 199 + kfree(phy); 200 + 201 + return 0; 202 + } 203 + 204 + static struct mei_bus_driver microread_driver = { 205 + .driver = { 206 + .name = MICROREAD_DRIVER_NAME, 207 + }, 208 + .id = { 209 + .name = MICROREAD_DRIVER_NAME, 210 + .uuid = MICROREAD_UUID, 211 + }, 212 + 213 + .probe = microread_mei_probe, 214 + .remove = microread_mei_remove, 215 + }; 216 + 217 + static int microread_mei_init(void) 218 + { 219 + int r; 220 + 221 + pr_debug(DRIVER_DESC ": %s\n", __func__); 222 + 223 + r = mei_driver_register(&microread_driver); 224 + if (r) { 225 + pr_err(MICROREAD_DRIVER_NAME ": driver registration failed\n"); 226 + return r; 227 + } 228 + 229 + return 0; 230 + } 231 + 232 + static void microread_mei_exit(void) 233 + { 234 + mei_driver_unregister(&microread_driver); 235 + } 236 + 237 + module_init(microread_mei_init); 238 + module_exit(microread_mei_exit); 239 + 240 + MODULE_LICENSE("GPL"); 241 + MODULE_DESCRIPTION(DRIVER_DESC);