diff options
author | Tomas Winkler <tomas.winkler@intel.com> | 2015-05-07 08:54:04 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-05-24 14:15:54 -0400 |
commit | be9b720a0ccba096d669bc86634f900b82b9bf71 (patch) | |
tree | 56218e7c1cec0f63c072d95ddbfaf5b63160e1b7 /drivers/nfc/mei_phy.c | |
parent | 007d64eb2232b91aa86b51abc1742936807e0bd4 (diff) |
NFC: mei_phy: move all nfc logic from mei driver to nfc
move nfc logic to mei_phy module, we prefer as much as
possible not to deal with a particualr client protocol
in the mei generic infrasutcutre
Cc: 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/nfc/mei_phy.c')
-rw-r--r-- | drivers/nfc/mei_phy.c | 293 |
1 files changed, 266 insertions, 27 deletions
diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 11c7cbdade66..7f1495d649bb 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c | |||
@@ -24,7 +24,52 @@ | |||
24 | 24 | ||
25 | #include "mei_phy.h" | 25 | #include "mei_phy.h" |
26 | 26 | ||
27 | struct mei_nfc_hdr { | 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 { | ||
28 | u8 cmd; | 73 | u8 cmd; |
29 | u8 status; | 74 | u8 status; |
30 | u16 req_id; | 75 | u16 req_id; |
@@ -32,6 +77,16 @@ struct mei_nfc_hdr { | |||
32 | u16 data_size; | 77 | u16 data_size; |
33 | } __packed; | 78 | } __packed; |
34 | 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 | |||
35 | #define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD) | 90 | #define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD) |
36 | 91 | ||
37 | #define MEI_DUMP_SKB_IN(info, skb) \ | 92 | #define MEI_DUMP_SKB_IN(info, skb) \ |
@@ -45,51 +100,156 @@ do { \ | |||
45 | do { \ | 100 | do { \ |
46 | pr_debug("%s:\n", info); \ | 101 | pr_debug("%s:\n", info); \ |
47 | print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET, \ | 102 | print_hex_dump_debug("mei out: ", DUMP_PREFIX_OFFSET, \ |
48 | 16, 1, (skb)->data, (skb)->len, false); \ | 103 | 16, 1, (skb)->data, (skb)->len, false); \ |
49 | } while (0) | 104 | } while (0) |
50 | 105 | ||
51 | int nfc_mei_phy_enable(void *phy_id) | 106 | |
107 | static int mei_nfc_if_version(struct nfc_mei_phy *phy) | ||
52 | { | 108 | { |
53 | int r; | 109 | |
54 | struct nfc_mei_phy *phy = phy_id; | 110 | struct mei_nfc_cmd cmd; |
111 | struct mei_nfc_reply *reply = NULL; | ||
112 | struct mei_nfc_if_version *version; | ||
113 | size_t if_version_length; | ||
114 | int bytes_recv, r; | ||
55 | 115 | ||
56 | pr_info("%s\n", __func__); | 116 | pr_info("%s\n", __func__); |
57 | 117 | ||
58 | if (phy->powered == 1) | 118 | memset(&cmd, 0, sizeof(struct mei_nfc_cmd)); |
59 | return 0; | 119 | cmd.command = MEI_NFC_CMD_MAINTENANCE; |
120 | cmd.data_size = 1; | ||
121 | cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; | ||
60 | 122 | ||
61 | r = mei_cl_enable_device(phy->device); | 123 | r = mei_cl_send(phy->device, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); |
62 | if (r < 0) { | 124 | if (r < 0) { |
63 | pr_err("Could not enable device\n"); | 125 | pr_err("Could not send IF version cmd\n"); |
64 | return r; | 126 | return r; |
65 | } | 127 | } |
66 | 128 | ||
67 | r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy); | 129 | /* to be sure on the stack we alloc memory */ |
68 | if (r) { | 130 | if_version_length = sizeof(struct mei_nfc_reply) + |
69 | pr_err("Event cb registration failed\n"); | 131 | sizeof(struct mei_nfc_if_version); |
70 | mei_cl_disable_device(phy->device); | ||
71 | phy->powered = 0; | ||
72 | 132 | ||
73 | return r; | 133 | reply = kzalloc(if_version_length, GFP_KERNEL); |
134 | if (!reply) | ||
135 | return -ENOMEM; | ||
136 | |||
137 | bytes_recv = mei_cl_recv(phy->device, (u8 *)reply, if_version_length); | ||
138 | if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { | ||
139 | pr_err("Could not read IF version\n"); | ||
140 | r = -EIO; | ||
141 | goto err; | ||
74 | } | 142 | } |
75 | 143 | ||
76 | phy->powered = 1; | 144 | version = (struct mei_nfc_if_version *)reply->data; |
77 | 145 | ||
78 | return 0; | 146 | phy->fw_ivn = version->fw_ivn; |
147 | phy->vendor_id = version->vendor_id; | ||
148 | phy->radio_type = version->radio_type; | ||
149 | |||
150 | err: | ||
151 | kfree(reply); | ||
152 | return r; | ||
79 | } | 153 | } |
80 | EXPORT_SYMBOL_GPL(nfc_mei_phy_enable); | ||
81 | 154 | ||
82 | void nfc_mei_phy_disable(void *phy_id) | 155 | static int mei_nfc_connect(struct nfc_mei_phy *phy) |
83 | { | 156 | { |
84 | struct nfc_mei_phy *phy = phy_id; | 157 | struct mei_nfc_cmd *cmd, *reply; |
158 | struct mei_nfc_connect *connect; | ||
159 | struct mei_nfc_connect_resp *connect_resp; | ||
160 | size_t connect_length, connect_resp_length; | ||
161 | int bytes_recv, r; | ||
85 | 162 | ||
86 | pr_info("%s\n", __func__); | 163 | pr_info("%s\n", __func__); |
87 | 164 | ||
88 | mei_cl_disable_device(phy->device); | 165 | connect_length = sizeof(struct mei_nfc_cmd) + |
166 | sizeof(struct mei_nfc_connect); | ||
89 | 167 | ||
90 | phy->powered = 0; | 168 | connect_resp_length = sizeof(struct mei_nfc_cmd) + |
169 | sizeof(struct mei_nfc_connect_resp); | ||
170 | |||
171 | cmd = kzalloc(connect_length, GFP_KERNEL); | ||
172 | if (!cmd) | ||
173 | return -ENOMEM; | ||
174 | connect = (struct mei_nfc_connect *)cmd->data; | ||
175 | |||
176 | reply = kzalloc(connect_resp_length, GFP_KERNEL); | ||
177 | if (!reply) { | ||
178 | kfree(cmd); | ||
179 | return -ENOMEM; | ||
180 | } | ||
181 | |||
182 | connect_resp = (struct mei_nfc_connect_resp *)reply->data; | ||
183 | |||
184 | cmd->command = MEI_NFC_CMD_MAINTENANCE; | ||
185 | cmd->data_size = 3; | ||
186 | cmd->sub_command = MEI_NFC_SUBCMD_CONNECT; | ||
187 | connect->fw_ivn = phy->fw_ivn; | ||
188 | connect->vendor_id = phy->vendor_id; | ||
189 | |||
190 | r = mei_cl_send(phy->device, (u8 *)cmd, connect_length); | ||
191 | if (r < 0) { | ||
192 | pr_err("Could not send connect cmd %d\n", r); | ||
193 | goto err; | ||
194 | } | ||
195 | |||
196 | bytes_recv = mei_cl_recv(phy->device, (u8 *)reply, connect_resp_length); | ||
197 | if (bytes_recv < 0) { | ||
198 | r = bytes_recv; | ||
199 | pr_err("Could not read connect response %d\n", r); | ||
200 | goto err; | ||
201 | } | ||
202 | |||
203 | pr_info("IVN 0x%x Vendor ID 0x%x\n", | ||
204 | connect_resp->fw_ivn, connect_resp->vendor_id); | ||
205 | |||
206 | pr_info("ME FW %d.%d.%d.%d\n", | ||
207 | connect_resp->me_major, connect_resp->me_minor, | ||
208 | connect_resp->me_hotfix, connect_resp->me_build); | ||
209 | |||
210 | r = 0; | ||
211 | |||
212 | err: | ||
213 | kfree(reply); | ||
214 | kfree(cmd); | ||
215 | |||
216 | return r; | ||
217 | } | ||
218 | |||
219 | static int mei_nfc_send(struct nfc_mei_phy *phy, u8 *buf, size_t length) | ||
220 | { | ||
221 | struct mei_nfc_hci_hdr *hdr; | ||
222 | u8 *mei_buf; | ||
223 | int err; | ||
224 | |||
225 | err = -ENOMEM; | ||
226 | mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); | ||
227 | if (!mei_buf) | ||
228 | goto out; | ||
229 | |||
230 | hdr = (struct mei_nfc_hci_hdr *) mei_buf; | ||
231 | hdr->cmd = MEI_NFC_CMD_HCI_SEND; | ||
232 | hdr->status = 0; | ||
233 | hdr->req_id = phy->req_id; | ||
234 | hdr->reserved = 0; | ||
235 | hdr->data_size = length; | ||
236 | |||
237 | memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); | ||
238 | err = mei_cl_send(phy->device, mei_buf, length + MEI_NFC_HEADER_SIZE); | ||
239 | if (err < 0) | ||
240 | goto out; | ||
241 | |||
242 | if (!wait_event_interruptible_timeout(phy->send_wq, | ||
243 | phy->recv_req_id == phy->req_id, HZ)) { | ||
244 | pr_err("NFC MEI command timeout\n"); | ||
245 | err = -ETIME; | ||
246 | } else { | ||
247 | phy->req_id++; | ||
248 | } | ||
249 | out: | ||
250 | kfree(mei_buf); | ||
251 | return err; | ||
91 | } | 252 | } |
92 | EXPORT_SYMBOL_GPL(nfc_mei_phy_disable); | ||
93 | 253 | ||
94 | /* | 254 | /* |
95 | * Writing a frame must not return the number of written bytes. | 255 | * Writing a frame must not return the number of written bytes. |
@@ -103,14 +263,37 @@ static int nfc_mei_phy_write(void *phy_id, struct sk_buff *skb) | |||
103 | 263 | ||
104 | MEI_DUMP_SKB_OUT("mei frame sent", skb); | 264 | MEI_DUMP_SKB_OUT("mei frame sent", skb); |
105 | 265 | ||
106 | r = mei_cl_send(phy->device, skb->data, skb->len); | 266 | r = mei_nfc_send(phy, skb->data, skb->len); |
107 | if (r > 0) | 267 | if (r > 0) |
108 | r = 0; | 268 | r = 0; |
109 | 269 | ||
110 | return r; | 270 | return r; |
111 | } | 271 | } |
112 | 272 | ||
113 | void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context) | 273 | static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length) |
274 | { | ||
275 | struct mei_nfc_hci_hdr *hci_hdr; | ||
276 | int received_length; | ||
277 | |||
278 | received_length = mei_cl_recv(phy->device, buf, length); | ||
279 | if (received_length < 0) | ||
280 | return received_length; | ||
281 | |||
282 | hci_hdr = (struct mei_nfc_hci_hdr *) buf; | ||
283 | |||
284 | if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) { | ||
285 | phy->recv_req_id = hci_hdr->req_id; | ||
286 | wake_up(&phy->send_wq); | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | return received_length; | ||
292 | } | ||
293 | |||
294 | |||
295 | static void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, | ||
296 | void *context) | ||
114 | { | 297 | { |
115 | struct nfc_mei_phy *phy = context; | 298 | struct nfc_mei_phy *phy = context; |
116 | 299 | ||
@@ -125,7 +308,7 @@ void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context) | |||
125 | if (!skb) | 308 | if (!skb) |
126 | return; | 309 | return; |
127 | 310 | ||
128 | reply_size = mei_cl_recv(device, skb->data, MEI_NFC_MAX_READ); | 311 | reply_size = mei_nfc_recv(phy, skb->data, MEI_NFC_MAX_READ); |
129 | if (reply_size < MEI_NFC_HEADER_SIZE) { | 312 | if (reply_size < MEI_NFC_HEADER_SIZE) { |
130 | kfree_skb(skb); | 313 | kfree_skb(skb); |
131 | return; | 314 | return; |
@@ -139,7 +322,61 @@ void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context) | |||
139 | nfc_hci_recv_frame(phy->hdev, skb); | 322 | nfc_hci_recv_frame(phy->hdev, skb); |
140 | } | 323 | } |
141 | } | 324 | } |
142 | EXPORT_SYMBOL_GPL(nfc_mei_event_cb); | 325 | |
326 | static int nfc_mei_phy_enable(void *phy_id) | ||
327 | { | ||
328 | int r; | ||
329 | struct nfc_mei_phy *phy = phy_id; | ||
330 | |||
331 | pr_info("%s\n", __func__); | ||
332 | |||
333 | if (phy->powered == 1) | ||
334 | return 0; | ||
335 | |||
336 | r = mei_cl_enable_device(phy->device); | ||
337 | if (r < 0) { | ||
338 | pr_err("Could not enable device %d\n", r); | ||
339 | return r; | ||
340 | } | ||
341 | |||
342 | r = mei_nfc_if_version(phy); | ||
343 | if (r < 0) { | ||
344 | pr_err("Could not enable device %d\n", r); | ||
345 | goto err; | ||
346 | } | ||
347 | |||
348 | r = mei_nfc_connect(phy); | ||
349 | if (r < 0) { | ||
350 | pr_err("Could not connect to device %d\n", r); | ||
351 | goto err; | ||
352 | } | ||
353 | |||
354 | r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy); | ||
355 | if (r) { | ||
356 | pr_err("Event cb registration failed %d\n", r); | ||
357 | goto err; | ||
358 | } | ||
359 | |||
360 | phy->powered = 1; | ||
361 | |||
362 | return 0; | ||
363 | |||
364 | err: | ||
365 | phy->powered = 0; | ||
366 | mei_cl_disable_device(phy->device); | ||
367 | return r; | ||
368 | } | ||
369 | |||
370 | static void nfc_mei_phy_disable(void *phy_id) | ||
371 | { | ||
372 | struct nfc_mei_phy *phy = phy_id; | ||
373 | |||
374 | pr_info("%s\n", __func__); | ||
375 | |||
376 | mei_cl_disable_device(phy->device); | ||
377 | |||
378 | phy->powered = 0; | ||
379 | } | ||
143 | 380 | ||
144 | struct nfc_phy_ops mei_phy_ops = { | 381 | struct nfc_phy_ops mei_phy_ops = { |
145 | .write = nfc_mei_phy_write, | 382 | .write = nfc_mei_phy_write, |
@@ -157,6 +394,7 @@ struct nfc_mei_phy *nfc_mei_phy_alloc(struct mei_cl_device *device) | |||
157 | return NULL; | 394 | return NULL; |
158 | 395 | ||
159 | phy->device = device; | 396 | phy->device = device; |
397 | init_waitqueue_head(&phy->send_wq); | ||
160 | mei_cl_set_drvdata(device, phy); | 398 | mei_cl_set_drvdata(device, phy); |
161 | 399 | ||
162 | return phy; | 400 | return phy; |
@@ -165,6 +403,7 @@ EXPORT_SYMBOL_GPL(nfc_mei_phy_alloc); | |||
165 | 403 | ||
166 | void nfc_mei_phy_free(struct nfc_mei_phy *phy) | 404 | void nfc_mei_phy_free(struct nfc_mei_phy *phy) |
167 | { | 405 | { |
406 | mei_cl_disable_device(phy->device); | ||
168 | kfree(phy); | 407 | kfree(phy); |
169 | } | 408 | } |
170 | EXPORT_SYMBOL_GPL(nfc_mei_phy_free); | 409 | EXPORT_SYMBOL_GPL(nfc_mei_phy_free); |