2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-08-13 03:01:28 +03:00
/*
* Copyright ( c ) 2016 , Linaro Ltd .
* Copyright ( c ) 2015 , Sony Mobile Communications Inc .
*/
# include <linux/module.h>
# include <linux/slab.h>
2017-03-28 08:26:33 +03:00
# include <linux/rpmsg.h>
2017-09-08 16:57:55 +03:00
# include <linux/of.h>
2016-08-13 03:01:28 +03:00
# include <linux/soc/qcom/wcnss_ctrl.h>
# include <linux/platform_device.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
# include "btqca.h"
struct btqcomsmd {
struct hci_dev * hdev ;
2017-03-28 08:26:33 +03:00
struct rpmsg_endpoint * acl_channel ;
struct rpmsg_endpoint * cmd_channel ;
2016-08-13 03:01:28 +03:00
} ;
static int btqcomsmd_recv ( struct hci_dev * hdev , unsigned int type ,
const void * data , size_t count )
{
struct sk_buff * skb ;
/* Use GFP_ATOMIC as we're in IRQ context */
skb = bt_skb_alloc ( count , GFP_ATOMIC ) ;
if ( ! skb ) {
hdev - > stat . err_rx + + ;
return - ENOMEM ;
}
hci_skb_pkt_type ( skb ) = type ;
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:20 +03:00
skb_put_data ( skb , data , count ) ;
2016-08-13 03:01:28 +03:00
return hci_recv_frame ( hdev , skb ) ;
}
2017-03-28 08:26:33 +03:00
static int btqcomsmd_acl_callback ( struct rpmsg_device * rpdev , void * data ,
int count , void * priv , u32 addr )
2016-08-13 03:01:28 +03:00
{
2017-03-28 08:26:33 +03:00
struct btqcomsmd * btq = priv ;
2016-08-13 03:01:28 +03:00
btq - > hdev - > stat . byte_rx + = count ;
return btqcomsmd_recv ( btq - > hdev , HCI_ACLDATA_PKT , data , count ) ;
}
2017-03-28 08:26:33 +03:00
static int btqcomsmd_cmd_callback ( struct rpmsg_device * rpdev , void * data ,
int count , void * priv , u32 addr )
2016-08-13 03:01:28 +03:00
{
2017-03-28 08:26:33 +03:00
struct btqcomsmd * btq = priv ;
2016-08-13 03:01:28 +03:00
2018-04-06 12:23:45 +03:00
btq - > hdev - > stat . byte_rx + = count ;
2016-08-13 03:01:28 +03:00
return btqcomsmd_recv ( btq - > hdev , HCI_EVENT_PKT , data , count ) ;
}
static int btqcomsmd_send ( struct hci_dev * hdev , struct sk_buff * skb )
{
struct btqcomsmd * btq = hci_get_drvdata ( hdev ) ;
int ret ;
switch ( hci_skb_pkt_type ( skb ) ) {
case HCI_ACLDATA_PKT :
2017-03-28 08:26:33 +03:00
ret = rpmsg_send ( btq - > acl_channel , skb - > data , skb - > len ) ;
2018-04-06 12:23:45 +03:00
if ( ret ) {
hdev - > stat . err_tx + + ;
break ;
}
2016-08-13 03:01:28 +03:00
hdev - > stat . acl_tx + + ;
hdev - > stat . byte_tx + = skb - > len ;
break ;
case HCI_COMMAND_PKT :
2017-03-28 08:26:33 +03:00
ret = rpmsg_send ( btq - > cmd_channel , skb - > data , skb - > len ) ;
2018-04-06 12:23:45 +03:00
if ( ret ) {
hdev - > stat . err_tx + + ;
break ;
}
2016-08-13 03:01:28 +03:00
hdev - > stat . cmd_tx + + ;
2018-04-06 12:23:45 +03:00
hdev - > stat . byte_tx + = skb - > len ;
2016-08-13 03:01:28 +03:00
break ;
default :
ret = - EILSEQ ;
break ;
}
2017-11-22 17:03:17 +03:00
if ( ! ret )
kfree_skb ( skb ) ;
2016-08-13 03:01:28 +03:00
return ret ;
}
static int btqcomsmd_open ( struct hci_dev * hdev )
{
return 0 ;
}
static int btqcomsmd_close ( struct hci_dev * hdev )
{
return 0 ;
}
2017-09-05 13:26:03 +03:00
static int btqcomsmd_setup ( struct hci_dev * hdev )
{
struct sk_buff * skb ;
skb = __hci_cmd_sync ( hdev , HCI_OP_RESET , 0 , NULL , HCI_INIT_TIMEOUT ) ;
if ( IS_ERR ( skb ) )
return PTR_ERR ( skb ) ;
kfree_skb ( skb ) ;
2019-02-19 23:05:58 +03:00
/* Devices do not have persistent storage for BD address. Retrieve
* it from the firmware node property .
2017-09-05 13:26:03 +03:00
*/
2019-02-19 23:05:58 +03:00
set_bit ( HCI_QUIRK_USE_BDADDR_PROPERTY , & hdev - > quirks ) ;
2017-09-05 13:26:03 +03:00
return 0 ;
}
2016-08-13 03:01:28 +03:00
static int btqcomsmd_probe ( struct platform_device * pdev )
{
struct btqcomsmd * btq ;
struct hci_dev * hdev ;
void * wcnss ;
int ret ;
btq = devm_kzalloc ( & pdev - > dev , sizeof ( * btq ) , GFP_KERNEL ) ;
if ( ! btq )
return - ENOMEM ;
wcnss = dev_get_drvdata ( pdev - > dev . parent ) ;
btq - > acl_channel = qcom_wcnss_open_channel ( wcnss , " APPS_RIVA_BT_ACL " ,
2017-03-28 08:26:33 +03:00
btqcomsmd_acl_callback , btq ) ;
2016-08-13 03:01:28 +03:00
if ( IS_ERR ( btq - > acl_channel ) )
return PTR_ERR ( btq - > acl_channel ) ;
btq - > cmd_channel = qcom_wcnss_open_channel ( wcnss , " APPS_RIVA_BT_CMD " ,
2017-03-28 08:26:33 +03:00
btqcomsmd_cmd_callback , btq ) ;
2020-12-12 12:46:58 +03:00
if ( IS_ERR ( btq - > cmd_channel ) ) {
ret = PTR_ERR ( btq - > cmd_channel ) ;
goto destroy_acl_channel ;
}
2016-08-13 03:01:28 +03:00
hdev = hci_alloc_dev ( ) ;
2020-12-12 12:46:58 +03:00
if ( ! hdev ) {
ret = - ENOMEM ;
goto destroy_cmd_channel ;
}
2016-08-13 03:01:28 +03:00
hci_set_drvdata ( hdev , btq ) ;
btq - > hdev = hdev ;
SET_HCIDEV_DEV ( hdev , & pdev - > dev ) ;
hdev - > bus = HCI_SMD ;
hdev - > open = btqcomsmd_open ;
hdev - > close = btqcomsmd_close ;
hdev - > send = btqcomsmd_send ;
2017-09-05 13:26:03 +03:00
hdev - > setup = btqcomsmd_setup ;
2016-08-13 03:01:28 +03:00
hdev - > set_bdaddr = qca_set_bdaddr_rome ;
ret = hci_register_dev ( hdev ) ;
2020-12-12 12:46:58 +03:00
if ( ret < 0 )
goto hci_free_dev ;
2016-08-13 03:01:28 +03:00
platform_set_drvdata ( pdev , btq ) ;
return 0 ;
2020-12-12 12:46:58 +03:00
hci_free_dev :
hci_free_dev ( hdev ) ;
destroy_cmd_channel :
rpmsg_destroy_ept ( btq - > cmd_channel ) ;
destroy_acl_channel :
rpmsg_destroy_ept ( btq - > acl_channel ) ;
return ret ;
2016-08-13 03:01:28 +03:00
}
static int btqcomsmd_remove ( struct platform_device * pdev )
{
struct btqcomsmd * btq = platform_get_drvdata ( pdev ) ;
hci_unregister_dev ( btq - > hdev ) ;
hci_free_dev ( btq - > hdev ) ;
2017-03-28 08:26:33 +03:00
rpmsg_destroy_ept ( btq - > cmd_channel ) ;
rpmsg_destroy_ept ( btq - > acl_channel ) ;
2016-08-13 03:01:28 +03:00
return 0 ;
}
static const struct of_device_id btqcomsmd_of_match [ ] = {
{ . compatible = " qcom,wcnss-bt " , } ,
{ } ,
} ;
2017-01-02 16:09:56 +03:00
MODULE_DEVICE_TABLE ( of , btqcomsmd_of_match ) ;
2016-08-13 03:01:28 +03:00
static struct platform_driver btqcomsmd_driver = {
. probe = btqcomsmd_probe ,
. remove = btqcomsmd_remove ,
. driver = {
. name = " btqcomsmd " ,
. of_match_table = btqcomsmd_of_match ,
} ,
} ;
module_platform_driver ( btqcomsmd_driver ) ;
MODULE_AUTHOR ( " Bjorn Andersson <bjorn.andersson@sonymobile.com> " ) ;
MODULE_DESCRIPTION ( " Qualcomm SMD HCI driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;