brintos

brintos / linux-shallow public Read only

0
0
Text · 4.5 KiB · 6c1f584 Raw
196 lines · c
1/*2 * Copyright (c) 2017 Redpine Signals Inc.3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15 */16#include <linux/module.h>17#include <linux/kernel.h>18#include <net/bluetooth/bluetooth.h>19#include <net/bluetooth/hci_core.h>20#include <linux/unaligned.h>21#include <net/rsi_91x.h>22 23#define RSI_DMA_ALIGN	824#define RSI_FRAME_DESC_SIZE	1625#define RSI_HEADROOM_FOR_BT_HAL	(RSI_FRAME_DESC_SIZE + RSI_DMA_ALIGN)26 27struct rsi_hci_adapter {28	void *priv;29	struct rsi_proto_ops *proto_ops;30	struct hci_dev *hdev;31};32 33static int rsi_hci_open(struct hci_dev *hdev)34{35	return 0;36}37 38static int rsi_hci_close(struct hci_dev *hdev)39{40	return 0;41}42 43static int rsi_hci_flush(struct hci_dev *hdev)44{45	return 0;46}47 48static int rsi_hci_send_pkt(struct hci_dev *hdev, struct sk_buff *skb)49{50	struct rsi_hci_adapter *h_adapter = hci_get_drvdata(hdev);51	struct sk_buff *new_skb = NULL;52 53	switch (hci_skb_pkt_type(skb)) {54	case HCI_COMMAND_PKT:55		hdev->stat.cmd_tx++;56		break;57	case HCI_ACLDATA_PKT:58		hdev->stat.acl_tx++;59		break;60	case HCI_SCODATA_PKT:61		hdev->stat.sco_tx++;62		break;63	}64 65	if (skb_headroom(skb) < RSI_HEADROOM_FOR_BT_HAL) {66		/* Insufficient skb headroom - allocate a new skb */67		new_skb = skb_realloc_headroom(skb, RSI_HEADROOM_FOR_BT_HAL);68		if (unlikely(!new_skb))69			return -ENOMEM;70		bt_cb(new_skb)->pkt_type = hci_skb_pkt_type(skb);71		kfree_skb(skb);72		skb = new_skb;73		if (!IS_ALIGNED((unsigned long)skb->data, RSI_DMA_ALIGN)) {74			u8 *skb_data = skb->data;75			int skb_len = skb->len;76 77			skb_push(skb, RSI_DMA_ALIGN);78			skb_pull(skb, PTR_ALIGN(skb->data,79						RSI_DMA_ALIGN) - skb->data);80			memmove(skb->data, skb_data, skb_len);81			skb_trim(skb, skb_len);82		}83	}84 85	return h_adapter->proto_ops->coex_send_pkt(h_adapter->priv, skb,86						   RSI_BT_Q);87}88 89static int rsi_hci_recv_pkt(void *priv, const u8 *pkt)90{91	struct rsi_hci_adapter *h_adapter = priv;92	struct hci_dev *hdev = h_adapter->hdev;93	struct sk_buff *skb;94	int pkt_len = get_unaligned_le16(pkt) & 0x0fff;95 96	skb = dev_alloc_skb(pkt_len);97	if (!skb)98		return -ENOMEM;99 100	memcpy(skb->data, pkt + RSI_FRAME_DESC_SIZE, pkt_len);101	skb_put(skb, pkt_len);102	h_adapter->hdev->stat.byte_rx += skb->len;103 104	hci_skb_pkt_type(skb) = pkt[14];105 106	return hci_recv_frame(hdev, skb);107}108 109static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)110{111	struct rsi_hci_adapter *h_adapter = NULL;112	struct hci_dev *hdev;113	int err = 0;114 115	h_adapter = kzalloc(sizeof(*h_adapter), GFP_KERNEL);116	if (!h_adapter)117		return -ENOMEM;118 119	h_adapter->priv = priv;120	ops->set_bt_context(priv, h_adapter);121	h_adapter->proto_ops = ops;122 123	hdev = hci_alloc_dev();124	if (!hdev) {125		BT_ERR("Failed to alloc HCI device");126		goto err;127	}128 129	h_adapter->hdev = hdev;130 131	if (ops->get_host_intf(priv) == RSI_HOST_INTF_SDIO)132		hdev->bus = HCI_SDIO;133	else134		hdev->bus = HCI_USB;135 136	hci_set_drvdata(hdev, h_adapter);137	hdev->open = rsi_hci_open;138	hdev->close = rsi_hci_close;139	hdev->flush = rsi_hci_flush;140	hdev->send = rsi_hci_send_pkt;141 142	err = hci_register_dev(hdev);143	if (err < 0) {144		BT_ERR("HCI registration failed with errcode %d", err);145		hci_free_dev(hdev);146		goto err;147	}148 149	return 0;150err:151	h_adapter->hdev = NULL;152	kfree(h_adapter);153	return -EINVAL;154}155 156static void rsi_hci_detach(void *priv)157{158	struct rsi_hci_adapter *h_adapter = priv;159	struct hci_dev *hdev;160 161	if (!h_adapter)162		return;163 164	hdev = h_adapter->hdev;165	if (hdev) {166		hci_unregister_dev(hdev);167		hci_free_dev(hdev);168		h_adapter->hdev = NULL;169	}170 171	kfree(h_adapter);172}173 174const struct rsi_mod_ops rsi_bt_ops = {175	.attach	= rsi_hci_attach,176	.detach	= rsi_hci_detach,177	.recv_pkt = rsi_hci_recv_pkt,178};179EXPORT_SYMBOL(rsi_bt_ops);180 181static int rsi_91x_bt_module_init(void)182{183	return 0;184}185 186static void rsi_91x_bt_module_exit(void)187{188	return;189}190 191module_init(rsi_91x_bt_module_init);192module_exit(rsi_91x_bt_module_exit);193MODULE_AUTHOR("Redpine Signals Inc");194MODULE_DESCRIPTION("RSI BT driver");195MODULE_LICENSE("Dual BSD/GPL");196