aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nfc/nfcwilink.c
diff options
context:
space:
mode:
authorIlan Elias <ilane@ti.com>2012-01-17 07:11:32 -0500
committerJohn W. Linville <linville@tuxdriver.com>2012-01-24 14:21:56 -0500
commit1195d89b2defd92829ed54938e60312e62dc8747 (patch)
treeade660adcea4217cf1af401d4ec0b078fe6d3860 /drivers/nfc/nfcwilink.c
parent3ed1326d2e693d555e62241c2e2209f01506214e (diff)
NFC: Download TI NFC init script
Download TI NFC init script during nfcwilink open operation, after the NFC channel is registered with TI shared transport. TI NFC init script is written in BTS format. First, read the chip version via a special vendor specific command. Second, we request the relevant BTS file from the user space, and then send the BTS commands to the chip. Signed-off-by: Ilan Elias <ilane@ti.com> Acked-by: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/nfc/nfcwilink.c')
-rw-r--r--drivers/nfc/nfcwilink.c288
1 files changed, 284 insertions, 4 deletions
diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c
index ee60d0844332..90af28d611f6 100644
--- a/drivers/nfc/nfcwilink.c
+++ b/drivers/nfc/nfcwilink.c
@@ -29,6 +29,7 @@
29#include <linux/platform_device.h> 29#include <linux/platform_device.h>
30#include <linux/module.h> 30#include <linux/module.h>
31#include <linux/types.h> 31#include <linux/types.h>
32#include <linux/firmware.h>
32#include <linux/nfc.h> 33#include <linux/nfc.h>
33#include <net/nfc/nci.h> 34#include <net/nfc/nci.h>
34#include <net/nfc/nci_core.h> 35#include <net/nfc/nci_core.h>
@@ -41,6 +42,17 @@
41#define NFCWILINK_OFFSET_LEN_IN_HDR 1 42#define NFCWILINK_OFFSET_LEN_IN_HDR 1
42#define NFCWILINK_LEN_SIZE 2 43#define NFCWILINK_LEN_SIZE 2
43#define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */ 44#define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */
45#define NFCWILINK_CMD_TIMEOUT 5000 /* 5 sec */
46
47#define BTS_FILE_NAME_MAX_SIZE 40
48#define BTS_FILE_HDR_MAGIC 0x42535442
49#define BTS_FILE_CMD_MAX_LEN 0xff
50#define BTS_FILE_ACTION_TYPE_SEND_CMD 1
51
52#define NCI_VS_NFCC_INFO_CMD_GID 0x2f
53#define NCI_VS_NFCC_INFO_CMD_OID 0x12
54#define NCI_VS_NFCC_INFO_RSP_GID 0x4f
55#define NCI_VS_NFCC_INFO_RSP_OID 0x12
44 56
45struct nfcwilink_hdr { 57struct nfcwilink_hdr {
46 __u8 chnl; 58 __u8 chnl;
@@ -48,6 +60,36 @@ struct nfcwilink_hdr {
48 __le16 len; 60 __le16 len;
49} __packed; 61} __packed;
50 62
63struct nci_vs_nfcc_info_cmd {
64 __u8 gid;
65 __u8 oid;
66 __u8 plen;
67} __packed;
68
69struct nci_vs_nfcc_info_rsp {
70 __u8 gid;
71 __u8 oid;
72 __u8 plen;
73 __u8 status;
74 __u8 hw_id;
75 __u8 sw_ver_x;
76 __u8 sw_ver_z;
77 __u8 patch_id;
78} __packed;
79
80struct bts_file_hdr {
81 __le32 magic;
82 __le32 ver;
83 __u8 rfu[24];
84 __u8 actions[0];
85} __packed;
86
87struct bts_file_action {
88 __le16 type;
89 __le16 len;
90 __u8 data[0];
91} __packed;
92
51struct nfcwilink { 93struct nfcwilink {
52 struct platform_device *pdev; 94 struct platform_device *pdev;
53 struct nci_dev *ndev; 95 struct nci_dev *ndev;
@@ -55,14 +97,241 @@ struct nfcwilink {
55 97
56 char st_register_cb_status; 98 char st_register_cb_status;
57 long (*st_write) (struct sk_buff *); 99 long (*st_write) (struct sk_buff *);
58 struct completion st_register_completed; 100
101 struct completion completed;
102
103 struct nci_vs_nfcc_info_rsp nfcc_info;
59}; 104};
60 105
61/* NFCWILINK driver flags */ 106/* NFCWILINK driver flags */
62enum { 107enum {
63 NFCWILINK_RUNNING, 108 NFCWILINK_RUNNING,
109 NFCWILINK_FW_DOWNLOAD,
64}; 110};
65 111
112static int nfcwilink_send(struct sk_buff *skb);
113
114static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how)
115{
116 struct sk_buff *skb;
117
118 skb = alloc_skb(len + NFCWILINK_HDR_LEN, how);
119 if (skb)
120 skb_reserve(skb, NFCWILINK_HDR_LEN);
121
122 return skb;
123}
124
125static void nfcwilink_fw_download_receive(struct nfcwilink *drv,
126 struct sk_buff *skb)
127{
128 struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data;
129
130 /* Detect NCI_VS_NFCC_INFO_RSP and store the result */
131 if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) &&
132 (rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) {
133 memcpy(&drv->nfcc_info, rsp,
134 sizeof(struct nci_vs_nfcc_info_rsp));
135 }
136
137 kfree_skb(skb);
138
139 complete(&drv->completed);
140}
141
142static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name)
143{
144 struct nci_vs_nfcc_info_cmd *cmd;
145 struct sk_buff *skb;
146 unsigned long comp_ret;
147 int rc;
148
149 nfc_dev_dbg(&drv->pdev->dev, "get_bts_file_name entry");
150
151 skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd),
152 GFP_KERNEL);
153 if (!skb) {
154 nfc_dev_err(&drv->pdev->dev,
155 "no memory for nci_vs_nfcc_info_cmd");
156 return -ENOMEM;
157 }
158
159 skb->dev = (void *)drv->ndev;
160
161 cmd = (struct nci_vs_nfcc_info_cmd *)
162 skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd));
163 cmd->gid = NCI_VS_NFCC_INFO_CMD_GID;
164 cmd->oid = NCI_VS_NFCC_INFO_CMD_OID;
165 cmd->plen = 0;
166
167 drv->nfcc_info.plen = 0;
168
169 rc = nfcwilink_send(skb);
170 if (rc)
171 return rc;
172
173 comp_ret = wait_for_completion_timeout(&drv->completed,
174 msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
175 nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
176 comp_ret);
177 if (comp_ret == 0) {
178 nfc_dev_err(&drv->pdev->dev,
179 "timeout on wait_for_completion_timeout");
180 return -ETIMEDOUT;
181 }
182
183 nfc_dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d",
184 drv->nfcc_info.plen,
185 drv->nfcc_info.status);
186
187 if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) {
188 nfc_dev_err(&drv->pdev->dev,
189 "invalid nci_vs_nfcc_info_rsp");
190 return -EINVAL;
191 }
192
193 snprintf(file_name, BTS_FILE_NAME_MAX_SIZE,
194 "TINfcInit_%d.%d.%d.%d.bts",
195 drv->nfcc_info.hw_id,
196 drv->nfcc_info.sw_ver_x,
197 drv->nfcc_info.sw_ver_z,
198 drv->nfcc_info.patch_id);
199
200 nfc_dev_info(&drv->pdev->dev, "nfcwilink FW file name: %s", file_name);
201
202 return 0;
203}
204
205static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len)
206{
207 struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data;
208 struct sk_buff *skb;
209 unsigned long comp_ret;
210 int rc;
211
212 nfc_dev_dbg(&drv->pdev->dev, "send_bts_cmd entry");
213
214 /* verify valid cmd for the NFC channel */
215 if ((len <= sizeof(struct nfcwilink_hdr)) ||
216 (len > BTS_FILE_CMD_MAX_LEN) ||
217 (hdr->chnl != NFCWILINK_CHNL) ||
218 (hdr->opcode != NFCWILINK_OPCODE)) {
219 nfc_dev_err(&drv->pdev->dev,
220 "ignoring invalid bts cmd, len %d, chnl %d, opcode %d",
221 len, hdr->chnl, hdr->opcode);
222 return 0;
223 }
224
225 /* remove the ST header */
226 len -= sizeof(struct nfcwilink_hdr);
227 data += sizeof(struct nfcwilink_hdr);
228
229 skb = nfcwilink_skb_alloc(len, GFP_KERNEL);
230 if (!skb) {
231 nfc_dev_err(&drv->pdev->dev, "no memory for bts cmd");
232 return -ENOMEM;
233 }
234
235 skb->dev = (void *)drv->ndev;
236
237 memcpy(skb_put(skb, len), data, len);
238
239 rc = nfcwilink_send(skb);
240 if (rc)
241 return rc;
242
243 comp_ret = wait_for_completion_timeout(&drv->completed,
244 msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT));
245 nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld",
246 comp_ret);
247 if (comp_ret == 0) {
248 nfc_dev_err(&drv->pdev->dev,
249 "timeout on wait_for_completion_timeout");
250 return -ETIMEDOUT;
251 }
252
253 return 0;
254}
255
256static int nfcwilink_download_fw(struct nfcwilink *drv)
257{
258 unsigned char file_name[BTS_FILE_NAME_MAX_SIZE];
259 const struct firmware *fw;
260 __u16 action_type, action_len;
261 __u8 *ptr;
262 int len, rc;
263
264 nfc_dev_dbg(&drv->pdev->dev, "download_fw entry");
265
266 set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
267
268 rc = nfcwilink_get_bts_file_name(drv, file_name);
269 if (rc)
270 goto exit;
271
272 rc = request_firmware(&fw, file_name, &drv->pdev->dev);
273 if (rc) {
274 nfc_dev_err(&drv->pdev->dev, "request_firmware failed %d", rc);
275
276 /* if the file is not found, don't exit with failure */
277 if (rc == -ENOENT)
278 rc = 0;
279
280 goto exit;
281 }
282
283 len = fw->size;
284 ptr = (__u8 *)fw->data;
285
286 if ((len == 0) || (ptr == NULL)) {
287 nfc_dev_dbg(&drv->pdev->dev,
288 "request_firmware returned size %d", len);
289 goto release_fw;
290 }
291
292 if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) !=
293 BTS_FILE_HDR_MAGIC) {
294 nfc_dev_err(&drv->pdev->dev, "wrong bts magic number");
295 rc = -EINVAL;
296 goto release_fw;
297 }
298
299 /* remove the BTS header */
300 len -= sizeof(struct bts_file_hdr);
301 ptr += sizeof(struct bts_file_hdr);
302
303 while (len > 0) {
304 action_type =
305 __le16_to_cpu(((struct bts_file_action *)ptr)->type);
306 action_len =
307 __le16_to_cpu(((struct bts_file_action *)ptr)->len);
308
309 nfc_dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d",
310 action_type, action_len);
311
312 switch (action_type) {
313 case BTS_FILE_ACTION_TYPE_SEND_CMD:
314 rc = nfcwilink_send_bts_cmd(drv,
315 ((struct bts_file_action *)ptr)->data,
316 action_len);
317 if (rc)
318 goto release_fw;
319 break;
320 }
321
322 /* advance to the next action */
323 len -= (sizeof(struct bts_file_action) + action_len);
324 ptr += (sizeof(struct bts_file_action) + action_len);
325 }
326
327release_fw:
328 release_firmware(fw);
329
330exit:
331 clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags);
332 return rc;
333}
334
66/* Called by ST when registration is complete */ 335/* Called by ST when registration is complete */
67static void nfcwilink_register_complete(void *priv_data, char data) 336static void nfcwilink_register_complete(void *priv_data, char data)
68{ 337{
@@ -74,7 +343,7 @@ static void nfcwilink_register_complete(void *priv_data, char data)
74 drv->st_register_cb_status = data; 343 drv->st_register_cb_status = data;
75 344
76 /* complete the wait in nfc_st_open() */ 345 /* complete the wait in nfc_st_open() */
77 complete(&drv->st_register_completed); 346 complete(&drv->completed);
78} 347}
79 348
80/* Called by ST when receive data is available */ 349/* Called by ST when receive data is available */
@@ -97,6 +366,11 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb)
97 (apart for the chnl byte, which is not received in the hdr) */ 366 (apart for the chnl byte, which is not received in the hdr) */
98 skb_pull(skb, (NFCWILINK_HDR_LEN-1)); 367 skb_pull(skb, (NFCWILINK_HDR_LEN-1));
99 368
369 if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) {
370 nfcwilink_fw_download_receive(drv, skb);
371 return 0;
372 }
373
100 skb->dev = (void *) drv->ndev; 374 skb->dev = (void *) drv->ndev;
101 375
102 /* Forward skb to NCI core layer */ 376 /* Forward skb to NCI core layer */
@@ -137,14 +411,14 @@ static int nfcwilink_open(struct nci_dev *ndev)
137 411
138 nfcwilink_proto.priv_data = drv; 412 nfcwilink_proto.priv_data = drv;
139 413
140 init_completion(&drv->st_register_completed); 414 init_completion(&drv->completed);
141 drv->st_register_cb_status = -EINPROGRESS; 415 drv->st_register_cb_status = -EINPROGRESS;
142 416
143 rc = st_register(&nfcwilink_proto); 417 rc = st_register(&nfcwilink_proto);
144 if (rc < 0) { 418 if (rc < 0) {
145 if (rc == -EINPROGRESS) { 419 if (rc == -EINPROGRESS) {
146 comp_ret = wait_for_completion_timeout( 420 comp_ret = wait_for_completion_timeout(
147 &drv->st_register_completed, 421 &drv->completed,
148 msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT)); 422 msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT));
149 423
150 nfc_dev_dbg(&drv->pdev->dev, 424 nfc_dev_dbg(&drv->pdev->dev,
@@ -172,6 +446,12 @@ static int nfcwilink_open(struct nci_dev *ndev)
172 BUG_ON(nfcwilink_proto.write == NULL); 446 BUG_ON(nfcwilink_proto.write == NULL);
173 drv->st_write = nfcwilink_proto.write; 447 drv->st_write = nfcwilink_proto.write;
174 448
449 if (nfcwilink_download_fw(drv)) {
450 nfc_dev_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d",
451 rc);
452 /* open should succeed, even if the FW download failed */
453 }
454
175 goto exit; 455 goto exit;
176 456
177clear_exit: 457clear_exit: