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

Commit f22b2480 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: android: Fix crash on composition switch from ncm"

parents 7e0c8271 fb2291af
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -1031,10 +1031,6 @@ ncm_function_init(struct android_usb_function *f, struct usb_composite_dev *c)

	f->config = config;

	config->fi = usb_get_function_instance("ncm");
	if (IS_ERR(config->fi))
		return PTR_ERR(config->fi);

	return 0;
}

@@ -1067,6 +1063,10 @@ ncm_function_bind_config(struct android_usb_function *f,
		ncm->ethaddr[0], ncm->ethaddr[1], ncm->ethaddr[2],
		ncm->ethaddr[3], ncm->ethaddr[4], ncm->ethaddr[5]);

	ncm->fi = usb_get_function_instance("ncm");
	if (IS_ERR(ncm->fi))
		return PTR_ERR(ncm->fi);

	ncm_opts = container_of(ncm->fi, struct f_ncm_opts, func_inst);
	strlcpy(ncm_opts->net->name, "ncm%d", sizeof(ncm_opts->net->name));

@@ -1099,8 +1099,8 @@ static void ncm_function_unbind_config(struct android_usb_function *f,
						struct usb_configuration *c)
{
	struct ncm_function_config *ncm = f->config;
	if (ncm->func)
		usb_remove_function(c, ncm->func);

	usb_put_function_instance(ncm->fi);
}

static ssize_t ncm_ethaddr_show(struct device *dev,
+73 −251
Original line number Diff line number Diff line
@@ -68,18 +68,7 @@ struct f_ncm {
	 * callback and ethernet open/close
	 */
	spinlock_t			lock;

	struct net_device               *netdev;

	/* For multi-frame NDP TX */
	struct sk_buff			*skb_tx_data;
	struct sk_buff			*skb_tx_ndp;
	u16				ndp_dgram_count;
	bool				timer_force_tx;
	struct tasklet_struct		tx_tasklet;
	struct hrtimer			task_timer;

	bool				timer_stopping;
};

static inline struct f_ncm *func_to_ncm(struct usb_function *f)
@@ -104,20 +93,14 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g)
 * If the host can group frames, allow it to do that, 16K is selected,
 * because it's used by default by the current linux host driver
 */
#define NTB_DEFAULT_IN_SIZE	16384
#define NTB_DEFAULT_IN_SIZE	USB_CDC_NCM_NTB_MIN_IN_SIZE
#define NTB_OUT_SIZE		16384

/* Allocation for storing the NDP, 32 should suffice for a
 * 16k packet. This allows a maximum of 32 * 507 Byte packets to
 * be transmitted in a single 16kB skb, though when sending full size
 * packets this limit will be plenty.
 * Smaller packets are not likely to be trying to maximize the
 * throughput and will be mstly sending smaller infrequent frames.
/*
 * skbs of size less than that will not be aligned
 * to NCM's dwNtbInMaxSize to save bus bandwidth
 */
#define TX_MAX_NUM_DPE		32

/* Delay for the transmit to wait before sending an unfilled NTB frame. */
#define TX_TIMEOUT_NSECS	300000
#define MAX_TX_NONFIXED		(512 * 3)

#define FORMATS_SUPPORTED	(USB_CDC_NCM_NTB16_SUPPORTED |	\
				 USB_CDC_NCM_NTB32_SUPPORTED)
@@ -895,7 +878,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)

		if (ncm->port.in_ep->driver_data) {
			DBG(cdev, "reset ncm\n");
			ncm->timer_stopping = true;
			ncm->netdev = NULL;
			gether_disconnect(&ncm->port);
			ncm_reset_values(ncm);
@@ -934,7 +916,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
			if (IS_ERR(net))
				return PTR_ERR(net);
			ncm->netdev = net;
			ncm->timer_stopping = false;
		}

		spin_lock(&ncm->lock);
@@ -961,240 +942,92 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf)
	return ncm->port.in_ep->driver_data ? 1 : 0;
}

static struct sk_buff *package_for_tx(struct f_ncm *ncm)
{
	__le16		*ntb_iter;
	struct sk_buff	*skb2 = NULL;
	unsigned	ndp_pad;
	unsigned	ndp_index;
	unsigned	new_len;

	const struct ndp_parser_opts *opts = ncm->parser_opts;
	const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
	const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;

