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

Commit b12fa273 authored by Amit Blay's avatar Amit Blay
Browse files

usb: gadget: rndis: add flexibility to trigger RX via SysFS



Currently RNDIS/uether driver starts when usb cable is connected
and network interface is up. This might lead to DHCP pkt drop (very
initial packets from host/windows PC) if RNDIS bridges are not setup
appropriately in userspace. DHCP packet drop results in delayed IP
assignment.
Provide SysFS parameter to enable and trigger RX from
userspace.

Change-Id: I9dbef11eb97ac87bff47cea1138fe5d7e343377f
Signed-off-by: default avatarAmit Blay <ablay@codeaurora.org>
parent 86155e46
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -1895,12 +1895,28 @@ static DEVICE_ATTR(max_pkt_per_xfer, S_IRUGO | S_IWUSR,
				   rndis_max_pkt_per_xfer_show,
				   rndis_max_pkt_per_xfer_store);

static ssize_t rndis_rx_trigger_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	int value;

	if (sscanf(buf, "%d", &value) == 1) {
		rndis_rx_trigger();
		return size;
	}
	return -EINVAL;
}

static DEVICE_ATTR(rx_trigger, S_IWUSR, NULL,
					     rndis_rx_trigger_store);

static struct device_attribute *rndis_function_attributes[] = {
	&dev_attr_manufacturer,
	&dev_attr_wceis,
	&dev_attr_ethaddr,
	&dev_attr_vendorID,
	&dev_attr_max_pkt_per_xfer,
	&dev_attr_rx_trigger,
	NULL
};

+32 −0
Original line number Diff line number Diff line
@@ -76,6 +76,10 @@ module_param(rndis_ul_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer,
	"Maximum packets per transfer for UL aggregation");

