예제 #1
0
파일: cdc_ncm.c 프로젝트: smx-smx/dsl-n55u
/* Select the MBIM altsetting iff it is preferred and available,
 * returning the number of the corresponding data interface altsetting
 */
u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
{
	struct usb_host_interface *alt;

	/* The MBIM spec defines a NCM compatible default altsetting,
	 * which we may have matched:
	 *
	 *  "Functions that implement both NCM 1.0 and MBIM (an
	 *   “NCM/MBIM function”) according to this recommendation
	 *   shall provide two alternate settings for the
	 *   Communication Interface.  Alternate setting 0, and the
	 *   associated class and endpoint descriptors, shall be
	 *   constructed according to the rules given for the
	 *   Communication Interface in section 5 of [USBNCM10].
	 *   Alternate setting 1, and the associated class and
	 *   endpoint descriptors, shall be constructed according to
	 *   the rules given in section 6 (USB Device Model) of this
	 *   specification."
	 */
	if (prefer_mbim && intf->num_altsetting == 2) {
		alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM);
		if (alt && cdc_ncm_comm_intf_is_mbim(alt) &&
		    !usb_set_interface(dev->udev,
				       intf->cur_altsetting->desc.bInterfaceNumber,
				       CDC_NCM_COMM_ALTSETTING_MBIM))
			return CDC_NCM_DATA_ALTSETTING_MBIM;
	}
	return CDC_NCM_DATA_ALTSETTING_NCM;
}
예제 #2
0
static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
{
	/* MBIM backwards compatible function? */
	cdc_ncm_select_altsetting(dev, intf);
	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
		return -ENODEV;

	/* NCM data altsetting is always 1 */
	return cdc_ncm_bind_common(dev, intf, 1);
}
예제 #3
0
static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
{
	struct cdc_ncm_ctx *ctx;
	struct usb_driver *subdriver = ERR_PTR(-ENODEV);
	int ret = -ENODEV;
	u8 data_altsetting = 1;
	struct cdc_mbim_state *info = (void *)&dev->data;

	/* should we change control altsetting on a NCM/MBIM function? */
	if (cdc_ncm_select_altsetting(intf) == CDC_NCM_COMM_ALTSETTING_MBIM) {
		data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM;
		ret = cdc_mbim_set_ctrlalt(dev, intf, CDC_NCM_COMM_ALTSETTING_MBIM);
		if (ret)
			goto err;
		ret = -ENODEV;
	}

	/* we will hit this for NCM/MBIM functions if prefer_mbim is false */
	if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
		goto err;

	ret = cdc_ncm_bind_common(dev, intf, data_altsetting, dev->driver_info->data);
	if (ret)
		goto err;

	ctx = info->ctx;

	/* The MBIM descriptor and the status endpoint are required */
	if (ctx->mbim_desc && dev->status)
		subdriver = usb_cdc_wdm_register(ctx->control,
						 &dev->status->desc,
						 le16_to_cpu(ctx->mbim_desc->wMaxControlMessage),
						 cdc_mbim_wdm_manage_power);
	if (IS_ERR(subdriver)) {
		ret = PTR_ERR(subdriver);
		cdc_ncm_unbind(dev, intf);
		goto err;
	}

	/* can't let usbnet use the interrupt endpoint */
	dev->status = NULL;
	info->subdriver = subdriver;

	/* MBIM cannot do ARP */
	dev->net->flags |= IFF_NOARP;

	/* no need to put the VLAN tci in the packet headers */
	dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER;

	/* monitor VLAN additions and removals */
	dev->net->netdev_ops = &cdc_mbim_netdev_ops;
err:
	return ret;
}
예제 #4
0
파일: cdc_ncm.c 프로젝트: AiWinters/linux
static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
{
	int ret;

	/* The MBIM spec defines a NCM compatible default altsetting,
	 * which we may have matched:
	 *
	 *  "Functions that implement both NCM 1.0 and MBIM (an
	 *   “NCM/MBIM function”) according to this recommendation
	 *   shall provide two alternate settings for the
	 *   Communication Interface.  Alternate setting 0, and the
	 *   associated class and endpoint descriptors, shall be
	 *   constructed according to the rules given for the
	 *   Communication Interface in section 5 of [USBNCM10].
	 *   Alternate setting 1, and the associated class and
	 *   endpoint descriptors, shall be constructed according to
	 *   the rules given in section 6 (USB Device Model) of this
	 *   specification."
	 *
	 * Do not bind to such interfaces, allowing cdc_mbim to handle
	 * them
	 */
#if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM)
	if ((intf->num_altsetting == 2) &&
	    !usb_set_interface(dev->udev,
			       intf->cur_altsetting->desc.bInterfaceNumber,
			       CDC_NCM_COMM_ALTSETTING_MBIM)) {
		if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
			return -ENODEV;
		else
			usb_set_interface(dev->udev,
					  intf->cur_altsetting->desc.bInterfaceNumber,
					  CDC_NCM_COMM_ALTSETTING_NCM);
	}
#endif

	/* NCM data altsetting is always 1 */
	ret = cdc_ncm_bind_common(dev, intf, 1);

	/*
	 * We should get an event when network connection is "connected" or
	 * "disconnected". Set network connection in "disconnected" state
	 * (carrier is OFF) during attach, so the IP network stack does not
	 * start IPv6 negotiation and more.
	 */
	netif_carrier_off(dev->net);
	return ret;
}
예제 #5
0
파일: cdc_mbim.c 프로젝트: 7799/linux
static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
{
	struct cdc_ncm_ctx *ctx;
	struct usb_driver *subdriver = ERR_PTR(-ENODEV);
	int ret = -ENODEV;
	u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf);
	struct cdc_mbim_state *info = (void *)&dev->data;

	/* Probably NCM, defer for cdc_ncm_bind */
	if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
		goto err;

	ret = cdc_ncm_bind_common(dev, intf, data_altsetting);
	if (ret)
		goto err;

	ctx = info->ctx;

	/* The MBIM descriptor and the status endpoint are required */
	if (ctx->mbim_desc && dev->status)
		subdriver = usb_cdc_wdm_register(ctx->control,
						 &dev->status->desc,
						 le16_to_cpu(ctx->mbim_desc->wMaxControlMessage),
						 cdc_mbim_wdm_manage_power);
	if (IS_ERR(subdriver)) {
		ret = PTR_ERR(subdriver);
		cdc_ncm_unbind(dev, intf);
		goto err;
	}

	/* can't let usbnet use the interrupt endpoint */
	dev->status = NULL;
	info->subdriver = subdriver;

	/* MBIM cannot do ARP */
	dev->net->flags |= IFF_NOARP;

	/* no need to put the VLAN tci in the packet headers */
	dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX;
