2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-07-19 11:04:07 +04:00
/*
* Atheros Communication Bluetooth HCIATH3K UART protocol
*
* HCIATH3K ( HCI Atheros AR300x Protocol ) is a Atheros Communication ' s
* power management protocol extension to H4 to support AR300x Bluetooth Chip .
*
* Copyright ( c ) 2009 - 2010 Atheros Communications Inc .
*
* Acknowledgements :
* This file is based on hci_h4 . c , which was written
* by Maxim Krasnyansky and Marcel Holtmann .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/errno.h>
# include <linux/ioctl.h>
# include <linux/skbuff.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
# include "hci_uart.h"
struct ath_struct {
struct hci_uart * hu ;
unsigned int cur_sleep ;
2015-04-05 07:59:26 +03:00
struct sk_buff * rx_skb ;
2010-07-19 11:04:07 +04:00
struct sk_buff_head txq ;
struct work_struct ctxtsw ;
} ;
2017-10-19 15:29:10 +03:00
# define OP_WRITE_TAG 0x01
# define INDEX_BDADDR 0x01
struct ath_vendor_cmd {
__u8 opcode ;
__le16 index ;
__u8 len ;
__u8 data [ 251 ] ;
} __packed ;
2010-07-19 11:04:07 +04:00
static int ath_wakeup_ar3k ( struct tty_struct * tty )
{
2011-02-14 19:28:18 +03:00
int status = tty - > driver - > ops - > tiocmget ( tty ) ;
2010-07-19 11:04:07 +04:00
if ( status & TIOCM_CTS )
return status ;
/* Clear RTS first */
2015-01-25 22:44:54 +03:00
tty - > driver - > ops - > tiocmget ( tty ) ;
2011-02-14 19:28:18 +03:00
tty - > driver - > ops - > tiocmset ( tty , 0x00 , TIOCM_RTS ) ;
2018-01-27 13:17:38 +03:00
msleep ( 20 ) ;
2010-07-19 11:04:07 +04:00
/* Set RTS, wake up board */
2015-01-25 22:44:54 +03:00
tty - > driver - > ops - > tiocmget ( tty ) ;
2011-02-14 19:28:18 +03:00
tty - > driver - > ops - > tiocmset ( tty , TIOCM_RTS , 0x00 ) ;
2018-01-27 13:17:38 +03:00
msleep ( 20 ) ;
2010-07-19 11:04:07 +04:00
2011-02-14 19:28:18 +03:00
status = tty - > driver - > ops - > tiocmget ( tty ) ;
2010-07-19 11:04:07 +04:00
return status ;
}
static void ath_hci_uart_work ( struct work_struct * work )
{
int status ;
struct ath_struct * ath ;
struct hci_uart * hu ;
struct tty_struct * tty ;
ath = container_of ( work , struct ath_struct , ctxtsw ) ;
hu = ath - > hu ;
tty = hu - > tty ;
/* verify and wake up controller */
if ( ath - > cur_sleep ) {
status = ath_wakeup_ar3k ( tty ) ;
if ( ! ( status & TIOCM_CTS ) )
return ;
}
/* Ready to send Data */
clear_bit ( HCI_UART_SENDING , & hu - > tx_state ) ;
hci_uart_tx_wakeup ( hu ) ;
}
static int ath_open ( struct hci_uart * hu )
{
struct ath_struct * ath ;
BT_DBG ( " hu %p " , hu ) ;
2019-07-30 12:33:45 +03:00
if ( ! hci_uart_has_flow_control ( hu ) )
return - EOPNOTSUPP ;
2012-01-07 18:19:40 +04:00
ath = kzalloc ( sizeof ( * ath ) , GFP_KERNEL ) ;
2010-07-19 11:04:07 +04:00
if ( ! ath )
return - ENOMEM ;
skb_queue_head_init ( & ath - > txq ) ;
hu - > priv = ath ;
ath - > hu = hu ;
INIT_WORK ( & ath - > ctxtsw , ath_hci_uart_work ) ;
return 0 ;
}
2015-04-11 15:35:37 +03:00
static int ath_close ( struct hci_uart * hu )
2010-07-19 11:04:07 +04:00
{
struct ath_struct * ath = hu - > priv ;
BT_DBG ( " hu %p " , hu ) ;
skb_queue_purge ( & ath - > txq ) ;
2015-04-11 15:35:37 +03:00
kfree_skb ( ath - > rx_skb ) ;
cancel_work_sync ( & ath - > ctxtsw ) ;
hu - > priv = NULL ;
kfree ( ath ) ;
2010-07-19 11:04:07 +04:00
return 0 ;
}
2015-04-11 15:35:37 +03:00
static int ath_flush ( struct hci_uart * hu )
2010-07-19 11:04:07 +04:00
{
struct ath_struct * ath = hu - > priv ;
BT_DBG ( " hu %p " , hu ) ;
skb_queue_purge ( & ath - > txq ) ;
2015-04-11 15:35:37 +03:00
return 0 ;
}
2015-04-05 07:59:26 +03:00
2017-10-19 15:29:10 +03:00
static int ath_vendor_cmd ( struct hci_dev * hdev , uint8_t opcode , uint16_t index ,
const void * data , size_t dlen )
2015-04-11 15:35:38 +03:00
{
struct sk_buff * skb ;
2017-10-19 15:29:10 +03:00
struct ath_vendor_cmd cmd ;
if ( dlen > sizeof ( cmd . data ) )
return - EINVAL ;
cmd . opcode = opcode ;
cmd . index = cpu_to_le16 ( index ) ;
cmd . len = dlen ;
memcpy ( cmd . data , data , dlen ) ;
skb = __hci_cmd_sync ( hdev , 0xfc0b , dlen + 4 , & cmd , HCI_INIT_TIMEOUT ) ;
if ( IS_ERR ( skb ) )
return PTR_ERR ( skb ) ;
2015-04-11 15:35:38 +03:00
kfree_skb ( skb ) ;
return 0 ;
}
2017-10-19 15:29:10 +03:00
static int ath_set_bdaddr ( struct hci_dev * hdev , const bdaddr_t * bdaddr )
{
return ath_vendor_cmd ( hdev , OP_WRITE_TAG , INDEX_BDADDR , bdaddr ,
sizeof ( * bdaddr ) ) ;
}
2015-04-11 15:35:38 +03:00
static int ath_setup ( struct hci_uart * hu )
{
BT_DBG ( " hu %p " , hu ) ;
hu - > hdev - > set_bdaddr = ath_set_bdaddr ;
return 0 ;
}
2015-04-11 15:35:37 +03:00
static const struct h4_recv_pkt ath_recv_pkts [ ] = {
{ H4_RECV_ACL , . recv = hci_recv_frame } ,
{ H4_RECV_SCO , . recv = hci_recv_frame } ,
{ H4_RECV_EVENT , . recv = hci_recv_frame } ,
} ;
2010-07-19 11:04:07 +04:00
2015-04-11 15:35:37 +03:00
static int ath_recv ( struct hci_uart * hu , const void * data , int count )
{
struct ath_struct * ath = hu - > priv ;
2010-07-19 11:04:07 +04:00
2015-04-11 15:35:37 +03:00
ath - > rx_skb = h4_recv_buf ( hu - > hdev , ath - > rx_skb , data , count ,
ath_recv_pkts , ARRAY_SIZE ( ath_recv_pkts ) ) ;
if ( IS_ERR ( ath - > rx_skb ) ) {
int err = PTR_ERR ( ath - > rx_skb ) ;
2017-10-30 12:42:59 +03:00
bt_dev_err ( hu - > hdev , " Frame reassembly failed (%d) " , err ) ;
2015-06-17 15:10:39 +03:00
ath - > rx_skb = NULL ;
2015-04-11 15:35:37 +03:00
return err ;
}
return count ;
2010-07-19 11:04:07 +04:00
}
# define HCI_OP_ATH_SLEEP 0xFC04
static int ath_enqueue ( struct hci_uart * hu , struct sk_buff * skb )
{
struct ath_struct * ath = hu - > priv ;
2015-11-05 09:33:56 +03:00
if ( hci_skb_pkt_type ( skb ) = = HCI_SCODATA_PKT ) {
2010-07-23 14:11:04 +04:00
kfree_skb ( skb ) ;
2010-07-19 11:04:07 +04:00
return 0 ;
}
2015-04-11 15:35:37 +03:00
/* Update power management enable flag with parameters of
2010-07-19 11:04:07 +04:00
* HCI sleep enable vendor specific HCI command .
*/
2015-11-05 09:33:56 +03:00
if ( hci_skb_pkt_type ( skb ) = = HCI_COMMAND_PKT ) {
2010-07-19 11:04:07 +04:00
struct hci_command_hdr * hdr = ( void * ) skb - > data ;
if ( __le16_to_cpu ( hdr - > opcode ) = = HCI_OP_ATH_SLEEP )
ath - > cur_sleep = skb - > data [ HCI_COMMAND_HDR_SIZE ] ;
}
BT_DBG ( " hu %p skb %p " , hu , skb ) ;
/* Prepend skb with frame type */
2015-11-05 09:33:56 +03:00
memcpy ( skb_push ( skb , 1 ) , & hci_skb_pkt_type ( skb ) , 1 ) ;
2010-07-19 11:04:07 +04:00
skb_queue_tail ( & ath - > txq , skb ) ;
set_bit ( HCI_UART_SENDING , & hu - > tx_state ) ;
schedule_work ( & ath - > ctxtsw ) ;
return 0 ;
}
static struct sk_buff * ath_dequeue ( struct hci_uart * hu )
{
struct ath_struct * ath = hu - > priv ;
return skb_dequeue ( & ath - > txq ) ;
}
2015-04-05 08:11:43 +03:00
static const struct hci_uart_proto athp = {
2015-04-05 08:27:34 +03:00
. id = HCI_UART_ATH3K ,
. name = " ATH3K " ,
2015-10-20 22:30:45 +03:00
. manufacturer = 69 ,
2015-04-05 08:27:34 +03:00
. open = ath_open ,
. close = ath_close ,
2015-04-11 15:35:37 +03:00
. flush = ath_flush ,
2015-04-11 15:35:38 +03:00
. setup = ath_setup ,
2015-04-05 08:27:34 +03:00
. recv = ath_recv ,
. enqueue = ath_enqueue ,
. dequeue = ath_dequeue ,
2010-07-19 11:04:07 +04:00
} ;
2010-07-24 09:04:44 +04:00
int __init ath_init ( void )
2010-07-19 11:04:07 +04:00
{
2015-04-05 08:27:35 +03:00
return hci_uart_register_proto ( & athp ) ;
2010-07-19 11:04:07 +04:00
}
2010-07-24 09:04:44 +04:00
int __exit ath_deinit ( void )
2010-07-19 11:04:07 +04:00
{
return hci_uart_unregister_proto ( & athp ) ;
}