Merge "drivers: net: rmnet: Initial implementation"
This commit is contained in:
commit
c57c1bf5d9
20 changed files with 3373 additions and 0 deletions
82
Documentation/networking/rmnet.txt
Normal file
82
Documentation/networking/rmnet.txt
Normal file
|
@ -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
|
||||
|
||||
https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/\
|
||||
dataservices/tree/rmnetctl
|
|
@ -425,4 +425,6 @@ config FUJITSU_ES
|
|||
|
||||
source "drivers/net/hyperv/Kconfig"
|
||||
|
||||
source "drivers/net/rmnet/Kconfig"
|
||||
|
||||
endif # NETDEVICES
|
||||
|
|
|
@ -70,3 +70,4 @@ obj-$(CONFIG_HYPERV_NET) += hyperv/
|
|||
obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o
|
||||
|
||||
obj-$(CONFIG_FUJITSU_ES) += fjes/
|
||||
obj-$(CONFIG_RMNET) += rmnet/
|
||||
|
|
21
drivers/net/rmnet/Kconfig
Normal file
21
drivers/net/rmnet/Kconfig
Normal file
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# RMNET MAP driver
|
||||
#
|
||||
|
||||
menuconfig RMNET
|
||||
depends on NETDEVICES
|
||||
bool "RmNet MAP driver"
|
||||
---help---
|
||||
If you say Y here, then the rmnet module will be statically
|
||||
compiled into the kernel. The rmnet module provides MAP
|
||||
functionality for embedded and bridged traffic.
|
||||
if RMNET
|
||||
|
||||
config RMNET_DEBUG
|
||||
bool "RmNet Debug Logging"
|
||||
---help---
|
||||
Say Y here if you want RmNet to be able to log packets in main
|
||||
system log. This should not be enabled on production builds as it can
|
||||
impact system performance. Note that simply enabling it here will not
|
||||
enable the logging; it must be enabled at run-time as well.
|
||||
endif # RMNET
|
14
drivers/net/rmnet/Makefile
Normal file
14
drivers/net/rmnet/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Makefile for the RMNET module
|
||||
#
|
||||
|
||||
rmnet-y := rmnet_main.o
|
||||
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
|
||||
rmnet-y += rmnet_stats.o
|
||||
obj-$(CONFIG_RMNET) += rmnet.o
|
||||
|
||||
CFLAGS_rmnet_main.o := -I$(src)
|
1157
drivers/net/rmnet/rmnet_config.c
Normal file
1157
drivers/net/rmnet/rmnet_config.c
Normal file
File diff suppressed because it is too large
Load diff
107
drivers/net/rmnet/rmnet_config.h
Normal file
107
drivers/net/rmnet/rmnet_config.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
/* 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/types.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#ifndef _RMNET_CONFIG_H_
|
||||
#define _RMNET_CONFIG_H_
|
||||
|
||||
#define RMNET_MAX_LOGICAL_EP 256
|
||||
|
||||
/* struct rmnet_logical_ep_conf_s - Logical end-point configuration
|
||||
*
|
||||
* @refcount: Reference count for this endpoint. 0 signifies the endpoint is not
|
||||
* configured for use
|
||||
* @rmnet_mode: Specifies how the traffic should be finally delivered. Possible
|
||||
* options are available in enum rmnet_config_endpoint_modes_e
|
||||
* @mux_id: Virtual channel ID used by MAP protocol
|
||||
* @egress_dev: Next device to deliver the packet to. Exact usage of this
|
||||
* parmeter depends on the rmnet_mode
|
||||
*/
|
||||
struct rmnet_logical_ep_conf_s {
|
||||
u8 refcount;
|
||||
u8 rmnet_mode;
|
||||
u8 mux_id;
|
||||
struct timespec flush_time;
|
||||
struct net_device *egress_dev;
|
||||
};
|
||||
|
||||
/* struct rmnet_phys_ep_conf_s - Physical endpoint configuration
|
||||
* One instance of this structure is instantiated for each net_device associated
|
||||
* with rmnet.
|
||||
*
|
||||
* @dev: The device which is associated with rmnet. Corresponds to this
|
||||
* specific instance of rmnet_phys_ep_conf_s
|
||||
* @local_ep: Default non-muxed endpoint. Used for non-MAP protocols/formats
|
||||
* @muxed_ep: All multiplexed logical endpoints associated with this device
|
||||
* @ingress_data_format: RMNET_INGRESS_FORMAT_* flags from rmnet.h
|
||||
* @egress_data_format: RMNET_EGRESS_FORMAT_* flags from rmnet.h
|
||||
*
|
||||
* @egress_agg_size: Maximum size (bytes) of data which should be aggregated
|
||||
* @egress_agg_count: Maximum count (packets) of data which should be aggregated
|
||||
* Smaller of the two parameters above are chosen for
|
||||
* aggregation
|
||||
* @tail_spacing: Guaranteed padding (bytes) when de-aggregating ingress frames
|
||||
* @agg_time: Wall clock time when aggregated frame was created
|
||||
* @agg_last: Last time the aggregation routing was invoked
|
||||
*/
|
||||
struct rmnet_phys_ep_conf_s {
|
||||
struct net_device *dev;
|
||||
struct rmnet_logical_ep_conf_s local_ep;
|
||||
struct rmnet_logical_ep_conf_s muxed_ep[RMNET_MAX_LOGICAL_EP];
|
||||
u32 ingress_data_format;
|
||||
u32 egress_data_format;
|
||||
};
|
||||
|
||||
int rmnet_config_init(void);
|
||||
void rmnet_config_exit(void);
|
||||
|
||||
int rmnet_unassociate_network_device(struct net_device *dev);
|
||||
int rmnet_set_ingress_data_format(struct net_device *dev,
|
||||
u32 ingress_data_format);
|
||||
int rmnet_set_egress_data_format(struct net_device *dev,
|
||||
u32 egress_data_format,
|
||||
u16 agg_size,
|
||||
u16 agg_count);
|
||||
int rmnet_associate_network_device(struct net_device *dev);
|
||||
int _rmnet_set_logical_endpoint_config(struct net_device *dev,
|
||||
int config_id,
|
||||
struct rmnet_logical_ep_conf_s *epconfig);
|
||||
int rmnet_set_logical_endpoint_config(struct net_device *dev,
|
||||
int config_id,
|
||||
u8 rmnet_mode,
|
||||
struct net_device *egress_dev);
|
||||
int _rmnet_unset_logical_endpoint_config(struct net_device *dev,
|
||||
int config_id);
|
||||
int rmnet_unset_logical_endpoint_config(struct net_device *dev,
|
||||
int config_id);
|
||||
int _rmnet_get_logical_endpoint_config(struct net_device *dev,
|
||||
int config_id,
|
||||
struct rmnet_logical_ep_conf_s *epconfig);
|
||||
int rmnet_get_logical_endpoint_config(struct net_device *dev,
|
||||
int config_id,
|
||||
u8 *rmnet_mode,
|
||||
u8 *egress_dev_name,
|
||||
size_t egress_dev_name_size);
|
||||
void rmnet_config_netlink_msg_handler (struct sk_buff *skb);
|
||||
int rmnet_config_notify_cb(struct notifier_block *nb,
|
||||
unsigned long event, void *data);
|
||||
int rmnet_create_vnd(int id);
|
||||
int rmnet_create_vnd_prefix(int id, const char *name);
|
||||
int rmnet_free_vnd(int id);
|
||||
|
||||
#endif /* _RMNET_CONFIG_H_ */
|
550
drivers/net/rmnet/rmnet_handlers.c
Normal file
550
drivers/net/rmnet/rmnet_handlers.c
Normal file
|
@ -0,0 +1,550 @@
|
|||
/* 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/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/rmnet.h>
|
||||
#include <linux/netdev_features.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_vnd.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_stats.h"
|
||||
#include "rmnet_handlers.h"
|
||||
|
||||
RMNET_LOG_MODULE(RMNET_LOGMASK_HANDLER);
|
||||
|
||||
#ifdef CONFIG_RMNET_DEBUG
|
||||
unsigned int dump_pkt_rx;
|
||||
module_param(dump_pkt_rx, uint, 0644);
|
||||
MODULE_PARM_DESC(dump_pkt_rx, "Dump packets entering ingress handler");
|
||||
|
||||
unsigned int dump_pkt_tx;
|
||||
module_param(dump_pkt_tx, uint, 0644);
|
||||
MODULE_PARM_DESC(dump_pkt_tx, "Dump packets exiting egress handler");
|
||||
#endif /* CONFIG_RMNET_DEBUG */
|
||||
|
||||
#define RMNET_IP_VERSION_4 0x40
|
||||
#define RMNET_IP_VERSION_6 0x60
|
||||
|
||||
/* Helper Functions */
|
||||
|
||||
/* __rmnet_set_skb_proto() - Set skb->protocol field
|
||||
* @skb: packet being modified
|
||||
*
|
||||
* Peek at the first byte of the packet and set the protocol. There is not
|
||||
* good way to determine if a packet has a MAP header. As of writing this,
|
||||
* the reserved bit in the MAP frame will prevent it from overlapping with
|
||||
* IPv4/IPv6 frames. This could change in the future!
|
||||
*/
|
||||
static inline 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;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RMNET_DEBUG
|
||||
/* rmnet_print_packet() - Print packet / diagnostics
|
||||
* @skb: Packet to print
|
||||
* @printlen: Number of bytes to print
|
||||
* @dev: Name of interface
|
||||
* @dir: Character representing direction (e.g.. 'r' for receive)
|
||||
*
|
||||
* This function prints out raw bytes in an SKB. Use of this will have major
|
||||
* performance impacts and may even trigger watchdog resets if too much is being
|
||||
* printed. Hence, this should always be compiled out unless absolutely needed.
|
||||
*/
|
||||
void rmnet_print_packet(const struct sk_buff *skb, const char *dev, char dir)
|
||||
{
|
||||
char buffer[200];
|
||||
unsigned int len, printlen;
|
||||
int i, buffloc = 0;
|
||||
|
||||
switch (dir) {
|
||||
case 'r':
|
||||
printlen = dump_pkt_rx;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
printlen = dump_pkt_tx;
|
||||
break;
|
||||
|
||||
default:
|
||||
printlen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!printlen)
|
||||
return;
|
||||
|
||||
pr_err("[%s][%c] - PKT skb->len=%d skb->head=%pK skb->data=%pK\n",
|
||||
dev, dir, skb->len, (void *)skb->head, (void *)skb->data);
|
||||
pr_err("[%s][%c] - PKT skb->tail=%pK skb->end=%pK\n",
|
||||
dev, dir, skb_tail_pointer(skb), skb_end_pointer(skb));
|
||||
|
||||
if (skb->len > 0)
|
||||
len = skb->len;
|
||||
else
|
||||
len = ((unsigned int)(uintptr_t)skb->end) -
|
||||
((unsigned int)(uintptr_t)skb->data);
|
||||
|
||||
pr_err("[%s][%c] - PKT len: %d, printing first %d bytes\n",
|
||||
dev, dir, len, printlen);
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
for (i = 0; (i < printlen) && (i < len); i++) {
|
||||
if ((i % 16) == 0) {
|
||||
pr_err("[%s][%c] - PKT%s\n", dev, dir, buffer);
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
buffloc = 0;
|
||||
buffloc += snprintf(&buffer[buffloc],
|
||||
sizeof(buffer) - buffloc, "%04X:",
|
||||
i);
|
||||
}
|
||||
|
||||
buffloc += snprintf(&buffer[buffloc], sizeof(buffer) - buffloc,
|
||||
" %02x", skb->data[i]);
|
||||
}
|
||||
pr_err("[%s][%c] - PKT%s\n", dev, dir, buffer);
|
||||
}
|
||||
#else
|
||||
void rmnet_print_packet(const struct sk_buff *skb, const char *dev, char dir)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_RMNET_DEBUG */
|
||||
|
||||
/* Generic handler */
|
||||
|
||||
/* rmnet_bridge_handler() - Bridge related functionality
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_CONSUMED in all cases
|
||||
*/
|
||||
static rx_handler_result_t rmnet_bridge_handler
|
||||
(struct sk_buff *skb, struct rmnet_logical_ep_conf_s *ep)
|
||||
{
|
||||
if (!ep->egress_dev) {
|
||||
LOGD("Missing egress device for packet arriving on %s",
|
||||
skb->dev->name);
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_BRDG_NO_EGRESS);
|
||||
} else {
|
||||
rmnet_egress_handler(skb, ep);
|
||||
}
|
||||
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
#ifdef NET_SKBUFF_DATA_USES_OFFSET
|
||||
static void rmnet_reset_mac_header(struct sk_buff *skb)
|
||||
{
|
||||
skb->mac_header = 0;
|
||||
skb->mac_len = 0;
|
||||
}
|
||||
#else
|
||||
static void rmnet_reset_mac_header(struct sk_buff *skb)
|
||||
{
|
||||
skb->mac_header = skb->network_header;
|
||||
skb->mac_len = 0;
|
||||
}
|
||||
#endif /*NET_SKBUFF_DATA_USES_OFFSET*/
|
||||
|
||||
/* __rmnet_deliver_skb() - Deliver skb
|
||||
*
|
||||
* Determines where to deliver skb. Options are: consume by network stack,
|
||||
* pass to bridge handler, or pass to virtual network device
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_CONSUMED if packet forwarded or dropped
|
||||
* - RX_HANDLER_PASS if packet is to be consumed by network stack as-is
|
||||
*/
|
||||
static rx_handler_result_t __rmnet_deliver_skb
|
||||
(struct sk_buff *skb, struct rmnet_logical_ep_conf_s *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);
|
||||
switch (rmnet_vnd_rx_fixup(skb, skb->dev)) {
|
||||
case RX_HANDLER_CONSUMED:
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
case RX_HANDLER_PASS:
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
rmnet_reset_mac_header(skb);
|
||||
netif_receive_skb(skb);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
return RX_HANDLER_PASS;
|
||||
|
||||
default:
|
||||
LOGD("Unknown ep mode %d", ep->rmnet_mode);
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_DELIVER_NO_EP);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
}
|
||||
|
||||
/* rmnet_ingress_deliver_packet() - Ingress handler for raw IP and bridged
|
||||
* MAP packets.
|
||||
* @skb: Packet needing a destination.
|
||||
* @config: Physical end point configuration that the packet arrived on.
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_CONSUMED if packet forwarded/dropped
|
||||
* - RX_HANDLER_PASS if packet should be passed up the stack by caller
|
||||
*/
|
||||
static rx_handler_result_t rmnet_ingress_deliver_packet
|
||||
(struct sk_buff *skb, struct rmnet_phys_ep_conf_s *config)
|
||||
{
|
||||
if (!config) {
|
||||
LOGD("%s", "NULL physical EP provided");
|
||||
kfree_skb(skb);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
if (!(config->local_ep.refcount)) {
|
||||
LOGD("Packet on %s has no local endpoint configuration",
|
||||
skb->dev->name);
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_IPINGRESS_NO_EP);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
skb->dev = config->local_ep.egress_dev;
|
||||
|
||||
return __rmnet_deliver_skb(skb, &config->local_ep);
|
||||
}
|
||||
|
||||
/* MAP handler */
|
||||
|
||||
/* _rmnet_map_ingress_handler() - Actual MAP ingress handler
|
||||
* @skb: Packet being received
|
||||
* @config: Physical endpoint configuration for the ingress device
|
||||
*
|
||||
* Most MAP ingress functions are processed here. Packets are processed
|
||||
* individually; aggregated packets should use rmnet_map_ingress_handler()
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_CONSUMED if packet is dropped
|
||||
* - result of __rmnet_deliver_skb() for all other cases
|
||||
*/
|
||||
static rx_handler_result_t _rmnet_map_ingress_handler
|
||||
(struct sk_buff *skb, struct rmnet_phys_ep_conf_s *config)
|
||||
{
|
||||
struct rmnet_logical_ep_conf_s *ep;
|
||||
u8 mux_id;
|
||||
u16 len;
|
||||
|
||||
if (RMNET_MAP_GET_CD_BIT(skb)) {
|
||||
if (config->ingress_data_format
|
||||
& RMNET_INGRESS_FORMAT_MAP_COMMANDS)
|
||||
return rmnet_map_command(skb, config);
|
||||
|
||||
LOGM("MAP command packet on %s; %s", skb->dev->name,
|
||||
"Not configured for MAP commands");
|
||||
rmnet_kfree_skb(skb,
|
||||
RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPC);
|
||||
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) {
|
||||
LOGD("Got packet on %s with bad mux id %d",
|
||||
skb->dev->name, mux_id);
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_BAD_MUX);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
ep = &config->muxed_ep[mux_id];
|
||||
|
||||
if (!ep->refcount) {
|
||||
LOGD("Packet on %s:%d; has no logical endpoint config",
|
||||
skb->dev->name, mux_id);
|
||||
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING)
|
||||
skb->dev = ep->egress_dev;
|
||||
|
||||
/* Subtract MAP header */
|
||||
skb_pull(skb, sizeof(struct rmnet_map_header_s));
|
||||
skb_trim(skb, len);
|
||||
__rmnet_set_skb_proto(skb);
|
||||
return __rmnet_deliver_skb(skb, ep);
|
||||
}
|
||||
|
||||
/* rmnet_map_ingress_handler() - MAP ingress handler
|
||||
* @skb: Packet being received
|
||||
* @config: Physical endpoint configuration for the ingress device
|
||||
*
|
||||
* Called if and only if MAP is configured in the ingress device's ingress data
|
||||
* format. Deaggregation is done here, actual MAP processing is done in
|
||||
* _rmnet_map_ingress_handler().
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_CONSUMED for aggregated packets
|
||||
* - RX_HANDLER_CONSUMED for dropped packets
|
||||
* - result of _rmnet_map_ingress_handler() for all other cases
|
||||
*/
|
||||
static rx_handler_result_t rmnet_map_ingress_handler
|
||||
(struct sk_buff *skb, struct rmnet_phys_ep_conf_s *config)
|
||||
{
|
||||
struct sk_buff *skbn;
|
||||
int rc, co = 0;
|
||||
|
||||
if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) {
|
||||
while ((skbn = rmnet_map_deaggregate(skb, config)) != NULL) {
|
||||
_rmnet_map_ingress_handler(skbn, config);
|
||||
co++;
|
||||
}
|
||||
LOGD("De-aggregated %d packets", co);
|
||||
rmnet_stats_deagg_pkts(co);
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF);
|
||||
rc = RX_HANDLER_CONSUMED;
|
||||
} else {
|
||||
rc = _rmnet_map_ingress_handler(skb, config);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* rmnet_map_egress_handler() - MAP egress handler
|
||||
* @skb: Packet being sent
|
||||
* @config: Physical endpoint configuration for the egress device
|
||||
* @ep: logical endpoint configuration of the packet originator
|
||||
* (e.g.. RmNet virtual network device)
|
||||
* @orig_dev: The originator vnd device
|
||||
*
|
||||
* Called if and only if MAP is configured in the egress device's egress data
|
||||
* format. Will expand skb if there is insufficient headroom for MAP protocol.
|
||||
* Note: headroomexpansion will incur a performance penalty.
|
||||
*
|
||||
* Return:
|
||||
* - 0 on success
|
||||
* - 1 on failure
|
||||
*/
|
||||
static int rmnet_map_egress_handler(struct sk_buff *skb,
|
||||
struct rmnet_phys_ep_conf_s *config,
|
||||
struct rmnet_logical_ep_conf_s *ep,
|
||||
struct net_device *orig_dev)
|
||||
{
|
||||
int required_headroom, additional_header_length;
|
||||
struct rmnet_map_header_s *map_header;
|
||||
|
||||
additional_header_length = 0;
|
||||
required_headroom = sizeof(struct rmnet_map_header_s);
|
||||
|
||||
LOGD("headroom of %d bytes", required_headroom);
|
||||
|
||||
if (skb_headroom(skb) < required_headroom) {
|
||||
if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) {
|
||||
LOGD("Failed to add headroom of %d bytes",
|
||||
required_headroom);
|
||||
return RMNET_MAP_CONSUMED;
|
||||
}
|
||||
}
|
||||
|
||||
map_header = rmnet_map_add_map_header
|
||||
(skb, additional_header_length, RMNET_MAP_NO_PAD_BYTES);
|
||||
|
||||
if (!map_header) {
|
||||
LOGD("%s", "Failed to add MAP header to egress packet");
|
||||
return RMNET_MAP_CONSUMED;
|
||||
}
|
||||
|
||||
if (config->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 */
|
||||
|
||||
/* rmnet_ingress_handler() - Ingress handler entry point
|
||||
* @skb: Packet being received
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_PASS if packet is not processed by handler (caller must
|
||||
* deal with the packet)
|
||||
* - RX_HANDLER_CONSUMED if packet is forwarded or processed by MAP
|
||||
*/
|
||||
rx_handler_result_t rmnet_ingress_handler(struct sk_buff *skb)
|
||||
{
|
||||
struct rmnet_phys_ep_conf_s *config;
|
||||
struct net_device *dev;
|
||||
int rc;
|
||||
|
||||
if (!skb)
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
dev = skb->dev;
|
||||
rmnet_print_packet(skb, dev->name, 'r');
|
||||
|
||||
config = (struct rmnet_phys_ep_conf_s *)
|
||||
rcu_dereference(skb->dev->rx_handler_data);
|
||||
|
||||
if (!config) {
|
||||
LOGD("%s is not associated with rmnet", skb->dev->name);
|
||||
kfree_skb(skb);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
/* Sometimes devices operate in ethernet mode even thouth there is no
|
||||
* ethernet header. This causes the skb->protocol to contain a bogus
|
||||
* value and the skb->data pointer to be off by 14 bytes. Fix it if
|
||||
* configured to do so
|
||||
*/
|
||||
if (config->ingress_data_format & RMNET_INGRESS_FIX_ETHERNET) {
|
||||
skb_push(skb, RMNET_ETHERNET_HEADER_LENGTH);
|
||||
__rmnet_set_skb_proto(skb);
|
||||
}
|
||||
|
||||
if (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) {
|
||||
rc = rmnet_map_ingress_handler(skb, config);
|
||||
} else {
|
||||
switch (ntohs(skb->protocol)) {
|
||||
case ETH_P_MAP:
|
||||
if (config->local_ep.rmnet_mode ==
|
||||
RMNET_EPMODE_BRIDGE) {
|
||||
rc = rmnet_ingress_deliver_packet(skb, config);
|
||||
} else {
|
||||
LOGD("MAP packet on %s; MAP not set",
|
||||
dev->name);
|
||||
rmnet_kfree_skb
|
||||
(skb,
|
||||
RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPD);
|
||||
rc = RX_HANDLER_CONSUMED;
|
||||
}
|
||||
break;
|
||||
|
||||
case ETH_P_ARP:
|
||||
case ETH_P_IP:
|
||||
case ETH_P_IPV6:
|
||||
rc = rmnet_ingress_deliver_packet(skb, config);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGD("Unknown skb->proto 0x%04X",
|
||||
ntohs(skb->protocol) & 0xFFFF);
|
||||
rc = RX_HANDLER_PASS;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* rmnet_rx_handler() - Rx handler callback registered with kernel
|
||||
* @pskb: Packet to be processed by rx handler
|
||||
*
|
||||
* Standard kernel-expected footprint for rx handlers. Calls
|
||||
* rmnet_ingress_handler with correctly formatted arguments
|
||||
*
|
||||
* Return:
|
||||
* - Whatever rmnet_ingress_handler() returns
|
||||
*/
|
||||
rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
|
||||
{
|
||||
return rmnet_ingress_handler(*pskb);
|
||||
}
|
||||
|
||||
/* rmnet_egress_handler() - Egress handler entry point
|
||||
* @skb: packet to transmit
|
||||
* @ep: logical endpoint configuration of the packet originator
|
||||
* (e.g.. RmNet virtual network device)
|
||||
*
|
||||
* 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_logical_ep_conf_s *ep)
|
||||
{
|
||||
struct rmnet_phys_ep_conf_s *config;
|
||||
struct net_device *orig_dev;
|
||||
int rc;
|
||||
|
||||
orig_dev = skb->dev;
|
||||
skb->dev = ep->egress_dev;
|
||||
|
||||
config = (struct rmnet_phys_ep_conf_s *)
|
||||
rcu_dereference(skb->dev->rx_handler_data);
|
||||
|
||||
if (!config) {
|
||||
LOGD("%s is not associated with rmnet", skb->dev->name);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD("Packet going out on %s with egress format 0x%08X",
|
||||
skb->dev->name, config->egress_data_format);
|
||||
|
||||
if (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP) {
|
||||
switch (rmnet_map_egress_handler(skb, config, ep, orig_dev)) {
|
||||
case RMNET_MAP_CONSUMED:
|
||||
LOGD("%s", "MAP process consumed packet");
|
||||
return;
|
||||
|
||||
case RMNET_MAP_SUCCESS:
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGD("MAP egress failed on packet on %s",
|
||||
skb->dev->name);
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_EGR_MAPFAIL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ep->rmnet_mode == RMNET_EPMODE_VND)
|
||||
rmnet_vnd_tx_fixup(skb, orig_dev);
|
||||
|
||||
rmnet_print_packet(skb, skb->dev->name, 't');
|
||||
rc = dev_queue_xmit(skb);
|
||||
if (rc != 0) {
|
||||
LOGD("Failed to queue packet for transmission on [%s]",
|
||||
skb->dev->name);
|
||||
}
|
||||
rmnet_stats_queue_xmit(rc, RMNET_STATS_QUEUE_XMIT_EGRESS);
|
||||
}
|
24
drivers/net/rmnet/rmnet_handlers.h
Normal file
24
drivers/net/rmnet/rmnet_handlers.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/* 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_
|
||||
|
||||
void rmnet_egress_handler(struct sk_buff *skb,
|
||||
struct rmnet_logical_ep_conf_s *ep);
|
||||
|
||||
rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);
|
||||
|
||||
#endif /* _RMNET_HANDLERS_H_ */
|
60
drivers/net/rmnet/rmnet_main.c
Normal file
60
drivers/net/rmnet/rmnet_main.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* 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 generic framework
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_vnd.h"
|
||||
|
||||
/* Trace Points */
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "rmnet_trace.h"
|
||||
|
||||
/* Module Parameters */
|
||||
unsigned int rmnet_log_level = RMNET_LOG_LVL_ERR | RMNET_LOG_LVL_HI;
|
||||
module_param(rmnet_log_level, uint, 0644);
|
||||
MODULE_PARM_DESC(log_level, "Logging level");
|
||||
|
||||
unsigned int rmnet_log_module_mask;
|
||||
module_param(rmnet_log_module_mask, uint, 0644);
|
||||
MODULE_PARM_DESC(rmnet_log_module_mask, "Logging module mask");
|
||||
|
||||
/* Startup/Shutdown */
|
||||
|
||||
/* rmnet_init() - Module initialization
|
||||
*
|
||||
* todo: check for (and init) startup errors
|
||||
*/
|
||||
static int __init rmnet_init(void)
|
||||
{
|
||||
rmnet_config_init();
|
||||
rmnet_vnd_init();
|
||||
|
||||
LOGL("%s", "RMNET Data driver loaded successfully");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rmnet_exit(void)
|
||||
{
|
||||
rmnet_config_exit();
|
||||
rmnet_vnd_exit();
|
||||
}
|
||||
|
||||
module_init(rmnet_init)
|
||||
module_exit(rmnet_exit)
|
||||
MODULE_LICENSE("GPL v2");
|
100
drivers/net/rmnet/rmnet_map.h
Normal file
100
drivers/net/rmnet/rmnet_map.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/* 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/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifndef _RMNET_MAP_H_
|
||||
#define _RMNET_MAP_H_
|
||||
|
||||
struct rmnet_map_control_command_s {
|
||||
u8 command_name;
|
||||
u8 cmd_type:2;
|
||||
u8 reserved:6;
|
||||
u16 reserved2;
|
||||
u32 transaction_id;
|
||||
union {
|
||||
u8 data[65528];
|
||||
struct {
|
||||
u16 ip_family:2;
|
||||
u16 reserved:14;
|
||||
u16 flow_control_seq_num;
|
||||
u32 qos_id;
|
||||
} flow_control;
|
||||
};
|
||||
} __aligned(1);
|
||||
|
||||
enum rmnet_map_results_e {
|
||||
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_mux_errors_e {
|
||||
RMNET_MAP_MUX_SUCCESS,
|
||||
RMNET_MAP_MUX_INVALID_MUX_ID,
|
||||
RMNET_MAP_MUX_INVALID_PAD_LENGTH,
|
||||
RMNET_MAP_MUX_INVALID_PKT_LENGTH,
|
||||
/* This should always be the last element */
|
||||
RMNET_MAP_MUX_ENUM_LENGTH
|
||||
};
|
||||
|
||||
enum rmnet_map_commands_e {
|
||||
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_s {
|
||||
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_s *) \
|
||||
(Y)->data)->mux_id)
|
||||
#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header_s *) \
|
||||
(Y)->data)->cd_bit)
|
||||
#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header_s *) \
|
||||
(Y)->data)->pad_len)
|
||||
#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command_s *) \
|
||||
((Y)->data + \
|
||||
sizeof(struct rmnet_map_header_s)))
|
||||
#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header_s *) \
|
||||
(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_phys_ep_conf_s *config);
|
||||
|
||||
struct rmnet_map_header_s *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_phys_ep_conf_s *config);
|
||||
|
||||
#endif /* _RMNET_MAP_H_ */
|
180
drivers/net/rmnet/rmnet_map_command.c
Normal file
180
drivers/net/rmnet/rmnet_map_command.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
/* 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rmnet.h>
|
||||
#include <net/pkt_sched.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_vnd.h"
|
||||
#include "rmnet_stats.h"
|
||||
|
||||
RMNET_LOG_MODULE(RMNET_LOGMASK_MAPC);
|
||||
|
||||
unsigned long int rmnet_map_command_stats[RMNET_MAP_COMMAND_ENUM_LENGTH];
|
||||
module_param_array(rmnet_map_command_stats, ulong, 0, 0444);
|
||||
MODULE_PARM_DESC(rmnet_map_command_stats, "MAP command statistics");
|
||||
|
||||
/* rmnet_map_do_flow_control() - Process MAP flow control command
|
||||
* @skb: Socket buffer containing the MAP flow control message
|
||||
* @config: Physical end-point configuration of ingress device
|
||||
* @enable: boolean for enable/disable
|
||||
*
|
||||
* Process in-band MAP flow control messages. Assumes mux ID is mapped to a
|
||||
* RmNet Data vitrual network device.
|
||||
*
|
||||
* Return:
|
||||
* - RMNET_MAP_COMMAND_UNSUPPORTED on any error
|
||||
* - RMNET_MAP_COMMAND_ACK on success
|
||||
*/
|
||||
static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
|
||||
struct rmnet_phys_ep_conf_s *config,
|
||||
int enable)
|
||||
{
|
||||
struct rmnet_map_control_command_s *cmd;
|
||||
struct net_device *vnd;
|
||||
struct rmnet_logical_ep_conf_s *ep;
|
||||
u8 mux_id;
|
||||
u16 ip_family;
|
||||
u16 fc_seq;
|
||||
u32 qos_id;
|
||||
int r;
|
||||
|
||||
if (unlikely(!skb || !config))
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
mux_id = RMNET_MAP_GET_MUX_ID(skb);
|
||||
cmd = RMNET_MAP_GET_CMD_START(skb);
|
||||
|
||||
if (mux_id >= RMNET_MAX_LOGICAL_EP) {
|
||||
LOGD("Got packet on %s with bad mux id %d",
|
||||
skb->dev->name, mux_id);
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_BAD_MUX);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
ep = &config->muxed_ep[mux_id];
|
||||
|
||||
if (!ep->refcount) {
|
||||
LOGD("Packet on %s:%d; has no logical endpoint config",
|
||||
skb->dev->name, mux_id);
|
||||
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_MUX_NO_EP);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
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);
|
||||
LOGD("dev:%s, qos_id:0x%08X, ip_family:%hd, fc_seq %hd, en:%d",
|
||||
skb->dev->name, qos_id, ip_family & 3, fc_seq, enable);
|
||||
|
||||
if (r) {
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED);
|
||||
return RMNET_MAP_COMMAND_UNSUPPORTED;
|
||||
} else {
|
||||
return RMNET_MAP_COMMAND_ACK;
|
||||
}
|
||||
}
|
||||
|
||||
/* rmnet_map_send_ack() - Send N/ACK message for MAP commands
|
||||
* @skb: Socket buffer containing the MAP command message
|
||||
* @type: N/ACK message selector
|
||||
* @config: Physical end-point configuration of ingress device
|
||||
*
|
||||
* skb is modified to contain the message type selector. The message is then
|
||||
* transmitted on skb->dev. Note that this function grabs global Tx lock on
|
||||
* skb->dev for latency reasons.
|
||||
*
|
||||
* Return:
|
||||
* - void
|
||||
*/
|
||||
static void rmnet_map_send_ack(struct sk_buff *skb,
|
||||
unsigned char type,
|
||||
struct rmnet_phys_ep_conf_s *config)
|
||||
{
|
||||
struct rmnet_map_control_command_s *cmd;
|
||||
int xmit_status;
|
||||
|
||||
if (unlikely(!skb))
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
LOGD("MAP command ACK=%hhu sent with rc: %d", type & 0x03, xmit_status);
|
||||
}
|
||||
|
||||
/* rmnet_map_command() - Entry point for handling MAP commands
|
||||
* @skb: Socket buffer containing the MAP command message
|
||||
* @config: Physical end-point configuration of ingress device
|
||||
*
|
||||
* Process MAP command frame and send N/ACK message as appropriate. Message cmd
|
||||
* name is decoded here and appropriate handler is called.
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_CONSUMED. Command frames are always consumed.
|
||||
*/
|
||||
rx_handler_result_t rmnet_map_command(struct sk_buff *skb,
|
||||
struct rmnet_phys_ep_conf_s *config)
|
||||
{
|
||||
struct rmnet_map_control_command_s *cmd;
|
||||
unsigned char command_name;
|
||||
unsigned char rc = 0;
|
||||
|
||||
if (unlikely(!skb))
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
cmd = RMNET_MAP_GET_CMD_START(skb);
|
||||
command_name = cmd->command_name;
|
||||
|
||||
if (command_name < RMNET_MAP_COMMAND_ENUM_LENGTH)
|
||||
rmnet_map_command_stats[command_name]++;
|
||||
|
||||
switch (command_name) {
|
||||
case RMNET_MAP_COMMAND_FLOW_ENABLE:
|
||||
rc = rmnet_map_do_flow_control(skb, config, 1);
|
||||
break;
|
||||
|
||||
case RMNET_MAP_COMMAND_FLOW_DISABLE:
|
||||
rc = rmnet_map_do_flow_control(skb, config, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
rmnet_map_command_stats[RMNET_MAP_COMMAND_UNKNOWN]++;
|
||||
LOGM("Uknown MAP command: %d", command_name);
|
||||
rc = RMNET_MAP_COMMAND_UNSUPPORTED;
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED);
|
||||
break;
|
||||
}
|
||||
if (rc == RMNET_MAP_COMMAND_ACK)
|
||||
rmnet_map_send_ack(skb, rc, config);
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
147
drivers/net/rmnet/rmnet_map_data.c
Normal file
147
drivers/net/rmnet/rmnet_map_data.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
/* 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rmnet.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/in.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/ip6_checksum.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_stats.h"
|
||||
|
||||
RMNET_LOG_MODULE(RMNET_LOGMASK_MAPD);
|
||||
|
||||
#define RMNET_MAP_DEAGGR_SPACING 64
|
||||
#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)
|
||||
|
||||
/* rmnet_map_add_map_header() - Adds MAP header to front of skb->data
|
||||
* @skb: Socket buffer ("packet") to modify
|
||||
* @hdrlen: Number of bytes of header data which should not be included in
|
||||
* MAP length field
|
||||
* @pad: Specify if padding the MAP packet to make it 4 byte aligned is
|
||||
* necessary
|
||||
*
|
||||
* Padding is calculated and set appropriately in MAP header. Mux ID is
|
||||
* initialized to 0.
|
||||
*
|
||||
* Return:
|
||||
* - Pointer to MAP structure
|
||||
* - 0 (null) if insufficient headroom
|
||||
* - 0 (null) if insufficient tailroom for padding bytes
|
||||
*
|
||||
* todo: Parameterize skb alignment
|
||||
*/
|
||||
struct rmnet_map_header_s *rmnet_map_add_map_header(struct sk_buff *skb,
|
||||
int hdrlen, int pad)
|
||||
{
|
||||
u32 padding, map_datalen;
|
||||
u8 *padbytes;
|
||||
struct rmnet_map_header_s *map_header;
|
||||
|
||||
if (skb_headroom(skb) < sizeof(struct rmnet_map_header_s))
|
||||
return 0;
|
||||
|
||||
map_datalen = skb->len - hdrlen;
|
||||
map_header = (struct rmnet_map_header_s *)
|
||||
skb_push(skb, sizeof(struct rmnet_map_header_s));
|
||||
memset(map_header, 0, sizeof(struct rmnet_map_header_s));
|
||||
|
||||
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 0;
|
||||
|
||||
padbytes = (u8 *)skb_put(skb, padding);
|
||||
LOGD("pad: %d", padding);
|
||||
memset(padbytes, 0, padding);
|
||||
|
||||
done:
|
||||
map_header->pkt_len = htons(map_datalen + padding);
|
||||
map_header->pad_len = padding & 0x3F;
|
||||
|
||||
return map_header;
|
||||
}
|
||||
|
||||
/* rmnet_map_deaggregate() - Deaggregates a single packet
|
||||
* @skb: Source socket buffer containing multiple MAP frames
|
||||
* @config: Physical endpoint configuration of the ingress device
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Return:
|
||||
* - Pointer to new skb
|
||||
* - 0 (null) if no more aggregated packets
|
||||
*/
|
||||
struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
|
||||
struct rmnet_phys_ep_conf_s *config)
|
||||
{
|
||||
struct sk_buff *skbn;
|
||||
struct rmnet_map_header_s *maph;
|
||||
u32 packet_len;
|
||||
|
||||
if (skb->len == 0)
|
||||
return 0;
|
||||
|
||||
maph = (struct rmnet_map_header_s *)skb->data;
|
||||
packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header_s);
|
||||
|
||||
if ((((int)skb->len) - ((int)packet_len)) < 0) {
|
||||
LOGM("%s", "Got malformed packet. Dropping");
|
||||
return 0;
|
||||
}
|
||||
|
||||
skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);
|
||||
if (!skbn)
|
||||
return 0;
|
||||
|
||||
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) {
|
||||
LOGD("Dropping empty MAP frame");
|
||||
rmnet_kfree_skb(skbn, RMNET_STATS_SKBFREE_DEAGG_DATA_LEN_0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return skbn;
|
||||
}
|
76
drivers/net/rmnet/rmnet_private.h
Normal file
76
drivers/net/rmnet/rmnet_private.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* 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_DEV_NAME_STR "rmnet"
|
||||
#define RMNET_NEEDED_HEADROOM 16
|
||||
#define RMNET_TX_QUEUE_LEN 1000
|
||||
#define RMNET_ETHERNET_HEADER_LENGTH 14
|
||||
|
||||
extern unsigned int rmnet_log_level;
|
||||
extern unsigned int rmnet_log_module_mask;
|
||||
|
||||
#define RMNET_INIT_OK 0
|
||||
#define RMNET_INIT_ERROR 1
|
||||
|
||||
#define RMNET_LOG_LVL_DBG BIT(4)
|
||||
#define RMNET_LOG_LVL_LOW BIT(3)
|
||||
#define RMNET_LOG_LVL_MED BIT(2)
|
||||
#define RMNET_LOG_LVL_HI BIT(1)
|
||||
#define RMNET_LOG_LVL_ERR BIT(0)
|
||||
|
||||
#define RMNET_LOG_MODULE(X) \
|
||||
static u32 rmnet_mod_mask = X
|
||||
|
||||
#define RMNET_LOGMASK_CONFIG BIT(0)
|
||||
#define RMNET_LOGMASK_HANDLER BIT(1)
|
||||
#define RMNET_LOGMASK_VND BIT(2)
|
||||
#define RMNET_LOGMASK_MAPD BIT(3)
|
||||
#define RMNET_LOGMASK_MAPC BIT(4)
|
||||
|
||||
#define LOGE(fmt, ...) do { if (rmnet_log_level & RMNET_LOG_LVL_ERR) \
|
||||
pr_err("[RMNET:ERR] %s(): " fmt "\n", __func__, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define LOGH(fmt, ...) do { if (rmnet_log_level & RMNET_LOG_LVL_HI) \
|
||||
pr_err("[RMNET:HI] %s(): " fmt "\n", __func__, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define LOGM(fmt, ...) do { if (rmnet_log_level & RMNET_LOG_LVL_MED) \
|
||||
pr_warn("[RMNET:MED] %s(): " fmt "\n", __func__, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define LOGL(fmt, ...) do { if (unlikely \
|
||||
(rmnet_log_level & RMNET_LOG_LVL_LOW)) \
|
||||
pr_notice("[RMNET:LOW] %s(): " fmt "\n", __func__, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/* Don't use pr_debug as it is compiled out of the kernel. We can be sure of
|
||||
* minimal impact as LOGD is not enabled by default.
|
||||
*/
|
||||
#define LOGD(fmt, ...) do { if (unlikely( \
|
||||
(rmnet_log_level & RMNET_LOG_LVL_DBG) &&\
|
||||
(rmnet_log_module_mask & rmnet_mod_mask))) \
|
||||
pr_notice("[RMNET:DBG] %s(): " fmt "\n", __func__, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif /* _RMNET_PRIVATE_H_ */
|
86
drivers/net/rmnet/rmnet_stats.c
Normal file
86
drivers/net/rmnet/rmnet_stats.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* Copyright (c) 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 statistics
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_stats.h"
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_map.h"
|
||||
|
||||
enum rmnet_deagg_e {
|
||||
RMNET_STATS_AGG_BUFF,
|
||||
RMNET_STATS_AGG_PKT,
|
||||
RMNET_STATS_AGG_MAX
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(rmnet_skb_free_lock);
|
||||
unsigned long int skb_free[RMNET_STATS_SKBFREE_MAX];
|
||||
module_param_array(skb_free, ulong, 0, 0444);
|
||||
MODULE_PARM_DESC(skb_free, "SKBs dropped or freed");
|
||||
|
||||
static DEFINE_SPINLOCK(rmnet_queue_xmit_lock);
|
||||
unsigned long int queue_xmit[RMNET_STATS_QUEUE_XMIT_MAX * 2];
|
||||
module_param_array(queue_xmit, ulong, 0, 0444);
|
||||
MODULE_PARM_DESC(queue_xmit, "SKBs queued for transmit");
|
||||
|
||||
static DEFINE_SPINLOCK(rmnet_deagg_count);
|
||||
unsigned long int deagg_count[RMNET_STATS_AGG_MAX];
|
||||
module_param_array(deagg_count, ulong, 0, 0444);
|
||||
MODULE_PARM_DESC(deagg_count, "SKBs De-aggregated");
|
||||
|
||||
void rmnet_kfree_skb(struct sk_buff *skb, unsigned int reason)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (reason >= RMNET_STATS_SKBFREE_MAX)
|
||||
reason = RMNET_STATS_SKBFREE_UNKNOWN;
|
||||
|
||||
spin_lock_irqsave(&rmnet_skb_free_lock, flags);
|
||||
skb_free[reason]++;
|
||||
spin_unlock_irqrestore(&rmnet_skb_free_lock, flags);
|
||||
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
void rmnet_stats_queue_xmit(int rc, unsigned int reason)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (rc != 0)
|
||||
reason += RMNET_STATS_QUEUE_XMIT_MAX;
|
||||
if (reason >= RMNET_STATS_QUEUE_XMIT_MAX * 2)
|
||||
reason = RMNET_STATS_SKBFREE_UNKNOWN;
|
||||
|
||||
spin_lock_irqsave(&rmnet_queue_xmit_lock, flags);
|
||||
queue_xmit[reason]++;
|
||||
spin_unlock_irqrestore(&rmnet_queue_xmit_lock, flags);
|
||||
}
|
||||
|
||||
void rmnet_stats_deagg_pkts(int aggcount)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rmnet_deagg_count, flags);
|
||||
deagg_count[RMNET_STATS_AGG_BUFF]++;
|
||||
deagg_count[RMNET_STATS_AGG_PKT] += aggcount;
|
||||
spin_unlock_irqrestore(&rmnet_deagg_count, flags);
|
||||
}
|
61
drivers/net/rmnet/rmnet_stats.h
Normal file
61
drivers/net/rmnet/rmnet_stats.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* Copyright (c) 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 statistics
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _RMNET_STATS_H_
|
||||
#define _RMNET_STATS_H_
|
||||
|
||||
enum rmnet_skb_free_e {
|
||||
RMNET_STATS_SKBFREE_UNKNOWN,
|
||||
RMNET_STATS_SKBFREE_BRDG_NO_EGRESS,
|
||||
RMNET_STATS_SKBFREE_DELIVER_NO_EP,
|
||||
RMNET_STATS_SKBFREE_IPINGRESS_NO_EP,
|
||||
RMNET_STATS_SKBFREE_MAPINGRESS_BAD_MUX,
|
||||
RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP,
|
||||
RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF,
|
||||
RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPD,
|
||||
RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPC,
|
||||
RMNET_STATS_SKBFREE_EGR_MAPFAIL,
|
||||
RMNET_STATS_SKBFREE_VND_NO_EGRESS,
|
||||
RMNET_STATS_SKBFREE_MAPC_BAD_MUX,
|
||||
RMNET_STATS_SKBFREE_MAPC_MUX_NO_EP,
|
||||
RMNET_STATS_SKBFREE_AGG_CPY_EXPAND,
|
||||
RMNET_STATS_SKBFREE_AGG_INTO_BUFF,
|
||||
RMNET_STATS_SKBFREE_DEAGG_MALFORMED,
|
||||
RMNET_STATS_SKBFREE_DEAGG_CLONE_FAIL,
|
||||
RMNET_STATS_SKBFREE_DEAGG_UNKNOWN_IP_TYPE,
|
||||
RMNET_STATS_SKBFREE_DEAGG_DATA_LEN_0,
|
||||
RMNET_STATS_SKBFREE_INGRESS_BAD_MAP_CKSUM,
|
||||
RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED,
|
||||
RMNET_STATS_SKBFREE_MAX
|
||||
};
|
||||
|
||||
enum rmnet_queue_xmit_e {
|
||||
RMNET_STATS_QUEUE_XMIT_UNKNOWN,
|
||||
RMNET_STATS_QUEUE_XMIT_EGRESS,
|
||||
RMNET_STATS_QUEUE_XMIT_AGG_FILL_BUFFER,
|
||||
RMNET_STATS_QUEUE_XMIT_AGG_TIMEOUT,
|
||||
RMNET_STATS_QUEUE_XMIT_AGG_CPY_EXP_FAIL,
|
||||
RMNET_STATS_QUEUE_XMIT_AGG_SKIP,
|
||||
RMNET_STATS_QUEUE_XMIT_MAX
|
||||
};
|
||||
|
||||
void rmnet_kfree_skb(struct sk_buff *skb, unsigned int reason);
|
||||
void rmnet_stats_queue_xmit(int rc, unsigned int reason);
|
||||
void rmnet_stats_deagg_pkts(int aggcount);
|
||||
void rmnet_stats_agg_pkts(int aggcount);
|
||||
void rmnet_stats_dl_checksum(unsigned int rc);
|
||||
void rmnet_stats_ul_checksum(unsigned int rc);
|
||||
#endif /* _RMNET_STATS_H_ */
|
457
drivers/net/rmnet/rmnet_vnd.c
Normal file
457
drivers/net/rmnet/rmnet_vnd.c
Normal file
|
@ -0,0 +1,457 @@
|
|||
/* 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/types.h>
|
||||
#include <linux/rmnet.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <net/pkt_sched.h>
|
||||
#include <linux/atomic.h>
|
||||
#include "rmnet_config.h"
|
||||
#include "rmnet_handlers.h"
|
||||
#include "rmnet_private.h"
|
||||
#include "rmnet_map.h"
|
||||
#include "rmnet_vnd.h"
|
||||
#include "rmnet_stats.h"
|
||||
|
||||
RMNET_LOG_MODULE(RMNET_LOGMASK_VND);
|
||||
|
||||
struct net_device *rmnet_devices[RMNET_MAX_VND];
|
||||
|
||||
struct rmnet_vnd_private_s {
|
||||
struct rmnet_logical_ep_conf_s local_ep;
|
||||
};
|
||||
|
||||
/* RX/TX Fixup */
|
||||
|
||||
/* rmnet_vnd_rx_fixup() - Virtual Network Device receive fixup hook
|
||||
* @skb: Socket buffer ("packet") to modify
|
||||
* @dev: Virtual network device
|
||||
*
|
||||
* Additional VND specific packet processing for ingress packets
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_PASS if packet should continue to process in stack
|
||||
* - RX_HANDLER_CONSUMED if packet should not be processed in stack
|
||||
*
|
||||
*/
|
||||
int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
if (unlikely(!dev || !skb))
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += skb->len;
|
||||
|
||||
return RX_HANDLER_PASS;
|
||||
}
|
||||
|
||||
/* rmnet_vnd_tx_fixup() - Virtual Network Device transmic fixup hook
|
||||
* @skb: Socket buffer ("packet") to modify
|
||||
* @dev: Virtual network device
|
||||
*
|
||||
* Additional VND specific packet processing for egress packets
|
||||
*
|
||||
* Return:
|
||||
* - RX_HANDLER_PASS if packet should continue to be transmitted
|
||||
* - RX_HANDLER_CONSUMED if packet should not be transmitted by stack
|
||||
*/
|
||||
int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct rmnet_vnd_private_s *dev_conf;
|
||||
|
||||
dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
|
||||
|
||||
if (unlikely(!dev || !skb))
|
||||
return RX_HANDLER_CONSUMED;
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
|
||||
return RX_HANDLER_PASS;
|
||||
}
|
||||
|
||||
/* Network Device Operations */
|
||||
|
||||
/* rmnet_vnd_start_xmit() - Transmit NDO callback
|
||||
* @skb: Socket buffer ("packet") being sent from network stack
|
||||
* @dev: Virtual Network Device
|
||||
*
|
||||
* Standard network driver operations hook to transmit packets on virtual
|
||||
* network device. Called by network stack. Packet is not transmitted directly
|
||||
* from here; instead it is given to the rmnet egress handler.
|
||||
*
|
||||
* Return:
|
||||
* - NETDEV_TX_OK under all cirumstances (cannot block/fail)
|
||||
*/
|
||||
static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct rmnet_vnd_private_s *dev_conf;
|
||||
|
||||
dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
|
||||
if (dev_conf->local_ep.egress_dev) {
|
||||
rmnet_egress_handler(skb, &dev_conf->local_ep);
|
||||
} else {
|
||||
dev->stats.tx_dropped++;
|
||||
rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_VND_NO_EGRESS);
|
||||
}
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* rmnet_vnd_change_mtu() - Change MTU NDO callback
|
||||
* @dev: Virtual network device
|
||||
* @new_mtu: New MTU value to set (in bytes)
|
||||
*
|
||||
* Standard network driver operations hook to set the MTU. Called by kernel to
|
||||
* set the device MTU. Checks if desired MTU is less than zero or greater than
|
||||
* RMNET_MAX_PACKET_SIZE;
|
||||
*
|
||||
* Return:
|
||||
* - 0 if successful
|
||||
* - -EINVAL if new_mtu is out of range
|
||||
*/
|
||||
static int rmnet_vnd_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
dev->mtu = new_mtu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops rmnet_vnd_ops = {
|
||||
.ndo_init = 0,
|
||||
.ndo_start_xmit = rmnet_vnd_start_xmit,
|
||||
.ndo_change_mtu = rmnet_vnd_change_mtu,
|
||||
.ndo_set_mac_address = 0,
|
||||
.ndo_validate_addr = 0,
|
||||
};
|
||||
|
||||
/* rmnet_vnd_setup() - net_device initialization callback
|
||||
* @dev: Virtual network device
|
||||
*
|
||||
* Called by kernel whenever a new rmnet<n> device is created. Sets MTU,
|
||||
* flags, ARP type, needed headroom, etc...
|
||||
*/
|
||||
static void rmnet_vnd_setup(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_vnd_private_s *dev_conf;
|
||||
|
||||
LOGM("Setting up device %s", dev->name);
|
||||
|
||||
/* Clear out private data */
|
||||
dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
|
||||
memset(dev_conf, 0, sizeof(struct rmnet_vnd_private_s));
|
||||
|
||||
dev->netdev_ops = &rmnet_vnd_ops;
|
||||
dev->mtu = RMNET_DFLT_PACKET_SIZE;
|
||||
dev->needed_headroom = RMNET_NEEDED_HEADROOM;
|
||||
random_ether_addr(dev->dev_addr);
|
||||
dev->tx_queue_len = RMNET_TX_QUEUE_LEN;
|
||||
|
||||
/* Raw IP mode */
|
||||
dev->header_ops = 0; /* No header */
|
||||
dev->type = ARPHRD_RAWIP;
|
||||
dev->hard_header_len = 0;
|
||||
dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
|
||||
}
|
||||
|
||||
/* Exposed API */
|
||||
|
||||
/* rmnet_vnd_exit() - Shutdown cleanup hook
|
||||
*
|
||||
* Called by RmNet main on module unload. Cleans up data structures and
|
||||
* unregisters/frees net_devices.
|
||||
*/
|
||||
void rmnet_vnd_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RMNET_MAX_VND; i++)
|
||||
if (rmnet_devices[i]) {
|
||||
unregister_netdev(rmnet_devices[i]);
|
||||
free_netdev(rmnet_devices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* rmnet_vnd_init() - Init hook
|
||||
*
|
||||
* Called by RmNet main on module load. Initializes data structures
|
||||
*/
|
||||
int rmnet_vnd_init(void)
|
||||
{
|
||||
memset(rmnet_devices, 0,
|
||||
sizeof(struct net_device *) * RMNET_MAX_VND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rmnet_vnd_create_dev() - Create a new virtual network device node.
|
||||
* @id: Virtual device node id
|
||||
* @new_device: Pointer to newly created device node
|
||||
* @prefix: Device name prefix
|
||||
*
|
||||
* Allocates structures for new virtual network devices. Sets the name of the
|
||||
* new device and registers it with the network stack. Device will appear in
|
||||
* ifconfig list after this is called. If the prefix is null, then
|
||||
* RMNET_DEV_NAME_STR will be assumed.
|
||||
*
|
||||
* Return:
|
||||
* - 0 if successful
|
||||
* - RMNET_CONFIG_BAD_ARGUMENTS if id is out of range or prefix is too long
|
||||
* - RMNET_CONFIG_DEVICE_IN_USE if id already in use
|
||||
* - RMNET_CONFIG_NOMEM if net_device allocation failed
|
||||
* - RMNET_CONFIG_UNKNOWN_ERROR if register_netdevice() fails
|
||||
*/
|
||||
int rmnet_vnd_create_dev(int id, struct net_device **new_device,
|
||||
const char *prefix)
|
||||
{
|
||||
struct net_device *dev;
|
||||
char dev_prefix[IFNAMSIZ];
|
||||
int p, rc = 0;
|
||||
|
||||
if (id < 0 || id >= RMNET_MAX_VND) {
|
||||
*new_device = 0;
|
||||
return RMNET_CONFIG_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
if (rmnet_devices[id]) {
|
||||
*new_device = 0;
|
||||
return RMNET_CONFIG_DEVICE_IN_USE;
|
||||
}
|
||||
|
||||
if (!prefix)
|
||||
p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d",
|
||||
RMNET_DEV_NAME_STR);
|
||||
else
|
||||
p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d", prefix);
|
||||
if (p >= (IFNAMSIZ - 1)) {
|
||||
LOGE("Specified prefix longer than IFNAMSIZ");
|
||||
return RMNET_CONFIG_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
dev = alloc_netdev(sizeof(struct rmnet_vnd_private_s),
|
||||
dev_prefix,
|
||||
NET_NAME_ENUM,
|
||||
rmnet_vnd_setup);
|
||||
if (!dev) {
|
||||
LOGE("Failed to to allocate netdev for id %d", id);
|
||||
*new_device = 0;
|
||||
return RMNET_CONFIG_NOMEM;
|
||||
}
|
||||
|
||||
rc = register_netdevice(dev);
|
||||
if (rc != 0) {
|
||||
LOGE("Failed to to register netdev [%s]", dev->name);
|
||||
free_netdev(dev);
|
||||
*new_device = 0;
|
||||
rc = RMNET_CONFIG_UNKNOWN_ERROR;
|
||||
} else {
|
||||
rmnet_devices[id] = dev;
|
||||
*new_device = dev;
|
||||
LOGM("Registered device %s", dev->name);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* rmnet_vnd_free_dev() - free a virtual network device node.
|
||||
* @id: Virtual device node id
|
||||
*
|
||||
* Unregisters the virtual network device node and frees it.
|
||||
* unregister_netdev locks the rtnl mutex, so the mutex must not be locked
|
||||
* by the caller of the function. unregister_netdev enqueues the request to
|
||||
* unregister the device into a TODO queue. The requests in the TODO queue
|
||||
* are only done after rtnl mutex is unlocked, therefore free_netdev has to
|
||||
* called after unlocking rtnl mutex.
|
||||
*
|
||||
* Return:
|
||||
* - 0 if successful
|
||||
* - RMNET_CONFIG_NO_SUCH_DEVICE if id is invalid or not in range
|
||||
* - RMNET_CONFIG_DEVICE_IN_USE if device has logical ep that wasn't unset
|
||||
*/
|
||||
int rmnet_vnd_free_dev(int id)
|
||||
{
|
||||
struct rmnet_logical_ep_conf_s *epconfig_l;
|
||||
struct net_device *dev;
|
||||
|
||||
rtnl_lock();
|
||||
if ((id < 0) || (id >= RMNET_MAX_VND) || !rmnet_devices[id]) {
|
||||
rtnl_unlock();
|
||||
LOGM("Invalid id [%d]", id);
|
||||
return RMNET_CONFIG_NO_SUCH_DEVICE;
|
||||
}
|
||||
|
||||
epconfig_l = rmnet_vnd_get_le_config(rmnet_devices[id]);
|
||||
if (epconfig_l && epconfig_l->refcount) {
|
||||
rtnl_unlock();
|
||||
return RMNET_CONFIG_DEVICE_IN_USE;
|
||||
}
|
||||
|
||||
dev = rmnet_devices[id];
|
||||
rmnet_devices[id] = 0;
|
||||
rtnl_unlock();
|
||||
|
||||
if (dev) {
|
||||
unregister_netdev(dev);
|
||||
free_netdev(dev);
|
||||
return 0;
|
||||
} else {
|
||||
return RMNET_CONFIG_NO_SUCH_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
/* rmnet_vnd_get_name() - Gets the string name of a VND based on ID
|
||||
* @id: Virtual device node id
|
||||
* @name: Buffer to store name of virtual device node
|
||||
* @name_len: Length of name buffer
|
||||
*
|
||||
* Copies the name of the virtual device node into the users buffer. Will throw
|
||||
* an error if the buffer is null, or too small to hold the device name.
|
||||
*
|
||||
* Return:
|
||||
* - 0 if successful
|
||||
* - -EINVAL if name is null
|
||||
* - -EINVAL if id is invalid or not in range
|
||||
* - -EINVAL if name is too small to hold things
|
||||
*/
|
||||
int rmnet_vnd_get_name(int id, char *name, int name_len)
|
||||
{
|
||||
int p;
|
||||
|
||||
if (!name) {
|
||||
LOGM("%s", "Bad arguments; name buffer null");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((id < 0) || (id >= RMNET_MAX_VND) || !rmnet_devices[id]) {
|
||||
LOGM("Invalid id [%d]", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
p = strlcpy(name, rmnet_devices[id]->name, name_len);
|
||||
if (p >= name_len) {
|
||||
LOGM("Buffer to small (%d) to fit device name", name_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
LOGL("Found mapping [%d]->\"%s\"", id, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rmnet_vnd_is_vnd() - Determine if net_device is RmNet owned virtual devices
|
||||
* @dev: Network device to test
|
||||
*
|
||||
* Searches through list of known RmNet virtual devices. This function is O(n)
|
||||
* and should not be used in the data path.
|
||||
*
|
||||
* Return:
|
||||
* - 0 if device is not RmNet virtual device
|
||||
* - 1 if device is RmNet virtual device
|
||||
*/
|
||||
int rmnet_vnd_is_vnd(struct net_device *dev)
|
||||
{
|
||||
/* This is not an efficient search, but, this will only be called in
|
||||
* a configuration context, and the list is small.
|
||||
*/
|
||||
int i;
|
||||
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < RMNET_MAX_VND; i++)
|
||||
if (dev == rmnet_devices[i])
|
||||
return i + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rmnet_vnd_get_le_config() - Get the logical endpoint configuration
|
||||
* @dev: Virtual device node
|
||||
*
|
||||
* Gets the logical endpoint configuration for a RmNet virtual network device
|
||||
* node. Caller should confirm that devices is a RmNet VND before calling.
|
||||
*
|
||||
* Return:
|
||||
* - Pointer to logical endpoint configuration structure
|
||||
* - 0 (null) if dev is null
|
||||
*/
|
||||
struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev)
|
||||
{
|
||||
struct rmnet_vnd_private_s *dev_conf;
|
||||
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
|
||||
if (!dev_conf)
|
||||
return 0;
|
||||
|
||||
return &dev_conf->local_ep;
|
||||
}
|
||||
|
||||
/* rmnet_vnd_do_flow_control() - Process flow control request
|
||||
* @dev: Virtual network device node to do lookup on
|
||||
* @enable: boolean to enable/disable flow.
|
||||
*
|
||||
* Return:
|
||||
* - 0 if successful
|
||||
* - -EINVAL if dev is not RmNet virtual network device node
|
||||
*/
|
||||
int rmnet_vnd_do_flow_control(struct net_device *dev, int enable)
|
||||
{
|
||||
struct rmnet_vnd_private_s *dev_conf;
|
||||
|
||||
if (unlikely(!dev))
|
||||
return -EINVAL;
|
||||
|
||||
if (!rmnet_vnd_is_vnd(dev))
|
||||
return -EINVAL;
|
||||
|
||||
dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
|
||||
|
||||
if (unlikely(!dev_conf))
|
||||
return -EINVAL;
|
||||
|
||||
LOGD("Setting VND TX queue state to %d", 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(dev);
|
||||
else
|
||||
netif_stop_queue(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rmnet_vnd_get_by_id() - Get VND by array index ID
|
||||
* @id: Virtual network deice id [0:RMNET_MAX_VND]
|
||||
*
|
||||
* Return:
|
||||
* - 0 if no device or ID out of range
|
||||
* - otherwise return pointer to VND net_device struct
|
||||
*/
|
||||
struct net_device *rmnet_vnd_get_by_id(int id)
|
||||
{
|
||||
if (id < 0 || id >= RMNET_MAX_VND) {
|
||||
pr_err("Bug; VND ID out of bounds");
|
||||
return 0;
|
||||
}
|
||||
return rmnet_devices[id];
|
||||
}
|
34
drivers/net/rmnet/rmnet_vnd.h
Normal file
34
drivers/net/rmnet/rmnet_vnd.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifndef _RMNET_VND_H_
|
||||
#define _RMNET_VND_H_
|
||||
|
||||
int rmnet_vnd_do_flow_control(struct net_device *dev, int enable);
|
||||
struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev);
|
||||
int rmnet_vnd_get_name(int id, char *name, int name_len);
|
||||
int rmnet_vnd_create_dev(int id, struct net_device **new_device,
|
||||
const char *prefix);
|
||||
int rmnet_vnd_free_dev(int id);
|
||||
int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
|
||||
int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
|
||||
int rmnet_vnd_is_vnd(struct net_device *dev);
|
||||
int rmnet_vnd_init(void);
|
||||
void rmnet_vnd_exit(void);
|
||||
struct net_device *rmnet_vnd_get_by_id(int id);
|
||||
|
||||
#endif /* _RMNET_VND_H_ */
|
|
@ -402,6 +402,7 @@ header-y += reiserfs_xattr.h
|
|||
header-y += resource.h
|
||||
header-y += rfkill.h
|
||||
header-y += rmnet_data.h
|
||||
header-y += rmnet.h
|
||||
header-y += romfs_fs.h
|
||||
header-y += rose.h
|
||||
header-y += route.h
|
||||
|
|
213
include/uapi/linux/rmnet.h
Normal file
213
include/uapi/linux/rmnet.h
Normal file
|
@ -0,0 +1,213 @@
|
|||
/* 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 configuration specification
|
||||
*/
|
||||
|
||||
#ifndef _RMNET_DATA_H_
|
||||
#define _RMNET_DATA_H_
|
||||
|
||||
/* Netlink API */
|
||||
#define RMNET_NETLINK_PROTO 31
|
||||
#define RMNET_MAX_STR_LEN 16
|
||||
#define RMNET_NL_DATA_MAX_LEN 64
|
||||
|
||||
#define RMNET_NETLINK_MSG_COMMAND 0
|
||||
#define RMNET_NETLINK_MSG_RETURNCODE 1
|
||||
#define RMNET_NETLINK_MSG_RETURNDATA 2
|
||||
|
||||
/* Constants */
|
||||
#define RMNET_EGRESS_FORMAT__RESERVED__ (1<<0)
|
||||
#define RMNET_EGRESS_FORMAT_MAP (1<<1)
|
||||
#define RMNET_EGRESS_FORMAT_AGGREGATION (1<<2)
|
||||
#define RMNET_EGRESS_FORMAT_MUXING (1<<3)
|
||||
#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 (1<<4)
|
||||
#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 (1<<5)
|
||||
|
||||
#define RMNET_INGRESS_FIX_ETHERNET (1<<0)
|
||||
#define RMNET_INGRESS_FORMAT_MAP (1<<1)
|
||||
#define RMNET_INGRESS_FORMAT_DEAGGREGATION (1<<2)
|
||||
#define RMNET_INGRESS_FORMAT_DEMUXING (1<<3)
|
||||
#define RMNET_INGRESS_FORMAT_MAP_COMMANDS (1<<4)
|
||||
#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 (1<<5)
|
||||
#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 (1<<6)
|
||||
|
||||
struct rmnet_nl_msg_s {
|
||||
__be16 reserved;
|
||||
__be16 message_type;
|
||||
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
__u16 crd:2,
|
||||
reserved2:14;
|
||||
#elif defined(__BIG_ENDIAN_BITFIELD)
|
||||
__u16 reserved2:14,
|
||||
crd:2;
|
||||
#endif
|
||||
union {
|
||||
__be16 arg_length;
|
||||
__be16 return_code;
|
||||
};
|
||||
union {
|
||||
__u8 data[RMNET_NL_DATA_MAX_LEN];
|
||||
struct {
|
||||
__u8 dev[RMNET_MAX_STR_LEN];
|
||||
__be32 flags;
|
||||
__be16 agg_size;
|
||||
__be16 agg_count;
|
||||
__u8 tail_spacing;
|
||||
} data_format;
|
||||
struct {
|
||||
__u8 dev[RMNET_MAX_STR_LEN];
|
||||
__be32 ep_id;
|
||||
__u8 operating_mode;
|
||||
__u8 next_dev[RMNET_MAX_STR_LEN];
|
||||
} local_ep_config;
|
||||
struct {
|
||||
__be32 id;
|
||||
__u8 vnd_name[RMNET_MAX_STR_LEN];
|
||||
} vnd;
|
||||
};
|
||||
};
|
||||
|
||||
/* RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE - Register RMNET data driver
|
||||
* on a particular device.
|
||||
* Args: char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE 0
|
||||
|
||||
/* RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE - Unregister RMNET data
|
||||
* driver on a particular
|
||||
* device.
|
||||
* Args: char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE 1
|
||||
|
||||
/* RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED - Get if RMNET data
|
||||
* driver is registered on a
|
||||
* particular device.
|
||||
* Args: char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* Returns: 1 if registered, 0 if not
|
||||
*/
|
||||
#define RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED 2
|
||||
|
||||
/* RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT - Sets the egress data
|
||||
* format for a particular
|
||||
* link.
|
||||
* Args: __be32 egress_flags
|
||||
* char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT 3
|
||||
|
||||
/* RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT - Gets the egress data
|
||||
* format for a particular
|
||||
* link.
|
||||
* Args: char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* Returns: 4-bytes data: __be32 egress_flags
|
||||
*/
|
||||
#define RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT 4
|
||||
|
||||
/* RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT - Sets the ingress data
|
||||
* format for a particular
|
||||
* link.
|
||||
* Args: __be32 ingress_flags
|
||||
* char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT 5
|
||||
|
||||
/* RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT - Gets the ingress data
|
||||
* format for a particular
|
||||
* link.
|
||||
* Args: char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* Returns: 4-bytes data: __be32 ingress_flags
|
||||
*/
|
||||
#define RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT 6
|
||||
|
||||
/* RMNET_NETLINK_SET_LOGICAL_EP_CONFIG - Sets the logical endpoint
|
||||
* configuration for a particular
|
||||
* link.
|
||||
* Args: char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* __be32 logical_ep_id, valid values are -1 through 31
|
||||
* __u8 rmnet_mode: one of none, vnd, bridged
|
||||
* char[] egress_dev_name: Egress device if operating in bridge mode
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_SET_LOGICAL_EP_CONFIG 7
|
||||
|
||||
/* RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG - Un-sets the logical endpoint
|
||||
* configuration for a particular
|
||||
* link.
|
||||
* Args: char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* __be32 logical_ep_id, valid values are -1 through 31
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG 8
|
||||
|
||||
/* RMNET_NETLINK_GET_LOGICAL_EP_CONFIG - Gets the logical endpoint
|
||||
* configuration for a particular
|
||||
* link.
|
||||
* Args: char[] dev_name: Null terminated ASCII string, max length: 15
|
||||
* __be32 logical_ep_id, valid values are -1 through 31
|
||||
* Returns: __u8 rmnet_mode: one of none, vnd, bridged
|
||||
* char[] egress_dev_name: Egress device
|
||||
*/
|
||||
#define RMNET_NETLINK_GET_LOGICAL_EP_CONFIG 9
|
||||
|
||||
/* RMNET_NETLINK_NEW_VND - Creates a new virtual network device node
|
||||
* Args: __be32 node number
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_NEW_VND 10
|
||||
|
||||
/* RMNET_NETLINK_NEW_VND_WITH_PREFIX - Creates a new virtual network
|
||||
* device node with the specified
|
||||
* prefix for the device name
|
||||
* Args: __be32 node number
|
||||
* char[] vnd_name - Use as prefix
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_NEW_VND_WITH_PREFIX 11
|
||||
|
||||
/* RMNET_NETLINK_GET_VND_NAME - Gets the string name of a VND from ID
|
||||
* Args: __be32 node number
|
||||
* Returns: char[] vnd_name
|
||||
*/
|
||||
#define RMNET_NETLINK_GET_VND_NAME 12
|
||||
|
||||
/* RMNET_NETLINK_FREE_VND - Removes virtual network device node
|
||||
* Args: __be32 node number
|
||||
* Returns: status code
|
||||
*/
|
||||
#define RMNET_NETLINK_FREE_VND 13
|
||||
|
||||
/* 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
|
||||
/* Must be the last item in the list */
|
||||
#define RMNET_EPMODE_LENGTH 3
|
||||
|
||||
#define RMNET_CONFIG_OK 0
|
||||
#define RMNET_CONFIG_UNKNOWN_MESSAGE 1
|
||||
#define RMNET_CONFIG_UNKNOWN_ERROR 2
|
||||
#define RMNET_CONFIG_NOMEM 3
|
||||
#define RMNET_CONFIG_DEVICE_IN_USE 4
|
||||
#define RMNET_CONFIG_INVALID_REQUEST 5
|
||||
#define RMNET_CONFIG_NO_SUCH_DEVICE 6
|
||||
#define RMNET_CONFIG_BAD_ARGUMENTS 7
|
||||
#define RMNET_CONFIG_BAD_EGRESS_DEVICE 8
|
||||
#define RMNET_CONFIG_TC_HANDLE_FULL 9
|
||||
|
||||
#endif /* _RMNET_DATA_H_ */
|
Loading…
Add table
Reference in a new issue