Merge "drivers: net: rmnet: Initial implementation"

This commit is contained in:
Linux Build Service Account 2017-03-31 21:24:35 -07:00 committed by Gerrit - the friendly Code Review server
commit c57c1bf5d9
20 changed files with 3373 additions and 0 deletions

View 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

View file

@ -425,4 +425,6 @@ config FUJITSU_ES
source "drivers/net/hyperv/Kconfig"
source "drivers/net/rmnet/Kconfig"
endif # NETDEVICES

View file

@ -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
View 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

View 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)

File diff suppressed because it is too large Load diff

View 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_ */

View 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);
}

View 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_ */

View 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");

View 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_ */

View 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;
}

View 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;
}

View 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_ */

View 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);
}

View 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_ */

View 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];
}

View 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_ */

View file

@ -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
View 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_ */