2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2015-02-01 22:26:14 +01:00
/*
* The NFC Controller Interface is the communication protocol between an
* NFC Controller ( NFCC ) and a Device Host ( DH ) .
* This is the HCI over NCI implementation , as specified in the 10.2
* section of the NCI 1.1 specification .
*
* Copyright ( C ) 2014 STMicroelectronics SAS . All rights reserved .
*/
# include <linux/skbuff.h>
# include "../nfc.h"
# include <net/nfc/nci.h>
# include <net/nfc/nci_core.h>
# include <linux/nfc.h>
struct nci_data {
u8 conn_id ;
u8 pipe ;
u8 cmd ;
const u8 * data ;
u32 data_len ;
} __packed ;
struct nci_hci_create_pipe_params {
u8 src_gate ;
u8 dest_host ;
u8 dest_gate ;
} __packed ;
struct nci_hci_create_pipe_resp {
u8 src_host ;
u8 src_gate ;
u8 dest_host ;
u8 dest_gate ;
u8 pipe ;
} __packed ;
struct nci_hci_delete_pipe_noti {
u8 pipe ;
} __packed ;
struct nci_hci_all_pipe_cleared_noti {
u8 host ;
} __packed ;
struct nci_hcp_message {
u8 header ; /* type -cmd,evt,rsp- + instruction */
u8 data [ ] ;
} __packed ;
struct nci_hcp_packet {
u8 header ; /* cbit+pipe */
struct nci_hcp_message message ;
} __packed ;
# define NCI_HCI_ANY_SET_PARAMETER 0x01
# define NCI_HCI_ANY_GET_PARAMETER 0x02
# define NCI_HCI_ANY_CLOSE_PIPE 0x04
2015-10-25 22:54:23 +01:00
# define NCI_HCI_ADM_CLEAR_ALL_PIPE 0x14
2015-02-01 22:26:14 +01:00
# define NCI_HFP_NO_CHAINING 0x80
# define NCI_NFCEE_ID_HCI 0x80
# define NCI_EVT_HOT_PLUG 0x03
# define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01
2015-10-25 22:54:25 +01:00
# define NCI_HCI_ADM_CREATE_PIPE 0x10
# define NCI_HCI_ADM_DELETE_PIPE 0x11
2015-02-01 22:26:14 +01:00
/* HCP headers */
# define NCI_HCI_HCP_PACKET_HEADER_LEN 1
# define NCI_HCI_HCP_MESSAGE_HEADER_LEN 1
# define NCI_HCI_HCP_HEADER_LEN 2
/* HCP types */
# define NCI_HCI_HCP_COMMAND 0x00
# define NCI_HCI_HCP_EVENT 0x01
# define NCI_HCI_HCP_RESPONSE 0x02
# define NCI_HCI_ADM_NOTIFY_PIPE_CREATED 0x12
# define NCI_HCI_ADM_NOTIFY_PIPE_DELETED 0x13
# define NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED 0x15
# define NCI_HCI_FRAGMENT 0x7f
# define NCI_HCP_HEADER(type, instr) ((((type) & 0x03) << 6) |\
( ( instr ) & 0x3f ) )
# define NCI_HCP_MSG_GET_TYPE(header) ((header & 0xc0) >> 6)
# define NCI_HCP_MSG_GET_CMD(header) (header & 0x3f)
# define NCI_HCP_MSG_GET_PIPE(header) (header & 0x7f)
2015-10-25 22:54:21 +01:00
static int nci_hci_result_to_errno ( u8 result )
{
switch ( result ) {
case NCI_HCI_ANY_OK :
return 0 ;
case NCI_HCI_ANY_E_REG_PAR_UNKNOWN :
return - EOPNOTSUPP ;
case NCI_HCI_ANY_E_TIMEOUT :
return - ETIME ;
default :
return - 1 ;
}
}
2015-02-01 22:26:14 +01:00
/* HCI core */
static void nci_hci_reset_pipes ( struct nci_hci_dev * hdev )
{
int i ;
for ( i = 0 ; i < NCI_HCI_MAX_PIPES ; i + + ) {
hdev - > pipes [ i ] . gate = NCI_HCI_INVALID_GATE ;
hdev - > pipes [ i ] . host = NCI_HCI_INVALID_HOST ;
}
memset ( hdev - > gate2pipe , NCI_HCI_INVALID_PIPE , sizeof ( hdev - > gate2pipe ) ) ;
}
static void nci_hci_reset_pipes_per_host ( struct nci_dev * ndev , u8 host )
{
int i ;
for ( i = 0 ; i < NCI_HCI_MAX_PIPES ; i + + ) {
if ( ndev - > hci_dev - > pipes [ i ] . host = = host ) {
ndev - > hci_dev - > pipes [ i ] . gate = NCI_HCI_INVALID_GATE ;
ndev - > hci_dev - > pipes [ i ] . host = NCI_HCI_INVALID_HOST ;
}
}
}
/* Fragment HCI data over NCI packet.
* NFC Forum NCI 10.2 .2 Data Exchange :
* The payload of the Data Packets sent on the Logical Connection SHALL be
* valid HCP packets , as defined within [ ETSI_102622 ] . Each Data Packet SHALL
* contain a single HCP packet . NCI Segmentation and Reassembly SHALL NOT be
* applied to Data Messages in either direction . The HCI fragmentation mechanism
* is used if required .
*/
static int nci_hci_send_data ( struct nci_dev * ndev , u8 pipe ,
const u8 data_type , const u8 * data ,
size_t data_len )
{
struct nci_conn_info * conn_info ;
struct sk_buff * skb ;
int len , i , r ;
u8 cb = pipe ;
conn_info = ndev - > hci_dev - > conn_info ;
if ( ! conn_info )
return - EPROTO ;
2015-10-25 22:54:20 +01:00
i = 0 ;
skb = nci_skb_alloc ( ndev , conn_info - > max_pkt_payload_len +
2015-02-01 22:26:14 +01:00
NCI_DATA_HDR_SIZE , GFP_KERNEL ) ;
if ( ! skb )
return - ENOMEM ;
2015-10-25 22:54:20 +01:00
skb_reserve ( skb , NCI_DATA_HDR_SIZE + 2 ) ;
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 14:29:23 +02:00
* ( u8 * ) skb_push ( skb , 1 ) = data_type ;
2015-02-01 22:26:14 +01:00
do {
2015-10-25 22:54:20 +01:00
len = conn_info - > max_pkt_payload_len ;
2015-02-01 22:26:14 +01:00
/* If last packet add NCI_HFP_NO_CHAINING */
if ( i + conn_info - > max_pkt_payload_len -
( skb - > len + 1 ) > = data_len ) {
cb | = NCI_HFP_NO_CHAINING ;
len = data_len - i ;
} else {
len = conn_info - > max_pkt_payload_len - skb - > len - 1 ;
}
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 14:29:23 +02:00
* ( u8 * ) skb_push ( skb , 1 ) = cb ;
2015-02-01 22:26:14 +01:00
if ( len > 0 )
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 14:29:20 +02:00
skb_put_data ( skb , data + i , len ) ;
2015-02-01 22:26:14 +01:00
r = nci_send_data ( ndev , conn_info - > conn_id , skb ) ;
if ( r < 0 )
return r ;
i + = len ;
2015-10-25 22:54:20 +01:00
2015-02-01 22:26:14 +01:00
if ( i < data_len ) {
2015-10-25 22:54:20 +01:00
skb = nci_skb_alloc ( ndev ,
conn_info - > max_pkt_payload_len +
NCI_DATA_HDR_SIZE , GFP_KERNEL ) ;
if ( ! skb )
return - ENOMEM ;
skb_reserve ( skb , NCI_DATA_HDR_SIZE + 1 ) ;
2015-02-01 22:26:14 +01:00
}
} while ( i < data_len ) ;
return i ;
}
static void nci_hci_send_data_req ( struct nci_dev * ndev , unsigned long opt )
{
struct nci_data * data = ( struct nci_data * ) opt ;
nci_hci_send_data ( ndev , data - > pipe , data - > cmd ,
data - > data , data - > data_len ) ;
}
int nci_hci_send_event ( struct nci_dev * ndev , u8 gate , u8 event ,
const u8 * param , size_t param_len )
{
u8 pipe = ndev - > hci_dev - > gate2pipe [ gate ] ;
if ( pipe = = NCI_HCI_INVALID_PIPE )
return - EADDRNOTAVAIL ;
return nci_hci_send_data ( ndev , pipe ,
NCI_HCP_HEADER ( NCI_HCI_HCP_EVENT , event ) ,
param , param_len ) ;
}
EXPORT_SYMBOL ( nci_hci_send_event ) ;
int nci_hci_send_cmd ( struct nci_dev * ndev , u8 gate , u8 cmd ,
const u8 * param , size_t param_len ,
struct sk_buff * * skb )
{
2015-10-25 22:54:21 +01:00
struct nci_hcp_message * message ;
struct nci_conn_info * conn_info ;
2015-02-01 22:26:14 +01:00
struct nci_data data ;
int r ;
u8 pipe = ndev - > hci_dev - > gate2pipe [ gate ] ;
if ( pipe = = NCI_HCI_INVALID_PIPE )
return - EADDRNOTAVAIL ;
conn_info = ndev - > hci_dev - > conn_info ;
if ( ! conn_info )
return - EPROTO ;
data . conn_id = conn_info - > conn_id ;
data . pipe = pipe ;
data . cmd = NCI_HCP_HEADER ( NCI_HCI_HCP_COMMAND , cmd ) ;
data . data = param ;
data . data_len = param_len ;
r = nci_request ( ndev , nci_hci_send_data_req , ( unsigned long ) & data ,
msecs_to_jiffies ( NCI_DATA_TIMEOUT ) ) ;
2015-10-25 22:54:21 +01:00
if ( r = = NCI_STATUS_OK ) {
message = ( struct nci_hcp_message * ) conn_info - > rx_skb - > data ;
r = nci_hci_result_to_errno (
NCI_HCP_MSG_GET_CMD ( message - > header ) ) ;
skb_pull ( conn_info - > rx_skb , NCI_HCI_HCP_MESSAGE_HEADER_LEN ) ;
if ( ! r & & skb )
* skb = conn_info - > rx_skb ;
}
2015-02-01 22:26:14 +01:00
return r ;
}
EXPORT_SYMBOL ( nci_hci_send_cmd ) ;
2015-10-25 22:54:23 +01:00
int nci_hci_clear_all_pipes ( struct nci_dev * ndev )
{
int r ;
r = nci_hci_send_cmd ( ndev , NCI_HCI_ADMIN_GATE ,
NCI_HCI_ADM_CLEAR_ALL_PIPE , NULL , 0 , NULL ) ;
if ( r < 0 )
return r ;
nci_hci_reset_pipes ( ndev - > hci_dev ) ;
return r ;
}
EXPORT_SYMBOL ( nci_hci_clear_all_pipes ) ;
2015-02-01 22:26:14 +01:00
static void nci_hci_event_received ( struct nci_dev * ndev , u8 pipe ,
u8 event , struct sk_buff * skb )
{
if ( ndev - > ops - > hci_event_received )
ndev - > ops - > hci_event_received ( ndev , pipe , event , skb ) ;
}
static void nci_hci_cmd_received ( struct nci_dev * ndev , u8 pipe ,
u8 cmd , struct sk_buff * skb )
{
u8 gate = ndev - > hci_dev - > pipes [ pipe ] . gate ;
u8 status = NCI_HCI_ANY_OK | ~ NCI_HCI_FRAGMENT ;
u8 dest_gate , new_pipe ;
struct nci_hci_create_pipe_resp * create_info ;
struct nci_hci_delete_pipe_noti * delete_info ;
struct nci_hci_all_pipe_cleared_noti * cleared_info ;
pr_debug ( " from gate %x pipe %x cmd %x \n " , gate , pipe , cmd ) ;
switch ( cmd ) {
case NCI_HCI_ADM_NOTIFY_PIPE_CREATED :
if ( skb - > len ! = 5 ) {
status = NCI_HCI_ANY_E_NOK ;
goto exit ;
}
create_info = ( struct nci_hci_create_pipe_resp * ) skb - > data ;
dest_gate = create_info - > dest_gate ;
new_pipe = create_info - > pipe ;
2019-04-03 10:12:48 +03:00
if ( new_pipe > = NCI_HCI_MAX_PIPES ) {
status = NCI_HCI_ANY_E_NOK ;
goto exit ;
}
2015-02-01 22:26:14 +01:00
/* Save the new created pipe and bind with local gate,
* the description for skb - > data [ 3 ] is destination gate id
* but since we received this cmd from host controller , we
* are the destination and it is our local gate
*/
ndev - > hci_dev - > gate2pipe [ dest_gate ] = new_pipe ;
ndev - > hci_dev - > pipes [ new_pipe ] . gate = dest_gate ;
ndev - > hci_dev - > pipes [ new_pipe ] . host =
create_info - > src_host ;
break ;
case NCI_HCI_ANY_OPEN_PIPE :
/* If the pipe is not created report an error */
if ( gate = = NCI_HCI_INVALID_GATE ) {
status = NCI_HCI_ANY_E_NOK ;
goto exit ;
}
break ;
case NCI_HCI_ADM_NOTIFY_PIPE_DELETED :
if ( skb - > len ! = 1 ) {
status = NCI_HCI_ANY_E_NOK ;
goto exit ;
}
delete_info = ( struct nci_hci_delete_pipe_noti * ) skb - > data ;
2019-04-03 10:12:48 +03:00
if ( delete_info - > pipe > = NCI_HCI_MAX_PIPES ) {
status = NCI_HCI_ANY_E_NOK ;
goto exit ;
}
2015-02-01 22:26:14 +01:00
ndev - > hci_dev - > pipes [ delete_info - > pipe ] . gate =
NCI_HCI_INVALID_GATE ;
ndev - > hci_dev - > pipes [ delete_info - > pipe ] . host =
NCI_HCI_INVALID_HOST ;
break ;
case NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED :
if ( skb - > len ! = 1 ) {
status = NCI_HCI_ANY_E_NOK ;
goto exit ;
}
cleared_info =
( struct nci_hci_all_pipe_cleared_noti * ) skb - > data ;
nci_hci_reset_pipes_per_host ( ndev , cleared_info - > host ) ;
break ;
default :
pr_debug ( " Discarded unknown cmd %x to gate %x \n " , cmd , gate ) ;
break ;
}
if ( ndev - > ops - > hci_cmd_received )
ndev - > ops - > hci_cmd_received ( ndev , pipe , cmd , skb ) ;
exit :
nci_hci_send_data ( ndev , pipe , status , NULL , 0 ) ;
kfree_skb ( skb ) ;
}
static void nci_hci_resp_received ( struct nci_dev * ndev , u8 pipe ,
u8 result , struct sk_buff * skb )
{
struct nci_conn_info * conn_info ;
u8 status = result ;
conn_info = ndev - > hci_dev - > conn_info ;
if ( ! conn_info ) {
status = NCI_STATUS_REJECTED ;
goto exit ;
}
conn_info - > rx_skb = skb ;
exit :
2015-10-25 22:54:21 +01:00
nci_req_complete ( ndev , NCI_STATUS_OK ) ;
2015-02-01 22:26:14 +01:00
}
/* Receive hcp message for pipe, with type and cmd.
* skb contains optional message data only .
*/
static void nci_hci_hcp_message_rx ( struct nci_dev * ndev , u8 pipe ,
u8 type , u8 instruction , struct sk_buff * skb )
{
switch ( type ) {
case NCI_HCI_HCP_RESPONSE :
nci_hci_resp_received ( ndev , pipe , instruction , skb ) ;
break ;
case NCI_HCI_HCP_COMMAND :
nci_hci_cmd_received ( ndev , pipe , instruction , skb ) ;
break ;
case NCI_HCI_HCP_EVENT :
nci_hci_event_received ( ndev , pipe , instruction , skb ) ;
break ;
default :
pr_err ( " UNKNOWN MSG Type %d, instruction=%d \n " ,
type , instruction ) ;
kfree_skb ( skb ) ;
break ;
}
2015-10-25 22:54:44 +01:00
nci_req_complete ( ndev , NCI_STATUS_OK ) ;
2015-02-01 22:26:14 +01:00
}
static void nci_hci_msg_rx_work ( struct work_struct * work )
{
struct nci_hci_dev * hdev =
container_of ( work , struct nci_hci_dev , msg_rx_work ) ;
struct sk_buff * skb ;
struct nci_hcp_message * message ;
u8 pipe , type , instruction ;
while ( ( skb = skb_dequeue ( & hdev - > msg_rx_queue ) ) ! = NULL ) {
2015-10-25 22:54:22 +01:00
pipe = NCI_HCP_MSG_GET_PIPE ( skb - > data [ 0 ] ) ;
2015-02-01 22:26:14 +01:00
skb_pull ( skb , NCI_HCI_HCP_PACKET_HEADER_LEN ) ;
message = ( struct nci_hcp_message * ) skb - > data ;
type = NCI_HCP_MSG_GET_TYPE ( message - > header ) ;
instruction = NCI_HCP_MSG_GET_CMD ( message - > header ) ;
skb_pull ( skb , NCI_HCI_HCP_MESSAGE_HEADER_LEN ) ;
nci_hci_hcp_message_rx ( hdev - > ndev , pipe ,
type , instruction , skb ) ;
}
}
void nci_hci_data_received_cb ( void * context ,
struct sk_buff * skb , int err )
{
struct nci_dev * ndev = ( struct nci_dev * ) context ;
struct nci_hcp_packet * packet ;
2015-10-25 22:54:21 +01:00
u8 pipe , type ;
2015-02-01 22:26:14 +01:00
struct sk_buff * hcp_skb ;
struct sk_buff * frag_skb ;
int msg_len ;
pr_debug ( " \n " ) ;
if ( err ) {
nci_req_complete ( ndev , err ) ;
return ;
}
packet = ( struct nci_hcp_packet * ) skb - > data ;
if ( ( packet - > header & ~ NCI_HCI_FRAGMENT ) = = 0 ) {
skb_queue_tail ( & ndev - > hci_dev - > rx_hcp_frags , skb ) ;
return ;
}
/* it's the last fragment. Does it need re-aggregation? */
if ( skb_queue_len ( & ndev - > hci_dev - > rx_hcp_frags ) ) {
2015-10-25 22:54:22 +01:00
pipe = NCI_HCP_MSG_GET_PIPE ( packet - > header ) ;
2015-02-01 22:26:14 +01:00
skb_queue_tail ( & ndev - > hci_dev - > rx_hcp_frags , skb ) ;
msg_len = 0 ;
skb_queue_walk ( & ndev - > hci_dev - > rx_hcp_frags , frag_skb ) {
msg_len + = ( frag_skb - > len -
NCI_HCI_HCP_PACKET_HEADER_LEN ) ;
}
hcp_skb = nfc_alloc_recv_skb ( NCI_HCI_HCP_PACKET_HEADER_LEN +
msg_len , GFP_KERNEL ) ;
if ( ! hcp_skb ) {
nci_req_complete ( ndev , - ENOMEM ) ;
return ;
}
networking: add and use skb_put_u8()
Joe and Bjørn suggested that it'd be nicer to not have the
cast in the fairly common case of doing
*(u8 *)skb_put(skb, 1) = c;
Add skb_put_u8() for this case, and use it across the code,
using the following spatch:
@@
expression SKB, C, S;
typedef u8;
identifier fn = {skb_put};
fresh identifier fn2 = fn ## "_u8";
@@
- *(u8 *)fn(SKB, S) = C;
+ fn2(SKB, C);
Note that due to the "S", the spatch isn't perfect, it should
have checked that S is 1, but there's also places that use a
sizeof expression like sizeof(var) or sizeof(u8) etc. Turns
out that nobody ever did something like
*(u8 *)skb_put(skb, 2) = c;
which would be wrong anyway since the second byte wouldn't be
initialized.
Suggested-by: Joe Perches <joe@perches.com>
Suggested-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 14:29:24 +02:00
skb_put_u8 ( hcp_skb , pipe ) ;
2015-02-01 22:26:14 +01:00
skb_queue_walk ( & ndev - > hci_dev - > rx_hcp_frags , frag_skb ) {
2015-10-25 22:54:21 +01:00
msg_len = frag_skb - > len - NCI_HCI_HCP_PACKET_HEADER_LEN ;
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 14:29:20 +02:00
skb_put_data ( hcp_skb ,
frag_skb - > data + NCI_HCI_HCP_PACKET_HEADER_LEN ,
msg_len ) ;
2015-02-01 22:26:14 +01:00
}
skb_queue_purge ( & ndev - > hci_dev - > rx_hcp_frags ) ;
} else {
packet - > header & = NCI_HCI_FRAGMENT ;
hcp_skb = skb ;
}
/* if this is a response, dispatch immediately to
* unblock waiting cmd context . Otherwise , enqueue to dispatch
* in separate context where handler can also execute command .
*/
packet = ( struct nci_hcp_packet * ) hcp_skb - > data ;
type = NCI_HCP_MSG_GET_TYPE ( packet - > message . header ) ;
if ( type = = NCI_HCI_HCP_RESPONSE ) {
2015-10-25 22:54:21 +01:00
pipe = NCI_HCP_MSG_GET_PIPE ( packet - > header ) ;
skb_pull ( hcp_skb , NCI_HCI_HCP_PACKET_HEADER_LEN ) ;
nci_hci_hcp_message_rx ( ndev , pipe , type ,
NCI_STATUS_OK , hcp_skb ) ;
2015-02-01 22:26:14 +01:00
} else {
skb_queue_tail ( & ndev - > hci_dev - > msg_rx_queue , hcp_skb ) ;
schedule_work ( & ndev - > hci_dev - > msg_rx_work ) ;
}
}
int nci_hci_open_pipe ( struct nci_dev * ndev , u8 pipe )
{
struct nci_data data ;
struct nci_conn_info * conn_info ;
conn_info = ndev - > hci_dev - > conn_info ;
if ( ! conn_info )
return - EPROTO ;
data . conn_id = conn_info - > conn_id ;
data . pipe = pipe ;
data . cmd = NCI_HCP_HEADER ( NCI_HCI_HCP_COMMAND ,
NCI_HCI_ANY_OPEN_PIPE ) ;
data . data = NULL ;
data . data_len = 0 ;
return nci_request ( ndev , nci_hci_send_data_req ,
( unsigned long ) & data ,
msecs_to_jiffies ( NCI_DATA_TIMEOUT ) ) ;
}
EXPORT_SYMBOL ( nci_hci_open_pipe ) ;
2015-10-25 22:54:25 +01:00
static u8 nci_hci_create_pipe ( struct nci_dev * ndev , u8 dest_host ,
u8 dest_gate , int * result )
{
u8 pipe ;
struct sk_buff * skb ;
struct nci_hci_create_pipe_params params ;
struct nci_hci_create_pipe_resp * resp ;
pr_debug ( " gate=%d \n " , dest_gate ) ;
params . src_gate = NCI_HCI_ADMIN_GATE ;
params . dest_host = dest_host ;
params . dest_gate = dest_gate ;
* result = nci_hci_send_cmd ( ndev , NCI_HCI_ADMIN_GATE ,
NCI_HCI_ADM_CREATE_PIPE ,
( u8 * ) & params , sizeof ( params ) , & skb ) ;
if ( * result < 0 )
return NCI_HCI_INVALID_PIPE ;
resp = ( struct nci_hci_create_pipe_resp * ) skb - > data ;
pipe = resp - > pipe ;
kfree_skb ( skb ) ;
pr_debug ( " pipe created=%d \n " , pipe ) ;
return pipe ;
}
static int nci_hci_delete_pipe ( struct nci_dev * ndev , u8 pipe )
{
pr_debug ( " \n " ) ;
return nci_hci_send_cmd ( ndev , NCI_HCI_ADMIN_GATE ,
NCI_HCI_ADM_DELETE_PIPE , & pipe , 1 , NULL ) ;
}
2015-02-01 22:26:14 +01:00
int nci_hci_set_param ( struct nci_dev * ndev , u8 gate , u8 idx ,
const u8 * param , size_t param_len )
{
2015-10-25 22:54:21 +01:00
struct nci_hcp_message * message ;
2015-02-01 22:26:14 +01:00
struct nci_conn_info * conn_info ;
struct nci_data data ;
int r ;
u8 * tmp ;
u8 pipe = ndev - > hci_dev - > gate2pipe [ gate ] ;
pr_debug ( " idx=%d to gate %d \n " , idx , gate ) ;
if ( pipe = = NCI_HCI_INVALID_PIPE )
return - EADDRNOTAVAIL ;
conn_info = ndev - > hci_dev - > conn_info ;
if ( ! conn_info )
return - EPROTO ;
tmp = kmalloc ( 1 + param_len , GFP_KERNEL ) ;
if ( ! tmp )
return - ENOMEM ;
* tmp = idx ;
memcpy ( tmp + 1 , param , param_len ) ;
data . conn_id = conn_info - > conn_id ;
data . pipe = pipe ;
data . cmd = NCI_HCP_HEADER ( NCI_HCI_HCP_COMMAND ,
NCI_HCI_ANY_SET_PARAMETER ) ;
data . data = tmp ;
data . data_len = param_len + 1 ;
r = nci_request ( ndev , nci_hci_send_data_req ,
( unsigned long ) & data ,
msecs_to_jiffies ( NCI_DATA_TIMEOUT ) ) ;
2015-10-25 22:54:21 +01:00
if ( r = = NCI_STATUS_OK ) {
message = ( struct nci_hcp_message * ) conn_info - > rx_skb - > data ;
r = nci_hci_result_to_errno (
NCI_HCP_MSG_GET_CMD ( message - > header ) ) ;
skb_pull ( conn_info - > rx_skb , NCI_HCI_HCP_MESSAGE_HEADER_LEN ) ;
}
2015-02-01 22:26:14 +01:00
kfree ( tmp ) ;
return r ;
}
EXPORT_SYMBOL ( nci_hci_set_param ) ;
int nci_hci_get_param ( struct nci_dev * ndev , u8 gate , u8 idx ,
struct sk_buff * * skb )
{
2015-10-25 22:54:21 +01:00
struct nci_hcp_message * message ;
2015-02-01 22:26:14 +01:00
struct nci_conn_info * conn_info ;
struct nci_data data ;
int r ;
u8 pipe = ndev - > hci_dev - > gate2pipe [ gate ] ;
pr_debug ( " idx=%d to gate %d \n " , idx , gate ) ;
if ( pipe = = NCI_HCI_INVALID_PIPE )
return - EADDRNOTAVAIL ;
conn_info = ndev - > hci_dev - > conn_info ;
if ( ! conn_info )
return - EPROTO ;
data . conn_id = conn_info - > conn_id ;
data . pipe = pipe ;
data . cmd = NCI_HCP_HEADER ( NCI_HCI_HCP_COMMAND ,
NCI_HCI_ANY_GET_PARAMETER ) ;
data . data = & idx ;
data . data_len = 1 ;
r = nci_request ( ndev , nci_hci_send_data_req , ( unsigned long ) & data ,
msecs_to_jiffies ( NCI_DATA_TIMEOUT ) ) ;
2015-10-25 22:54:21 +01:00
if ( r = = NCI_STATUS_OK ) {
message = ( struct nci_hcp_message * ) conn_info - > rx_skb - > data ;
r = nci_hci_result_to_errno (
NCI_HCP_MSG_GET_CMD ( message - > header ) ) ;
skb_pull ( conn_info - > rx_skb , NCI_HCI_HCP_MESSAGE_HEADER_LEN ) ;
if ( ! r & & skb )
* skb = conn_info - > rx_skb ;
}
2015-02-01 22:26:14 +01:00
return r ;
}
EXPORT_SYMBOL ( nci_hci_get_param ) ;
int nci_hci_connect_gate ( struct nci_dev * ndev ,
u8 dest_host , u8 dest_gate , u8 pipe )
{
2015-10-25 22:54:25 +01:00
bool pipe_created = false ;
2015-02-01 22:26:14 +01:00
int r ;
if ( pipe = = NCI_HCI_DO_NOT_OPEN_PIPE )
return 0 ;
if ( ndev - > hci_dev - > gate2pipe [ dest_gate ] ! = NCI_HCI_INVALID_PIPE )
return - EADDRINUSE ;
if ( pipe ! = NCI_HCI_INVALID_PIPE )
goto open_pipe ;
switch ( dest_gate ) {
case NCI_HCI_LINK_MGMT_GATE :
pipe = NCI_HCI_LINK_MGMT_PIPE ;
break ;
case NCI_HCI_ADMIN_GATE :
pipe = NCI_HCI_ADMIN_PIPE ;
break ;
2015-10-25 22:54:25 +01:00
default :
pipe = nci_hci_create_pipe ( ndev , dest_host , dest_gate , & r ) ;
2015-12-23 23:45:15 +01:00
if ( pipe = = NCI_HCI_INVALID_PIPE )
2015-10-25 22:54:25 +01:00
return r ;
pipe_created = true ;
break ;
2015-02-01 22:26:14 +01:00
}
open_pipe :
r = nci_hci_open_pipe ( ndev , pipe ) ;
2015-10-25 22:54:25 +01:00
if ( r < 0 ) {
if ( pipe_created ) {
if ( nci_hci_delete_pipe ( ndev , pipe ) < 0 ) {
/* TODO: Cannot clean by deleting pipe...
* - > inconsistent state
*/
}
}
2015-02-01 22:26:14 +01:00
return r ;
2015-10-25 22:54:25 +01:00
}
2015-02-01 22:26:14 +01:00
ndev - > hci_dev - > pipes [ pipe ] . gate = dest_gate ;
ndev - > hci_dev - > pipes [ pipe ] . host = dest_host ;
ndev - > hci_dev - > gate2pipe [ dest_gate ] = pipe ;
return 0 ;
}
EXPORT_SYMBOL ( nci_hci_connect_gate ) ;
static int nci_hci_dev_connect_gates ( struct nci_dev * ndev ,
u8 gate_count ,
struct nci_hci_gate * gates )
{
int r ;
while ( gate_count - - ) {
r = nci_hci_connect_gate ( ndev , gates - > dest_host ,
gates - > gate , gates - > pipe ) ;
if ( r < 0 )
return r ;
gates + + ;
}
return 0 ;
}
int nci_hci_dev_session_init ( struct nci_dev * ndev )
{
2015-02-03 19:48:07 +01:00
struct nci_conn_info * conn_info ;
2015-02-01 22:26:14 +01:00
struct sk_buff * skb ;
int r ;
ndev - > hci_dev - > count_pipes = 0 ;
ndev - > hci_dev - > expected_pipes = 0 ;
2015-02-03 19:48:07 +01:00
conn_info = ndev - > hci_dev - > conn_info ;
if ( ! conn_info )
return - EPROTO ;
conn_info - > data_exchange_cb = nci_hci_data_received_cb ;
conn_info - > data_exchange_cb_context = ndev ;
2015-02-01 22:26:14 +01:00
nci_hci_reset_pipes ( ndev - > hci_dev ) ;
if ( ndev - > hci_dev - > init_data . gates [ 0 ] . gate ! = NCI_HCI_ADMIN_GATE )
return - EPROTO ;
r = nci_hci_connect_gate ( ndev ,
ndev - > hci_dev - > init_data . gates [ 0 ] . dest_host ,
ndev - > hci_dev - > init_data . gates [ 0 ] . gate ,
ndev - > hci_dev - > init_data . gates [ 0 ] . pipe ) ;
if ( r < 0 )
2015-05-31 17:44:45 -07:00
return r ;
2015-02-01 22:26:14 +01:00
r = nci_hci_get_param ( ndev , NCI_HCI_ADMIN_GATE ,
NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY , & skb ) ;
if ( r < 0 )
2015-05-31 17:44:45 -07:00
return r ;
2015-02-01 22:26:14 +01:00
if ( skb - > len & &
skb - > len = = strlen ( ndev - > hci_dev - > init_data . session_id ) & &
2015-05-31 17:44:45 -07:00
! memcmp ( ndev - > hci_dev - > init_data . session_id , skb - > data , skb - > len ) & &
2015-02-01 22:26:14 +01:00
ndev - > ops - > hci_load_session ) {
/* Restore gate<->pipe table from some proprietary location. */
r = ndev - > ops - > hci_load_session ( ndev ) ;
} else {
2015-10-25 22:54:24 +01:00
r = nci_hci_clear_all_pipes ( ndev ) ;
if ( r < 0 )
goto exit ;
2015-02-01 22:26:14 +01:00
r = nci_hci_dev_connect_gates ( ndev ,
ndev - > hci_dev - > init_data . gate_count ,
ndev - > hci_dev - > init_data . gates ) ;
if ( r < 0 )
goto exit ;
r = nci_hci_set_param ( ndev , NCI_HCI_ADMIN_GATE ,
NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY ,
ndev - > hci_dev - > init_data . session_id ,
strlen ( ndev - > hci_dev - > init_data . session_id ) ) ;
}
exit :
kfree_skb ( skb ) ;
return r ;
}
EXPORT_SYMBOL ( nci_hci_dev_session_init ) ;
struct nci_hci_dev * nci_hci_allocate ( struct nci_dev * ndev )
{
struct nci_hci_dev * hdev ;
hdev = kzalloc ( sizeof ( * hdev ) , GFP_KERNEL ) ;
if ( ! hdev )
return NULL ;
skb_queue_head_init ( & hdev - > rx_hcp_frags ) ;
INIT_WORK ( & hdev - > msg_rx_work , nci_hci_msg_rx_work ) ;
skb_queue_head_init ( & hdev - > msg_rx_queue ) ;
hdev - > ndev = ndev ;
return hdev ;
}