Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7b8d417d authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: Add support for rndis uplink aggregation"

parents 3df179a5 8beabf51
Loading
Loading
Loading
Loading
+15 −0
Original line number Original line Diff line number Diff line
@@ -66,6 +66,8 @@
 *   - MS-Windows drivers sometimes emit undocumented requests.
 *   - MS-Windows drivers sometimes emit undocumented requests.
 */
 */


#define RNDIS_UL_MAX_PKT_PER_XFER	3

struct f_rndis {
struct f_rndis {
	struct gether			port;
	struct gether			port;
	u8				ctrl_id, data_id;
	u8				ctrl_id, data_id;
@@ -670,6 +672,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
	struct usb_string	*us;
	struct usb_string	*us;
	int			status;
	int			status;
	struct usb_ep		*ep;
	struct usb_ep		*ep;
	unsigned int		max;


	struct f_rndis_opts *rndis_opts;
	struct f_rndis_opts *rndis_opts;


@@ -795,6 +798,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)


	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
	rndis_set_host_mac(rndis->params, rndis->ethaddr);
	rndis_set_host_mac(rndis->params, rndis->ethaddr);
	max = gether_get_ul_max_pkts_per_xfer(rndis_opts->net);
	if (!max)
		max = RNDIS_UL_MAX_PKT_PER_XFER;

	rndis_set_max_pkt_xfer(rndis->params, max);


	if (rndis->manufacturer && rndis->vendorID &&
	if (rndis->manufacturer && rndis->vendorID &&
			rndis_set_param_vendor(rndis->params, rndis->vendorID,
			rndis_set_param_vendor(rndis->params, rndis->vendorID,
@@ -875,6 +883,10 @@ USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, subclass);
/* f_rndis_opts_protocol */
/* f_rndis_opts_protocol */
USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, protocol);
USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(rndis, protocol);


/* f_rndis_opts_ul_max_pkt_per_xfer */
USB_ETHER_CONFIGFS_ITEM_ATTR_UL_MAX_PKT_PER_XFER(rndis);


static struct configfs_attribute *rndis_attrs[] = {
static struct configfs_attribute *rndis_attrs[] = {
	&rndis_opts_attr_dev_addr,
	&rndis_opts_attr_dev_addr,
	&rndis_opts_attr_host_addr,
	&rndis_opts_attr_host_addr,
@@ -883,6 +895,7 @@ static struct configfs_attribute *rndis_attrs[] = {
	&rndis_opts_attr_class,
	&rndis_opts_attr_class,
	&rndis_opts_attr_subclass,
	&rndis_opts_attr_subclass,
	&rndis_opts_attr_protocol,
	&rndis_opts_attr_protocol,
	&rndis_opts_attr_ul_max_pkt_per_xfer,
	NULL,
	NULL,
};
};


@@ -1004,6 +1017,8 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
	rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
	rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
	rndis->port.wrap = rndis_add_header;
	rndis->port.wrap = rndis_add_header;
	rndis->port.unwrap = rndis_rm_hdr;
	rndis->port.unwrap = rndis_rm_hdr;
	if (!gether_get_ul_max_pkts_per_xfer(opts->net))
		rndis->port.ul_max_pkts_per_xfer = RNDIS_UL_MAX_PKT_PER_XFER;


	rndis->port.func.name = "rndis";
	rndis->port.func.name = "rndis";
	/* descriptors are per-instance copies */
	/* descriptors are per-instance copies */
+65 −19
Original line number Original line Diff line number Diff line
@@ -39,7 +39,6 @@


#include "rndis.h"
#include "rndis.h"



/* The driver for your USB chip needs to support ep0 OUT to work with
/* The driver for your USB chip needs to support ep0 OUT to work with
 * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
 * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
 *
 *
@@ -574,12 +573,12 @@ static int rndis_init_response(struct rndis_params *params,
	resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
	resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
	resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
	resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
	resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
	resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
	resp->MaxPacketsPerTransfer = cpu_to_le32(1);
	resp->MaxPacketsPerTransfer = cpu_to_le32(params->max_pkt_per_xfer);
	resp->MaxTransferSize = cpu_to_le32(
	resp->MaxTransferSize = cpu_to_le32(params->max_pkt_per_xfer *
		  params->dev->mtu
		(params->dev->mtu
		+ sizeof(struct ethhdr)
		+ sizeof(struct ethhdr)
		+ sizeof(struct rndis_packet_msg_type)
		+ sizeof(struct rndis_packet_msg_type)
		+ 22);
		+ 22));
	resp->PacketAlignmentFactor = cpu_to_le32(0);
	resp->PacketAlignmentFactor = cpu_to_le32(0);
	resp->AFListOffset = cpu_to_le32(0);
	resp->AFListOffset = cpu_to_le32(0);
	resp->AFListSize = cpu_to_le32(0);
	resp->AFListSize = cpu_to_le32(0);
@@ -993,6 +992,13 @@ int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed)
}
}
EXPORT_SYMBOL_GPL(rndis_set_param_medium);
EXPORT_SYMBOL_GPL(rndis_set_param_medium);


void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer)
{
	pr_debug("%s:\n", __func__);

	params->max_pkt_per_xfer = max_pkt_per_xfer;
}

void rndis_add_hdr(struct sk_buff *skb)
void rndis_add_hdr(struct sk_buff *skb)
{
{
	struct rndis_packet_msg_type *header;
	struct rndis_packet_msg_type *header;
@@ -1059,23 +1065,63 @@ int rndis_rm_hdr(struct gether *port,
			struct sk_buff *skb,
			struct sk_buff *skb,
			struct sk_buff_head *list)
			struct sk_buff_head *list)
{
{
	/* tmp points to a struct rndis_packet_msg_type */
	while (skb->len) {
	__le32 *tmp = (void *)skb->data;
		struct rndis_packet_msg_type *hdr;
		struct sk_buff          *skb2;
		u32             msg_len, data_offset, data_len;


	/* MessageType, MessageLength */
		/* some rndis hosts send extra byte to avoid zlp, ignore it */
	if (cpu_to_le32(RNDIS_MSG_PACKET)
		if (skb->len == 1) {
			!= get_unaligned(tmp++)) {
			dev_kfree_skb_any(skb);
			return 0;
		}

		if (skb->len < sizeof(*hdr)) {
			pr_err("invalid rndis pkt: skblen:%u hdr_len:%lu\n",
					skb->len, sizeof(*hdr));
			dev_kfree_skb_any(skb);
			dev_kfree_skb_any(skb);
			return -EINVAL;
			return -EINVAL;
		}
		}
	tmp++;


	/* DataOffset, DataLength */
		hdr = (void *)skb->data;
	if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
		msg_len = le32_to_cpu(hdr->MessageLength);
		data_offset = le32_to_cpu(hdr->DataOffset);
		data_len = le32_to_cpu(hdr->DataLength);

		if (skb->len < msg_len ||
				((data_offset + data_len + 8) > msg_len)) {
			pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n",
					le32_to_cpu(hdr->MessageType), msg_len,
					data_offset, data_len, skb->len);
			dev_kfree_skb_any(skb);
			dev_kfree_skb_any(skb);
			return -EOVERFLOW;
			return -EOVERFLOW;
		}
		}
	skb_trim(skb, get_unaligned_le32(tmp++));
		if (le32_to_cpu(hdr->MessageType) != RNDIS_MSG_PACKET) {
			pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n",
					le32_to_cpu(hdr->MessageType), msg_len,
					data_offset, data_len, skb->len);
			dev_kfree_skb_any(skb);
			return -EINVAL;
		}

		skb_pull(skb, data_offset + 8);

		if (msg_len == skb->len) {
			skb_trim(skb, data_len);
			break;
		}

		skb2 = skb_clone(skb, GFP_ATOMIC);
		if (!skb2) {
			pr_err("%s:skb clone failed\n", __func__);
			dev_kfree_skb_any(skb);
			return -ENOMEM;
		}

		skb_pull(skb, msg_len - sizeof(*hdr));
		skb_trim(skb2, data_len);
		skb_queue_tail(list, skb2);
	}