	/* Stop the timer */
	hrtimer_try_to_cancel(&ncm->task_timer);

	ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
			ncm->skb_tx_data->len;
	ndp_index = ncm->skb_tx_data->len + ndp_pad;
	new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;

	/* Set the final BlockLength and wNdpIndex */
	ntb_iter = (void *) ncm->skb_tx_data->data;
	/* Increment pointer to BlockLength */
	ntb_iter += 2 + 1 + 1;
	put_ncm(&ntb_iter, opts->block_length, new_len);
	put_ncm(&ntb_iter, opts->ndp_index, ndp_index);

	/* Set the final NDP wLength */
	new_len = opts->ndp_size +
			(ncm->ndp_dgram_count * dgram_idx_len);
	ncm->ndp_dgram_count = 0;
	/* Increment from start to wLength */
	ntb_iter = (void *) ncm->skb_tx_ndp->data;
	ntb_iter += 2;
	put_unaligned_le16(new_len, ntb_iter);

	/* Merge the skbs */
	swap(skb2, ncm->skb_tx_data);
	if (ncm->skb_tx_data) {
		dev_kfree_skb_any(ncm->skb_tx_data);
		ncm->skb_tx_data = NULL;
	}

	/* Insert NDP alignment. */
	ntb_iter = (void *) skb_put(skb2, ndp_pad);
	memset(ntb_iter, 0, ndp_pad);

	/* Copy NTB across. */
	ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len);
	memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
	dev_kfree_skb_any(ncm->skb_tx_ndp);
	ncm->skb_tx_ndp = NULL;

	/* Insert zero'd datagram. */
	ntb_iter = (void *) skb_put(skb2, dgram_idx_len);
	memset(ntb_iter, 0, dgram_idx_len);

	return skb2;
}

static struct sk_buff *ncm_wrap_ntb(struct gether *port,
				    struct sk_buff *skb)
{
	struct f_ncm	*ncm = func_to_ncm(&port->func);
	struct sk_buff	*skb2 = NULL;
	struct sk_buff	*skb2;
	int		ncb_len = 0;
	__le16		*ntb_data;
	__le16		*ntb_ndp;
	int		dgram_pad;
	__le16		*tmp;
	int		div;
	int		rem;
	int		pad;
	int		ndp_align;
	int		ndp_pad;

	unsigned	max_size = ncm->port.fixed_in_len;
	const struct ndp_parser_opts *opts = ncm->parser_opts;
	const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
	const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
	const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
	const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
	unsigned	crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;

