1/* 2 * Atheros Communication Bluetooth HCIATH3K UART protocol 3 * 4 * HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's 5 * power management protocol extension to H4 to support AR300x Bluetooth Chip. 6 * 7 * Copyright (c) 2009-2010 Atheros Communications Inc. 8 * 9 * Acknowledgements: 10 * This file is based on hci_h4.c, which was written 11 * by Maxim Krasnyansky and Marcel Holtmann. 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 26 * 27 */ 28 29#include <linux/module.h> 30#include <linux/kernel.h> 31 32#include <linux/init.h> 33#include <linux/slab.h> 34#include <linux/tty.h> 35#include <linux/errno.h> 36#include <linux/ioctl.h> 37#include <linux/skbuff.h> 38 39#include <net/bluetooth/bluetooth.h> 40#include <net/bluetooth/hci_core.h> 41 42#include "hci_uart.h" 43 44struct ath_struct { 45 struct hci_uart *hu; 46 unsigned int cur_sleep; 47 48 struct sk_buff *rx_skb; 49 struct sk_buff_head txq; 50 struct work_struct ctxtsw; 51}; 52 53static int ath_wakeup_ar3k(struct tty_struct *tty) 54{ 55 int status = tty->driver->ops->tiocmget(tty); 56 57 if (status & TIOCM_CTS) 58 return status; 59 60 /* Clear RTS first */ 61 tty->driver->ops->tiocmget(tty); 62 tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS); 63 mdelay(20); 64 65 /* Set RTS, wake up board */ 66 tty->driver->ops->tiocmget(tty); 67 tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00); 68 mdelay(20); 69 70 status = tty->driver->ops->tiocmget(tty); 71 return status; 72} 73 74static void ath_hci_uart_work(struct work_struct *work) 75{ 76 int status; 77 struct ath_struct *ath; 78 struct hci_uart *hu; 79 struct tty_struct *tty; 80 81 ath = container_of(work, struct ath_struct, ctxtsw); 82 83 hu = ath->hu; 84 tty = hu->tty; 85 86 /* verify and wake up controller */ 87 if (ath->cur_sleep) { 88 status = ath_wakeup_ar3k(tty); 89 if (!(status & TIOCM_CTS)) 90 return; 91 } 92 93 /* Ready to send Data */ 94 clear_bit(HCI_UART_SENDING, &hu->tx_state); 95 hci_uart_tx_wakeup(hu); 96} 97 98static int ath_open(struct hci_uart *hu) 99{ 100 struct ath_struct *ath; 101 102 BT_DBG("hu %p", hu); 103 104 ath = kzalloc(sizeof(*ath), GFP_KERNEL); 105 if (!ath) 106 return -ENOMEM; 107 108 skb_queue_head_init(&ath->txq); 109 110 hu->priv = ath; 111 ath->hu = hu; 112 113 INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); 114 115 return 0; 116} 117 118static int ath_close(struct hci_uart *hu) 119{ 120 struct ath_struct *ath = hu->priv; 121 122 BT_DBG("hu %p", hu); 123 124 skb_queue_purge(&ath->txq); 125 126 kfree_skb(ath->rx_skb); 127 128 cancel_work_sync(&ath->ctxtsw); 129 130 hu->priv = NULL; 131 kfree(ath); 132 133 return 0; 134} 135 136static int ath_flush(struct hci_uart *hu) 137{ 138 struct ath_struct *ath = hu->priv; 139 140 BT_DBG("hu %p", hu); 141 142 skb_queue_purge(&ath->txq); 143 144 return 0; 145} 146 147static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) 148{ 149 struct sk_buff *skb; 150 u8 buf[10]; 151 int err; 152 153 buf[0] = 0x01; 154 buf[1] = 0x01; 155 buf[2] = 0x00; 156 buf[3] = sizeof(bdaddr_t); 157 memcpy(buf + 4, bdaddr, sizeof(bdaddr_t)); 158 159 skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT); 160 if (IS_ERR(skb)) { 161 err = PTR_ERR(skb); 162 BT_ERR("%s: Change address command failed (%d)", 163 hdev->name, err); 164 return err; 165 } 166 kfree_skb(skb); 167 168 return 0; 169} 170 171static int ath_setup(struct hci_uart *hu) 172{ 173 BT_DBG("hu %p", hu); 174 175 hu->hdev->set_bdaddr = ath_set_bdaddr; 176 177 return 0; 178} 179 180static const struct h4_recv_pkt ath_recv_pkts[] = { 181 { H4_RECV_ACL, .recv = hci_recv_frame }, 182 { H4_RECV_SCO, .recv = hci_recv_frame }, 183 { H4_RECV_EVENT, .recv = hci_recv_frame }, 184}; 185 186static int ath_recv(struct hci_uart *hu, const void *data, int count) 187{ 188 struct ath_struct *ath = hu->priv; 189 190 ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count, 191 ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts)); 192 if (IS_ERR(ath->rx_skb)) { 193 int err = PTR_ERR(ath->rx_skb); 194 BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); 195 ath->rx_skb = NULL; 196 return err; 197 } 198 199 return count; 200} 201 202#define HCI_OP_ATH_SLEEP 0xFC04 203 204static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) 205{ 206 struct ath_struct *ath = hu->priv; 207 208 if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { 209 kfree_skb(skb); 210 return 0; 211 } 212 213 /* Update power management enable flag with parameters of 214 * HCI sleep enable vendor specific HCI command. 215 */ 216 if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { 217 struct hci_command_hdr *hdr = (void *)skb->data; 218 219 if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP) 220 ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE]; 221 } 222 223 BT_DBG("hu %p skb %p", hu, skb); 224 225 /* Prepend skb with frame type */ 226 memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); 227 228 skb_queue_tail(&ath->txq, skb); 229 set_bit(HCI_UART_SENDING, &hu->tx_state); 230 231 schedule_work(&ath->ctxtsw); 232 233 return 0; 234} 235 236static struct sk_buff *ath_dequeue(struct hci_uart *hu) 237{ 238 struct ath_struct *ath = hu->priv; 239 240 return skb_dequeue(&ath->txq); 241} 242 243static const struct hci_uart_proto athp = { 244 .id = HCI_UART_ATH3K, 245 .name = "ATH3K", 246 .manufacturer = 69, 247 .open = ath_open, 248 .close = ath_close, 249 .flush = ath_flush, 250 .setup = ath_setup, 251 .recv = ath_recv, 252 .enqueue = ath_enqueue, 253 .dequeue = ath_dequeue, 254}; 255 256int __init ath_init(void) 257{ 258 return hci_uart_register_proto(&athp); 259} 260 261int __exit ath_deinit(void) 262{ 263 return hci_uart_unregister_proto(&athp); 264} 265