	skb_queue_tail(list, skb);
	skb_queue_tail(list, skb);
	return 0;
	return 0;
+2 −0
Original line number Original line Diff line number Diff line
@@ -170,6 +170,7 @@ typedef struct rndis_params {
	struct net_device	*dev;
	struct net_device	*dev;


	u32			vendorID;
	u32			vendorID;
	u8			max_pkt_per_xfer;
	const char		*vendorDescr;
	const char		*vendorDescr;
	void			(*resp_avail)(void *v);
	void			(*resp_avail)(void *v);
	void			*v;
	void			*v;
@@ -186,6 +187,7 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
			    const char *vendorDescr);
			    const char *vendorDescr);
int  rndis_set_param_medium(struct rndis_params *params, u32 medium,
int  rndis_set_param_medium(struct rndis_params *params, u32 medium,
			     u32 speed);
			     u32 speed);
void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer);
void rndis_add_hdr(struct sk_buff *skb);
void rndis_add_hdr(struct sk_buff *skb);
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
			struct sk_buff_head *list);
			struct sk_buff_head *list);
+27 −0
Original line number Original line Diff line number Diff line
@@ -67,6 +67,7 @@ struct eth_dev {
	unsigned		qmult;
	unsigned		qmult;


	unsigned		header_len;
	unsigned		header_len;
	unsigned int		ul_max_pkts_per_xfer;
	struct sk_buff		*(*wrap)(struct gether *, struct sk_buff *skb);
	struct sk_buff		*(*wrap)(struct gether *, struct sk_buff *skb);
	int			(*unwrap)(struct gether *,
	int			(*unwrap)(struct gether *,
						struct sk_buff *skb,
						struct sk_buff *skb,
@@ -213,10 +214,14 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
		size -= size % out->maxpacket;
		size -= size % out->maxpacket;
	}
	}


	if (dev->ul_max_pkts_per_xfer)
		size *= dev->ul_max_pkts_per_xfer;

	if (dev->port_usb->is_fixed)
	if (dev->port_usb->is_fixed)
		size = max_t(size_t, size, dev->port_usb->fixed_out_len);
		size = max_t(size_t, size, dev->port_usb->fixed_out_len);
	spin_unlock_irqrestore(&dev->lock, flags);
	spin_unlock_irqrestore(&dev->lock, flags);


	DBG(dev, "%s: size: %zd\n", __func__, size);
	skb = __netdev_alloc_skb(dev->net, size + NET_IP_ALIGN, gfp_flags);
	skb = __netdev_alloc_skb(dev->net, size + NET_IP_ALIGN, gfp_flags);
	if (skb == NULL) {
	if (skb == NULL) {
		DBG(dev, "no rx skb\n");
		DBG(dev, "no rx skb\n");
@@ -1012,6 +1017,26 @@ int gether_get_ifname(struct net_device *net, char *name, int len)
}
}
EXPORT_SYMBOL_GPL(gether_get_ifname);
EXPORT_SYMBOL_GPL(gether_get_ifname);


unsigned int gether_get_ul_max_pkts_per_xfer(struct net_device *net)
{
	struct eth_dev *dev;

	dev = netdev_priv(net);
	return dev->ul_max_pkts_per_xfer;
}
EXPORT_SYMBOL(gether_get_ul_max_pkts_per_xfer);

int gether_set_ul_max_pkts_per_xfer(struct net_device *net, unsigned int max)
{
	struct eth_dev *dev;

	dev = netdev_priv(net);
	dev->ul_max_pkts_per_xfer = max;

	return 0;
}
EXPORT_SYMBOL(gether_set_ul_max_pkts_per_xfer);

/**
/**
 * gether_cleanup - remove Ethernet-over-USB device
 * gether_cleanup - remove Ethernet-over-USB device
 * Context: may sleep
 * Context: may sleep
@@ -1081,6 +1106,8 @@ struct net_device *gether_connect(struct gether *link)
		dev->header_len = link->header_len;
		dev->header_len = link->header_len;
		dev->unwrap = link->unwrap;
		dev->unwrap = link->unwrap;
		dev->wrap = link->wrap;
		dev->wrap = link->wrap;
		if (!dev->ul_max_pkts_per_xfer)
			dev->ul_max_pkts_per_xfer = link->ul_max_pkts_per_xfer;


		spin_lock(&dev->lock);
		spin_lock(&dev->lock);
		dev->port_usb = link;
		dev->port_usb = link;
+17 −0
Original line number Original line Diff line number Diff line
@@ -69,6 +69,10 @@ struct gether {
	bool				is_fixed;
	bool				is_fixed;
	u32				fixed_out_len;
	u32				fixed_out_len;
	u32				fixed_in_len;
	u32				fixed_in_len;
	unsigned int			ul_max_pkts_per_xfer;
/* Max number of SKB packets to be used to create Multi Packet RNDIS */
#define TX_SKB_HOLD_THRESHOLD		3
	bool				multi_pkt_xfer;
	bool				supports_multi_frame;
	bool				supports_multi_frame;
	struct sk_buff			*(*wrap)(struct gether *port,
	struct sk_buff			*(*wrap)(struct gether *port,
						struct sk_buff *skb);
						struct sk_buff *skb);
@@ -244,6 +248,19 @@ unsigned gether_get_qmult(struct net_device *net);
 */
 */
int gether_get_ifname(struct net_device *net, char *name, int len);
int gether_get_ifname(struct net_device *net, char *name, int len);


/**
 * gether_get_ul_max_pkts_per_xfer - get max pks/xfer for UL aggrregarion
 * @net: device representing this link
 */
unsigned int gether_get_ul_max_pkts_per_xfer(struct net_device *net);

/**
 * gether_set_ul_max_pkts_per_xfer - set max pks/xfer for UL aggrregarion
 * @net: device representing this link
 * @max: max number of packets
 */
int gether_set_ul_max_pkts_per_xfer(struct net_device *net, unsigned int max);

void gether_cleanup(struct eth_dev *dev);
void gether_cleanup(struct eth_dev *dev);


/* connect/disconnect is handled by individual functions */
/* connect/disconnect is handled by individual functions */
Loading