	if (!skb && !ncm->skb_tx_data)
		return NULL;
	div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
	rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
	ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);

	if (skb) {
		/* Add the CRC if required up front */
		if (ncm->is_crc) {
			uint32_t	crc;
			__le16		*crc_pos;
	ncb_len += opts->nth_size;
	ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len;
	ncb_len += ndp_pad;
	ncb_len += opts->ndp_size;
	ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */
	ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero Datagram entry */
	pad = ALIGN(ncb_len, div) + rem - ncb_len;
	ncb_len += pad;

			crc = ~crc32_le(~0,
					skb->data,
					skb->len);
			crc_pos = (void *) skb_put(skb, sizeof(uint32_t));
			put_unaligned_le32(crc, crc_pos);
		}

		/* If the new skb is too big for the current NCM NTB then
		 * set the current stored skb to be sent now and clear it
		 * ready for new data.
		 * NOTE: Assume maximum align for speed of calculation.
		 */
		if (ncm->skb_tx_data
		    && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
		    || (ncm->skb_tx_data->len +
		    div + rem + skb->len +
		    ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
		    > max_size)) {
			skb2 = package_for_tx(ncm);
			if (!skb2)
				goto err;
	if (ncb_len + skb->len + crc_len > max_size) {
		dev_kfree_skb_any(skb);
		return NULL;
	}

		if (!ncm->skb_tx_data) {
			ncb_len = opts->nth_size;
			dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
			ncb_len += dgram_pad;

			/* Create a new skb for the NTH and datagrams. */
			ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
			if (!ncm->skb_tx_data)
				goto err;

			ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len);
			memset(ntb_data, 0, ncb_len);
			/* dwSignature */
			put_unaligned_le32(opts->nth_sign, ntb_data);
			ntb_data += 2;
			/* wHeaderLength */
			put_unaligned_le16(opts->nth_size, ntb_data++);

			/* Allocate an skb for storing the NDP,
			 * TX_MAX_NUM_DPE should easily suffice for a
			 * 16k packet.
			 */
			ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
						    + opts->dpe_size
						    * TX_MAX_NUM_DPE),
	skb2 = skb_copy_expand(skb, ncb_len,
				max_size - skb->len - ncb_len - crc_len,
				GFP_ATOMIC);
			if (!ncm->skb_tx_ndp)
				goto err;
			ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp,
						    opts->ndp_size);
			memset(ntb_ndp, 0, ncb_len);
			/* dwSignature */
			put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
			ntb_ndp += 2;

			/* There is always a zeroed entry */
			ncm->ndp_dgram_count = 1;

			/* Note: we skip opts->next_ndp_index */
		}

		/* Delay the timer. */
		hrtimer_start(&ncm->task_timer,
			      ktime_set(0, TX_TIMEOUT_NSECS),
			      HRTIMER_MODE_REL);

		/* Add the datagram position entries */
		ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len);
		memset(ntb_ndp, 0, dgram_idx_len);

		ncb_len = ncm->skb_tx_data->len;
		dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
		ncb_len += dgram_pad;

		/* (d)wDatagramIndex */
		put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
		/* (d)wDatagramLength */
		put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
		ncm->ndp_dgram_count++;

		/* Add the new data to the skb */
		ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad);
		memset(ntb_data, 0, dgram_pad);
		ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len);
		memcpy(ntb_data, skb->data, skb->len);
	dev_kfree_skb_any(skb);
		skb = NULL;

	} else if (ncm->skb_tx_data && ncm->timer_force_tx) {
		/* If the tx was requested because of a timeout then send */
		skb2 = package_for_tx(ncm);
	if (!skb2)
			goto err;
	}
		return NULL;

	return skb2;
	skb = skb2;
	tmp = (void *) skb_push(skb, ncb_len);
	memset(tmp, 0, ncb_len);
	put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */
	tmp += 2;
	/* wHeaderLength */
	put_unaligned_le16(opts->nth_size, tmp++);
	tmp++; /* skip wSequence */
	put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */
	/* (d)wFpIndex */
	/* the first pointer is right after the NTH + align */
	put_ncm(&tmp, opts->ndp_index, opts->nth_size + ndp_pad);
	tmp = (void *)tmp + ndp_pad;
	/* NDP */
	put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */
	tmp += 2;
	/* wLength */
	put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++);

err:
	ncm->netdev->stats.tx_dropped++;
	tmp += opts->reserved1;
	tmp += opts->next_ndp_index; /* skip reserved (d)wNextFpIndex */
	tmp += opts->reserved2;

	if (skb)
		dev_kfree_skb_any(skb);
	if (ncm->skb_tx_data)
		dev_kfree_skb_any(ncm->skb_tx_data);
	if (ncm->skb_tx_ndp)
		dev_kfree_skb_any(ncm->skb_tx_ndp);
	if (ncm->is_crc) {
		uint32_t crc;

	return NULL;
		crc = ~crc32_le(~0,
				skb->data + ncb_len,
				skb->len - ncb_len);
		put_unaligned_le32(crc, skb->data + skb->len);
		skb_put(skb, crc_len);
	}

/*
 * This transmits the NTB if there are frames waiting.
 */
static void ncm_tx_tasklet(unsigned long data)
{
	struct f_ncm	*ncm = (void *)data;
	/* (d)wDatagramIndex[0] */
	put_ncm(&tmp, opts->dgram_item_len, ncb_len);
	/* (d)wDatagramLength[0] */
	put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len);
	/* (d)wDatagramIndex[1] and  (d)wDatagramLength[1] already zeroed */

	if (ncm->timer_stopping)
		return;

	/* Only send if data is available. */
	if (ncm->skb_tx_data) {
		ncm->timer_force_tx = true;
	if (skb->len > MAX_TX_NONFIXED)
		memset(skb_put(skb, max_size - skb->len),
				0, max_size - skb->len);

		/* XXX This allowance of a NULL skb argument to ndo_start_xmit
		 * XXX is not sane.  The gadget layer should be redesigned so
		 * XXX that the dev->wrap() invocations to build SKBs is transparent
		 * XXX and performed in some way outside of the ndo_start_xmit
		 * XXX interface.
		 */
		ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);

		ncm->timer_force_tx = false;
	}
}

