2005-04-17 02:20:36 +04:00
/*
*
2005-10-28 21:20:45 +04:00
* Bluetooth HCI UART driver
*
* Copyright ( C ) 2000 - 2001 Qualcomm Incorporated
* Copyright ( C ) 2002 - 2003 Maxim Krasnyansky < maxk @ qualcomm . com >
* Copyright ( C ) 2004 - 2005 Marcel Holtmann < marcel @ holtmann . org >
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/interrupt.h>
# include <linux/ptrace.h>
# include <linux/poll.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/signal.h>
# include <linux/ioctl.h>
# include <linux/skbuff.h>
2015-04-06 09:44:59 +03:00
# include <asm/unaligned.h>
2005-04-17 02:20:36 +04:00
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
# include "hci_uart.h"
2005-10-28 21:20:45 +04:00
struct h4_struct {
2015-04-05 07:59:25 +03:00
struct sk_buff * rx_skb ;
2005-10-28 21:20:45 +04:00
struct sk_buff_head txq ;
} ;
2005-04-17 02:20:36 +04:00
/* Initialize protocol */
static int h4_open ( struct hci_uart * hu )
{
struct h4_struct * h4 ;
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
BT_DBG ( " hu %p " , hu ) ;
2005-10-28 21:20:45 +04:00
2012-01-07 18:19:38 +04:00
h4 = kzalloc ( sizeof ( * h4 ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! h4 )
return - ENOMEM ;
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
skb_queue_head_init ( & h4 - > txq ) ;
hu - > priv = h4 ;
return 0 ;
}
/* Flush protocol data */
static int h4_flush ( struct hci_uart * hu )
{
struct h4_struct * h4 = hu - > priv ;
BT_DBG ( " hu %p " , hu ) ;
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
skb_queue_purge ( & h4 - > txq ) ;
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* Close protocol */
static int h4_close ( struct hci_uart * hu )
{
struct h4_struct * h4 = hu - > priv ;
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
hu - > priv = NULL ;
BT_DBG ( " hu %p " , hu ) ;
skb_queue_purge ( & h4 - > txq ) ;
2005-10-28 21:20:45 +04:00
2015-04-05 07:59:25 +03:00
kfree_skb ( h4 - > rx_skb ) ;
2005-04-17 02:20:36 +04:00
hu - > priv = NULL ;
kfree ( h4 ) ;
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* Enqueue frame for transmittion (padding, crc, etc) */
static int h4_enqueue ( struct hci_uart * hu , struct sk_buff * skb )
{
struct h4_struct * h4 = hu - > priv ;
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 ) ;
2005-04-17 02:20:36 +04:00
skb_queue_tail ( & h4 - > txq , skb ) ;
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2015-04-06 09:44:59 +03:00
static const struct h4_recv_pkt h4_recv_pkts [ ] = {
{ H4_RECV_ACL , . recv = hci_recv_frame } ,
{ H4_RECV_SCO , . recv = hci_recv_frame } ,
{ H4_RECV_EVENT , . recv = hci_recv_frame } ,
} ;
2005-04-17 02:20:36 +04:00
/* Recv data */
2015-04-05 06:59:41 +03:00
static int h4_recv ( struct hci_uart * hu , const void * data , int count )
2005-04-17 02:20:36 +04:00
{
2015-04-05 07:59:25 +03:00
struct h4_struct * h4 = hu - > priv ;
2011-04-08 01:53:45 +04:00
2013-04-02 16:24:21 +04:00
if ( ! test_bit ( HCI_UART_REGISTERED , & hu - > flags ) )
return - EUNATCH ;
2015-04-06 09:44:59 +03:00
h4 - > rx_skb = h4_recv_buf ( hu - > hdev , h4 - > rx_skb , data , count ,
h4_recv_pkts , ARRAY_SIZE ( h4_recv_pkts ) ) ;
2015-04-05 07:59:25 +03:00
if ( IS_ERR ( h4 - > rx_skb ) ) {
int err = PTR_ERR ( h4 - > 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
h4 - > rx_skb = NULL ;
2015-04-05 07:59:25 +03:00
return err ;
2011-04-08 01:53:45 +04:00
}
2005-10-28 21:20:45 +04:00
2005-04-17 02:20:36 +04:00
return count ;
}
static struct sk_buff * h4_dequeue ( struct hci_uart * hu )
{
struct h4_struct * h4 = hu - > priv ;
return skb_dequeue ( & h4 - > txq ) ;
}
2015-04-05 08:11:43 +03:00
static const struct hci_uart_proto h4p = {
2005-10-28 21:20:45 +04:00
. id = HCI_UART_H4 ,
2015-04-05 08:27:34 +03:00
. name = " H4 " ,
2005-10-28 21:20:45 +04:00
. open = h4_open ,
. close = h4_close ,
. recv = h4_recv ,
. enqueue = h4_enqueue ,
. dequeue = h4_dequeue ,
. flush = h4_flush ,
2005-04-17 02:20:36 +04:00
} ;
2005-10-28 21:20:45 +04:00
2010-07-24 09:04:44 +04:00
int __init h4_init ( void )
2005-04-17 02:20:36 +04:00
{
2015-04-05 08:27:35 +03:00
return hci_uart_register_proto ( & h4p ) ;
2005-04-17 02:20:36 +04:00
}
2010-07-24 09:04:44 +04:00
int __exit h4_deinit ( void )
2005-04-17 02:20:36 +04:00
{
return hci_uart_unregister_proto ( & h4p ) ;
}
2015-04-05 07:59:24 +03:00
struct sk_buff * h4_recv_buf ( struct hci_dev * hdev , struct sk_buff * skb ,
2015-04-06 09:44:59 +03:00
const unsigned char * buffer , int count ,
const struct h4_recv_pkt * pkts , int pkts_count )
2015-04-05 07:59:24 +03:00
{
2017-03-28 18:59:34 +03:00
struct hci_uart * hu = hci_get_drvdata ( hdev ) ;
2017-07-29 20:32:31 +03:00
u8 alignment = hu - > alignment ? hu - > alignment : 1 ;
2017-03-28 18:59:34 +03:00
2019-01-22 11:33:26 +03:00
/* Check for error from previous call */
if ( IS_ERR ( skb ) )
skb = NULL ;
2015-04-05 07:59:24 +03:00
while ( count ) {
2015-04-06 09:44:59 +03:00
int i , len ;
2015-04-05 07:59:24 +03:00
2017-03-28 18:59:34 +03:00
/* remove padding bytes from buffer */
for ( ; hu - > padding & & count > 0 ; hu - > padding - - ) {
count - - ;
buffer + + ;
}
if ( ! count )
break ;
2015-04-05 07:59:24 +03:00
if ( ! skb ) {
2015-04-06 09:44:59 +03:00
for ( i = 0 ; i < pkts_count ; i + + ) {
if ( buffer [ 0 ] ! = ( & pkts [ i ] ) - > type )
continue ;
2015-04-05 07:59:24 +03:00
2015-04-06 09:44:59 +03:00
skb = bt_skb_alloc ( ( & pkts [ i ] ) - > maxlen ,
2015-04-05 07:59:24 +03:00
GFP_ATOMIC ) ;
if ( ! skb )
return ERR_PTR ( - ENOMEM ) ;
2015-11-05 09:33:56 +03:00
hci_skb_pkt_type ( skb ) = ( & pkts [ i ] ) - > type ;
hci_skb_expect ( skb ) = ( & pkts [ i ] ) - > hlen ;
2015-04-05 07:59:24 +03:00
break ;
2015-04-06 09:44:59 +03:00
}
2015-04-05 07:59:24 +03:00
2015-04-06 09:44:59 +03:00
/* Check for invalid packet type */
if ( ! skb )
2015-04-05 07:59:24 +03:00
return ERR_PTR ( - EILSEQ ) ;
count - = 1 ;
buffer + = 1 ;
}
2015-11-05 09:33:56 +03:00
len = min_t ( uint , hci_skb_expect ( skb ) - skb - > len , count ) ;
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 , buffer , len ) ;
2015-04-05 07:59:24 +03:00
count - = len ;
buffer + = len ;
2015-04-06 09:44:59 +03:00
/* Check for partial packet */
2015-11-05 09:33:56 +03:00
if ( skb - > len < hci_skb_expect ( skb ) )
2015-04-06 09:44:59 +03:00
continue ;
for ( i = 0 ; i < pkts_count ; i + + ) {
2015-11-05 09:33:56 +03:00
if ( hci_skb_pkt_type ( skb ) = = ( & pkts [ i ] ) - > type )
2015-04-06 09:44:59 +03:00
break ;
}
if ( i > = pkts_count ) {
kfree_skb ( skb ) ;
return ERR_PTR ( - EILSEQ ) ;
}
2015-04-05 07:59:24 +03:00
2015-04-06 09:44:59 +03:00
if ( skb - > len = = ( & pkts [ i ] ) - > hlen ) {
u16 dlen ;
2015-04-05 07:59:24 +03:00
2015-04-06 09:44:59 +03:00
switch ( ( & pkts [ i ] ) - > lsize ) {
case 0 :
/* No variable data length */
2015-08-24 19:57:57 +03:00
dlen = 0 ;
2015-04-06 09:44:59 +03:00
break ;
case 1 :
/* Single octet variable length */
dlen = skb - > data [ ( & pkts [ i ] ) - > loff ] ;
2015-11-05 09:33:56 +03:00
hci_skb_expect ( skb ) + = dlen ;
2015-04-05 07:59:24 +03:00
2015-04-06 09:44:59 +03:00
if ( skb_tailroom ( skb ) < dlen ) {
2015-04-05 07:59:24 +03:00
kfree_skb ( skb ) ;
return ERR_PTR ( - EMSGSIZE ) ;
}
2015-04-06 09:44:59 +03:00
break ;
case 2 :
/* Double octet variable length */
dlen = get_unaligned_le16 ( skb - > data +
( & pkts [ i ] ) - > loff ) ;
2015-11-05 09:33:56 +03:00
hci_skb_expect ( skb ) + = dlen ;
2015-04-05 07:59:24 +03:00
2015-04-06 09:44:59 +03:00
if ( skb_tailroom ( skb ) < dlen ) {
2015-04-05 07:59:24 +03:00
kfree_skb ( skb ) ;
return ERR_PTR ( - EMSGSIZE ) ;
}
2015-04-06 09:44:59 +03:00
break ;
default :
/* Unsupported variable length */
kfree_skb ( skb ) ;
return ERR_PTR ( - EILSEQ ) ;
2015-04-05 07:59:24 +03:00
}
2015-08-24 19:57:57 +03:00
if ( ! dlen ) {
2017-03-28 18:59:34 +03:00
hu - > padding = ( skb - > len - 1 ) % alignment ;
hu - > padding = ( alignment - hu - > padding ) % alignment ;
2015-08-24 19:57:57 +03:00
/* No more data, complete frame */
( & pkts [ i ] ) - > recv ( hdev , skb ) ;
skb = NULL ;
}
2015-04-06 09:44:59 +03:00
} else {
2017-03-28 18:59:34 +03:00
hu - > padding = ( skb - > len - 1 ) % alignment ;
hu - > padding = ( alignment - hu - > padding ) % alignment ;
2015-04-05 07:59:24 +03:00
/* Complete frame */
2015-04-06 09:44:59 +03:00
( & pkts [ i ] ) - > recv ( hdev , skb ) ;
2015-04-05 07:59:24 +03:00
skb = NULL ;
}
}
return skb ;
}
2015-10-08 04:06:53 +03:00
EXPORT_SYMBOL_GPL ( h4_recv_buf ) ;