mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 21:59:12 -04:00
Merge "usb: Add support for rndis uplink aggregation"
This commit is contained in:
commit
7b8d417d82
6 changed files with 167 additions and 19 deletions
|
|
@ -66,6 +66,8 @@
|
|||
* - MS-Windows drivers sometimes emit undocumented requests.
|
||||
*/
|
||||
|
||||
#define RNDIS_UL_MAX_PKT_PER_XFER 3
|
||||
|
||||
struct f_rndis {
|
||||
struct gether port;
|
||||
u8 ctrl_id, data_id;
|
||||
|
|
@ -670,6 +672,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
unsigned int max;
|
||||
|
||||
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_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 &&
|
||||
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 */
|
||||
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[] = {
|
||||
&rndis_opts_attr_dev_addr,
|
||||
&rndis_opts_attr_host_addr,
|
||||
|
|
@ -883,6 +895,7 @@ static struct configfs_attribute *rndis_attrs[] = {
|
|||
&rndis_opts_attr_class,
|
||||
&rndis_opts_attr_subclass,
|
||||
&rndis_opts_attr_protocol,
|
||||
&rndis_opts_attr_ul_max_pkt_per_xfer,
|
||||
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.wrap = rndis_add_header;
|
||||
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";
|
||||
/* descriptors are per-instance copies */
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@
|
|||
|
||||
#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).
|
||||
*
|
||||
|
|
@ -574,12 +573,12 @@ static int rndis_init_response(struct rndis_params *params,
|
|||
resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
|
||||
resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
|
||||
resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
|
||||
resp->MaxPacketsPerTransfer = cpu_to_le32(1);
|
||||
resp->MaxTransferSize = cpu_to_le32(
|
||||
params->dev->mtu
|
||||
resp->MaxPacketsPerTransfer = cpu_to_le32(params->max_pkt_per_xfer);
|
||||
resp->MaxTransferSize = cpu_to_le32(params->max_pkt_per_xfer *
|
||||
(params->dev->mtu
|
||||
+ sizeof(struct ethhdr)
|
||||
+ sizeof(struct rndis_packet_msg_type)
|
||||
+ 22);
|
||||
+ 22));
|
||||
resp->PacketAlignmentFactor = cpu_to_le32(0);
|
||||
resp->AFListOffset = 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);
|
||||
|
||||
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)
|
||||
{
|
||||
struct rndis_packet_msg_type *header;
|
||||
|
|
@ -1059,23 +1065,63 @@ int rndis_rm_hdr(struct gether *port,
|
|||
struct sk_buff *skb,
|
||||
struct sk_buff_head *list)
|
||||
{
|
||||
/* tmp points to a struct rndis_packet_msg_type */
|
||||
__le32 *tmp = (void *)skb->data;
|
||||
while (skb->len) {
|
||||
struct rndis_packet_msg_type *hdr;
|
||||
struct sk_buff *skb2;
|
||||
u32 msg_len, data_offset, data_len;
|
||||
|
||||
/* MessageType, MessageLength */
|
||||
if (cpu_to_le32(RNDIS_MSG_PACKET)
|
||||
!= get_unaligned(tmp++)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
tmp++;
|
||||
/* some rndis hosts send extra byte to avoid zlp, ignore it */
|
||||
if (skb->len == 1) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* DataOffset, DataLength */
|
||||
if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return -EOVERFLOW;
|
||||
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);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hdr = (void *)skb->data;
|
||||
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);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
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_trim(skb, get_unaligned_le32(tmp++));
|
||||
|
||||
skb_queue_tail(list, skb);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ typedef struct rndis_params {
|
|||
struct net_device *dev;
|
||||
|
||||
u32 vendorID;
|
||||
u8 max_pkt_per_xfer;
|
||||
const char *vendorDescr;
|
||||
void (*resp_avail)(void *v);
|
||||
void *v;
|
||||
|
|
@ -186,6 +187,7 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
|
|||
const char *vendorDescr);
|
||||
int rndis_set_param_medium(struct rndis_params *params, u32 medium,
|
||||
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);
|
||||
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
|
||||
struct sk_buff_head *list);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ struct eth_dev {
|
|||
unsigned qmult;
|
||||
|
||||
unsigned header_len;
|
||||
unsigned int ul_max_pkts_per_xfer;
|
||||
struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb);
|
||||
int (*unwrap)(struct gether *,
|
||||
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;
|
||||
}
|
||||
|
||||
if (dev->ul_max_pkts_per_xfer)
|
||||
size *= dev->ul_max_pkts_per_xfer;
|
||||
|
||||
if (dev->port_usb->is_fixed)
|
||||
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
|
||||
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);
|
||||
if (skb == NULL) {
|
||||
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);
|
||||
|
||||
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
|
||||
* Context: may sleep
|
||||
|
|
@ -1081,6 +1106,8 @@ struct net_device *gether_connect(struct gether *link)
|
|||
dev->header_len = link->header_len;
|
||||
dev->unwrap = link->unwrap;
|
||||
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);
|
||||
dev->port_usb = link;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ struct gether {
|
|||
bool is_fixed;
|
||||
u32 fixed_out_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;
|
||||
struct sk_buff *(*wrap)(struct gether *port,
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/* connect/disconnect is handled by individual functions */
|
||||
|
|
|
|||
|
|
@ -220,4 +220,45 @@ out: \
|
|||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, _n_)
|
||||
|
||||
#define USB_ETHER_CONFIGFS_ITEM_ATTR_UL_MAX_PKT_PER_XFER(_f_) \
|
||||
static ssize_t \
|
||||
_f_##_opts_ul_max_pkt_per_xfer_show(struct config_item *item,\
|
||||
char *page) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
unsigned int max; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
max = gether_get_ul_max_pkts_per_xfer(opts->net); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return scnprintf(page, PAGE_SIZE, "%d\n", max); \
|
||||
} \
|
||||
\
|
||||
static ssize_t \
|
||||
_f_##_opts_ul_max_pkt_per_xfer_store(struct config_item *item,\
|
||||
const char *page, size_t len)\
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
u8 val; \
|
||||
int ret; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto out; \
|
||||
} \
|
||||
\
|
||||
ret = kstrtou8(page, 0, &val); \
|
||||
if (ret) \
|
||||
goto out; \
|
||||
\
|
||||
gether_set_ul_max_pkts_per_xfer(opts->net, val); \
|
||||
ret = len; \
|
||||
out: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
CONFIGFS_ATTR(_f_##_opts_, ul_max_pkt_per_xfer)
|
||||
|
||||
#endif /* __U_ETHER_CONFIGFS_H */
|
||||
|
|
|
|||
Loading…
Reference in a new issue