aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2013-04-10 21:03:29 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-04-10 21:56:53 -0400
commit59fcd7c63abf0340f551f487264b67ff5f7a0b86 (patch)
treea9ed905d15418c50dc7b92cee1e4b17c4f62dbb3
parente46980a10a76ec3282dd6832c1974b880acd23d3 (diff)
mei: nfc: Initial nfc implementation
NFC ME device is exported through the MEI bus to be consumed by the NFC subsystem. NFC is represented by two mei clients: An info one and the actual NFC one. In order to properly build the ME id we first need to retrieve the firmware information from the info client and then disconnect from it. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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}