aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/misc/mei/Makefile1
-rw-r--r--drivers/misc/mei/client.c3
-rw-r--r--drivers/misc/mei/init.c2
-rw-r--r--drivers/misc/mei/mei_dev.h10
-rw-r--r--drivers/misc/mei/nfc.c311
5 files changed, 327 insertions, 0 deletions
diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile
index 3612d576a677..08698a466268 100644
--- a/drivers/misc/mei/Makefile
+++ b/drivers/misc/mei/Makefile
@@ -11,6 +11,7 @@ mei-objs += main.o
11mei-objs += amthif.o 11mei-objs += amthif.o
12mei-objs += wd.o 12mei-objs += wd.o
13mei-objs += bus.o 13mei-objs += bus.o
14mei-objs += nfc.o
14mei-$(CONFIG_DEBUG_FS) += debugfs.o 15mei-$(CONFIG_DEBUG_FS) += debugfs.o
15 16
16obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o 17obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index ecadd0053ba9..9541aa90d8f7 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -358,6 +358,9 @@ void mei_host_client_init(struct work_struct *work)
358 mei_amthif_host_init(dev); 358 mei_amthif_host_init(dev);
359 else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) 359 else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid))
360 mei_wd_host_init(dev); 360 mei_wd_host_init(dev);
361 else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid))
362 mei_nfc_host_init(dev);
363
361 } 364 }
362 365
363 dev->dev_state = MEI_DEV_ENABLED; 366 dev->dev_state = MEI_DEV_ENABLED;
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index 54b51c05fa41..4e102ad7ebc0 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -219,6 +219,8 @@ void mei_stop(struct mei_device *dev)
219 219
220 mei_wd_stop(dev); 220 mei_wd_stop(dev);
221 221
222 mei_nfc_host_exit();
223
222 dev->dev_state = MEI_DEV_POWER_DOWN; 224 dev->dev_state = MEI_DEV_POWER_DOWN;
223 mei_reset(dev, 0); 225 mei_reset(dev, 0);
224 226
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index c02967d01527..cd5b6d4bbbd4 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -509,6 +509,16 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
509 509
510void mei_amthif_run_next_cmd(struct mei_device *dev); 510void mei_amthif_run_next_cmd(struct mei_device *dev);
511 511
512/*
513 * NFC functions
514 */
515int mei_nfc_host_init(struct mei_device *dev);
516void mei_nfc_host_exit(void);
517
518/*
519 * NFC Client UUID
520 */
521extern const uuid_le mei_nfc_guid;
512 522
513int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, 523int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
514 struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); 524 struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list);
diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c
new file mode 100644
index 000000000000..37d4b09bc795
--- /dev/null
+++ b/drivers/misc/mei/nfc.c
@@ -0,0 +1,311 @@
1/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
4 * Copyright (c) 2003-2013, Intel Corporation.
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 it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 */
16
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/moduleparam.h>
20#include <linux/device.h>
21#include <linux/pci.h>
22#include <linux/mei_cl_bus.h>
23
24#include "mei_dev.h"
25#include "client.h"
26
27struct mei_nfc_cmd {
28 u8 command;
29 u8 status;
30 u16 req_id;
31 u32 reserved;
32 u16 data_size;
33 u8 sub_command;
34 u8 data[];
35} __packed;
36
37struct mei_nfc_reply {
38 u8 command;
39 u8 status;
40 u16 req_id;
41 u32 reserved;
42 u16 data_size;
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
72struct mei_nfc_hci_hdr {
73 u8 cmd;
74 u8 status;
75 u16 req_id;
76 u32 reserved;
77 u16 data_size;
78} __packed;
79
80#define MEI_NFC_CMD_MAINTENANCE 0x00
81#define MEI_NFC_CMD_HCI_SEND 0x01
82#define MEI_NFC_CMD_HCI_RECV 0x02
83
84#define MEI_NFC_SUBCMD_CONNECT 0x00
85#define MEI_NFC_SUBCMD_IF_VERSION 0x01
86
87#define MEI_NFC_HEADER_SIZE 10
88
89/** mei_nfc_dev - NFC mei device
90 *
91 * @cl: NFC host client
92 * @cl_info: NFC info host client
93 * @init_work: perform connection to the info client
94 * @fw_ivn: NFC Intervace Version Number
95 * @vendor_id: NFC manufacturer ID
96 * @radio_type: NFC radio type
97 */
98struct mei_nfc_dev {
99 struct mei_cl *cl;
100 struct mei_cl *cl_info;
101 struct work_struct init_work;
102 u8 fw_ivn;
103 u8 vendor_id;
104 u8 radio_type;
105};
106
107static struct mei_nfc_dev nfc_dev;
108
109/* UUIDs for NFC F/W clients */
110const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50,
111 0x94, 0xd4, 0x50, 0x26,
112 0x67, 0x23, 0x77, 0x5c);
113
114static const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d,
115 0x48, 0xa4, 0xef, 0xab,
116 0xba, 0x8a, 0x12, 0x06);
117
118static void mei_nfc_free(struct mei_nfc_dev *ndev)
119{
120 if (ndev->cl) {
121 list_del(&ndev->cl->device_link);
122 mei_cl_unlink(ndev->cl);
123 kfree(ndev->cl);
124 }
125
126 if (ndev->cl_info) {
127 list_del(&ndev->cl_info->device_link);
128 mei_cl_unlink(ndev->cl_info);
129 kfree(ndev->cl_info);
130 }
131}
132
133static int mei_nfc_if_version(struct mei_nfc_dev *ndev)
134{
135 struct mei_device *dev;
136 struct mei_cl *cl;
137
138 struct mei_nfc_cmd cmd;
139 struct mei_nfc_reply *reply = NULL;
140 struct mei_nfc_if_version *version;
141 size_t if_version_length;
142 int bytes_recv, ret;
143
144 cl = ndev->cl_info;
145 dev = cl->dev;
146
147 memset(&cmd, 0, sizeof(struct mei_nfc_cmd));
148 cmd.command = MEI_NFC_CMD_MAINTENANCE;
149 cmd.data_size = 1;
150 cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION;
151
152 ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd));
153 if (ret < 0) {
154 dev_err(&dev->pdev->dev, "Could not send IF version cmd\n");
155 return ret;
156 }
157
158 /* to be sure on the stack we alloc memory */
159 if_version_length = sizeof(struct mei_nfc_reply) +
160 sizeof(struct mei_nfc_if_version);
161
162 reply = kzalloc(if_version_length, GFP_KERNEL);
163 if (!reply)
164 return -ENOMEM;
165
166 bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length);
167 if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) {
168 dev_err(&dev->pdev->dev, "Could not read IF version\n");
169 ret = -EIO;
170 goto err;
171 }
172
173 version = (struct mei_nfc_if_version *)reply->data;
174
175 ndev->fw_ivn = version->fw_ivn;
176 ndev->vendor_id = version->vendor_id;
177 ndev->radio_type = version->radio_type;
178
179err:
180 kfree(reply);
181 return ret;
182}
183
184static void mei_nfc_init(struct work_struct *work)
185{
186 struct mei_device *dev;
187 struct mei_nfc_dev *ndev;
188 struct mei_cl *cl_info;
189
190 ndev = container_of(work, struct mei_nfc_dev, init_work);
191
192 cl_info = ndev->cl_info;
193 dev = cl_info->dev;
194
195 mutex_lock(&dev->device_lock);
196
197 if (mei_cl_connect(cl_info, NULL) < 0) {
198 mutex_unlock(&dev->device_lock);
199 dev_err(&dev->pdev->dev,
200 "Could not connect to the NFC INFO ME client");
201
202 goto err;
203 }
204
205 mutex_unlock(&dev->device_lock);
206
207 if (mei_nfc_if_version(ndev) < 0) {
208 dev_err(&dev->pdev->dev, "Could not get the NFC interfave version");
209
210 goto err;
211 }
212
213 dev_info(&dev->pdev->dev,
214 "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n",
215 ndev->fw_ivn, ndev->vendor_id, ndev->radio_type);
216
217 mutex_lock(&dev->device_lock);
218
219 if (mei_cl_disconnect(cl_info) < 0) {
220 mutex_unlock(&dev->device_lock);
221 dev_err(&dev->pdev->dev,
222 "Could not disconnect the NFC INFO ME client");
223
224 goto err;
225 }
226
227 mutex_unlock(&dev->device_lock);
228
229 return;
230
231err:
232 mei_nfc_free(ndev);
233
234 return;
235}
236
237
238int mei_nfc_host_init(struct mei_device *dev)
239{
240 struct mei_nfc_dev *ndev = &nfc_dev;
241 struct mei_cl *cl_info, *cl = NULL;
242 int i, ret;
243
244 /* already initialzed */
245 if (ndev->cl_info)
246 return 0;
247
248 cl_info = mei_cl_allocate(dev);
249 cl = mei_cl_allocate(dev);
250
251 if (!cl || !cl_info) {
252 ret = -ENOMEM;
253 goto err;
254 }
255
256 /* check for valid client id */
257 i = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid);
258 if (i < 0) {
259 dev_info(&dev->pdev->dev, "nfc: failed to find the client\n");
260 ret = -ENOENT;
261 goto err;
262 }
263
264 cl_info->me_client_id = dev->me_clients[i].client_id;
265
266 ret = mei_cl_link(cl_info, MEI_HOST_CLIENT_ID_ANY);
267 if (ret)
268 goto err;
269
270 cl_info->device_uuid = mei_nfc_info_guid;
271
272 list_add_tail(&cl_info->device_link, &dev->device_list);
273
274 /* check for valid client id */
275 i = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
276 if (i < 0) {
277 dev_info(&dev->pdev->dev, "nfc: failed to find the client\n");
278 ret = -ENOENT;
279 goto err;
280 }
281
282 cl->me_client_id = dev->me_clients[i].client_id;
283
284 ret = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY);
285 if (ret)
286 goto err;
287
288 cl->device_uuid = mei_nfc_guid;
289
290 list_add_tail(&cl->device_link, &dev->device_list);
291
292 ndev->cl_info = cl_info;
293 ndev->cl = cl;
294
295 INIT_WORK(&ndev->init_work, mei_nfc_init);
296 schedule_work(&ndev->init_work);
297
298 return 0;
299
300err:
301 mei_nfc_free(ndev);
302
303 return ret;
304}
305
306void mei_nfc_host_exit(void)
307{
308 struct mei_nfc_dev *ndev = &nfc_dev;
309
310 mei_nfc_free(ndev);
311}