static unsigned int rx_trigger_enabled;
module_param(rx_trigger_enabled, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(rx_trigger_enabled, "rx trigger_enable");

struct f_rndis {
	struct gether			port;
	u8				ctrl_id, data_id;
@@ -89,6 +93,28 @@ struct f_rndis {
	atomic_t			notify_count;
};

static struct f_rndis *__rndis;

int
rndis_rx_trigger(void)
{
	struct f_rndis *rndis = __rndis;

	if (!rx_trigger_enabled || !rndis) {
		pr_err("can't set rx trigger\n");
		return -EINVAL;
	}

	if (rndis->port.rx_triggered)
		return 0;


	rndis->port.rx_triggered = true;
	gether_up(&rndis->port);

	return 0;
}

static inline struct f_rndis *func_to_rndis(struct usb_function *f)
{
	return container_of(f, struct f_rndis, port.func);
@@ -592,6 +618,8 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
	} else if (intf == rndis->data_id) {
		struct net_device	*net;

		rndis->port.rx_triggered = false;

		if (rndis->port.in_ep->driver_data) {
			DBG(cdev, "reset rndis\n");
			gether_disconnect(&rndis->port);
@@ -839,6 +867,7 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f)
	usb_ep_free_request(rndis->notify, rndis->notify_req);

	kfree(rndis);
	__rndis = NULL;
}

/* Some controllers can't support RNDIS ... */
@@ -879,6 +908,8 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
	if (!rndis)
		goto fail;

	__rndis = rndis;

	memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
	rndis->vendorID = vendorID;
	rndis->manufacturer = manufacturer;
@@ -893,6 +924,7 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
	rndis->port.unwrap = rndis_rm_hdr;
	rndis->port.ul_max_pkts_per_xfer = rndis_ul_max_pkt_per_xfer;
	rndis->port.dl_max_pkts_per_xfer = rndis_dl_max_pkt_per_xfer;
	rndis->port.rx_trigger_enabled = rx_trigger_enabled;

	rndis->port.func.name = "rndis";
	rndis->port.func.strings = rndis_strings;
+29 −3
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ struct eth_dev {
	unsigned		header_len;
	unsigned int		ul_max_pkts_per_xfer;
	unsigned int		dl_max_pkts_per_xfer;
	bool			rx_trigger_enabled;
	struct sk_buff		*(*wrap)(struct gether *, struct sk_buff *skb);
	int			(*unwrap)(struct gether *,
						struct sk_buff *skb,
@@ -831,13 +832,21 @@ static int eth_open(struct net_device *net)
{
	struct eth_dev	*dev = netdev_priv(net);
	struct gether	*link;
	bool wait_for_rx_trigger;

	DBG(dev, "%s\n", __func__);
	if (netif_carrier_ok(dev->net))
		eth_start(dev, GFP_KERNEL);

	spin_lock_irq(&dev->lock);
	link = dev->port_usb;
	spin_unlock_irq(&dev->lock);

	wait_for_rx_trigger = dev->rx_trigger_enabled && link &&
		!link->rx_triggered;

	if (netif_carrier_ok(dev->net) && !wait_for_rx_trigger)
		eth_start(dev, GFP_KERNEL);

	spin_lock_irq(&dev->lock);
	if (link && link->open)
		link->open(link);
	spin_unlock_irq(&dev->lock);
@@ -1060,6 +1069,7 @@ struct net_device *gether_connect(struct gether *link)
{
	struct eth_dev		*dev = link->ioport;
	int			result = 0;
	bool wait_for_rx_trigger;

	if (!dev)
		return ERR_PTR(-EINVAL);
@@ -1092,6 +1102,7 @@ struct net_device *gether_connect(struct gether *link)
		dev->wrap = link->wrap;
		dev->ul_max_pkts_per_xfer = link->ul_max_pkts_per_xfer;
		dev->dl_max_pkts_per_xfer = link->dl_max_pkts_per_xfer;
		dev->rx_trigger_enabled = link->rx_trigger_enabled;

		spin_lock(&dev->lock);
		dev->tx_skb_hold_count = 0;
@@ -1108,7 +1119,11 @@ struct net_device *gether_connect(struct gether *link)
		spin_unlock(&dev->lock);

		netif_carrier_on(dev->net);
		if (netif_running(dev->net))

		wait_for_rx_trigger = dev->rx_trigger_enabled &&
			!link->rx_triggered;

		if (netif_running(dev->net) && !wait_for_rx_trigger)
			eth_start(dev, GFP_ATOMIC);

	/* on error, disable any endpoints  */
@@ -1196,12 +1211,23 @@ void gether_disconnect(struct gether *link)
	dev->header_len = 0;
	dev->unwrap = NULL;
	dev->wrap = NULL;
	dev->rx_trigger_enabled = 0;

	spin_lock(&dev->lock);
	dev->port_usb = NULL;
	spin_unlock(&dev->lock);
}

int gether_up(struct gether *link)
{
	struct eth_dev *dev = link->ioport;

	if (dev && netif_carrier_ok(dev->net))
		eth_start(dev, GFP_KERNEL);

	return 0;
}

static int __init gether_init(void)
{
	uether_wq  = create_singlethread_workqueue("uether");
+10 −1
Original line number Diff line number Diff line
@@ -58,6 +58,8 @@ struct gether {
	unsigned			ul_max_pkts_per_xfer;
	unsigned			dl_max_pkts_per_xfer;
	bool				multi_pkt_xfer;
	bool				rx_trigger_enabled;
	bool				rx_triggered;
	struct sk_buff			*(*wrap)(struct gether *port,
						struct sk_buff *skb);
	int				(*unwrap)(struct gether *port,
@@ -102,6 +104,7 @@ void gether_cleanup(struct eth_dev *dev);
/* connect/disconnect is handled by individual functions */
struct net_device *gether_connect(struct gether *);
void gether_disconnect(struct gether *);
int gether_up(struct gether *);

/* Some controllers can't support CDC Ethernet (ECM) ... */
static inline bool can_support_ecm(struct usb_gadget *gadget)
@@ -129,6 +132,7 @@ int eem_bind_config(struct usb_configuration *c, struct eth_dev *dev);

int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
		u32 vendorID, const char *manufacturer, struct eth_dev *dev);
int rndis_rx_trigger(void);

#else

@@ -139,6 +143,12 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
	return 0;
}

static inline int
rndis_rx_trigger(void)
{
	return 0;
}

#endif

/**
@@ -159,5 +169,4 @@ static inline int rndis_bind_config(struct usb_configuration *c,
	return rndis_bind_config_vendor(c, ethaddr, 0, NULL, dev);
}


#endif /* __U_ETHER_H */