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

Configure Feed

Select the types of activity you want to include in your feed.

at v4.16-rc5 416 lines 9.2 kB view raw
1/* 2 * MEI Library for mei bus nfc device access 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, see <http://www.gnu.org/licenses/>. 17 */ 18 19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 20 21#include <linux/module.h> 22#include <linux/slab.h> 23#include <linux/nfc.h> 24 25#include "mei_phy.h" 26 27struct mei_nfc_hdr { 28 u8 cmd; 29 u8 status; 30 u16 req_id; 31 u32 reserved; 32 u16 data_size; 33} __packed; 34 35struct mei_nfc_cmd { 36 struct mei_nfc_hdr hdr; 37 u8 sub_command; 38 u8 data[]; 39} __packed; 40 41struct mei_nfc_reply { 42 struct mei_nfc_hdr hdr; 43 u8 sub_command; 44 u8 reply_status; 45 u8 data[]; 46} __packed; 47 48struct mei_nfc_if_version { 49 u8 radio_version_sw[3]; 50 u8 reserved[3]; 51 u8 radio_version_hw[3]; 52 u8 i2c_addr; 53 u8 fw_ivn; 54 u8 vendor_id; 55 u8 radio_type; 56} __packed; 57 58struct mei_nfc_connect { 59 u8 fw_ivn; 60 u8 vendor_id; 61} __packed; 62 63struct mei_nfc_connect_resp { 64 u8 fw_ivn; 65 u8 vendor_id; 66 u16 me_major; 67 u16 me_minor; 68 u16 me_hotfix; 69 u16 me_build; 70} __packed; 71 72 73#define MEI_NFC_CMD_MAINTENANCE 0x00 74#define MEI_NFC_CMD_HCI_SEND 0x01 75#define MEI_NFC_CMD_HCI_RECV 0x02 76 77#define MEI_NFC_SUBCMD_CONNECT 0x00 78#define MEI_NFC_SUBCMD_IF_VERSION 0x01 79 80#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD) 81 82#define MEI_DUMP_SKB_IN(info, skb) \ 83do { \ 84 pr_debug("%s:\n", info); \ 85 print_hex_dump_debug("mei in : ", DUMP_PREFIX_OFFSET, \ 86 16, 1, (skb)->data, (skb)->len, false); \ 87} while (0) 88 89#define MEI_DUMP_SKB_OUT(info, skb) \ 90do { \ 91 pr_debug("%s:\n", info); \ 92 print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET, \ 93 16, 1, (skb)->data, (skb)->len, false); \ 94} while (0) 95 96#define MEI_DUMP_NFC_HDR(info, _hdr) \ 97do { \ 98 pr_debug("%s:\n", info); \ 99 pr_debug("cmd=%02d status=%d req_id=%d rsvd=%d size=%d\n", \ 100 (_hdr)->cmd, (_hdr)->status, (_hdr)->req_id, \ 101 (_hdr)->reserved, (_hdr)->data_size); \ 102} while (0) 103 104static int mei_nfc_if_version(struct nfc_mei_phy *phy) 105{ 106 107 struct mei_nfc_cmd cmd; 108 struct mei_nfc_reply *reply = NULL; 109 struct mei_nfc_if_version *version; 110 size_t if_version_length; 111 int bytes_recv, r; 112 113 pr_info("%s\n", __func__); 114 115 memset(&cmd, 0, sizeof(struct mei_nfc_cmd)); 116 cmd.hdr.cmd = MEI_NFC_CMD_MAINTENANCE; 117 cmd.hdr.data_size = 1; 118 cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; 119 120 MEI_DUMP_NFC_HDR("version", &cmd.hdr); 121 r = mei_cldev_send(phy->cldev, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); 122 if (r < 0) { 123 pr_err("Could not send IF version cmd\n"); 124 return r; 125 } 126 127 /* to be sure on the stack we alloc memory */ 128 if_version_length = sizeof(struct mei_nfc_reply) + 129 sizeof(struct mei_nfc_if_version); 130 131 reply = kzalloc(if_version_length, GFP_KERNEL); 132 if (!reply) 133 return -ENOMEM; 134 135 bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length); 136 if (bytes_recv < 0 || bytes_recv < if_version_length) { 137 pr_err("Could not read IF version\n"); 138 r = -EIO; 139 goto err; 140 } 141 142 version = (struct mei_nfc_if_version *)reply->data; 143 144 phy->fw_ivn = version->fw_ivn; 145 phy->vendor_id = version->vendor_id; 146 phy->radio_type = version->radio_type; 147 148err: 149 kfree(reply); 150 return r; 151} 152 153static int mei_nfc_connect(struct nfc_mei_phy *phy) 154{ 155 struct mei_nfc_cmd *cmd, *reply; 156 struct mei_nfc_connect *connect; 157 struct mei_nfc_connect_resp *connect_resp; 158 size_t connect_length, connect_resp_length; 159 int bytes_recv, r; 160 161 pr_info("%s\n", __func__); 162 163 connect_length = sizeof(struct mei_nfc_cmd) + 164 sizeof(struct mei_nfc_connect); 165 166 connect_resp_length = sizeof(struct mei_nfc_cmd) + 167 sizeof(struct mei_nfc_connect_resp); 168 169 cmd = kzalloc(connect_length, GFP_KERNEL); 170 if (!cmd) 171 return -ENOMEM; 172 connect = (struct mei_nfc_connect *)cmd->data; 173 174 reply = kzalloc(connect_resp_length, GFP_KERNEL); 175 if (!reply) { 176 kfree(cmd); 177 return -ENOMEM; 178 } 179 180 connect_resp = (struct mei_nfc_connect_resp *)reply->data; 181 182 cmd->hdr.cmd = MEI_NFC_CMD_MAINTENANCE; 183 cmd->hdr.data_size = 3; 184 cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; 185 connect->fw_ivn = phy->fw_ivn; 186 connect->vendor_id = phy->vendor_id; 187 188 MEI_DUMP_NFC_HDR("connect request", &cmd->hdr); 189 r = mei_cldev_send(phy->cldev, (u8 *)cmd, connect_length); 190 if (r < 0) { 191 pr_err("Could not send connect cmd %d\n", r); 192 goto err; 193 } 194 195 bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, 196 connect_resp_length); 197 if (bytes_recv < 0) { 198 r = bytes_recv; 199 pr_err("Could not read connect response %d\n", r); 200 goto err; 201 } 202 203 MEI_DUMP_NFC_HDR("connect reply", &reply->hdr); 204 205 pr_info("IVN 0x%x Vendor ID 0x%x\n", 206 connect_resp->fw_ivn, connect_resp->vendor_id); 207 208 pr_info("ME FW %d.%d.%d.%d\n", 209 connect_resp->me_major, connect_resp->me_minor, 210 connect_resp->me_hotfix, connect_resp->me_build); 211 212 r = 0; 213 214err: 215 kfree(reply); 216 kfree(cmd); 217 218 return r; 219} 220 221static int mei_nfc_send(struct nfc_mei_phy *phy, u8 *buf, size_t length) 222{ 223 struct mei_nfc_hdr *hdr; 224 u8 *mei_buf; 225 int err; 226 227 err = -ENOMEM; 228 mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); 229 if (!mei_buf) 230 goto out; 231 232 hdr = (struct mei_nfc_hdr *)mei_buf; 233 hdr->cmd = MEI_NFC_CMD_HCI_SEND; 234 hdr->status = 0; 235 hdr->req_id = phy->req_id; 236 hdr->reserved = 0; 237 hdr->data_size = length; 238 239 MEI_DUMP_NFC_HDR("send", hdr); 240 241 memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); 242 err = mei_cldev_send(phy->cldev, mei_buf, length + MEI_NFC_HEADER_SIZE); 243 if (err < 0) 244 goto out; 245 246 if (!wait_event_interruptible_timeout(phy->send_wq, 247 phy->recv_req_id == phy->req_id, HZ)) { 248 pr_err("NFC MEI command timeout\n"); 249 err = -ETIME; 250 } else { 251 phy->req_id++; 252 } 253out: 254 kfree(mei_buf); 255 return err; 256} 257 258/* 259 * Writing a frame must not return the number of written bytes. 260 * It must return either zero for success, or <0 for error. 261 * In addition, it must not alter the skb 262 */ 263static int nfc_mei_phy_write(void *phy_id, struct sk_buff *skb) 264{ 265 struct nfc_mei_phy *phy = phy_id; 266 int r; 267 268 MEI_DUMP_SKB_OUT("mei frame sent", skb); 269 270 r = mei_nfc_send(phy, skb->data, skb->len); 271 if (r > 0) 272 r = 0; 273 274 return r; 275} 276 277static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length) 278{ 279 struct mei_nfc_hdr *hdr; 280 int received_length; 281 282 received_length = mei_cldev_recv(phy->cldev, buf, length); 283 if (received_length < 0) 284 return received_length; 285 286 hdr = (struct mei_nfc_hdr *) buf; 287 288 MEI_DUMP_NFC_HDR("receive", hdr); 289 if (hdr->cmd == MEI_NFC_CMD_HCI_SEND) { 290 phy->recv_req_id = hdr->req_id; 291 wake_up(&phy->send_wq); 292 293 return 0; 294 } 295 296 return received_length; 297} 298 299 300static void nfc_mei_rx_cb(struct mei_cl_device *cldev) 301{ 302 struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev); 303 struct sk_buff *skb; 304 int reply_size; 305 306 if (!phy) 307 return; 308 309 if (phy->hard_fault != 0) 310 return; 311 312 skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL); 313 if (!skb) 314 return; 315 316 reply_size = mei_nfc_recv(phy, skb->data, MEI_NFC_MAX_READ); 317 if (reply_size < MEI_NFC_HEADER_SIZE) { 318 kfree_skb(skb); 319 return; 320 } 321 322 skb_put(skb, reply_size); 323 skb_pull(skb, MEI_NFC_HEADER_SIZE); 324 325 MEI_DUMP_SKB_IN("mei frame read", skb); 326 327 nfc_hci_recv_frame(phy->hdev, skb); 328} 329 330static int nfc_mei_phy_enable(void *phy_id) 331{ 332 int r; 333 struct nfc_mei_phy *phy = phy_id; 334 335 pr_info("%s\n", __func__); 336 337 if (phy->powered == 1) 338 return 0; 339 340 r = mei_cldev_enable(phy->cldev); 341 if (r < 0) { 342 pr_err("Could not enable device %d\n", r); 343 return r; 344 } 345 346 r = mei_nfc_if_version(phy); 347 if (r < 0) { 348 pr_err("Could not enable device %d\n", r); 349 goto err; 350 } 351 352 r = mei_nfc_connect(phy); 353 if (r < 0) { 354 pr_err("Could not connect to device %d\n", r); 355 goto err; 356 } 357 358 r = mei_cldev_register_rx_cb(phy->cldev, nfc_mei_rx_cb); 359 if (r) { 360 pr_err("Event cb registration failed %d\n", r); 361 goto err; 362 } 363 364 phy->powered = 1; 365 366 return 0; 367 368err: 369 phy->powered = 0; 370 mei_cldev_disable(phy->cldev); 371 return r; 372} 373 374static void nfc_mei_phy_disable(void *phy_id) 375{ 376 struct nfc_mei_phy *phy = phy_id; 377 378 pr_info("%s\n", __func__); 379 380 mei_cldev_disable(phy->cldev); 381 382 phy->powered = 0; 383} 384 385struct nfc_phy_ops mei_phy_ops = { 386 .write = nfc_mei_phy_write, 387 .enable = nfc_mei_phy_enable, 388 .disable = nfc_mei_phy_disable, 389}; 390EXPORT_SYMBOL_GPL(mei_phy_ops); 391 392struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *cldev) 393{ 394 struct nfc_mei_phy *phy; 395 396 phy = kzalloc(sizeof(struct nfc_mei_phy), GFP_KERNEL); 397 if (!phy) 398 return NULL; 399 400 phy->cldev = cldev; 401 init_waitqueue_head(&phy->send_wq); 402 mei_cldev_set_drvdata(cldev, phy); 403 404 return phy; 405} 406EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc); 407 408void nfc_mei_phy_free(struct nfc_mei_phy *phy) 409{ 410 mei_cldev_disable(phy->cldev); 411 kfree(phy); 412} 413EXPORT_SYMBOL_GPL(nfc_mei_phy_free); 414 415MODULE_LICENSE("GPL"); 416MODULE_DESCRIPTION("mei bus NFC device interface");