aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2013-04-10 21:03:31 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-04-10 21:56:53 -0400
commit36eda94fcf936ccee5a8693af7738174a56be898 (patch)
tree6cd7461b249a575077922f5d00fe85d1015c8a7e /drivers/misc
parent91a6b95f20e338ef63e55422b1f037665fc6440a (diff)
mei: nfc: Implement MEI bus ops
The send ops for NFC builds the command header, updates the request id and then waits for an ACK. The recv ops check if it receives data or an ACK and in the latter case wakes the send ops up. The enable ops sends the NFC HECI connect command. 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>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/mei/nfc.c170
1 files changed, 169 insertions, 1 deletions
diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c
index 33354d94c0c2..3adf8a70f26e 100644
--- a/drivers/misc/mei/nfc.c
+++ b/drivers/misc/mei/nfc.c
@@ -15,6 +15,7 @@
15 */ 15 */
16 16
17#include <linux/kernel.h> 17#include <linux/kernel.h>
18#include <linux/sched.h>
18#include <linux/module.h> 19#include <linux/module.h>
19#include <linux/moduleparam.h> 20#include <linux/moduleparam.h>
20#include <linux/device.h> 21#include <linux/device.h>
@@ -99,10 +100,14 @@ struct mei_nfc_dev {
99 struct mei_cl *cl; 100 struct mei_cl *cl;
100 struct mei_cl *cl_info; 101 struct mei_cl *cl_info;
101 struct work_struct init_work; 102 struct work_struct init_work;
103 wait_queue_head_t send_wq;
102 u8 fw_ivn; 104 u8 fw_ivn;
103 u8 vendor_id; 105 u8 vendor_id;
104 u8 radio_type; 106 u8 radio_type;
105 char *bus_name; 107 char *bus_name;
108
109 u16 req_id;
110 u16 recv_req_id;
106}; 111};
107 112
108static struct mei_nfc_dev nfc_dev; 113static struct mei_nfc_dev nfc_dev;
@@ -184,6 +189,73 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev)
184 return 0; 189 return 0;
185} 190}
186 191
192static int mei_nfc_connect(struct mei_nfc_dev *ndev)
193{
194 struct mei_device *dev;
195 struct mei_cl *cl;
196 struct mei_nfc_cmd *cmd, *reply;
197 struct mei_nfc_connect *connect;
198 struct mei_nfc_connect_resp *connect_resp;
199 size_t connect_length, connect_resp_length;
200 int bytes_recv, ret;
201
202 cl = ndev->cl;
203 dev = cl->dev;
204
205 connect_length = sizeof(struct mei_nfc_cmd) +
206 sizeof(struct mei_nfc_connect);
207
208 connect_resp_length = sizeof(struct mei_nfc_cmd) +
209 sizeof(struct mei_nfc_connect_resp);
210
211 cmd = kzalloc(connect_length, GFP_KERNEL);
212 if (!cmd)
213 return -ENOMEM;
214 connect = (struct mei_nfc_connect *)cmd->data;
215
216 reply = kzalloc(connect_resp_length, GFP_KERNEL);
217 if (!reply) {
218 kfree(cmd);
219 return -ENOMEM;
220 }
221
222 connect_resp = (struct mei_nfc_connect_resp *)reply->data;
223
224 cmd->command = MEI_NFC_CMD_MAINTENANCE;
225 cmd->data_size = 3;
226 cmd->sub_command = MEI_NFC_SUBCMD_CONNECT;
227 connect->fw_ivn = ndev->fw_ivn;
228 connect->vendor_id = ndev->vendor_id;
229
230 ret = __mei_cl_send(cl, (u8 *)cmd, connect_length);
231 if (ret < 0) {
232 dev_err(&dev->pdev->dev, "Could not send connect cmd\n");
233 goto err;
234 }
235
236 bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length);
237 if (bytes_recv < 0) {
238 dev_err(&dev->pdev->dev, "Could not read connect response\n");
239 ret = bytes_recv;
240 goto err;
241 }
242
243 dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n",
244 connect_resp->fw_ivn, connect_resp->vendor_id);
245
246 dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n",
247 connect_resp->me_major, connect_resp->me_minor,
248 connect_resp->me_hotfix, connect_resp->me_build);
249
250 ret = 0;
251
252err:
253 kfree(reply);
254 kfree(cmd);
255
256 return ret;
257}
258
187static int mei_nfc_if_version(struct mei_nfc_dev *ndev) 259static int mei_nfc_if_version(struct mei_nfc_dev *ndev)
188{ 260{
189 struct mei_device *dev; 261 struct mei_device *dev;
@@ -235,6 +307,100 @@ err:
235 return ret; 307 return ret;
236} 308}
237 309
310static int mei_nfc_enable(struct mei_cl_device *cldev)
311{
312 struct mei_device *dev;
313 struct mei_nfc_dev *ndev = &nfc_dev;
314 int ret;
315
316 dev = ndev->cl->dev;
317
318 ret = mei_nfc_connect(ndev);
319 if (ret < 0) {
320 dev_err(&dev->pdev->dev, "Could not connect to NFC");
321 return ret;
322 }
323
324 return 0;
325}
326
327static int mei_nfc_disable(struct mei_cl_device *cldev)
328{
329 return 0;
330}
331
332static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
333{
334 struct mei_device *dev;
335 struct mei_nfc_dev *ndev;
336 struct mei_nfc_hci_hdr *hdr;
337 u8 *mei_buf;
338 int err;
339
340 ndev = (struct mei_nfc_dev *) cldev->priv_data;
341 dev = ndev->cl->dev;
342
343 mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
344 if (!mei_buf)
345 return -ENOMEM;
346
347 hdr = (struct mei_nfc_hci_hdr *) mei_buf;
348 hdr->cmd = MEI_NFC_CMD_HCI_SEND;
349 hdr->status = 0;
350 hdr->req_id = ndev->req_id;
351 hdr->reserved = 0;
352 hdr->data_size = length;
353
354 memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
355
356 err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE);
357 if (err < 0)
358 return err;
359
360 kfree(mei_buf);
361
362 if (!wait_event_interruptible_timeout(ndev->send_wq,
363 ndev->recv_req_id == ndev->req_id, HZ)) {
364 dev_err(&dev->pdev->dev, "NFC MEI command timeout\n");
365 err = -ETIMEDOUT;
366 } else {
367 ndev->req_id++;
368 }
369
370 return err;
371}
372
373static int mei_nfc_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
374{
375 struct mei_nfc_dev *ndev;
376 struct mei_nfc_hci_hdr *hci_hdr;
377 int received_length;
378
379 ndev = (struct mei_nfc_dev *)cldev->priv_data;
380
381 received_length = __mei_cl_recv(ndev->cl, buf, length);
382 if (received_length < 0)
383 return received_length;
384
385 hci_hdr = (struct mei_nfc_hci_hdr *) buf;
386
387 if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) {
388 ndev->recv_req_id = hci_hdr->req_id;
389 wake_up(&ndev->send_wq);
390
391 return 0;
392 }
393
394 return received_length;
395}
396
397static struct mei_cl_ops nfc_ops = {
398 .enable = mei_nfc_enable,
399 .disable = mei_nfc_disable,
400 .send = mei_nfc_send,
401 .recv = mei_nfc_recv,
402};
403
238static void mei_nfc_init(struct work_struct *work) 404static void mei_nfc_init(struct work_struct *work)
239{ 405{
240 struct mei_device *dev; 406 struct mei_device *dev;
@@ -287,7 +453,7 @@ static void mei_nfc_init(struct work_struct *work)
287 return; 453 return;
288 } 454 }
289 455
290 cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, NULL); 456 cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, &nfc_ops);
291 if (!cldev) { 457 if (!cldev) {
292 dev_err(&dev->pdev->dev, 458 dev_err(&dev->pdev->dev,
293 "Could not add the NFC device to the MEI bus\n"); 459 "Could not add the NFC device to the MEI bus\n");
@@ -363,8 +529,10 @@ int mei_nfc_host_init(struct mei_device *dev)
363 529
364 ndev->cl_info = cl_info; 530 ndev->cl_info = cl_info;
365 ndev->cl = cl; 531 ndev->cl = cl;
532 ndev->req_id = 1;
366 533
367 INIT_WORK(&ndev->init_work, mei_nfc_init); 534 INIT_WORK(&ndev->init_work, mei_nfc_init);
535 init_waitqueue_head(&ndev->send_wq);
368 schedule_work(&ndev->init_work); 536 schedule_work(&ndev->init_work);
369 537
370 return 0; 538 return 0;