err:
	return ret;
}
예제 #6
0
파일: cdc_ncm.c 프로젝트: smx-smx/dsl-n55u
static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
{
	int ret;

	/* MBIM backwards compatible function? */
	cdc_ncm_select_altsetting(dev, intf);
	if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
		return -ENODEV;

	/* NCM data altsetting is always 1 */
	ret = cdc_ncm_bind_common(dev, intf, 1);

	/*
	 * We should get an event when network connection is "connected" or
	 * "disconnected". Set network connection in "disconnected" state
	 * (carrier is OFF) during attach, so the IP network stack does not
	 * start IPv6 negotiation and more.
	 */
	netif_carrier_off(dev->net);
	return ret;
}
예제 #7
0
파일: cdc_ncm.c 프로젝트: Mr-Aloof/wl500g
static int cdc_ncm_setup(struct usbnet *dev)
{
	struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
	struct usb_cdc_ncm_ntb_parameters ncm_parm;
	u32 val;
	u8 flags;
	u8 iface_no;
	int err;
	int eth_hlen;
	u16 mbim_mtu;
	u16 ntb_fmt_supported;
	__le16 max_datagram_size;

	iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;

	err = usb_control_msg(dev->udev,
				usb_rcvctrlpipe(dev->udev, 0),
				USB_CDC_GET_NTB_PARAMETERS,
				USB_TYPE_CLASS | USB_DIR_IN
				 | USB_RECIP_INTERFACE,
				0, iface_no, &ncm_parm,
				sizeof(ncm_parm), 10000);
	if (err < 0) {
		dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n");
		return err; /* GET_NTB_PARAMETERS is required */
	}

	/* read correct set of parameters according to device mode */
	ctx->rx_max = le32_to_cpu(ncm_parm.dwNtbInMaxSize);
	ctx->tx_max = le32_to_cpu(ncm_parm.dwNtbOutMaxSize);
	ctx->tx_remainder = le16_to_cpu(ncm_parm.wNdpOutPayloadRemainder);
	ctx->tx_modulus = le16_to_cpu(ncm_parm.wNdpOutDivisor);
	ctx->tx_ndp_modulus = le16_to_cpu(ncm_parm.wNdpOutAlignment);
	/* devices prior to NCM Errata shall set this field to zero */
	ctx->tx_max_datagrams = le16_to_cpu(ncm_parm.wNtbOutMaxDatagrams);
	ntb_fmt_supported = le16_to_cpu(ncm_parm.bmNtbFormatsSupported);

	/* there are some minor differences in NCM and MBIM defaults */
	if (cdc_ncm_comm_intf_is_mbim(ctx->control->cur_altsetting)) {
		if (!ctx->mbim_desc)
			return -EINVAL;
		eth_hlen = 0;
		flags = ctx->mbim_desc->bmNetworkCapabilities;
		ctx->max_datagram_size = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
		if (ctx->max_datagram_size < CDC_MBIM_MIN_DATAGRAM_SIZE)
			ctx->max_datagram_size = CDC_MBIM_MIN_DATAGRAM_SIZE;
	} else {
		if (!ctx->func_desc)
			return -EINVAL;
		eth_hlen = ETH_HLEN;
		flags = ctx->func_desc->bmNetworkCapabilities;
		ctx->max_datagram_size = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
		if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE)
			ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE;
	}

	/* common absolute max for NCM and MBIM */
	if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE)
		ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE;

	pr_debug("dwNtbInMaxSize=%u dwNtbOutMaxSize=%u "
		 "wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u "
		 "wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n",
		 ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus,
		 ctx->tx_ndp_modulus, ctx->tx_max_datagrams, flags);

	/* max count of tx datagrams */
	if ((ctx->tx_max_datagrams == 0) ||
			(ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX))
		ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX;

	/* verify maximum size of received NTB in bytes */
	if (ctx->rx_max < USB_CDC_NCM_NTB_MIN_IN_SIZE) {
		pr_debug("Using min receive length=%d\n",
						USB_CDC_NCM_NTB_MIN_IN_SIZE);
		ctx->rx_max = USB_CDC_NCM_NTB_MIN_IN_SIZE;
	}

	if (ctx->rx_max > CDC_NCM_NTB_MAX_SIZE_RX) {
		pr_debug("Using default maximum receive length=%d\n",
						CDC_NCM_NTB_MAX_SIZE_RX);
		ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX;
	}

	/* inform device about NTB input size changes */
	if (ctx->rx_max != le32_to_cpu(ncm_parm.dwNtbInMaxSize)) {
		__le32 *dwNtbInMaxSize;

		dwNtbInMaxSize = kzalloc(sizeof(*dwNtbInMaxSize), GFP_KERNEL);
		if (!dwNtbInMaxSize) {
			err = -ENOMEM;
			goto size_err;
		}
		*dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
		err = usb_control_msg(dev->udev,
				      usb_sndctrlpipe(dev->udev, 0),
				      USB_CDC_SET_NTB_INPUT_SIZE,
				      USB_TYPE_CLASS | USB_DIR_OUT
				      | USB_RECIP_INTERFACE,
				      0, iface_no, dwNtbInMaxSize, 4, 1000);
		kfree(dwNtbInMaxSize);
size_err:
		if (err < 0)
			pr_debug("Setting NTB Input Size failed\n");
	}

	/* verify maximum size of transmitted NTB in bytes */
	if (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX) {
		pr_debug("Using default maximum transmit length=%d\n",
						CDC_NCM_NTB_MAX_SIZE_TX);
		ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX;

		/* Adding a pad byte here simplifies the handling in
		 * cdc_ncm_fill_tx_frame, by making tx_max always
		 * represent the real skb max size.
		 */
		if (ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0)
			ctx->tx_max++;

	}

	/*
	 * verify that the structure alignment is:
	 * - power of two
	 * - not greater than the maximum transmit length
	 * - not less than four bytes
	 */
	val = ctx->tx_ndp_modulus;

	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
	    (val != ((-val) & val)) || (val >= ctx->tx_max)) {
		pr_debug("Using default alignment: 4 bytes\n");
		ctx->tx_ndp_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
	}

	/*
	 * verify that the payload alignment is:
	 * - power of two
	 * - not greater than the maximum transmit length
	 * - not less than four bytes
	 */
	val = ctx->tx_modulus;

	if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) ||
	    (val != ((-val) & val)) || (val >= ctx->tx_max)) {
		pr_debug("Using default transmit modulus: 4 bytes\n");
		ctx->tx_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE;
	}

	/* verify the payload remainder */
	if (ctx->tx_remainder >= ctx->tx_modulus) {
		pr_debug("Using default transmit remainder: 0 bytes\n");
		ctx->tx_remainder = 0;
	}

	/* adjust TX-remainder according to NCM specification. */
	ctx->tx_remainder = ((ctx->tx_remainder - eth_hlen) &
			     (ctx->tx_modulus - 1));

	/* additional configuration */

	/* set CRC Mode */
	if (flags & USB_CDC_NCM_NCAP_CRC_MODE) {
		err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
				USB_CDC_SET_CRC_MODE,
				USB_TYPE_CLASS | USB_DIR_OUT
				 | USB_RECIP_INTERFACE,
				USB_CDC_NCM_CRC_NOT_APPENDED,
				iface_no, NULL, 0, 1000);
		if (err < 0)
			pr_debug("Setting CRC mode off failed\n");
	}

	/* set NTB format, if both formats are supported */
	if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) {
		err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
				USB_CDC_SET_NTB_FORMAT, USB_TYPE_CLASS
				 | USB_DIR_OUT | USB_RECIP_INTERFACE,
				USB_CDC_NCM_NTB16_FORMAT,
				iface_no, NULL, 0, 1000);
		if (err < 0)
			pr_debug("Setting NTB format to 16-bit failed\n");
	}

	/* inform the device about the selected Max Datagram Size */
	if (!(flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
		goto out;

	/* read current mtu value from device */
	err = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
				USB_CDC_GET_MAX_DATAGRAM_SIZE,
				USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
				0, iface_no, &max_datagram_size, 2, 1000);
	if (err < 0) {
		dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n");
		goto out;
	}

	if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size)
		goto out;

	max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
	err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
				USB_CDC_SET_MAX_DATAGRAM_SIZE,
				USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
				0, iface_no, &max_datagram_size, 2, 1000);
	if (err < 0)
		dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n");

out:
	/* set MTU to max supported by the device if necessary */
	if (dev->net->mtu > ctx->max_datagram_size - eth_hlen)
		dev->net->mtu = ctx->max_datagram_size - eth_hlen;

	/* do not exceed operater preferred MTU */
	if (ctx->mbim_extended_desc) {
		mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU);
		if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
			dev->net->mtu = mbim_mtu;
	}

	return 0;
}