Commit | Line | Data |
---|---|---|
93aead46 IE |
1 | /* |
2 | * Texas Instrument's NFC Driver For Shared Transport. | |
3 | * | |
4 | * NFC Driver acts as interface between NCI core and | |
5 | * TI Shared Transport Layer. | |
6 | * | |
7 | * Copyright (C) 2011 Texas Instruments, Inc. | |
8 | * | |
9 | * Written by Ilan Elias <ilane@ti.com> | |
10 | * | |
11 | * Acknowledgements: | |
12 | * This file is based on btwilink.c, which was written | |
13 | * by Raja Mani and Pavan Savoy. | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License version 2 as | |
17 | * published by the Free Software Foundation. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | * GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
98b32dec | 25 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
93aead46 IE |
26 | * |
27 | */ | |
28 | #include <linux/platform_device.h> | |
baf79c33 | 29 | #include <linux/module.h> |
3ed1326d | 30 | #include <linux/types.h> |
1195d89b | 31 | #include <linux/firmware.h> |
93aead46 IE |
32 | #include <linux/nfc.h> |
33 | #include <net/nfc/nci.h> | |
34 | #include <net/nfc/nci_core.h> | |
35 | #include <linux/ti_wilink_st.h> | |
36 | ||
37 | #define NFCWILINK_CHNL 12 | |
38 | #define NFCWILINK_OPCODE 7 | |
39 | #define NFCWILINK_MAX_FRAME_SIZE 300 | |
40 | #define NFCWILINK_HDR_LEN 4 | |
41 | #define NFCWILINK_OFFSET_LEN_IN_HDR 1 | |
42 | #define NFCWILINK_LEN_SIZE 2 | |
43 | #define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */ | |
1195d89b IE |
44 | #define NFCWILINK_CMD_TIMEOUT 5000 /* 5 sec */ |
45 | ||
46 | #define BTS_FILE_NAME_MAX_SIZE 40 | |
47 | #define BTS_FILE_HDR_MAGIC 0x42535442 | |
48 | #define BTS_FILE_CMD_MAX_LEN 0xff | |
49 | #define BTS_FILE_ACTION_TYPE_SEND_CMD 1 | |
50 | ||
51 | #define NCI_VS_NFCC_INFO_CMD_GID 0x2f | |
52 | #define NCI_VS_NFCC_INFO_CMD_OID 0x12 | |
53 | #define NCI_VS_NFCC_INFO_RSP_GID 0x4f | |
54 | #define NCI_VS_NFCC_INFO_RSP_OID 0x12 | |
93aead46 IE |
55 | |
56 | struct nfcwilink_hdr { | |
3ed1326d IE |
57 | __u8 chnl; |
58 | __u8 opcode; | |
59 | __le16 len; | |
93aead46 IE |
60 | } __packed; |
61 | ||
1195d89b IE |
62 | struct nci_vs_nfcc_info_cmd { |
63 | __u8 gid; | |
64 | __u8 oid; | |
65 | __u8 plen; | |
66 | } __packed; | |
67 | ||
68 | struct nci_vs_nfcc_info_rsp { | |
69 | __u8 gid; | |
70 | __u8 oid; | |
71 | __u8 plen; | |
72 | __u8 status; | |
73 | __u8 hw_id; | |
74 | __u8 sw_ver_x; | |
75 | __u8 sw_ver_z; | |
76 | __u8 patch_id; | |
77 | } __packed; | |
78 | ||
79 | struct bts_file_hdr { | |
80 | __le32 magic; | |
81 | __le32 ver; | |
82 | __u8 rfu[24]; | |
83 | __u8 actions[0]; | |
84 | } __packed; | |
85 | ||
86 | struct bts_file_action { | |
87 | __le16 type; | |
88 | __le16 len; | |
89 | __u8 data[0]; | |
90 | } __packed; | |
91 | ||
93aead46 IE |
92 | struct nfcwilink { |
93 | struct platform_device *pdev; | |
94 | struct nci_dev *ndev; | |
95 | unsigned long flags; | |
96 | ||
97 | char st_register_cb_status; | |
98 | long (*st_write) (struct sk_buff *); | |
1195d89b IE |
99 | |
100 | struct completion completed; | |
101 | ||
102 | struct nci_vs_nfcc_info_rsp nfcc_info; | |
93aead46 IE |
103 | }; |
104 | ||
105 | /* NFCWILINK driver flags */ | |
106 | enum { | |
107 | NFCWILINK_RUNNING, | |
1195d89b | 108 | NFCWILINK_FW_DOWNLOAD, |
93aead46 IE |
109 | }; |
110 | ||
1095e69f | 111 | static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb); |
1195d89b IE |
112 | |
113 | static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how) | |
114 | { | |
115 | struct sk_buff *skb; | |
116 | ||
117 | skb = alloc_skb(len + NFCWILINK_HDR_LEN, how); | |
118 | if (skb) | |
119 | skb_reserve(skb, NFCWILINK_HDR_LEN); | |
120 | ||
121 | return skb; | |
122 | } | |
123 | ||
124 | static void nfcwilink_fw_download_receive(struct nfcwilink *drv, | |
125 | struct sk_buff *skb) | |
126 | { | |
127 | struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data; | |
128 | ||
129 | /* Detect NCI_VS_NFCC_INFO_RSP and store the result */ | |
130 | if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) && | |
131 | (rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) { | |
132 | memcpy(&drv->nfcc_info, rsp, | |
133 | sizeof(struct nci_vs_nfcc_info_rsp)); | |
134 | } | |
135 | ||
136 | kfree_skb(skb); | |
137 | ||
138 | complete(&drv->completed); | |
139 | } | |
140 | ||
141 | static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name) | |
142 | { | |
143 | struct nci_vs_nfcc_info_cmd *cmd; | |
144 | struct sk_buff *skb; | |
145 | unsigned long comp_ret; | |
146 | int rc; | |
147 | ||
1195d89b IE |
148 | skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd), |
149 | GFP_KERNEL); | |
150 | if (!skb) { | |
073a625f JP |
151 | nfc_err(&drv->pdev->dev, |
152 | "no memory for nci_vs_nfcc_info_cmd\n"); | |
1195d89b IE |
153 | return -ENOMEM; |
154 | } | |
155 | ||
1195d89b IE |
156 | cmd = (struct nci_vs_nfcc_info_cmd *) |
157 | skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd)); | |
158 | cmd->gid = NCI_VS_NFCC_INFO_CMD_GID; | |
159 | cmd->oid = NCI_VS_NFCC_INFO_CMD_OID; | |
160 | cmd->plen = 0; | |
161 | ||
162 | drv->nfcc_info.plen = 0; | |
163 | ||
1095e69f | 164 | rc = nfcwilink_send(drv->ndev, skb); |
1195d89b IE |
165 | if (rc) |
166 | return rc; | |
167 | ||
168 | comp_ret = wait_for_completion_timeout(&drv->completed, | |
169 | msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); | |
b4834839 JP |
170 | dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n", |
171 | comp_ret); | |
1195d89b | 172 | if (comp_ret == 0) { |
17936b43 | 173 | nfc_err(&drv->pdev->dev, |
b4834839 | 174 | "timeout on wait_for_completion_timeout\n"); |
1195d89b IE |
175 | return -ETIMEDOUT; |
176 | } | |
177 | ||
b4834839 JP |
178 | dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n", |
179 | drv->nfcc_info.plen, drv->nfcc_info.status); | |
1195d89b IE |
180 | |
181 | if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) { | |
073a625f | 182 | nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n"); |
1195d89b IE |
183 | return -EINVAL; |
184 | } | |
185 | ||
186 | snprintf(file_name, BTS_FILE_NAME_MAX_SIZE, | |
187 | "TINfcInit_%d.%d.%d.%d.bts", | |
188 | drv->nfcc_info.hw_id, | |
189 | drv->nfcc_info.sw_ver_x, | |
190 | drv->nfcc_info.sw_ver_z, | |
191 | drv->nfcc_info.patch_id); | |
192 | ||
073a625f | 193 | nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name); |
1195d89b IE |
194 | |
195 | return 0; | |
196 | } | |
197 | ||
198 | static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len) | |
199 | { | |
200 | struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data; | |
201 | struct sk_buff *skb; | |
202 | unsigned long comp_ret; | |
203 | int rc; | |
204 | ||
1195d89b IE |
205 | /* verify valid cmd for the NFC channel */ |
206 | if ((len <= sizeof(struct nfcwilink_hdr)) || | |
207 | (len > BTS_FILE_CMD_MAX_LEN) || | |
208 | (hdr->chnl != NFCWILINK_CHNL) || | |
209 | (hdr->opcode != NFCWILINK_OPCODE)) { | |
073a625f JP |
210 | nfc_err(&drv->pdev->dev, |
211 | "ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n", | |
1195d89b IE |
212 | len, hdr->chnl, hdr->opcode); |
213 | return 0; | |
214 | } | |
215 | ||
216 | /* remove the ST header */ | |
217 | len -= sizeof(struct nfcwilink_hdr); | |
218 | data += sizeof(struct nfcwilink_hdr); | |
219 | ||
220 | skb = nfcwilink_skb_alloc(len, GFP_KERNEL); | |
221 | if (!skb) { | |
073a625f | 222 | nfc_err(&drv->pdev->dev, "no memory for bts cmd\n"); |
1195d89b IE |
223 | return -ENOMEM; |
224 | } | |
225 | ||
1195d89b IE |
226 | memcpy(skb_put(skb, len), data, len); |
227 | ||
1095e69f | 228 | rc = nfcwilink_send(drv->ndev, skb); |
1195d89b IE |
229 | if (rc) |
230 | return rc; | |
231 | ||
232 | comp_ret = wait_for_completion_timeout(&drv->completed, | |
233 | msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); | |
b4834839 JP |
234 | dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n", |
235 | comp_ret); | |
1195d89b | 236 | if (comp_ret == 0) { |
073a625f JP |
237 | nfc_err(&drv->pdev->dev, |
238 | "timeout on wait_for_completion_timeout\n"); | |
1195d89b IE |
239 | return -ETIMEDOUT; |
240 | } | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
245 | static int nfcwilink_download_fw(struct nfcwilink *drv) | |
246 | { | |
247 | unsigned char file_name[BTS_FILE_NAME_MAX_SIZE]; | |
248 | const struct firmware *fw; | |
249 | __u16 action_type, action_len; | |
250 | __u8 *ptr; | |
251 | int len, rc; | |
252 | ||
1195d89b IE |
253 | set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); |
254 | ||
255 | rc = nfcwilink_get_bts_file_name(drv, file_name); | |
256 | if (rc) | |
257 | goto exit; | |
258 | ||
259 | rc = request_firmware(&fw, file_name, &drv->pdev->dev); | |
260 | if (rc) { | |
073a625f | 261 | nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc); |
1195d89b IE |
262 | |
263 | /* if the file is not found, don't exit with failure */ | |
264 | if (rc == -ENOENT) | |
265 | rc = 0; | |
266 | ||
267 | goto exit; | |
268 | } | |
269 | ||
270 | len = fw->size; | |
271 | ptr = (__u8 *)fw->data; | |
272 | ||
273 | if ((len == 0) || (ptr == NULL)) { | |
b4834839 JP |
274 | dev_dbg(&drv->pdev->dev, |
275 | "request_firmware returned size %d\n", len); | |
1195d89b IE |
276 | goto release_fw; |
277 | } | |
278 | ||
279 | if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) != | |
280 | BTS_FILE_HDR_MAGIC) { | |
073a625f | 281 | nfc_err(&drv->pdev->dev, "wrong bts magic number\n"); |
1195d89b IE |
282 | rc = -EINVAL; |
283 | goto release_fw; | |
284 | } | |
285 | ||
286 | /* remove the BTS header */ | |
287 | len -= sizeof(struct bts_file_hdr); | |
288 | ptr += sizeof(struct bts_file_hdr); | |
289 | ||
290 | while (len > 0) { | |
291 | action_type = | |
292 | __le16_to_cpu(((struct bts_file_action *)ptr)->type); | |
293 | action_len = | |
294 | __le16_to_cpu(((struct bts_file_action *)ptr)->len); | |
295 | ||
b4834839 JP |
296 | dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n", |
297 | action_type, action_len); | |
1195d89b IE |
298 | |
299 | switch (action_type) { | |
300 | case BTS_FILE_ACTION_TYPE_SEND_CMD: | |
301 | rc = nfcwilink_send_bts_cmd(drv, | |
302 | ((struct bts_file_action *)ptr)->data, | |
303 | action_len); | |
304 | if (rc) | |
305 | goto release_fw; | |
306 | break; | |
307 | } | |
308 | ||
309 | /* advance to the next action */ | |
310 | len -= (sizeof(struct bts_file_action) + action_len); | |
311 | ptr += (sizeof(struct bts_file_action) + action_len); | |
312 | } | |
313 | ||
314 | release_fw: | |
315 | release_firmware(fw); | |
316 | ||
317 | exit: | |
318 | clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); | |
319 | return rc; | |
320 | } | |
321 | ||
93aead46 IE |
322 | /* Called by ST when registration is complete */ |
323 | static void nfcwilink_register_complete(void *priv_data, char data) | |
324 | { | |
325 | struct nfcwilink *drv = priv_data; | |
326 | ||
93aead46 IE |
327 | /* store ST registration status */ |
328 | drv->st_register_cb_status = data; | |
329 | ||
330 | /* complete the wait in nfc_st_open() */ | |
1195d89b | 331 | complete(&drv->completed); |
93aead46 IE |
332 | } |
333 | ||
334 | /* Called by ST when receive data is available */ | |
335 | static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) | |
336 | { | |
337 | struct nfcwilink *drv = priv_data; | |
338 | int rc; | |
339 | ||
93aead46 IE |
340 | if (!skb) |
341 | return -EFAULT; | |
342 | ||
343 | if (!drv) { | |
344 | kfree_skb(skb); | |
345 | return -EFAULT; | |
346 | } | |
347 | ||
b4834839 | 348 | dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len); |
d6650a2c | 349 | |
93aead46 IE |
350 | /* strip the ST header |
351 | (apart for the chnl byte, which is not received in the hdr) */ | |
352 | skb_pull(skb, (NFCWILINK_HDR_LEN-1)); | |
353 | ||
1195d89b IE |
354 | if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) { |
355 | nfcwilink_fw_download_receive(drv, skb); | |
356 | return 0; | |
357 | } | |
358 | ||
93aead46 | 359 | /* Forward skb to NCI core layer */ |
1095e69f | 360 | rc = nci_recv_frame(drv->ndev, skb); |
93aead46 | 361 | if (rc < 0) { |
073a625f | 362 | nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc); |
93aead46 IE |
363 | return rc; |
364 | } | |
365 | ||
366 | return 0; | |
367 | } | |
368 | ||
369 | /* protocol structure registered with ST */ | |
370 | static struct st_proto_s nfcwilink_proto = { | |
371 | .chnl_id = NFCWILINK_CHNL, | |
372 | .max_frame_size = NFCWILINK_MAX_FRAME_SIZE, | |
373 | .hdr_len = (NFCWILINK_HDR_LEN-1), /* not including chnl byte */ | |
374 | .offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR, | |
375 | .len_size = NFCWILINK_LEN_SIZE, | |
376 | .reserve = 0, | |
377 | .recv = nfcwilink_receive, | |
378 | .reg_complete_cb = nfcwilink_register_complete, | |
379 | .write = NULL, | |
380 | }; | |
381 | ||
382 | static int nfcwilink_open(struct nci_dev *ndev) | |
383 | { | |
384 | struct nfcwilink *drv = nci_get_drvdata(ndev); | |
385 | unsigned long comp_ret; | |
386 | int rc; | |
387 | ||
93aead46 IE |
388 | if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) { |
389 | rc = -EBUSY; | |
390 | goto exit; | |
391 | } | |
392 | ||
393 | nfcwilink_proto.priv_data = drv; | |
394 | ||
1195d89b | 395 | init_completion(&drv->completed); |
93aead46 IE |
396 | drv->st_register_cb_status = -EINPROGRESS; |
397 | ||
398 | rc = st_register(&nfcwilink_proto); | |
399 | if (rc < 0) { | |
400 | if (rc == -EINPROGRESS) { | |
401 | comp_ret = wait_for_completion_timeout( | |
1195d89b | 402 | &drv->completed, |
93aead46 IE |
403 | msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT)); |
404 | ||
b4834839 JP |
405 | dev_dbg(&drv->pdev->dev, |
406 | "wait_for_completion_timeout returned %ld\n", | |
407 | comp_ret); | |
93aead46 IE |
408 | |
409 | if (comp_ret == 0) { | |
410 | /* timeout */ | |
411 | rc = -ETIMEDOUT; | |
412 | goto clear_exit; | |
413 | } else if (drv->st_register_cb_status != 0) { | |
414 | rc = drv->st_register_cb_status; | |
073a625f JP |
415 | nfc_err(&drv->pdev->dev, |
416 | "st_register_cb failed %d\n", rc); | |
93aead46 IE |
417 | goto clear_exit; |
418 | } | |
419 | } else { | |
073a625f | 420 | nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc); |
93aead46 IE |
421 | goto clear_exit; |
422 | } | |
423 | } | |
424 | ||
425 | /* st_register MUST fill the write callback */ | |
426 | BUG_ON(nfcwilink_proto.write == NULL); | |
427 | drv->st_write = nfcwilink_proto.write; | |
428 | ||
1195d89b | 429 | if (nfcwilink_download_fw(drv)) { |
073a625f JP |
430 | nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n", |
431 | rc); | |
1195d89b IE |
432 | /* open should succeed, even if the FW download failed */ |
433 | } | |
434 | ||
93aead46 IE |
435 | goto exit; |
436 | ||
437 | clear_exit: | |
438 | clear_bit(NFCWILINK_RUNNING, &drv->flags); | |
439 | ||
440 | exit: | |
441 | return rc; | |
442 | } | |
443 | ||
444 | static int nfcwilink_close(struct nci_dev *ndev) | |
445 | { | |
446 | struct nfcwilink *drv = nci_get_drvdata(ndev); | |
447 | int rc; | |
448 | ||
93aead46 IE |
449 | if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags)) |
450 | return 0; | |
451 | ||
452 | rc = st_unregister(&nfcwilink_proto); | |
453 | if (rc) | |
073a625f | 454 | nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc); |
93aead46 IE |
455 | |
456 | drv->st_write = NULL; | |
457 | ||
458 | return rc; | |
459 | } | |
460 | ||
1095e69f | 461 | static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb) |
93aead46 | 462 | { |
93aead46 IE |
463 | struct nfcwilink *drv = nci_get_drvdata(ndev); |
464 | struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000}; | |
465 | long len; | |
466 | ||
b4834839 | 467 | dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len); |
93aead46 | 468 | |
ea9917d6 IE |
469 | if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) { |
470 | kfree_skb(skb); | |
471 | return -EINVAL; | |
472 | } | |
93aead46 IE |
473 | |
474 | /* add the ST hdr to the start of the buffer */ | |
3ed1326d | 475 | hdr.len = cpu_to_le16(skb->len); |
93aead46 IE |
476 | memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN); |
477 | ||
478 | /* Insert skb to shared transport layer's transmit queue. | |
479 | * Freeing skb memory is taken care in shared transport layer, | |
480 | * so don't free skb memory here. | |
481 | */ | |
482 | len = drv->st_write(skb); | |
483 | if (len < 0) { | |
484 | kfree_skb(skb); | |
073a625f | 485 | nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len); |
93aead46 IE |
486 | return -EFAULT; |
487 | } | |
488 | ||
489 | return 0; | |
490 | } | |
491 | ||
492 | static struct nci_ops nfcwilink_ops = { | |
493 | .open = nfcwilink_open, | |
494 | .close = nfcwilink_close, | |
495 | .send = nfcwilink_send, | |
496 | }; | |
497 | ||
498 | static int nfcwilink_probe(struct platform_device *pdev) | |
499 | { | |
500 | static struct nfcwilink *drv; | |
501 | int rc; | |
3ed1326d | 502 | __u32 protocols; |
93aead46 | 503 | |
5f4d6214 | 504 | drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL); |
93aead46 IE |
505 | if (!drv) { |
506 | rc = -ENOMEM; | |
507 | goto exit; | |
508 | } | |
509 | ||
510 | drv->pdev = pdev; | |
511 | ||
512 | protocols = NFC_PROTO_JEWEL_MASK | |
01d719a2 SO |
513 | | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK |
514 | | NFC_PROTO_ISO14443_MASK | |
515 | | NFC_PROTO_ISO14443_B_MASK | |
516 | | NFC_PROTO_NFC_DEP_MASK; | |
93aead46 IE |
517 | |
518 | drv->ndev = nci_allocate_device(&nfcwilink_ops, | |
519 | protocols, | |
520 | NFCWILINK_HDR_LEN, | |
521 | 0); | |
522 | if (!drv->ndev) { | |
073a625f | 523 | nfc_err(&pdev->dev, "nci_allocate_device failed\n"); |
93aead46 | 524 | rc = -ENOMEM; |
5f4d6214 | 525 | goto exit; |
93aead46 IE |
526 | } |
527 | ||
528 | nci_set_parent_dev(drv->ndev, &pdev->dev); | |
529 | nci_set_drvdata(drv->ndev, drv); | |
530 | ||
531 | rc = nci_register_device(drv->ndev); | |
532 | if (rc < 0) { | |
073a625f | 533 | nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc); |
93aead46 IE |
534 | goto free_dev_exit; |
535 | } | |
536 | ||
537 | dev_set_drvdata(&pdev->dev, drv); | |
538 | ||
539 | goto exit; | |
540 | ||
541 | free_dev_exit: | |
542 | nci_free_device(drv->ndev); | |
543 | ||
93aead46 IE |
544 | exit: |
545 | return rc; | |
546 | } | |
547 | ||
548 | static int nfcwilink_remove(struct platform_device *pdev) | |
549 | { | |
550 | struct nfcwilink *drv = dev_get_drvdata(&pdev->dev); | |
551 | struct nci_dev *ndev; | |
552 | ||
93aead46 IE |
553 | if (!drv) |
554 | return -EFAULT; | |
555 | ||
556 | ndev = drv->ndev; | |
557 | ||
558 | nci_unregister_device(ndev); | |
559 | nci_free_device(ndev); | |
560 | ||
93aead46 IE |
561 | return 0; |
562 | } | |
563 | ||
564 | static struct platform_driver nfcwilink_driver = { | |
565 | .probe = nfcwilink_probe, | |
566 | .remove = nfcwilink_remove, | |
567 | .driver = { | |
568 | .name = "nfcwilink", | |
569 | .owner = THIS_MODULE, | |
570 | }, | |
571 | }; | |
572 | ||
058576dd | 573 | module_platform_driver(nfcwilink_driver); |
93aead46 IE |
574 | |
575 | /* ------ Module Info ------ */ | |
576 | ||
577 | MODULE_AUTHOR("Ilan Elias <ilane@ti.com>"); | |
578 | MODULE_DESCRIPTION("NFC Driver for TI Shared Transport"); | |
579 | MODULE_LICENSE("GPL"); |