// SPDX-License-Identifier: GPL-2.0
/*
* RNDIS MSG parser
*
* Authors: Benedikt Spranger, Pengutronix
* Robert Schwebel, Pengutronix
*
* This software was originally developed in conformance with
* Microsoft's Remote NDIS Specification License Agreement.
*
* 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
* Fixed message length bug in init_response
*
* 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
* Fixed rndis_rm_hdr length bug.
*
* Copyright (C) 2004 by David Brownell
* updates to merge with Linux 2.6, better match RNDIS spec
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/idr.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/netdevice.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include "u_rndis.h"
#undef VERBOSE_DEBUG
#include "rndis.h"
/* The driver for your USB chip needs to support ep0 OUT to work with
* RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
*
* Windows hosts need an INF file like Documentation/usb/linux.inf
* and will be happier if you provide the host_addr module parameter.
*/
#if 0
static int rndis_debug = 0;
module_param (rndis_debug, int, 0);
MODULE_PARM_DESC (rndis_debug, "enable debugging");
#else
#define rndis_debug 0
#endif
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
#define NAME_TEMPLATE "driver/rndis-%03d"
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
static DEFINE_IDA(rndis_ida);
/* Driver Version */
static const __le32 rndis_driver_version = cpu_to_le32(1);
/* Function Prototypes */
static rndis_resp_t *rndis_add_response(struct rndis_params *params,
u32 length);
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const struct proc_ops rndis_proc_ops;
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/* supported OIDs */
static const u32 oid_supported_list[] = {
/* the general stuff */
RNDIS_OID_GEN_SUPPORTED_LIST,
RNDIS_OID_GEN_HARDWARE_STATUS,
RNDIS_OID_GEN_MEDIA_SUPPORTED,
RNDIS_OID_GEN_MEDIA_IN_USE,
RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
RNDIS_OID_GEN_LINK_SPEED,
RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE,
RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE,
RNDIS_OID_GEN_VENDOR_ID,
RNDIS_OID_GEN_VENDOR_DESCRIPTION,
RNDIS_OID_GEN_VENDOR_DRIVER_VERSION,
RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE,
RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
RNDIS_OID_GEN_PHYSICAL_MEDIUM,
/* the statistical stuff */
RNDIS_OID_GEN_XMIT_OK,
RNDIS_OID_GEN_RCV_OK,
RNDIS_OID_GEN_XMIT_ERROR,
RNDIS_OID_GEN_RCV_ERROR,
RNDIS_OID_GEN_RCV_NO_BUFFER,
#ifdef RNDIS_OPTIONAL_STATS
RNDIS_OID_GEN_DIRECTED_BYTES_XMIT,
RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT,
RNDIS_OID_GEN_MULTICAST_BYTES_XMIT,
RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT,
RNDIS_OID_GEN_BROADCAST_BYTES_XMIT,
RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT,
RNDIS_OID_GEN_DIRECTED_BYTES_RCV,
RNDIS_OID_GEN_DIRECTED_FRAMES_RCV,
RNDIS_OID_GEN_MULTICAST_BYTES_RCV,
RNDIS_OID_GEN_MULTICAST_FRAMES_RCV,
RNDIS_OID_GEN_BROADCAST_BYTES_RCV,
RNDIS_OID_GEN_BROADCAST_FRAMES_RCV,
RNDIS_OID_GEN_RCV_CRC_ERROR,
RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH,
#endif /* RNDIS_OPTIONAL_STATS */
/* mandatory 802.3 */
/* the general stuff */
RNDIS_OID_802_3_PERMANENT_ADDRESS,
RNDIS_OID_802_3_CURRENT_ADDRESS,
RNDIS_OID_802_3_MULTICAST_LIST,
RNDIS_OID_802_3_MAC_OPTIONS,
RNDIS_OID_802_3_MAXIMUM_LIST_SIZE,
/* the statistical stuff */
RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT,
RNDIS_OID_802_3_XMIT_ONE_COLLISION,
RNDIS_OID_802_3_XMIT_MORE_COLLISIONS,
#ifdef RNDIS_OPTIONAL_STATS
RNDIS_OID_802_3_XMIT_DEFERRED,
RNDIS_OID_802_3_XMIT_MAX_COLLISIONS,
RNDIS_OID_802_3_RCV_OVERRUN,
RNDIS_OID_802_3_XMIT_UNDERRUN,
RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE,
RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST,
RNDIS_OID_802_3_XMIT_LATE_COLLISIONS,
#endif /* RNDIS_OPTIONAL_STATS */
#ifdef RNDIS_PM
/* PM and wakeup are "mandatory" for USB, but the RNDIS specs
* don't say what they mean ... and the NDIS specs are often
* confusing and/or ambiguous in this context. (That is, more
* so than their specs for the other OIDs.)
*
* FIXME someone who knows what these should do, please
* implement them!
*/
/* power management */
OID_PNP_CAPABILITIES,
OID_PNP_QUERY_POWER,
OID_PNP_SET_POWER,
#ifdef RNDIS_WAKEUP
/* wake up host */
OID_PNP_ENABLE_WAKE_UP,
OID_PNP_ADD_WAKE_UP_PATTERN,
OID_PNP_REMOVE_WAKE_UP_PATTERN,
#endif /* RNDIS_WAKEUP */
#endif /* RNDIS_PM */
};
/* NDIS Functions */
static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
unsigned buf_len, rndis_resp_t *r)
{
int retval = -ENOTSUPP;
u32 length = 4; /* usually */
__le32 *outbuf;
int i, count;
rndis_query_cmplt_type *resp;
struct net_device *net;
struct rtnl_link_stats64 temp;
const struct rtnl_link_stats64 *stats;
if (!r) return -ENOMEM;
resp = (rndis_query_cmplt_type *)r->buf;
if (!resp) return -ENOMEM;
if (buf_len && rndis_debug &