2 * Copyright (c) 2017 Redpine Signals Inc.
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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>
23 #define RSI_DMA_ALIGN 8
24 #define RSI_FRAME_DESC_SIZE 16
25 #define RSI_HEADROOM_FOR_BT_HAL (RSI_FRAME_DESC_SIZE + RSI_DMA_ALIGN)
27 struct rsi_hci_adapter
{
29 struct rsi_proto_ops
*proto_ops
;
33 static int rsi_hci_open(struct hci_dev
*hdev
)
38 static int rsi_hci_close(struct hci_dev
*hdev
)
43 static int rsi_hci_flush(struct hci_dev
*hdev
)
48 static int rsi_hci_send_pkt(struct hci_dev
*hdev
, struct sk_buff
*skb
)
50 struct rsi_hci_adapter
*h_adapter
= hci_get_drvdata(hdev
);
51 struct sk_buff
*new_skb
= NULL
;
53 switch (hci_skb_pkt_type(skb
)) {
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
))
70 bt_cb(new_skb
)->pkt_type
= hci_skb_pkt_type(skb
);
73 if (!IS_ALIGNED((unsigned long)skb
->data
, RSI_DMA_ALIGN
)) {
74 u8
*skb_data
= skb
->data
;
75 int skb_len
= skb
->len
;
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
);
85 return h_adapter
->proto_ops
->coex_send_pkt(h_adapter
->priv
, skb
,
89 static int rsi_hci_recv_pkt(void *priv
, const u8
*pkt
)
91 struct rsi_hci_adapter
*h_adapter
= priv
;
92 struct hci_dev
*hdev
= h_adapter
->hdev
;
94 int pkt_len
= get_unaligned_le16(pkt
) & 0x0fff;
96 skb
= dev_alloc_skb(pkt_len
);
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
;
104 hci_skb_pkt_type(skb
) = pkt
[14];
106 return hci_recv_frame(hdev
, skb
);
109 static int rsi_hci_attach(void *priv
, struct rsi_proto_ops
*ops
)
111 struct rsi_hci_adapter
*h_adapter
= NULL
;
112 struct hci_dev
*hdev
;
115 h_adapter
= kzalloc(sizeof(*h_adapter
), GFP_KERNEL
);
119 h_adapter
->priv
= priv
;
120 ops
->set_bt_context(priv
, h_adapter
);
121 h_adapter
->proto_ops
= ops
;
123 hdev
= hci_alloc_dev();
125 BT_ERR("Failed to alloc HCI device");
129 h_adapter
->hdev
= hdev
;
131 if (ops
->get_host_intf(priv
) == RSI_HOST_INTF_SDIO
)
132 hdev
->bus
= HCI_SDIO
;
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
;
142 err
= hci_register_dev(hdev
);
144 BT_ERR("HCI registration failed with errcode %d", err
);
151 h_adapter
->hdev
= NULL
;
156 static void rsi_hci_detach(void *priv
)
158 struct rsi_hci_adapter
*h_adapter
= priv
;
159 struct hci_dev
*hdev
;
164 hdev
= h_adapter
->hdev
;
166 hci_unregister_dev(hdev
);
168 h_adapter
->hdev
= NULL
;
174 const struct rsi_mod_ops rsi_bt_ops
= {
175 .attach
= rsi_hci_attach
,
176 .detach
= rsi_hci_detach
,
177 .recv_pkt
= rsi_hci_recv_pkt
,
179 EXPORT_SYMBOL(rsi_bt_ops
);
181 static int rsi_91x_bt_module_init(void)
186 static void rsi_91x_bt_module_exit(void)
191 module_init(rsi_91x_bt_module_init
);
192 module_exit(rsi_91x_bt_module_exit
);
193 MODULE_AUTHOR("Redpine Signals Inc");
194 MODULE_DESCRIPTION("RSI BT driver");
195 MODULE_LICENSE("Dual BSD/GPL");