brintos

brintos / linux-shallow public Read only

0
0
Text · 17.3 KiB · 18f3499 Raw
785 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Marvell Bluetooth driver4 *5 * Copyright (C) 2009, Marvell International Ltd.6 **/7 8#include <linux/module.h>9#include <linux/of.h>10#include <net/bluetooth/bluetooth.h>11#include <net/bluetooth/hci_core.h>12#include <linux/mmc/sdio_func.h>13 14#include "btmrvl_drv.h"15#include "btmrvl_sdio.h"16 17#define VERSION "1.0"18 19/*20 * This function is called by interface specific interrupt handler.21 * It updates Power Save & Host Sleep states, and wakes up the main22 * thread.23 */24void btmrvl_interrupt(struct btmrvl_private *priv)25{26	priv->adapter->ps_state = PS_AWAKE;27 28	priv->adapter->wakeup_tries = 0;29 30	priv->adapter->int_count++;31 32	if (priv->adapter->hs_state == HS_ACTIVATED) {33		BT_DBG("BT: HS DEACTIVATED in ISR!");34		priv->adapter->hs_state = HS_DEACTIVATED;35	}36 37	wake_up_interruptible(&priv->main_thread.wait_q);38}39EXPORT_SYMBOL_GPL(btmrvl_interrupt);40 41bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)42{43	struct hci_event_hdr *hdr = (void *) skb->data;44 45	if (hdr->evt == HCI_EV_CMD_COMPLETE) {46		struct hci_ev_cmd_complete *ec;47		u16 opcode;48 49		ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);50		opcode = __le16_to_cpu(ec->opcode);51 52		if (priv->btmrvl_dev.sendcmdflag) {53			priv->btmrvl_dev.sendcmdflag = false;54			priv->adapter->cmd_complete = true;55			wake_up_interruptible(&priv->adapter->cmd_wait_q);56 57			if (hci_opcode_ogf(opcode) == 0x3F) {58				BT_DBG("vendor event skipped: opcode=%#4.4x",59				       opcode);60				kfree_skb(skb);61				return false;62			}63		}64	}65 66	return true;67}68EXPORT_SYMBOL_GPL(btmrvl_check_evtpkt);69 70int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)71{72	struct btmrvl_adapter *adapter = priv->adapter;73	struct btmrvl_event *event;74	int ret = 0;75 76	event = (struct btmrvl_event *) skb->data;77	if (event->ec != 0xff) {78		BT_DBG("Not Marvell Event=%x", event->ec);79		ret = -EINVAL;80		goto exit;81	}82 83	switch (event->data[0]) {84	case BT_EVENT_AUTO_SLEEP_MODE:85		if (!event->data[2]) {86			if (event->data[1] == BT_PS_ENABLE)87				adapter->psmode = 1;88			else89				adapter->psmode = 0;90			BT_DBG("PS Mode:%s",91				(adapter->psmode) ? "Enable" : "Disable");92		} else {93			BT_DBG("PS Mode command failed");94		}95		break;96 97	case BT_EVENT_HOST_SLEEP_CONFIG:98		if (!event->data[3])99			BT_DBG("gpio=%x, gap=%x", event->data[1],100							event->data[2]);101		else102			BT_DBG("HSCFG command failed");103		break;104 105	case BT_EVENT_HOST_SLEEP_ENABLE:106		if (!event->data[1]) {107			adapter->hs_state = HS_ACTIVATED;108			if (adapter->psmode)109				adapter->ps_state = PS_SLEEP;110			wake_up_interruptible(&adapter->event_hs_wait_q);111			BT_DBG("HS ACTIVATED!");112		} else {113			BT_DBG("HS Enable failed");114		}115		break;116 117	case BT_EVENT_MODULE_CFG_REQ:118		if (priv->btmrvl_dev.sendcmdflag &&119				event->data[1] == MODULE_BRINGUP_REQ) {120			BT_DBG("EVENT:%s",121				((event->data[2] == MODULE_BROUGHT_UP) ||122				(event->data[2] == MODULE_ALREADY_UP)) ?123				"Bring-up succeed" : "Bring-up failed");124		} else if (priv->btmrvl_dev.sendcmdflag &&125				event->data[1] == MODULE_SHUTDOWN_REQ) {126			BT_DBG("EVENT:%s", (event->data[2]) ?127				"Shutdown failed" : "Shutdown succeed");128		} else {129			BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP");130			ret = -EINVAL;131		}132		break;133 134	case BT_EVENT_POWER_STATE:135		if (event->data[1] == BT_PS_SLEEP)136			adapter->ps_state = PS_SLEEP;137		BT_DBG("EVENT:%s",138			(adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");139		break;140 141	default:142		BT_DBG("Unknown Event=%d", event->data[0]);143		ret = -EINVAL;144		break;145	}146 147exit:148	if (!ret)149		kfree_skb(skb);150 151	return ret;152}153EXPORT_SYMBOL_GPL(btmrvl_process_event);154 155static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,156				const void *param, u8 len)157{158	struct sk_buff *skb;159	struct hci_command_hdr *hdr;160 161	if (priv->surprise_removed) {162		BT_ERR("Card is removed");163		return -EFAULT;164	}165 166	skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_KERNEL);167	if (!skb) {168		BT_ERR("No free skb");169		return -ENOMEM;170	}171 172	hdr = skb_put(skb, HCI_COMMAND_HDR_SIZE);173	hdr->opcode = cpu_to_le16(opcode);174	hdr->plen = len;175 176	if (len)177		skb_put_data(skb, param, len);178 179	hci_skb_pkt_type(skb) = MRVL_VENDOR_PKT;180 181	skb_queue_head(&priv->adapter->tx_queue, skb);182 183	priv->btmrvl_dev.sendcmdflag = true;184 185	priv->adapter->cmd_complete = false;186 187	wake_up_interruptible(&priv->main_thread.wait_q);188 189	if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,190					      priv->adapter->cmd_complete ||191					      priv->surprise_removed,192					      WAIT_UNTIL_CMD_RESP))193		return -ETIMEDOUT;194 195	if (priv->surprise_removed)196		return -EFAULT;197 198	return 0;199}200 201int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)202{203	int ret;204 205	ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1);206	if (ret)207		BT_ERR("module_cfg_cmd(%x) failed", subcmd);208 209	return ret;210}211EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);212 213static int btmrvl_enable_sco_routing_to_host(struct btmrvl_private *priv)214{215	int ret;216	u8 subcmd = 0;217 218	ret = btmrvl_send_sync_cmd(priv, BT_CMD_ROUTE_SCO_TO_HOST, &subcmd, 1);219	if (ret)220		BT_ERR("BT_CMD_ROUTE_SCO_TO_HOST command failed: %#x", ret);221 222	return ret;223}224 225int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd)226{227	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;228	int ret;229 230	if (!card->support_pscan_win_report)231		return 0;232 233	ret = btmrvl_send_sync_cmd(priv, BT_CMD_PSCAN_WIN_REPORT_ENABLE,234				   &subcmd, 1);235	if (ret)236		BT_ERR("PSCAN_WIN_REPORT_ENABLE command failed: %#x", ret);237 238	return ret;239}240EXPORT_SYMBOL_GPL(btmrvl_pscan_window_reporting);241 242int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)243{244	int ret;245	u8 param[2];246 247	param[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;248	param[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);249 250	BT_DBG("Sending HSCFG Command, gpio=0x%x, gap=0x%x",251	       param[0], param[1]);252 253	ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2);254	if (ret)255		BT_ERR("HSCFG command failed");256 257	return ret;258}259EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd);260 261int btmrvl_enable_ps(struct btmrvl_private *priv)262{263	int ret;264	u8 param;265 266	if (priv->btmrvl_dev.psmode)267		param = BT_PS_ENABLE;268	else269		param = BT_PS_DISABLE;270 271	ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, &param, 1);272	if (ret)273		BT_ERR("PSMODE command failed");274 275	return 0;276}277EXPORT_SYMBOL_GPL(btmrvl_enable_ps);278 279int btmrvl_enable_hs(struct btmrvl_private *priv)280{281	struct btmrvl_adapter *adapter = priv->adapter;282	int ret;283 284	ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);285	if (ret) {286		BT_ERR("Host sleep enable command failed");287		return ret;288	}289 290	ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q,291					       adapter->hs_state ||292					       priv->surprise_removed,293					       WAIT_UNTIL_HS_STATE_CHANGED);294	if (ret < 0 || priv->surprise_removed) {295		BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d",296		       ret, adapter->hs_state, adapter->ps_state,297		       adapter->wakeup_tries);298	} else if (!ret) {299		BT_ERR("hs_enable timeout: %d,%d,%d", adapter->hs_state,300		       adapter->ps_state, adapter->wakeup_tries);301		ret = -ETIMEDOUT;302	} else {303		BT_DBG("host sleep enabled: %d,%d,%d", adapter->hs_state,304		       adapter->ps_state, adapter->wakeup_tries);305		ret = 0;306	}307 308	return ret;309}310EXPORT_SYMBOL_GPL(btmrvl_enable_hs);311 312int btmrvl_prepare_command(struct btmrvl_private *priv)313{314	int ret = 0;315 316	if (priv->btmrvl_dev.hscfgcmd) {317		priv->btmrvl_dev.hscfgcmd = 0;318		btmrvl_send_hscfg_cmd(priv);319	}320 321	if (priv->btmrvl_dev.pscmd) {322		priv->btmrvl_dev.pscmd = 0;323		btmrvl_enable_ps(priv);324	}325 326	if (priv->btmrvl_dev.hscmd) {327		priv->btmrvl_dev.hscmd = 0;328 329		if (priv->btmrvl_dev.hsmode) {330			ret = btmrvl_enable_hs(priv);331		} else {332			ret = priv->hw_wakeup_firmware(priv);333			priv->adapter->hs_state = HS_DEACTIVATED;334			BT_DBG("BT: HS DEACTIVATED due to host activity!");335		}336	}337 338	return ret;339}340 341static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)342{343	int ret = 0;344 345	if (!skb || !skb->data)346		return -EINVAL;347 348	if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) {349		BT_ERR("Tx Error: Bad skb length %d : %d",350						skb->len, BTM_UPLD_SIZE);351		return -EINVAL;352	}353 354	skb_push(skb, BTM_HEADER_LEN);355 356	/* header type: byte[3]357	 * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor358	 * header length: byte[2][1][0]359	 */360 361	skb->data[0] = (skb->len & 0x0000ff);362	skb->data[1] = (skb->len & 0x00ff00) >> 8;363	skb->data[2] = (skb->len & 0xff0000) >> 16;364	skb->data[3] = hci_skb_pkt_type(skb);365 366	if (priv->hw_host_to_card)367		ret = priv->hw_host_to_card(priv, skb->data, skb->len);368 369	return ret;370}371 372static void btmrvl_init_adapter(struct btmrvl_private *priv)373{374	int buf_size;375 376	skb_queue_head_init(&priv->adapter->tx_queue);377 378	priv->adapter->ps_state = PS_AWAKE;379 380	buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN);381	priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL);382	if (!priv->adapter->hw_regs_buf) {383		priv->adapter->hw_regs = NULL;384		BT_ERR("Unable to allocate buffer for hw_regs.");385	} else {386		priv->adapter->hw_regs =387			(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf,388					 BTSDIO_DMA_ALIGN);389		BT_DBG("hw_regs_buf=%p hw_regs=%p",390		       priv->adapter->hw_regs_buf, priv->adapter->hw_regs);391	}392 393	init_waitqueue_head(&priv->adapter->cmd_wait_q);394	init_waitqueue_head(&priv->adapter->event_hs_wait_q);395}396 397static void btmrvl_free_adapter(struct btmrvl_private *priv)398{399	skb_queue_purge(&priv->adapter->tx_queue);400 401	kfree(priv->adapter->hw_regs_buf);402	kfree(priv->adapter);403 404	priv->adapter = NULL;405}406 407static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)408{409	struct btmrvl_private *priv = hci_get_drvdata(hdev);410 411	BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len);412 413	if (priv->adapter->is_suspending || priv->adapter->is_suspended) {414		BT_ERR("%s: Device is suspending or suspended", __func__);415		return -EBUSY;416	}417 418	switch (hci_skb_pkt_type(skb)) {419	case HCI_COMMAND_PKT:420		hdev->stat.cmd_tx++;421		break;422 423	case HCI_ACLDATA_PKT:424		hdev->stat.acl_tx++;425		break;426 427	case HCI_SCODATA_PKT:428		hdev->stat.sco_tx++;429		break;430	}431 432	skb_queue_tail(&priv->adapter->tx_queue, skb);433 434	if (!priv->adapter->is_suspended)435		wake_up_interruptible(&priv->main_thread.wait_q);436 437	return 0;438}439 440static int btmrvl_flush(struct hci_dev *hdev)441{442	struct btmrvl_private *priv = hci_get_drvdata(hdev);443 444	skb_queue_purge(&priv->adapter->tx_queue);445 446	return 0;447}448 449static int btmrvl_close(struct hci_dev *hdev)450{451	struct btmrvl_private *priv = hci_get_drvdata(hdev);452 453	skb_queue_purge(&priv->adapter->tx_queue);454 455	return 0;456}457 458static int btmrvl_open(struct hci_dev *hdev)459{460	return 0;461}462 463static int btmrvl_download_cal_data(struct btmrvl_private *priv,464				    u8 *data, int len)465{466	int ret;467 468	data[0] = 0x00;469	data[1] = 0x00;470	data[2] = 0x00;471	data[3] = len;472 473	print_hex_dump_bytes("Calibration data: ",474			     DUMP_PREFIX_OFFSET, data, BT_CAL_HDR_LEN + len);475 476	ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,477				   BT_CAL_HDR_LEN + len);478	if (ret)479		BT_ERR("Failed to download calibration data");480 481	return 0;482}483 484static int btmrvl_check_device_tree(struct btmrvl_private *priv)485{486	struct device_node *dt_node;487	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;488	u8 cal_data[BT_CAL_HDR_LEN + BT_CAL_DATA_SIZE];489	int ret = 0;490	u16 gpio, gap;491 492	if (card->plt_of_node) {493		dt_node = card->plt_of_node;494		ret = of_property_read_u16(dt_node, "marvell,wakeup-pin",495					   &gpio);496		if (ret)497			gpio = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;498 499		ret = of_property_read_u16(dt_node, "marvell,wakeup-gap-ms",500					   &gap);501		if (ret)502			gap = (u8)(priv->btmrvl_dev.gpio_gap & 0x00ff);503 504		priv->btmrvl_dev.gpio_gap = (gpio << 8) + gap;505 506		ret = of_property_read_u8_array(dt_node, "marvell,cal-data",507						cal_data + BT_CAL_HDR_LEN,508						BT_CAL_DATA_SIZE);509		if (ret)510			return ret;511 512		BT_DBG("Use cal data from device tree");513		ret = btmrvl_download_cal_data(priv, cal_data,514					       BT_CAL_DATA_SIZE);515		if (ret)516			BT_ERR("Fail to download calibrate data");517	}518 519	return ret;520}521 522static int btmrvl_setup(struct hci_dev *hdev)523{524	struct btmrvl_private *priv = hci_get_drvdata(hdev);525	int ret;526 527	ret = btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);528	if (ret)529		return ret;530 531	priv->btmrvl_dev.gpio_gap = 0xfffe;532 533	btmrvl_check_device_tree(priv);534 535	btmrvl_enable_sco_routing_to_host(priv);536 537	btmrvl_pscan_window_reporting(priv, 0x01);538 539	priv->btmrvl_dev.psmode = 1;540	btmrvl_enable_ps(priv);541 542	btmrvl_send_hscfg_cmd(priv);543 544	return 0;545}546 547static int btmrvl_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)548{549	struct sk_buff *skb;550	long ret;551	u8 buf[8];552 553	buf[0] = MRVL_VENDOR_PKT;554	buf[1] = sizeof(bdaddr_t);555	memcpy(buf + 2, bdaddr, sizeof(bdaddr_t));556 557	skb = __hci_cmd_sync(hdev, BT_CMD_SET_BDADDR, sizeof(buf), buf,558			     HCI_INIT_TIMEOUT);559	if (IS_ERR(skb)) {560		ret = PTR_ERR(skb);561		BT_ERR("%s: changing btmrvl device address failed (%ld)",562		       hdev->name, ret);563		return ret;564	}565	kfree_skb(skb);566 567	return 0;568}569 570static bool btmrvl_wakeup(struct hci_dev *hdev)571{572	struct btmrvl_private *priv = hci_get_drvdata(hdev);573	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;574 575	return device_may_wakeup(&card->func->dev);576}577 578/*579 * This function handles the event generated by firmware, rx data580 * received from firmware, and tx data sent from kernel.581 */582static int btmrvl_service_main_thread(void *data)583{584	struct btmrvl_thread *thread = data;585	struct btmrvl_private *priv = thread->priv;586	struct btmrvl_adapter *adapter = priv->adapter;587	wait_queue_entry_t wait;588	struct sk_buff *skb;589	ulong flags;590 591	init_waitqueue_entry(&wait, current);592 593	for (;;) {594		add_wait_queue(&thread->wait_q, &wait);595 596		set_current_state(TASK_INTERRUPTIBLE);597		if (kthread_should_stop() || priv->surprise_removed) {598			BT_DBG("main_thread: break from main thread");599			break;600		}601 602		if (adapter->wakeup_tries ||603				((!adapter->int_count) &&604				(!priv->btmrvl_dev.tx_dnld_rdy ||605				skb_queue_empty(&adapter->tx_queue)))) {606			BT_DBG("main_thread is sleeping...");607			schedule();608		}609 610		set_current_state(TASK_RUNNING);611 612		remove_wait_queue(&thread->wait_q, &wait);613 614		BT_DBG("main_thread woke up");615 616		if (kthread_should_stop() || priv->surprise_removed) {617			BT_DBG("main_thread: break from main thread");618			break;619		}620 621		spin_lock_irqsave(&priv->driver_lock, flags);622		if (adapter->int_count) {623			adapter->int_count = 0;624			spin_unlock_irqrestore(&priv->driver_lock, flags);625			priv->hw_process_int_status(priv);626		} else if (adapter->ps_state == PS_SLEEP &&627					!skb_queue_empty(&adapter->tx_queue)) {628			spin_unlock_irqrestore(&priv->driver_lock, flags);629			adapter->wakeup_tries++;630			priv->hw_wakeup_firmware(priv);631			continue;632		} else {633			spin_unlock_irqrestore(&priv->driver_lock, flags);634		}635 636		if (adapter->ps_state == PS_SLEEP)637			continue;638 639		if (!priv->btmrvl_dev.tx_dnld_rdy ||640		    priv->adapter->is_suspended)641			continue;642 643		skb = skb_dequeue(&adapter->tx_queue);644		if (skb) {645			if (btmrvl_tx_pkt(priv, skb))646				priv->btmrvl_dev.hcidev->stat.err_tx++;647			else648				priv->btmrvl_dev.hcidev->stat.byte_tx += skb->len;649 650			kfree_skb(skb);651		}652	}653 654	return 0;655}656 657int btmrvl_register_hdev(struct btmrvl_private *priv)658{659	struct hci_dev *hdev = NULL;660	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;661	int ret;662 663	hdev = hci_alloc_dev();664	if (!hdev) {665		BT_ERR("Can not allocate HCI device");666		goto err_hdev;667	}668 669	priv->btmrvl_dev.hcidev = hdev;670	hci_set_drvdata(hdev, priv);671 672	hdev->bus   = HCI_SDIO;673	hdev->open  = btmrvl_open;674	hdev->close = btmrvl_close;675	hdev->flush = btmrvl_flush;676	hdev->send  = btmrvl_send_frame;677	hdev->setup = btmrvl_setup;678	hdev->set_bdaddr = btmrvl_set_bdaddr;679	hdev->wakeup = btmrvl_wakeup;680	SET_HCIDEV_DEV(hdev, &card->func->dev);681 682	ret = hci_register_dev(hdev);683	if (ret < 0) {684		BT_ERR("Can not register HCI device");685		goto err_hci_register_dev;686	}687 688#ifdef CONFIG_DEBUG_FS689	btmrvl_debugfs_init(hdev);690#endif691 692	return 0;693 694err_hci_register_dev:695	hci_free_dev(hdev);696 697err_hdev:698	/* Stop the thread servicing the interrupts */699	kthread_stop(priv->main_thread.task);700 701	btmrvl_free_adapter(priv);702	kfree(priv);703 704	return -ENOMEM;705}706EXPORT_SYMBOL_GPL(btmrvl_register_hdev);707 708struct btmrvl_private *btmrvl_add_card(void *card)709{710	struct btmrvl_private *priv;711 712	priv = kzalloc(sizeof(*priv), GFP_KERNEL);713	if (!priv) {714		BT_ERR("Can not allocate priv");715		goto err_priv;716	}717 718	priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL);719	if (!priv->adapter) {720		BT_ERR("Allocate buffer for btmrvl_adapter failed!");721		goto err_adapter;722	}723 724	btmrvl_init_adapter(priv);725 726	BT_DBG("Starting kthread...");727	priv->main_thread.priv = priv;728	spin_lock_init(&priv->driver_lock);729 730	init_waitqueue_head(&priv->main_thread.wait_q);731	priv->main_thread.task = kthread_run(btmrvl_service_main_thread,732				&priv->main_thread, "btmrvl_main_service");733	if (IS_ERR(priv->main_thread.task))734		goto err_thread;735 736	priv->btmrvl_dev.card = card;737	priv->btmrvl_dev.tx_dnld_rdy = true;738 739	return priv;740 741err_thread:742	btmrvl_free_adapter(priv);743 744err_adapter:745	kfree(priv);746 747err_priv:748	return NULL;749}750EXPORT_SYMBOL_GPL(btmrvl_add_card);751 752int btmrvl_remove_card(struct btmrvl_private *priv)753{754	struct hci_dev *hdev;755 756	hdev = priv->btmrvl_dev.hcidev;757 758	wake_up_interruptible(&priv->adapter->cmd_wait_q);759	wake_up_interruptible(&priv->adapter->event_hs_wait_q);760 761	kthread_stop(priv->main_thread.task);762 763#ifdef CONFIG_DEBUG_FS764	btmrvl_debugfs_remove(hdev);765#endif766 767	hci_unregister_dev(hdev);768 769	hci_free_dev(hdev);770 771	priv->btmrvl_dev.hcidev = NULL;772 773	btmrvl_free_adapter(priv);774 775	kfree(priv);776 777	return 0;778}779EXPORT_SYMBOL_GPL(btmrvl_remove_card);780 781MODULE_AUTHOR("Marvell International Ltd.");782MODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION);783MODULE_VERSION(VERSION);784MODULE_LICENSE("GPL v2");785