forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drivers: net: ethernet: qualcomm: rmnet: Initial implementation
RmNet driver provides a transport agnostic MAP (multiplexing and aggregation protocol) support in embedded module. Module provides virtual network devices which can be attached to any IP-mode physical device. This will be used to provide all MAP functionality on future hardware in a single consistent location. Signed-off-by: Subash Abhinov Kasiviswanathan <[email protected]> Signed-off-by: David S. Miller <[email protected]>
- v5.11
- v5.11-rc7
- v5.11-rc6
- v5.11-rc5
- v5.11-rc4
- v5.11-rc3
- v5.11-rc2
- v5.11-rc1
- v5.10
- v5.10-rc7
- v5.10-rc6
- v5.10-rc5
- v5.10-rc4
- v5.10-rc3
- v5.10-rc2
- v5.10-rc1
- v5.9
- v5.9-rc8
- v5.9-rc7
- v5.9-rc6
- v5.9-rc5
- v5.9-rc4
- v5.9-rc3
- v5.9-rc2
- v5.9-rc1
- v5.8
- v5.8-rc7
- v5.8-rc6
- v5.8-rc5
- v5.8-rc4
- v5.8-rc3
- v5.8-rc2
- v5.8-rc1
- v5.7
- v5.7-rc7
- v5.7-rc6
- v5.7-rc5
- v5.7-rc4
- v5.7-rc3
- v5.7-rc2
- v5.7-rc1
- v5.6
- v5.6-rc7
- v5.6-rc6
- v5.6-rc5
- v5.6-rc4
- v5.6-rc3
- v5.6-rc2
- v5.6-rc1
- v5.5
- v5.5-rc7
- v5.5-rc6
- v5.5-rc5
- v5.5-rc4
- v5.5-rc3
- v5.5-rc2
- v5.5-rc1
- v5.4
- v5.4-rc8
- v5.4-rc7
- v5.4-rc6
- v5.4-rc5
- v5.4-rc4
- v5.4-rc3
- v5.4-rc2
- v5.4-rc1
- v5.3
- v5.3-rc8
- v5.3-rc7
- v5.3-rc6
- v5.3-rc5
- v5.3-rc4
- v5.3-rc3
- v5.3-rc2
- v5.3-rc1
- v5.2
- v5.2-rc7
- v5.2-rc6
- v5.2-rc5
- v5.2-rc4
- v5.2-rc3
- v5.2-rc2
- v5.2-rc1
- v5.1
- v5.1-rc7
- v5.1-rc6
- v5.1-rc5
- v5.1-rc4
- v5.1-rc3
- v5.1-rc2
- v5.1-rc1
- v5.0
- v5.0-rc8
- v5.0-rc7
- v5.0-rc6
- v5.0-rc5
- v5.0-rc4
- v5.0-rc3
- v5.0-rc2
- v5.0-rc1
- v4.20
- v4.20-rc7
- v4.20-rc6
- v4.20-rc5
- v4.20-rc4
- v4.20-rc3
- v4.20-rc2
- v4.20-rc1
- v4.19
- v4.19-rc8
- v4.19-rc7
- v4.19-rc6
- v4.19-rc5
- v4.19-rc4
- v4.19-rc3
- v4.19-rc2
- v4.19-rc1
- v4.18
- v4.18-rc8
- v4.18-rc7
- v4.18-rc6
- v4.18-rc5
- v4.18-rc4
- v4.18-rc3
- v4.18-rc2
- v4.18-rc1
- v4.17
- v4.17-rc7
- v4.17-rc6
- v4.17-rc5
- v4.17-rc4
- v4.17-rc3
- v4.17-rc2
- v4.17-rc1
- v4.16
- v4.16-rc7
- v4.16-rc6
- v4.16-rc5
- v4.16-rc4
- v4.16-rc3
- v4.16-rc2
- v4.16-rc1
- v4.15
- v4.15-rc9
- v4.15-rc8
- v4.15-rc7
- v4.15-rc6
- v4.15-rc5
- v4.15-rc4
- v4.15-rc3
- v4.15-rc2
- v4.15-rc1
- v4.14
- v4.14-rc8
- v4.14-rc7
- v4.14-rc6
- v4.14-rc5
- v4.14-rc4
- v4.14-rc3
- v4.14-rc2
- v4.14-rc1
Showing
15 changed files
with
1,424 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
1. Introduction | ||
|
||
rmnet driver is used for supporting the Multiplexing and aggregation | ||
Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm | ||
Technologies, Inc. modems. | ||
|
||
This driver can be used to register onto any physical network device in | ||
IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator. | ||
|
||
Multiplexing allows for creation of logical netdevices (rmnet devices) to | ||
handle multiple private data networks (PDN) like a default internet, tethering, | ||
multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends | ||
packets with MAP headers to rmnet. Based on the multiplexer id, rmnet | ||
routes to the appropriate PDN after removing the MAP header. | ||
|
||
Aggregation is required to achieve high data rates. This involves hardware | ||
sending aggregated bunch of MAP frames. rmnet driver will de-aggregate | ||
these MAP frames and send them to appropriate PDN's. | ||
|
||
2. Packet format | ||
|
||
a. MAP packet (data / control) | ||
|
||
MAP header has the same endianness of the IP packet. | ||
|
||
Packet format - | ||
|
||
Bit 0 1 2-7 8 - 15 16 - 31 | ||
Function Command / Data Reserved Pad Multiplexer ID Payload length | ||
Bit 32 - x | ||
Function Raw Bytes | ||
|
||
Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command | ||
or data packet. Control packet is used for transport level flow control. Data | ||
packets are standard IP packets. | ||
|
||
Reserved bits are usually zeroed out and to be ignored by receiver. | ||
|
||
Padding is number of bytes to be added for 4 byte alignment if required by | ||
hardware. | ||
|
||
Multiplexer ID is to indicate the PDN on which data has to be sent. | ||
|
||
Payload length includes the padding length but does not include MAP header | ||
length. | ||
|
||
b. MAP packet (command specific) | ||
|
||
Bit 0 1 2-7 8 - 15 16 - 31 | ||
Function Command Reserved Pad Multiplexer ID Payload length | ||
Bit 32 - 39 40 - 45 46 - 47 48 - 63 | ||
Function Command name Reserved Command Type Reserved | ||
Bit 64 - 95 | ||
Function Transaction ID | ||
Bit 96 - 127 | ||
Function Command data | ||
|
||
Command 1 indicates disabling flow while 2 is enabling flow | ||
|
||
Command types - | ||
0 for MAP command request | ||
1 is to acknowledge the receipt of a command | ||
2 is for unsupported commands | ||
3 is for error during processing of commands | ||
|
||
c. Aggregation | ||
|
||
Aggregation is multiple MAP packets (can be data or command) delivered to | ||
rmnet in a single linear skb. rmnet will process the individual | ||
packets and either ACK the MAP command or deliver the IP packet to the | ||
network stack as needed | ||
|
||
MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding.... | ||
MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad... | ||
|
||
3. Userspace configuration | ||
|
||
rmnet userspace configuration is done through netlink library librmnetctl | ||
and command line utility rmnetcli. Utility is hosted in codeaurora forum git. | ||
The driver uses rtnl_link_ops for communication. | ||
|
||
https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,5 @@ obj-$(CONFIG_QCA7000_UART) += qcauart.o | |
qcauart-objs := qca_uart.o | ||
|
||
obj-y += emac/ | ||
|
||
obj-$(CONFIG_RMNET) += rmnet/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# | ||
# RMNET MAP driver | ||
# | ||
|
||
menuconfig RMNET | ||
tristate "RmNet MAP driver" | ||
default n | ||
---help--- | ||
If you select this, you will enable the RMNET module which is used | ||
for handling data in the multiplexing and aggregation protocol (MAP) | ||
format in the embedded data path. RMNET devices can be attached to | ||
any IP mode physical device. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# | ||
# Makefile for the RMNET module | ||
# | ||
|
||
rmnet-y := rmnet_config.o | ||
rmnet-y += rmnet_vnd.o | ||
rmnet-y += rmnet_handlers.o | ||
rmnet-y += rmnet_map_data.o | ||
rmnet-y += rmnet_map_command.o | ||
obj-$(CONFIG_RMNET) += rmnet.o |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
* | ||
* RMNET Data configuration engine | ||
* | ||
*/ | ||
|
||
#include <linux/skbuff.h> | ||
|
||
#ifndef _RMNET_CONFIG_H_ | ||
#define _RMNET_CONFIG_H_ | ||
|
||
#define RMNET_MAX_LOGICAL_EP 255 | ||
#define RMNET_MAX_VND 32 | ||
|
||
/* Information about the next device to deliver the packet to. | ||
* Exact usage of this parameter depends on the rmnet_mode. | ||
*/ | ||
struct rmnet_endpoint { | ||
u8 rmnet_mode; | ||
u8 mux_id; | ||
struct net_device *egress_dev; | ||
}; | ||
|
||
/* One instance of this structure is instantiated for each real_dev associated | ||
* with rmnet. | ||
*/ | ||
struct rmnet_real_dev_info { | ||
struct net_device *dev; | ||
struct rmnet_endpoint local_ep; | ||
struct rmnet_endpoint muxed_ep[RMNET_MAX_LOGICAL_EP]; | ||
u32 ingress_data_format; | ||
u32 egress_data_format; | ||
struct net_device *rmnet_devices[RMNET_MAX_VND]; | ||
u8 nr_rmnet_devs; | ||
}; | ||
|
||
extern struct rtnl_link_ops rmnet_link_ops; | ||
|
||
struct rmnet_priv { | ||
struct rmnet_endpoint local_ep; | ||
u8 mux_id; | ||
}; | ||
|
||
struct rmnet_real_dev_info* | ||
rmnet_get_real_dev_info(struct net_device *real_dev); | ||
|
||
#endif /* _RMNET_CONFIG_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
* | ||
* RMNET Data ingress/egress handler | ||
* | ||
*/ | ||
|
||
#include <linux/netdevice.h> | ||
#include <linux/netdev_features.h> | ||
#include "rmnet_private.h" | ||
#include "rmnet_config.h" | ||
#include "rmnet_vnd.h" | ||
#include "rmnet_map.h" | ||
#include "rmnet_handlers.h" | ||
|
||
#define RMNET_IP_VERSION_4 0x40 | ||
#define RMNET_IP_VERSION_6 0x60 | ||
|
||
/* Helper Functions */ | ||
|
||
static void rmnet_set_skb_proto(struct sk_buff *skb) | ||
{ | ||
switch (skb->data[0] & 0xF0) { | ||
case RMNET_IP_VERSION_4: | ||
skb->protocol = htons(ETH_P_IP); | ||
break; | ||
case RMNET_IP_VERSION_6: | ||
skb->protocol = htons(ETH_P_IPV6); | ||
break; | ||
default: | ||
skb->protocol = htons(ETH_P_MAP); | ||
break; | ||
} | ||
} | ||
|
||
/* Generic handler */ | ||
|
||
static rx_handler_result_t | ||
rmnet_bridge_handler(struct sk_buff *skb, struct rmnet_endpoint *ep) | ||
{ | ||
if (!ep->egress_dev) | ||
kfree_skb(skb); | ||
else | ||
rmnet_egress_handler(skb, ep); | ||
|
||
return RX_HANDLER_CONSUMED; | ||
} | ||
|
||
static rx_handler_result_t | ||
rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_endpoint *ep) | ||
{ | ||
switch (ep->rmnet_mode) { | ||
case RMNET_EPMODE_NONE: | ||
return RX_HANDLER_PASS; | ||
|
||
case RMNET_EPMODE_BRIDGE: | ||
return rmnet_bridge_handler(skb, ep); | ||
|
||
case RMNET_EPMODE_VND: | ||
skb_reset_transport_header(skb); | ||
skb_reset_network_header(skb); | ||
rmnet_vnd_rx_fixup(skb, skb->dev); | ||
|
||
skb->pkt_type = PACKET_HOST; | ||
skb_set_mac_header(skb, 0); | ||
netif_receive_skb(skb); | ||
return RX_HANDLER_CONSUMED; | ||
|
||
default: | ||
kfree_skb(skb); | ||
return RX_HANDLER_CONSUMED; | ||
} | ||
} | ||
|
||
static rx_handler_result_t | ||
rmnet_ingress_deliver_packet(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *r) | ||
{ | ||
if (!r) { | ||
kfree_skb(skb); | ||
return RX_HANDLER_CONSUMED; | ||
} | ||
|
||
skb->dev = r->local_ep.egress_dev; | ||
|
||
return rmnet_deliver_skb(skb, &r->local_ep); | ||
} | ||
|
||
/* MAP handler */ | ||
|
||
static rx_handler_result_t | ||
__rmnet_map_ingress_handler(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *r) | ||
{ | ||
struct rmnet_endpoint *ep; | ||
u8 mux_id; | ||
u16 len; | ||
|
||
if (RMNET_MAP_GET_CD_BIT(skb)) { | ||
if (r->ingress_data_format | ||
& RMNET_INGRESS_FORMAT_MAP_COMMANDS) | ||
return rmnet_map_command(skb, r); | ||
|
||
kfree_skb(skb); | ||
return RX_HANDLER_CONSUMED; | ||
} | ||
|
||
mux_id = RMNET_MAP_GET_MUX_ID(skb); | ||
len = RMNET_MAP_GET_LENGTH(skb) - RMNET_MAP_GET_PAD(skb); | ||
|
||
if (mux_id >= RMNET_MAX_LOGICAL_EP) { | ||
kfree_skb(skb); | ||
return RX_HANDLER_CONSUMED; | ||
} | ||
|
||
ep = &r->muxed_ep[mux_id]; | ||
|
||
if (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING) | ||
skb->dev = ep->egress_dev; | ||
|
||
/* Subtract MAP header */ | ||
skb_pull(skb, sizeof(struct rmnet_map_header)); | ||
skb_trim(skb, len); | ||
rmnet_set_skb_proto(skb); | ||
return rmnet_deliver_skb(skb, ep); | ||
} | ||
|
||
static rx_handler_result_t | ||
rmnet_map_ingress_handler(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *r) | ||
{ | ||
struct sk_buff *skbn; | ||
int rc; | ||
|
||
if (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { | ||
while ((skbn = rmnet_map_deaggregate(skb, r)) != NULL) | ||
__rmnet_map_ingress_handler(skbn, r); | ||
|
||
consume_skb(skb); | ||
rc = RX_HANDLER_CONSUMED; | ||
} else { | ||
rc = __rmnet_map_ingress_handler(skb, r); | ||
} | ||
|
||
return rc; | ||
} | ||
|
||
static int rmnet_map_egress_handler(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *r, | ||
struct rmnet_endpoint *ep, | ||
struct net_device *orig_dev) | ||
{ | ||
int required_headroom, additional_header_len; | ||
struct rmnet_map_header *map_header; | ||
|
||
additional_header_len = 0; | ||
required_headroom = sizeof(struct rmnet_map_header); | ||
|
||
if (skb_headroom(skb) < required_headroom) { | ||
if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) | ||
return RMNET_MAP_CONSUMED; | ||
} | ||
|
||
map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); | ||
if (!map_header) | ||
return RMNET_MAP_CONSUMED; | ||
|
||
if (r->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) { | ||
if (ep->mux_id == 0xff) | ||
map_header->mux_id = 0; | ||
else | ||
map_header->mux_id = ep->mux_id; | ||
} | ||
|
||
skb->protocol = htons(ETH_P_MAP); | ||
|
||
return RMNET_MAP_SUCCESS; | ||
} | ||
|
||
/* Ingress / Egress Entry Points */ | ||
|
||
/* Processes packet as per ingress data format for receiving device. Logical | ||
* endpoint is determined from packet inspection. Packet is then sent to the | ||
* egress device listed in the logical endpoint configuration. | ||
*/ | ||
rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) | ||
{ | ||
struct rmnet_real_dev_info *r; | ||
struct sk_buff *skb = *pskb; | ||
struct net_device *dev; | ||
int rc; | ||
|
||
if (!skb) | ||
return RX_HANDLER_CONSUMED; | ||
|
||
dev = skb->dev; | ||
r = rmnet_get_real_dev_info(dev); | ||
|
||
if (r->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) { | ||
rc = rmnet_map_ingress_handler(skb, r); | ||
} else { | ||
switch (ntohs(skb->protocol)) { | ||
case ETH_P_MAP: | ||
if (r->local_ep.rmnet_mode == | ||
RMNET_EPMODE_BRIDGE) { | ||
rc = rmnet_ingress_deliver_packet(skb, r); | ||
} else { | ||
kfree_skb(skb); | ||
rc = RX_HANDLER_CONSUMED; | ||
} | ||
break; | ||
|
||
case ETH_P_IP: | ||
case ETH_P_IPV6: | ||
rc = rmnet_ingress_deliver_packet(skb, r); | ||
break; | ||
|
||
default: | ||
rc = RX_HANDLER_PASS; | ||
} | ||
} | ||
|
||
return rc; | ||
} | ||
|
||
/* Modifies packet as per logical endpoint configuration and egress data format | ||
* for egress device configured in logical endpoint. Packet is then transmitted | ||
* on the egress device. | ||
*/ | ||
void rmnet_egress_handler(struct sk_buff *skb, | ||
struct rmnet_endpoint *ep) | ||
{ | ||
struct rmnet_real_dev_info *r; | ||
struct net_device *orig_dev; | ||
|
||
orig_dev = skb->dev; | ||
skb->dev = ep->egress_dev; | ||
|
||
r = rmnet_get_real_dev_info(skb->dev); | ||
if (!r) { | ||
kfree_skb(skb); | ||
return; | ||
} | ||
|
||
if (r->egress_data_format & RMNET_EGRESS_FORMAT_MAP) { | ||
switch (rmnet_map_egress_handler(skb, r, ep, orig_dev)) { | ||
case RMNET_MAP_CONSUMED: | ||
return; | ||
|
||
case RMNET_MAP_SUCCESS: | ||
break; | ||
|
||
default: | ||
kfree_skb(skb); | ||
return; | ||
} | ||
} | ||
|
||
if (ep->rmnet_mode == RMNET_EPMODE_VND) | ||
rmnet_vnd_tx_fixup(skb, orig_dev); | ||
|
||
dev_queue_xmit(skb); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
* | ||
* RMNET Data ingress/egress handler | ||
* | ||
*/ | ||
|
||
#ifndef _RMNET_HANDLERS_H_ | ||
#define _RMNET_HANDLERS_H_ | ||
|
||
#include "rmnet_config.h" | ||
|
||
void rmnet_egress_handler(struct sk_buff *skb, | ||
struct rmnet_endpoint *ep); | ||
|
||
rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb); | ||
|
||
#endif /* _RMNET_HANDLERS_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
*/ | ||
|
||
#ifndef _RMNET_MAP_H_ | ||
#define _RMNET_MAP_H_ | ||
|
||
struct rmnet_map_control_command { | ||
u8 command_name; | ||
u8 cmd_type:2; | ||
u8 reserved:6; | ||
u16 reserved2; | ||
u32 transaction_id; | ||
union { | ||
struct { | ||
u16 ip_family:2; | ||
u16 reserved:14; | ||
u16 flow_control_seq_num; | ||
u32 qos_id; | ||
} flow_control; | ||
u8 data[0]; | ||
}; | ||
} __aligned(1); | ||
|
||
enum rmnet_map_results { | ||
RMNET_MAP_SUCCESS, | ||
RMNET_MAP_CONSUMED, | ||
RMNET_MAP_GENERAL_FAILURE, | ||
RMNET_MAP_NOT_ENABLED, | ||
RMNET_MAP_FAILED_AGGREGATION, | ||
RMNET_MAP_FAILED_MUX | ||
}; | ||
|
||
enum rmnet_map_commands { | ||
RMNET_MAP_COMMAND_NONE, | ||
RMNET_MAP_COMMAND_FLOW_DISABLE, | ||
RMNET_MAP_COMMAND_FLOW_ENABLE, | ||
/* These should always be the last 2 elements */ | ||
RMNET_MAP_COMMAND_UNKNOWN, | ||
RMNET_MAP_COMMAND_ENUM_LENGTH | ||
}; | ||
|
||
struct rmnet_map_header { | ||
u8 pad_len:6; | ||
u8 reserved_bit:1; | ||
u8 cd_bit:1; | ||
u8 mux_id; | ||
u16 pkt_len; | ||
} __aligned(1); | ||
|
||
#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \ | ||
(Y)->data)->mux_id) | ||
#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \ | ||
(Y)->data)->cd_bit) | ||
#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \ | ||
(Y)->data)->pad_len) | ||
#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command *) \ | ||
((Y)->data + \ | ||
sizeof(struct rmnet_map_header))) | ||
#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \ | ||
(Y)->data)->pkt_len)) | ||
|
||
#define RMNET_MAP_COMMAND_REQUEST 0 | ||
#define RMNET_MAP_COMMAND_ACK 1 | ||
#define RMNET_MAP_COMMAND_UNSUPPORTED 2 | ||
#define RMNET_MAP_COMMAND_INVALID 3 | ||
|
||
#define RMNET_MAP_NO_PAD_BYTES 0 | ||
#define RMNET_MAP_ADD_PAD_BYTES 1 | ||
|
||
u8 rmnet_map_demultiplex(struct sk_buff *skb); | ||
struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *rdinfo); | ||
|
||
struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, | ||
int hdrlen, int pad); | ||
rx_handler_result_t rmnet_map_command(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *rdinfo); | ||
|
||
#endif /* _RMNET_MAP_H_ */ |
107 changes: 107 additions & 0 deletions
107
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
*/ | ||
|
||
#include <linux/netdevice.h> | ||
#include "rmnet_config.h" | ||
#include "rmnet_map.h" | ||
#include "rmnet_private.h" | ||
#include "rmnet_vnd.h" | ||
|
||
static u8 rmnet_map_do_flow_control(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *rdinfo, | ||
int enable) | ||
{ | ||
struct rmnet_map_control_command *cmd; | ||
struct rmnet_endpoint *ep; | ||
struct net_device *vnd; | ||
u16 ip_family; | ||
u16 fc_seq; | ||
u32 qos_id; | ||
u8 mux_id; | ||
int r; | ||
|
||
mux_id = RMNET_MAP_GET_MUX_ID(skb); | ||
cmd = RMNET_MAP_GET_CMD_START(skb); | ||
|
||
if (mux_id >= RMNET_MAX_LOGICAL_EP) { | ||
kfree_skb(skb); | ||
return RX_HANDLER_CONSUMED; | ||
} | ||
|
||
ep = &rdinfo->muxed_ep[mux_id]; | ||
vnd = ep->egress_dev; | ||
|
||
ip_family = cmd->flow_control.ip_family; | ||
fc_seq = ntohs(cmd->flow_control.flow_control_seq_num); | ||
qos_id = ntohl(cmd->flow_control.qos_id); | ||
|
||
/* Ignore the ip family and pass the sequence number for both v4 and v6 | ||
* sequence. User space does not support creating dedicated flows for | ||
* the 2 protocols | ||
*/ | ||
r = rmnet_vnd_do_flow_control(vnd, enable); | ||
if (r) { | ||
kfree_skb(skb); | ||
return RMNET_MAP_COMMAND_UNSUPPORTED; | ||
} else { | ||
return RMNET_MAP_COMMAND_ACK; | ||
} | ||
} | ||
|
||
static void rmnet_map_send_ack(struct sk_buff *skb, | ||
unsigned char type, | ||
struct rmnet_real_dev_info *rdinfo) | ||
{ | ||
struct rmnet_map_control_command *cmd; | ||
int xmit_status; | ||
|
||
skb->protocol = htons(ETH_P_MAP); | ||
|
||
cmd = RMNET_MAP_GET_CMD_START(skb); | ||
cmd->cmd_type = type & 0x03; | ||
|
||
netif_tx_lock(skb->dev); | ||
xmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); | ||
netif_tx_unlock(skb->dev); | ||
} | ||
|
||
/* Process MAP command frame and send N/ACK message as appropriate. Message cmd | ||
* name is decoded here and appropriate handler is called. | ||
*/ | ||
rx_handler_result_t rmnet_map_command(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *rdinfo) | ||
{ | ||
struct rmnet_map_control_command *cmd; | ||
unsigned char command_name; | ||
unsigned char rc = 0; | ||
|
||
cmd = RMNET_MAP_GET_CMD_START(skb); | ||
command_name = cmd->command_name; | ||
|
||
switch (command_name) { | ||
case RMNET_MAP_COMMAND_FLOW_ENABLE: | ||
rc = rmnet_map_do_flow_control(skb, rdinfo, 1); | ||
break; | ||
|
||
case RMNET_MAP_COMMAND_FLOW_DISABLE: | ||
rc = rmnet_map_do_flow_control(skb, rdinfo, 0); | ||
break; | ||
|
||
default: | ||
rc = RMNET_MAP_COMMAND_UNSUPPORTED; | ||
kfree_skb(skb); | ||
break; | ||
} | ||
if (rc == RMNET_MAP_COMMAND_ACK) | ||
rmnet_map_send_ack(skb, rc, rdinfo); | ||
return RX_HANDLER_CONSUMED; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
* | ||
* RMNET Data MAP protocol | ||
* | ||
*/ | ||
|
||
#include <linux/netdevice.h> | ||
#include "rmnet_config.h" | ||
#include "rmnet_map.h" | ||
#include "rmnet_private.h" | ||
|
||
#define RMNET_MAP_DEAGGR_SPACING 64 | ||
#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) | ||
|
||
/* Adds MAP header to front of skb->data | ||
* Padding is calculated and set appropriately in MAP header. Mux ID is | ||
* initialized to 0. | ||
*/ | ||
struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, | ||
int hdrlen, int pad) | ||
{ | ||
struct rmnet_map_header *map_header; | ||
u32 padding, map_datalen; | ||
u8 *padbytes; | ||
|
||
if (skb_headroom(skb) < sizeof(struct rmnet_map_header)) | ||
return NULL; | ||
|
||
map_datalen = skb->len - hdrlen; | ||
map_header = (struct rmnet_map_header *) | ||
skb_push(skb, sizeof(struct rmnet_map_header)); | ||
memset(map_header, 0, sizeof(struct rmnet_map_header)); | ||
|
||
if (pad == RMNET_MAP_NO_PAD_BYTES) { | ||
map_header->pkt_len = htons(map_datalen); | ||
return map_header; | ||
} | ||
|
||
padding = ALIGN(map_datalen, 4) - map_datalen; | ||
|
||
if (padding == 0) | ||
goto done; | ||
|
||
if (skb_tailroom(skb) < padding) | ||
return NULL; | ||
|
||
padbytes = (u8 *)skb_put(skb, padding); | ||
memset(padbytes, 0, padding); | ||
|
||
done: | ||
map_header->pkt_len = htons(map_datalen + padding); | ||
map_header->pad_len = padding & 0x3F; | ||
|
||
return map_header; | ||
} | ||
|
||
/* Deaggregates a single packet | ||
* A whole new buffer is allocated for each portion of an aggregated frame. | ||
* Caller should keep calling deaggregate() on the source skb until 0 is | ||
* returned, indicating that there are no more packets to deaggregate. Caller | ||
* is responsible for freeing the original skb. | ||
*/ | ||
struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, | ||
struct rmnet_real_dev_info *rdinfo) | ||
{ | ||
struct rmnet_map_header *maph; | ||
struct sk_buff *skbn; | ||
u32 packet_len; | ||
|
||
if (skb->len == 0) | ||
return NULL; | ||
|
||
maph = (struct rmnet_map_header *)skb->data; | ||
packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header); | ||
|
||
if (((int)skb->len - (int)packet_len) < 0) | ||
return NULL; | ||
|
||
skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC); | ||
if (!skbn) | ||
return NULL; | ||
|
||
skbn->dev = skb->dev; | ||
skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM); | ||
skb_put(skbn, packet_len); | ||
memcpy(skbn->data, skb->data, packet_len); | ||
skb_pull(skb, packet_len); | ||
|
||
/* Some hardware can send us empty frames. Catch them */ | ||
if (ntohs(maph->pkt_len) == 0) { | ||
kfree_skb(skb); | ||
return NULL; | ||
} | ||
|
||
return skbn; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
*/ | ||
|
||
#ifndef _RMNET_PRIVATE_H_ | ||
#define _RMNET_PRIVATE_H_ | ||
|
||
#define RMNET_MAX_VND 32 | ||
#define RMNET_MAX_PACKET_SIZE 16384 | ||
#define RMNET_DFLT_PACKET_SIZE 1500 | ||
#define RMNET_NEEDED_HEADROOM 16 | ||
#define RMNET_TX_QUEUE_LEN 1000 | ||
|
||
/* Constants */ | ||
#define RMNET_EGRESS_FORMAT__RESERVED__ BIT(0) | ||
#define RMNET_EGRESS_FORMAT_MAP BIT(1) | ||
#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(2) | ||
#define RMNET_EGRESS_FORMAT_MUXING BIT(3) | ||
#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 BIT(4) | ||
#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 BIT(5) | ||
|
||
#define RMNET_INGRESS_FIX_ETHERNET BIT(0) | ||
#define RMNET_INGRESS_FORMAT_MAP BIT(1) | ||
#define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(2) | ||
#define RMNET_INGRESS_FORMAT_DEMUXING BIT(3) | ||
#define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(4) | ||
#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 BIT(5) | ||
#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 BIT(6) | ||
|
||
/* Pass the frame up the stack with no modifications to skb->dev */ | ||
#define RMNET_EPMODE_NONE (0) | ||
/* Replace skb->dev to a virtual rmnet device and pass up the stack */ | ||
#define RMNET_EPMODE_VND (1) | ||
/* Pass the frame directly to another device with dev_queue_xmit() */ | ||
#define RMNET_EPMODE_BRIDGE (2) | ||
|
||
#endif /* _RMNET_PRIVATE_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
* | ||
* | ||
* RMNET Data virtual network driver | ||
* | ||
*/ | ||
|
||
#include <linux/etherdevice.h> | ||
#include <linux/if_arp.h> | ||
#include <net/pkt_sched.h> | ||
#include "rmnet_config.h" | ||
#include "rmnet_handlers.h" | ||
#include "rmnet_private.h" | ||
#include "rmnet_map.h" | ||
#include "rmnet_vnd.h" | ||
|
||
/* RX/TX Fixup */ | ||
|
||
void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev) | ||
{ | ||
dev->stats.rx_packets++; | ||
dev->stats.rx_bytes += skb->len; | ||
} | ||
|
||
void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev) | ||
{ | ||
dev->stats.tx_packets++; | ||
dev->stats.tx_bytes += skb->len; | ||
} | ||
|
||
/* Network Device Operations */ | ||
|
||
static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, | ||
struct net_device *dev) | ||
{ | ||
struct rmnet_priv *priv; | ||
|
||
priv = netdev_priv(dev); | ||
if (priv->local_ep.egress_dev) { | ||
rmnet_egress_handler(skb, &priv->local_ep); | ||
} else { | ||
dev->stats.tx_dropped++; | ||
kfree_skb(skb); | ||
} | ||
return NETDEV_TX_OK; | ||
} | ||
|
||
static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) | ||
{ | ||
if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE) | ||
return -EINVAL; | ||
|
||
rmnet_dev->mtu = new_mtu; | ||
return 0; | ||
} | ||
|
||
static const struct net_device_ops rmnet_vnd_ops = { | ||
.ndo_start_xmit = rmnet_vnd_start_xmit, | ||
.ndo_change_mtu = rmnet_vnd_change_mtu, | ||
}; | ||
|
||
/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU, | ||
* flags, ARP type, needed headroom, etc... | ||
*/ | ||
void rmnet_vnd_setup(struct net_device *rmnet_dev) | ||
{ | ||
struct rmnet_priv *priv; | ||
|
||
priv = netdev_priv(rmnet_dev); | ||
netdev_dbg(rmnet_dev, "Setting up device %s\n", rmnet_dev->name); | ||
|
||
rmnet_dev->netdev_ops = &rmnet_vnd_ops; | ||
rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; | ||
rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; | ||
random_ether_addr(rmnet_dev->dev_addr); | ||
rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN; | ||
|
||
/* Raw IP mode */ | ||
rmnet_dev->header_ops = NULL; /* No header */ | ||
rmnet_dev->type = ARPHRD_RAWIP; | ||
rmnet_dev->hard_header_len = 0; | ||
rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); | ||
|
||
rmnet_dev->needs_free_netdev = true; | ||
} | ||
|
||
/* Exposed API */ | ||
|
||
int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, | ||
struct rmnet_real_dev_info *r) | ||
{ | ||
int rc; | ||
|
||
if (r->rmnet_devices[id]) | ||
return -EINVAL; | ||
|
||
rc = register_netdevice(rmnet_dev); | ||
if (!rc) { | ||
r->rmnet_devices[id] = rmnet_dev; | ||
r->nr_rmnet_devs++; | ||
rmnet_dev->rtnl_link_ops = &rmnet_link_ops; | ||
} | ||
|
||
return rc; | ||
} | ||
|
||
int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r) | ||
{ | ||
if (id >= RMNET_MAX_VND || !r->rmnet_devices[id]) | ||
return -EINVAL; | ||
|
||
r->rmnet_devices[id] = NULL; | ||
r->nr_rmnet_devs--; | ||
return 0; | ||
} | ||
|
||
u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev) | ||
{ | ||
struct rmnet_priv *priv; | ||
|
||
priv = netdev_priv(rmnet_dev); | ||
return priv->mux_id; | ||
} | ||
|
||
void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id) | ||
{ | ||
struct rmnet_priv *priv; | ||
|
||
priv = netdev_priv(rmnet_dev); | ||
priv->mux_id = mux_id; | ||
} | ||
|
||
/* Gets the logical endpoint configuration for a RmNet virtual network device | ||
* node. Caller should confirm that devices is a RmNet VND before calling. | ||
*/ | ||
struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *rmnet_dev) | ||
{ | ||
struct rmnet_priv *priv; | ||
|
||
if (!rmnet_dev) | ||
return NULL; | ||
|
||
priv = netdev_priv(rmnet_dev); | ||
|
||
return &priv->local_ep; | ||
} | ||
|
||
int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable) | ||
{ | ||
netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable); | ||
/* Although we expect similar number of enable/disable | ||
* commands, optimize for the disable. That is more | ||
* latency sensitive than enable | ||
*/ | ||
if (unlikely(enable)) | ||
netif_wake_queue(rmnet_dev); | ||
else | ||
netif_stop_queue(rmnet_dev); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 and | ||
* only version 2 as published by the Free Software Foundation. | ||
* | ||
* 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. | ||
* | ||
* RMNET Data Virtual Network Device APIs | ||
* | ||
*/ | ||
|
||
#ifndef _RMNET_VND_H_ | ||
#define _RMNET_VND_H_ | ||
|
||
int rmnet_vnd_do_flow_control(struct net_device *dev, int enable); | ||
struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *dev); | ||
int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, | ||
struct rmnet_real_dev_info *r); | ||
int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r); | ||
void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev); | ||
void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev); | ||
u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev); | ||
void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id); | ||
void rmnet_vnd_setup(struct net_device *dev); | ||
#endif /* _RMNET_VND_H_ */ |