diff options
-rw-r--r-- | drivers/misc/mei/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/mei/client.c | 3 | ||||
-rw-r--r-- | drivers/misc/mei/init.c | 2 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 10 | ||||
-rw-r--r-- | drivers/misc/mei/nfc.c | 311 |
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 | |||
11 | mei-objs += amthif.o | 11 | mei-objs += amthif.o |
12 | mei-objs += wd.o | 12 | mei-objs += wd.o |
13 | mei-objs += bus.o | 13 | mei-objs += bus.o |
14 | mei-objs += nfc.o | ||
14 | mei-$(CONFIG_DEBUG_FS) += debugfs.o | 15 | mei-$(CONFIG_DEBUG_FS) += debugfs.o |
15 | 16 | ||
16 | obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o | 17 | obj-$(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 | ||
510 | void mei_amthif_run_next_cmd(struct mei_device *dev); | 510 | void mei_amthif_run_next_cmd(struct mei_device *dev); |
511 | 511 | ||
512 | /* | ||
513 | * NFC functions | ||
514 | */ | ||
515 | int mei_nfc_host_init(struct mei_device *dev); | ||
516 | void mei_nfc_host_exit(void); | ||
517 | |||
518 | /* | ||
519 | * NFC Client UUID | ||
520 | */ | ||
521 | extern const uuid_le mei_nfc_guid; | ||
512 | 522 | ||
513 | int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots, | 523 | int 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 | |||
27 | struct 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 | |||
37 | struct 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 | |||
48 | struct 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 | |||
58 | struct mei_nfc_connect { | ||
59 | u8 fw_ivn; | ||
60 | u8 vendor_id; | ||
61 | } __packed; | ||
62 | |||
63 | struct 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 | struct 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 | */ | ||
98 | struct 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 | |||
107 | static struct mei_nfc_dev nfc_dev; | ||
108 | |||
109 | /* UUIDs for NFC F/W clients */ | ||
110 | const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, | ||
111 | 0x94, 0xd4, 0x50, 0x26, | ||
112 | 0x67, 0x23, 0x77, 0x5c); | ||
113 | |||
114 | static const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d, | ||
115 | 0x48, 0xa4, 0xef, 0xab, | ||
116 | 0xba, 0x8a, 0x12, 0x06); | ||
117 | |||
118 | static 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 | |||
133 | static 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 | |||
179 | err: | ||
180 | kfree(reply); | ||
181 | return ret; | ||
182 | } | ||
183 | |||
184 | static 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 | |||
231 | err: | ||
232 | mei_nfc_free(ndev); | ||
233 | |||
234 | return; | ||
235 | } | ||
236 | |||
237 | |||
238 | int 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 | |||
300 | err: | ||
301 | mei_nfc_free(ndev); | ||
302 | |||
303 | return ret; | ||
304 | } | ||
305 | |||
306 | void mei_nfc_host_exit(void) | ||
307 | { | ||
308 | struct mei_nfc_dev *ndev = &nfc_dev; | ||
309 | |||
310 | mei_nfc_free(ndev); | ||
311 | } | ||