diff options
Diffstat (limited to 'drivers/bluetooth/btmrvl_main.c')
-rw-r--r-- | drivers/bluetooth/btmrvl_main.c | 269 |
1 files changed, 176 insertions, 93 deletions
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 9a9f51875df5..6e7bd4e4adbb 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c | |||
@@ -57,8 +57,7 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb) | |||
57 | ocf = hci_opcode_ocf(opcode); | 57 | ocf = hci_opcode_ocf(opcode); |
58 | ogf = hci_opcode_ogf(opcode); | 58 | ogf = hci_opcode_ogf(opcode); |
59 | 59 | ||
60 | if (ocf == BT_CMD_MODULE_CFG_REQ && | 60 | if (priv->btmrvl_dev.sendcmdflag) { |
61 | priv->btmrvl_dev.sendcmdflag) { | ||
62 | priv->btmrvl_dev.sendcmdflag = false; | 61 | priv->btmrvl_dev.sendcmdflag = false; |
63 | priv->adapter->cmd_complete = true; | 62 | priv->adapter->cmd_complete = true; |
64 | wake_up_interruptible(&priv->adapter->cmd_wait_q); | 63 | wake_up_interruptible(&priv->adapter->cmd_wait_q); |
@@ -116,7 +115,6 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) | |||
116 | adapter->hs_state = HS_ACTIVATED; | 115 | adapter->hs_state = HS_ACTIVATED; |
117 | if (adapter->psmode) | 116 | if (adapter->psmode) |
118 | adapter->ps_state = PS_SLEEP; | 117 | adapter->ps_state = PS_SLEEP; |
119 | wake_up_interruptible(&adapter->cmd_wait_q); | ||
120 | BT_DBG("HS ACTIVATED!"); | 118 | BT_DBG("HS ACTIVATED!"); |
121 | } else { | 119 | } else { |
122 | BT_DBG("HS Enable failed"); | 120 | BT_DBG("HS Enable failed"); |
@@ -168,22 +166,24 @@ exit: | |||
168 | } | 166 | } |
169 | EXPORT_SYMBOL_GPL(btmrvl_process_event); | 167 | EXPORT_SYMBOL_GPL(btmrvl_process_event); |
170 | 168 | ||
171 | int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) | 169 | static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 cmd_no, |
170 | const void *param, u8 len) | ||
172 | { | 171 | { |
173 | struct sk_buff *skb; | 172 | struct sk_buff *skb; |
174 | struct btmrvl_cmd *cmd; | 173 | struct hci_command_hdr *hdr; |
175 | int ret = 0; | ||
176 | 174 | ||
177 | skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); | 175 | skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC); |
178 | if (skb == NULL) { | 176 | if (skb == NULL) { |
179 | BT_ERR("No free skb"); | 177 | BT_ERR("No free skb"); |
180 | return -ENOMEM; | 178 | return -ENOMEM; |
181 | } | 179 | } |
182 | 180 | ||
183 | cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); | 181 | hdr = (struct hci_command_hdr *)skb_put(skb, HCI_COMMAND_HDR_SIZE); |
184 | cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ)); | 182 | hdr->opcode = cpu_to_le16(hci_opcode_pack(OGF, cmd_no)); |
185 | cmd->length = 1; | 183 | hdr->plen = len; |
186 | cmd->data[0] = subcmd; | 184 | |
185 | if (len) | ||
186 | memcpy(skb_put(skb, len), param, len); | ||
187 | 187 | ||
188 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; | 188 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; |
189 | 189 | ||
@@ -194,19 +194,23 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) | |||
194 | 194 | ||
195 | priv->adapter->cmd_complete = false; | 195 | priv->adapter->cmd_complete = false; |
196 | 196 | ||
197 | BT_DBG("Queue module cfg Command"); | ||
198 | |||
199 | wake_up_interruptible(&priv->main_thread.wait_q); | 197 | wake_up_interruptible(&priv->main_thread.wait_q); |
200 | 198 | ||
201 | if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, | 199 | if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, |
202 | priv->adapter->cmd_complete, | 200 | priv->adapter->cmd_complete, |
203 | msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) { | 201 | msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) |
204 | ret = -ETIMEDOUT; | 202 | return -ETIMEDOUT; |
205 | BT_ERR("module_cfg_cmd(%x): timeout: %d", | ||
206 | subcmd, priv->btmrvl_dev.sendcmdflag); | ||
207 | } | ||
208 | 203 | ||
209 | BT_DBG("module cfg Command done"); | 204 | return 0; |
205 | } | ||
206 | |||
207 | int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) | ||
208 | { | ||
209 | int ret; | ||
210 | |||
211 | ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1); | ||
212 | if (ret) | ||
213 | BT_ERR("module_cfg_cmd(%x) failed\n", subcmd); | ||
210 | 214 | ||
211 | return ret; | 215 | return ret; |
212 | } | 216 | } |
@@ -214,61 +218,36 @@ EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd); | |||
214 | 218 | ||
215 | int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) | 219 | int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) |
216 | { | 220 | { |
217 | struct sk_buff *skb; | 221 | int ret; |
218 | struct btmrvl_cmd *cmd; | 222 | u8 param[2]; |
219 | |||
220 | skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); | ||
221 | if (!skb) { | ||
222 | BT_ERR("No free skb"); | ||
223 | return -ENOMEM; | ||
224 | } | ||
225 | |||
226 | cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); | ||
227 | cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, | ||
228 | BT_CMD_HOST_SLEEP_CONFIG)); | ||
229 | cmd->length = 2; | ||
230 | cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; | ||
231 | cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff); | ||
232 | 223 | ||
233 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; | 224 | param[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; |
225 | param[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff); | ||
234 | 226 | ||
235 | skb->dev = (void *) priv->btmrvl_dev.hcidev; | 227 | BT_DBG("Sending HSCFG Command, gpio=0x%x, gap=0x%x", |
236 | skb_queue_head(&priv->adapter->tx_queue, skb); | 228 | param[0], param[1]); |
237 | 229 | ||
238 | BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x", cmd->data[0], | 230 | ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2); |
239 | cmd->data[1]); | 231 | if (ret) |
232 | BT_ERR("HSCFG command failed\n"); | ||
240 | 233 | ||
241 | return 0; | 234 | return ret; |
242 | } | 235 | } |
243 | EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd); | 236 | EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd); |
244 | 237 | ||
245 | int btmrvl_enable_ps(struct btmrvl_private *priv) | 238 | int btmrvl_enable_ps(struct btmrvl_private *priv) |
246 | { | 239 | { |
247 | struct sk_buff *skb; | 240 | int ret; |
248 | struct btmrvl_cmd *cmd; | 241 | u8 param; |
249 | |||
250 | skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); | ||
251 | if (skb == NULL) { | ||
252 | BT_ERR("No free skb"); | ||
253 | return -ENOMEM; | ||
254 | } | ||
255 | |||
256 | cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); | ||
257 | cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, | ||
258 | BT_CMD_AUTO_SLEEP_MODE)); | ||
259 | cmd->length = 1; | ||
260 | 242 | ||
261 | if (priv->btmrvl_dev.psmode) | 243 | if (priv->btmrvl_dev.psmode) |
262 | cmd->data[0] = BT_PS_ENABLE; | 244 | param = BT_PS_ENABLE; |
263 | else | 245 | else |
264 | cmd->data[0] = BT_PS_DISABLE; | 246 | param = BT_PS_DISABLE; |
265 | |||
266 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; | ||
267 | 247 | ||
268 | skb->dev = (void *) priv->btmrvl_dev.hcidev; | 248 | ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, ¶m, 1); |
269 | skb_queue_head(&priv->adapter->tx_queue, skb); | 249 | if (ret) |
270 | 250 | BT_ERR("PSMODE command failed\n"); | |
271 | BT_DBG("Queue PSMODE Command:%d", cmd->data[0]); | ||
272 | 251 | ||
273 | return 0; | 252 | return 0; |
274 | } | 253 | } |
@@ -276,37 +255,11 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps); | |||
276 | 255 | ||
277 | int btmrvl_enable_hs(struct btmrvl_private *priv) | 256 | int btmrvl_enable_hs(struct btmrvl_private *priv) |
278 | { | 257 | { |
279 | struct sk_buff *skb; | 258 | int ret; |
280 | struct btmrvl_cmd *cmd; | ||
281 | int ret = 0; | ||
282 | |||
283 | skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); | ||
284 | if (skb == NULL) { | ||
285 | BT_ERR("No free skb"); | ||
286 | return -ENOMEM; | ||
287 | } | ||
288 | |||
289 | cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); | ||
290 | cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_ENABLE)); | ||
291 | cmd->length = 0; | ||
292 | |||
293 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; | ||
294 | |||
295 | skb->dev = (void *) priv->btmrvl_dev.hcidev; | ||
296 | skb_queue_head(&priv->adapter->tx_queue, skb); | ||
297 | |||
298 | BT_DBG("Queue hs enable Command"); | ||
299 | |||
300 | wake_up_interruptible(&priv->main_thread.wait_q); | ||
301 | 259 | ||
302 | if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, | 260 | ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); |
303 | priv->adapter->hs_state, | 261 | if (ret) |
304 | msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) { | 262 | BT_ERR("Host sleep enable command failed\n"); |
305 | ret = -ETIMEDOUT; | ||
306 | BT_ERR("timeout: %d, %d,%d", priv->adapter->hs_state, | ||
307 | priv->adapter->ps_state, | ||
308 | priv->adapter->wakeup_tries); | ||
309 | } | ||
310 | 263 | ||
311 | return ret; | 264 | return ret; |
312 | } | 265 | } |
@@ -480,6 +433,137 @@ static int btmrvl_open(struct hci_dev *hdev) | |||
480 | } | 433 | } |
481 | 434 | ||
482 | /* | 435 | /* |
436 | * This function parses provided calibration data input. It should contain | ||
437 | * hex bytes separated by space or new line character. Here is an example. | ||
438 | * 00 1C 01 37 FF FF FF FF 02 04 7F 01 | ||
439 | * CE BA 00 00 00 2D C6 C0 00 00 00 00 | ||
440 | * 00 F0 00 00 | ||
441 | */ | ||
442 | static int btmrvl_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 dst_size) | ||
443 | { | ||
444 | const u8 *s = src; | ||
445 | u8 *d = dst; | ||
446 | int ret; | ||
447 | u8 tmp[3]; | ||
448 | |||
449 | tmp[2] = '\0'; | ||
450 | while ((s - src) <= len - 2) { | ||
451 | if (isspace(*s)) { | ||
452 | s++; | ||
453 | continue; | ||
454 | } | ||
455 | |||
456 | if (isxdigit(*s)) { | ||
457 | if ((d - dst) >= dst_size) { | ||
458 | BT_ERR("calibration data file too big!!!"); | ||
459 | return -EINVAL; | ||
460 | } | ||
461 | |||
462 | memcpy(tmp, s, 2); | ||
463 | |||
464 | ret = kstrtou8(tmp, 16, d++); | ||
465 | if (ret < 0) | ||
466 | return ret; | ||
467 | |||
468 | s += 2; | ||
469 | } else { | ||
470 | return -EINVAL; | ||
471 | } | ||
472 | } | ||
473 | if (d == dst) | ||
474 | return -EINVAL; | ||
475 | |||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static int btmrvl_load_cal_data(struct btmrvl_private *priv, | ||
480 | u8 *config_data) | ||
481 | { | ||
482 | int i, ret; | ||
483 | u8 data[BT_CMD_DATA_SIZE]; | ||
484 | |||
485 | data[0] = 0x00; | ||
486 | data[1] = 0x00; | ||
487 | data[2] = 0x00; | ||
488 | data[3] = BT_CMD_DATA_SIZE - 4; | ||
489 | |||
490 | /* Swap cal-data bytes. Each four bytes are swapped. Considering 4 | ||
491 | * byte SDIO header offset, mapping of input and output bytes will be | ||
492 | * {3, 2, 1, 0} -> {0+4, 1+4, 2+4, 3+4}, | ||
493 | * {7, 6, 5, 4} -> {4+4, 5+4, 6+4, 7+4} */ | ||
494 | for (i = 4; i < BT_CMD_DATA_SIZE; i++) | ||
495 | data[i] = config_data[(i / 4) * 8 - 1 - i]; | ||
496 | |||
497 | print_hex_dump_bytes("Calibration data: ", | ||
498 | DUMP_PREFIX_OFFSET, data, BT_CMD_DATA_SIZE); | ||
499 | |||
500 | ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data, | ||
501 | BT_CMD_DATA_SIZE); | ||
502 | if (ret) | ||
503 | BT_ERR("Failed to download caibration data\n"); | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static int | ||
509 | btmrvl_process_cal_cfg(struct btmrvl_private *priv, u8 *data, u32 size) | ||
510 | { | ||
511 | u8 cal_data[BT_CAL_DATA_SIZE]; | ||
512 | int ret; | ||
513 | |||
514 | ret = btmrvl_parse_cal_cfg(data, size, cal_data, sizeof(cal_data)); | ||
515 | if (ret) | ||
516 | return ret; | ||
517 | |||
518 | ret = btmrvl_load_cal_data(priv, cal_data); | ||
519 | if (ret) { | ||
520 | BT_ERR("Fail to load calibrate data"); | ||
521 | return ret; | ||
522 | } | ||
523 | |||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static int btmrvl_cal_data_config(struct btmrvl_private *priv) | ||
528 | { | ||
529 | const struct firmware *cfg; | ||
530 | int ret; | ||
531 | const char *cal_data = priv->btmrvl_dev.cal_data; | ||
532 | |||
533 | if (!cal_data) | ||
534 | return 0; | ||
535 | |||
536 | ret = request_firmware(&cfg, cal_data, priv->btmrvl_dev.dev); | ||
537 | if (ret < 0) { | ||
538 | BT_DBG("Failed to get %s file, skipping cal data download", | ||
539 | cal_data); | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | ret = btmrvl_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size); | ||
544 | release_firmware(cfg); | ||
545 | return ret; | ||
546 | } | ||
547 | |||
548 | static int btmrvl_setup(struct hci_dev *hdev) | ||
549 | { | ||
550 | struct btmrvl_private *priv = hci_get_drvdata(hdev); | ||
551 | |||
552 | btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); | ||
553 | |||
554 | if (btmrvl_cal_data_config(priv)) | ||
555 | BT_ERR("Set cal data failed"); | ||
556 | |||
557 | priv->btmrvl_dev.psmode = 1; | ||
558 | btmrvl_enable_ps(priv); | ||
559 | |||
560 | priv->btmrvl_dev.gpio_gap = 0xffff; | ||
561 | btmrvl_send_hscfg_cmd(priv); | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | /* | ||
483 | * This function handles the event generated by firmware, rx data | 567 | * This function handles the event generated by firmware, rx data |
484 | * received from firmware, and tx data sent from kernel. | 568 | * received from firmware, and tx data sent from kernel. |
485 | */ | 569 | */ |
@@ -572,8 +656,7 @@ int btmrvl_register_hdev(struct btmrvl_private *priv) | |||
572 | hdev->flush = btmrvl_flush; | 656 | hdev->flush = btmrvl_flush; |
573 | hdev->send = btmrvl_send_frame; | 657 | hdev->send = btmrvl_send_frame; |
574 | hdev->ioctl = btmrvl_ioctl; | 658 | hdev->ioctl = btmrvl_ioctl; |
575 | 659 | hdev->setup = btmrvl_setup; | |
576 | btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); | ||
577 | 660 | ||
578 | hdev->dev_type = priv->btmrvl_dev.dev_type; | 661 | hdev->dev_type = priv->btmrvl_dev.dev_type; |
579 | 662 | ||