162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2017 Redpine Signals Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 1962306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h> 2062306a36Sopenharmony_ci#include <asm/unaligned.h> 2162306a36Sopenharmony_ci#include <net/rsi_91x.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define RSI_DMA_ALIGN 8 2462306a36Sopenharmony_ci#define RSI_FRAME_DESC_SIZE 16 2562306a36Sopenharmony_ci#define RSI_HEADROOM_FOR_BT_HAL (RSI_FRAME_DESC_SIZE + RSI_DMA_ALIGN) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct rsi_hci_adapter { 2862306a36Sopenharmony_ci void *priv; 2962306a36Sopenharmony_ci struct rsi_proto_ops *proto_ops; 3062306a36Sopenharmony_ci struct hci_dev *hdev; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int rsi_hci_open(struct hci_dev *hdev) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int rsi_hci_close(struct hci_dev *hdev) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int rsi_hci_flush(struct hci_dev *hdev) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int rsi_hci_send_pkt(struct hci_dev *hdev, struct sk_buff *skb) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct rsi_hci_adapter *h_adapter = hci_get_drvdata(hdev); 5162306a36Sopenharmony_ci struct sk_buff *new_skb = NULL; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci switch (hci_skb_pkt_type(skb)) { 5462306a36Sopenharmony_ci case HCI_COMMAND_PKT: 5562306a36Sopenharmony_ci hdev->stat.cmd_tx++; 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci case HCI_ACLDATA_PKT: 5862306a36Sopenharmony_ci hdev->stat.acl_tx++; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case HCI_SCODATA_PKT: 6162306a36Sopenharmony_ci hdev->stat.sco_tx++; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (skb_headroom(skb) < RSI_HEADROOM_FOR_BT_HAL) { 6662306a36Sopenharmony_ci /* Insufficient skb headroom - allocate a new skb */ 6762306a36Sopenharmony_ci new_skb = skb_realloc_headroom(skb, RSI_HEADROOM_FOR_BT_HAL); 6862306a36Sopenharmony_ci if (unlikely(!new_skb)) 6962306a36Sopenharmony_ci return -ENOMEM; 7062306a36Sopenharmony_ci bt_cb(new_skb)->pkt_type = hci_skb_pkt_type(skb); 7162306a36Sopenharmony_ci kfree_skb(skb); 7262306a36Sopenharmony_ci skb = new_skb; 7362306a36Sopenharmony_ci if (!IS_ALIGNED((unsigned long)skb->data, RSI_DMA_ALIGN)) { 7462306a36Sopenharmony_ci u8 *skb_data = skb->data; 7562306a36Sopenharmony_ci int skb_len = skb->len; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci skb_push(skb, RSI_DMA_ALIGN); 7862306a36Sopenharmony_ci skb_pull(skb, PTR_ALIGN(skb->data, 7962306a36Sopenharmony_ci RSI_DMA_ALIGN) - skb->data); 8062306a36Sopenharmony_ci memmove(skb->data, skb_data, skb_len); 8162306a36Sopenharmony_ci skb_trim(skb, skb_len); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return h_adapter->proto_ops->coex_send_pkt(h_adapter->priv, skb, 8662306a36Sopenharmony_ci RSI_BT_Q); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int rsi_hci_recv_pkt(void *priv, const u8 *pkt) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct rsi_hci_adapter *h_adapter = priv; 9262306a36Sopenharmony_ci struct hci_dev *hdev = h_adapter->hdev; 9362306a36Sopenharmony_ci struct sk_buff *skb; 9462306a36Sopenharmony_ci int pkt_len = get_unaligned_le16(pkt) & 0x0fff; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci skb = dev_alloc_skb(pkt_len); 9762306a36Sopenharmony_ci if (!skb) 9862306a36Sopenharmony_ci return -ENOMEM; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci memcpy(skb->data, pkt + RSI_FRAME_DESC_SIZE, pkt_len); 10162306a36Sopenharmony_ci skb_put(skb, pkt_len); 10262306a36Sopenharmony_ci h_adapter->hdev->stat.byte_rx += skb->len; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci hci_skb_pkt_type(skb) = pkt[14]; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return hci_recv_frame(hdev, skb); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct rsi_hci_adapter *h_adapter = NULL; 11262306a36Sopenharmony_ci struct hci_dev *hdev; 11362306a36Sopenharmony_ci int err = 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci h_adapter = kzalloc(sizeof(*h_adapter), GFP_KERNEL); 11662306a36Sopenharmony_ci if (!h_adapter) 11762306a36Sopenharmony_ci return -ENOMEM; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci h_adapter->priv = priv; 12062306a36Sopenharmony_ci ops->set_bt_context(priv, h_adapter); 12162306a36Sopenharmony_ci h_adapter->proto_ops = ops; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci hdev = hci_alloc_dev(); 12462306a36Sopenharmony_ci if (!hdev) { 12562306a36Sopenharmony_ci BT_ERR("Failed to alloc HCI device"); 12662306a36Sopenharmony_ci goto err; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci h_adapter->hdev = hdev; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (ops->get_host_intf(priv) == RSI_HOST_INTF_SDIO) 13262306a36Sopenharmony_ci hdev->bus = HCI_SDIO; 13362306a36Sopenharmony_ci else 13462306a36Sopenharmony_ci hdev->bus = HCI_USB; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci hci_set_drvdata(hdev, h_adapter); 13762306a36Sopenharmony_ci hdev->dev_type = HCI_PRIMARY; 13862306a36Sopenharmony_ci hdev->open = rsi_hci_open; 13962306a36Sopenharmony_ci hdev->close = rsi_hci_close; 14062306a36Sopenharmony_ci hdev->flush = rsi_hci_flush; 14162306a36Sopenharmony_ci hdev->send = rsi_hci_send_pkt; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci err = hci_register_dev(hdev); 14462306a36Sopenharmony_ci if (err < 0) { 14562306a36Sopenharmony_ci BT_ERR("HCI registration failed with errcode %d", err); 14662306a36Sopenharmony_ci hci_free_dev(hdev); 14762306a36Sopenharmony_ci goto err; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_cierr: 15262306a36Sopenharmony_ci h_adapter->hdev = NULL; 15362306a36Sopenharmony_ci kfree(h_adapter); 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void rsi_hci_detach(void *priv) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct rsi_hci_adapter *h_adapter = priv; 16062306a36Sopenharmony_ci struct hci_dev *hdev; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!h_adapter) 16362306a36Sopenharmony_ci return; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci hdev = h_adapter->hdev; 16662306a36Sopenharmony_ci if (hdev) { 16762306a36Sopenharmony_ci hci_unregister_dev(hdev); 16862306a36Sopenharmony_ci hci_free_dev(hdev); 16962306a36Sopenharmony_ci h_adapter->hdev = NULL; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci kfree(h_adapter); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciconst struct rsi_mod_ops rsi_bt_ops = { 17662306a36Sopenharmony_ci .attach = rsi_hci_attach, 17762306a36Sopenharmony_ci .detach = rsi_hci_detach, 17862306a36Sopenharmony_ci .recv_pkt = rsi_hci_recv_pkt, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ciEXPORT_SYMBOL(rsi_bt_ops); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int rsi_91x_bt_module_init(void) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void rsi_91x_bt_module_exit(void) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cimodule_init(rsi_91x_bt_module_init); 19362306a36Sopenharmony_cimodule_exit(rsi_91x_bt_module_exit); 19462306a36Sopenharmony_ciMODULE_AUTHOR("Redpine Signals Inc"); 19562306a36Sopenharmony_ciMODULE_DESCRIPTION("RSI BT driver"); 19662306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 197