/*
 * The transmit should only be run if no skb data has been sent
 * for a certain duration.
 */
static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
{
	struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
	tasklet_schedule(&ncm->tx_tasklet);
	return HRTIMER_NORESTART;
	return skb;
}

static int ncm_unwrap_ntb(struct gether *port,
@@ -1349,11 +1182,8 @@ static void ncm_disable(struct usb_function *f)

	DBG(cdev, "ncm deactivated\n");

	if (ncm->port.in_ep->driver_data) {
		ncm->timer_stopping = true;
		ncm->netdev = NULL;
	if (ncm->port.in_ep->driver_data)
		gether_disconnect(&ncm->port);
	}

	if (ncm->notify->driver_data) {
		usb_ep_disable(ncm->notify);
@@ -1531,10 +1361,6 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
	ncm->port.open = ncm_open;
	ncm->port.close = ncm_close;

	tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm);
	hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	ncm->task_timer.function = ncm_tx_timeout;

	DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
			ncm->port.in_ep->name, ncm->port.out_ep->name,
@@ -1647,9 +1473,6 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)

	DBG(c->cdev, "ncm unbind\n");

	hrtimer_cancel(&ncm->task_timer);
	tasklet_kill(&ncm->tx_tasklet);

	ncm_string_defs[0].id = 0;
	usb_free_all_descriptors(f);

@@ -1687,7 +1510,6 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
	ncm->port.ioport = netdev_priv(opts->net);
	mutex_unlock(&opts->lock);
	ncm->port.is_fixed = true;
	ncm->port.supports_multi_frame = true;

	ncm->port.func.name = "cdc_network";
	/* descriptors are per-instance copies */
+11 −7
Original line number Diff line number Diff line
@@ -1045,13 +1045,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
	}
	spin_unlock_irqrestore(&dev->lock, flags);

	if (skb && !in) {
	if (!in) {
		dev_kfree_skb_any(skb);
		return NETDEV_TX_OK;
	}

	/* apply outgoing CDC or RNDIS filters only for ETH packets */
	if (!test_bit(RMNET_MODE_LLP_IP, &dev->flags) && skb &&
	if (!test_bit(RMNET_MODE_LLP_IP, &dev->flags) &&
						!is_promisc(cdc_filter)) {
		u8		*dest = skb->data;

@@ -1097,10 +1097,6 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
	spin_unlock_irqrestore(&dev->lock, flags);

	if (!skb) {
		 /* Multi frame CDC protocols may store the frame for
		  * later which is not a dropped frame.
		  */
		if (!dev->port_usb->supports_multi_frame)
		dev->net->stats.tx_dropped++;

		/* no error code for dropped packets */
@@ -1722,10 +1718,14 @@ struct net_device *gether_setup_name_default(const char *netname)
	spin_lock_init(&dev->lock);
	spin_lock_init(&dev->req_lock);
	INIT_WORK(&dev->work, eth_work);
	INIT_WORK(&dev->rx_work, process_rx_w);
	INIT_WORK(&dev->tx_work, process_tx_w);
	INIT_LIST_HEAD(&dev->tx_reqs);
	INIT_LIST_HEAD(&dev->rx_reqs);
	INIT_WORK(&dev->cpu_policy_w, update_cpu_policy_w);

	skb_queue_head_init(&dev->rx_frames);
	skb_queue_head_init(&dev->tx_skb_q);

	/* network device setup */
	dev->net = net;
@@ -1740,6 +1740,10 @@ struct net_device *gether_setup_name_default(const char *netname)
	net->netdev_ops = &eth_netdev_ops;

	net->ethtool_ops = &ops;

	/* set operation mode to eth by default */
	set_bit(RMNET_MODE_LLP_ETH, &dev->flags);

	SET_NETDEV_DEVTYPE(net, &gadget_type);

	return net;
+0 −1
Original line number Diff line number Diff line
@@ -79,7 +79,6 @@ struct gether {
	uint32_t			dl_max_pkts_per_xfer;
	uint32_t			dl_max_xfer_size;
	bool				multi_pkt_xfer;
	bool				supports_multi_frame;
	bool				rx_trigger_enabled;
	bool				rx_triggered;
	struct sk_buff			*(*wrap)(struct gether *port,