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

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

Merge "USB: f_mbim: Add support for BAM-DMUX transport"

parents 1b17b747 c6079f4e
Loading
Loading
Loading
Loading
+162 −127
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ enum mbim_peripheral_ep_type {
	MBIM_DATA_EP_TYPE_HSUSB      = 0x2,
	MBIM_DATA_EP_TYPE_PCIE       = 0x3,
	MBIM_DATA_EP_TYPE_EMBEDDED   = 0x4,
	MBIM_DATA_EP_TYPE_BAM_DMUX   = 0x5,
};

struct mbim_peripheral_ep_info {
@@ -164,9 +165,6 @@ static inline struct f_mbim *func_to_mbim(struct usb_function *f)
#define MBIM_NTB_OUT_SIZE_IPA		(0x4000)

#define MBIM_FORMATS_SUPPORTED	USB_CDC_NCM_NTB16_SUPPORTED
static int mbim_ntb_out_size_sys2bam;
module_param(mbim_ntb_out_size_sys2bam, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(mbim_ntb_out_size_sys2bam, "MBIM OUT SIZE for SYS2BAM Mode");

static struct usb_cdc_ncm_ntb_parameters mbim_ntb_parameters = {
	.wLength = sizeof mbim_ntb_parameters,
@@ -243,7 +241,7 @@ static struct usb_cdc_mbim_desc mbim_desc = {
	.wMaxControlMessage =	cpu_to_le16(0x1000),
	.bNumberFilters =	0x20,
	.bMaxFilterSize =	0x80,
	.wMaxSegmentSize =	cpu_to_le16(0xfe0),
	.wMaxSegmentSize =	cpu_to_le16(0x800),
	.bmNetworkCapabilities = 0x20,
};

@@ -1135,19 +1133,42 @@ static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
			return 0;

		if (mbim->bam_port.in->driver_data) {
			pr_info("reset mbim\n");
			pr_info("reset mbim, alt-%d\n", alt);
			mbim_reset_values(mbim);
		}

		if (alt == 0) {
			/*
		 * CDC Network only sends data in non-default altsettings.
		 * Changing altsettings resets filters, statistics, etc.
			 * perform bam data disconnect handshake upon usb
			 * disconnect
			 */
		if (alt == 1) {
			pr_info("Alt set 1, initialize ports\n");
			switch (mbim->xport) {
			case USB_GADGET_XPORT_BAM:
				gbam_mbim_disconnect();
				break;
			case USB_GADGET_XPORT_BAM2BAM_IPA:
				bam_data_disconnect(&mbim->bam_port,
						USB_FUNC_MBIM, mbim->port_num);
				if (!gadget_is_dwc3(cdev->gadget))
					break;

			if (!mbim->bam_port.in->desc) {
				if (msm_ep_unconfig(mbim->bam_port.in) ||
				    msm_ep_unconfig(mbim->bam_port.out)) {
					pr_err("ep_unconfig failed\n");
					goto fail;
				}
			default:
				pr_err("unknown transport\n");
			}
			goto notify_ready;
		}

		pr_info("Alt set 1, initialize ports\n");

		/*
		 * CDC Network only sends data in non-default altsettings.
		 * Changing altsettings resets filters, statistics, etc.
		 */
		pr_info("Choose endpoints\n");

		ret = config_ep_by_speed(cdev->gadget, f,
@@ -1174,8 +1195,14 @@ static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
		pr_info("Set mbim port out_desc = 0x%p\n",
				mbim->bam_port.out->desc);

				if (mbim->xport == USB_GADGET_XPORT_BAM2BAM_IPA
					&& gadget_is_dwc3(cdev->gadget)) {
		pr_debug("Activate mbim\n");
		switch (mbim->xport) {
		case USB_GADGET_XPORT_BAM:
			gbam_mbim_connect(cdev->gadget, mbim->bam_port.in,
						mbim->bam_port.out);
			break;
		case USB_GADGET_XPORT_BAM2BAM_IPA:
			if (gadget_is_dwc3(cdev->gadget)) {
				if (msm_ep_config(mbim->bam_port.in) ||
					msm_ep_config(mbim->bam_port.out)) {
					pr_err("%s: ep_config failed\n",
@@ -1183,7 +1210,6 @@ static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
					goto fail;
				}
			}

			ret = bam_data_connect(&mbim->bam_port,
				mbim->xport, mbim->port_num,
				USB_FUNC_MBIM);
@@ -1192,29 +1218,11 @@ static int mbim_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
						ret);
				goto fail;
			}
			} else {
				pr_info("PORTS already SET\n");
			}
		}

		if (alt == 0 && mbim->bam_port.in->driver_data) {
			/*
			 * perform bam data disconnect handshake upon usb
			 * disconnect
			 */
			bam_data_disconnect(&mbim->bam_port, USB_FUNC_MBIM,
					mbim->port_num);
			if (mbim->xport == USB_GADGET_XPORT_BAM2BAM_IPA &&
					mbim->data_interface_up &&
					gadget_is_dwc3(cdev->gadget)) {
				if (msm_ep_unconfig(mbim->bam_port.in) ||
					msm_ep_unconfig(mbim->bam_port.out)) {
					pr_err("ep_unconfig failed\n");
					goto fail;
				}
			}
			break;
		default:
			pr_err("unknown transport\n");
		}

notify_ready:
		mbim->data_interface_up = alt;
		spin_lock(&mbim->lock);
		mbim->not_port.notify_state = MBIM_NOTIFY_RESPONSE_AVAILABLE;
@@ -1275,13 +1283,21 @@ static void mbim_disable(struct usb_function *f)
		return;
	}

	if (mbim->xport == USB_GADGET_XPORT_BAM2BAM_IPA &&
			gadget_is_dwc3(cdev->gadget)) {
	switch (mbim->xport) {
	case USB_GADGET_XPORT_BAM:
		gbam_mbim_disconnect();
		break;
	case USB_GADGET_XPORT_BAM2BAM_IPA:
		if (gadget_is_dwc3(cdev->gadget)) {
			msm_ep_unconfig(mbim->bam_port.out);
			msm_ep_unconfig(mbim->bam_port.in);
		}

	bam_data_disconnect(&mbim->bam_port, USB_FUNC_MBIM, mbim->port_num);
		bam_data_disconnect(&mbim->bam_port, USB_FUNC_MBIM,
						mbim->port_num);
		break;
	default:
		pr_err("unknown transport\n");
	}

	mbim->data_interface_up = false;
	pr_info("mbim deactivated\n");
@@ -1299,6 +1315,9 @@ static void mbim_suspend(struct usb_function *f)
	pr_debug("%s(): remote_wakeup:%d\n:", __func__,
			mbim->cdev->gadget->remote_wakeup);

	if (mbim->xport == USB_GADGET_XPORT_BAM)
		return;

	/* If the function is in Function Suspend state, avoid suspending the
	 * MBIM function again.
	 */
@@ -1331,6 +1350,9 @@ static void mbim_resume(struct usb_function *f)

	pr_info("mbim resumed\n");

	if (mbim->xport == USB_GADGET_XPORT_BAM)
		return;

	/*
	 * If the function is in USB3 Function Suspend state, resume is
	 * canceled. In this case resume is done by a Function Resume request.
@@ -1506,11 +1528,6 @@ mbim_bind(struct usb_configuration *c, struct usb_function *f)
	event->wIndex = cpu_to_le16(mbim->ctrl_id);
	event->wLength = cpu_to_le16(0);

	if (mbim->xport == USB_GADGET_XPORT_BAM2BAM_IPA)
		mbim_desc.wMaxSegmentSize = cpu_to_le16(0x800);
	else
		mbim_desc.wMaxSegmentSize = cpu_to_le16(0xfe0);

	/* copy descriptors, and track endpoint copies */
	f->fs_descriptors = usb_copy_descriptors(mbim_fs_function);
	if (!f->fs_descriptors)
@@ -1625,7 +1642,6 @@ int mbim_bind_config(struct usb_configuration *c, unsigned portno,
{
	struct f_mbim	*mbim = NULL;
	int status = 0;
	int mbim_out_max_size;

	pr_info("port number %u\n", portno);

@@ -1635,6 +1651,45 @@ int mbim_bind_config(struct usb_configuration *c, unsigned portno,
		return -ENODEV;
	}

	/* allocate and initialize one new instance */
	mbim = mbim_ports[portno].port;
	if (!mbim) {
		pr_err("mbim struct not allocated\n");
		return -ENOMEM;
	}

	mbim->xport = str_to_xport(xport_name);
	switch (mbim->xport) {
	case USB_GADGET_XPORT_BAM:
		status = gbam_mbim_setup();
		if (status)
			break;
		break;
	case USB_GADGET_XPORT_BAM2BAM_IPA:
		status = mbim_bam_setup(nr_mbim_ports);
		if (status)
			break;
		mbim_ntb_parameters.wNtbOutMaxDatagrams = 16;
		/* For IPA this is proven to give maximum throughput */
		mbim_ntb_parameters.dwNtbInMaxSize =
				cpu_to_le32(NTB_DEFAULT_IN_SIZE_IPA);
		mbim_ntb_parameters.dwNtbOutMaxSize =
				cpu_to_le32(MBIM_NTB_OUT_SIZE_IPA);
		/* update rx buffer size to be used by usb rx request buffer */
		mbim->bam_port.rx_buffer_size = MBIM_NTB_OUT_SIZE_IPA;
		mbim_ntb_parameters.wNdpInDivisor = 1;
		pr_debug("MBIM: dwNtbOutMaxSize:%d\n", MBIM_NTB_OUT_SIZE_IPA);
		break;
	default:
		status = -EINVAL;
	}

	if (status) {
		pr_err("%s transport setup failed\n", xport_name);
		return status;
	}


	/* maybe allocate device-global string IDs */
	if (mbim_string_defs[0].id == 0) {

@@ -1654,13 +1709,6 @@ int mbim_bind_config(struct usb_configuration *c, unsigned portno,
		mbim_data_intf.iInterface = status;
	}

	/* allocate and initialize one new instance */
	mbim = mbim_ports[0].port;
	if (!mbim) {
		pr_info("mbim struct not allocated\n");
		return -ENOMEM;
	}

	mbim->cdev = c->cdev;

	mbim_reset_values(mbim);
@@ -1677,34 +1725,6 @@ int mbim_bind_config(struct usb_configuration *c, unsigned portno,
	mbim->function.func_suspend = mbim_func_suspend;
	mbim->function.get_status = mbim_get_status;
	mbim->function.resume = mbim_resume;
	mbim->xport = str_to_xport(xport_name);

	/* Only BAM2BAM_IPA supported */
	if (mbim->xport != USB_GADGET_XPORT_BAM2BAM_IPA) {
		pr_err("Only IPA xport supported\n");
		return -EINVAL;
	}

	/* For IPA we use limit of 16 */
	mbim_ntb_parameters.wNtbOutMaxDatagrams = 16;
	/* For IPA this is proven to give maximum throughput */
	mbim_ntb_parameters.dwNtbInMaxSize =
	cpu_to_le32(NTB_DEFAULT_IN_SIZE_IPA);
	/*
	 * If mbim_ntb_out_size_sys2bam is set, use that value
	 * otherwise use default value.
	 */
	if (mbim_ntb_out_size_sys2bam)
		mbim_out_max_size = mbim_ntb_out_size_sys2bam;
	else
		mbim_out_max_size = MBIM_NTB_OUT_SIZE_IPA;

	mbim_ntb_parameters.dwNtbOutMaxSize =
			cpu_to_le32(mbim_out_max_size);
	/* update rx buffer size to be used by usb rx request buffer */
	mbim->bam_port.rx_buffer_size = mbim_out_max_size;
	mbim_ntb_parameters.wNdpInDivisor = 1;
	pr_debug("MBIM: dwNtbOutMaxSize:%d\n", mbim_out_max_size);

	INIT_LIST_HEAD(&mbim->cpkt_req_q);
	INIT_LIST_HEAD(&mbim->cpkt_resp_q);
@@ -1928,6 +1948,7 @@ static int mbim_release(struct inode *ip, struct file *fp)
	return 0;
}

#define BAM_DMUX_CHANNEL_ID 8
static long mbim_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
{
	struct f_mbim *mbim = fp->private_data;
@@ -1973,6 +1994,20 @@ static long mbim_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
			return -ENOTCONN;
		}

		switch (mbim->xport) {
		case USB_GADGET_XPORT_BAM:
			/*
			 * Rmnet and MBIM share the same BAM-DMUX channel.
			 * This channel number 8 should be in sync with
			 * the one defined in u_bam.c.
			 */
			info.ph_ep_info.ep_type = MBIM_DATA_EP_TYPE_BAM_DMUX;
			info.ph_ep_info.peripheral_iface_id =
						BAM_DMUX_CHANNEL_ID;
			info.ipa_ep_pair.cons_pipe_num = 0;
			info.ipa_ep_pair.prod_pipe_num = 0;
			break;
		case USB_GADGET_XPORT_BAM2BAM_IPA:
			port = &mbim->bam_port;
			if ((port->ipa_producer_ep == -1) ||
				(port->ipa_consumer_ep == -1)) {
@@ -1985,6 +2020,12 @@ static long mbim_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
			info.ph_ep_info.peripheral_iface_id = mbim->data_id;
			info.ipa_ep_pair.cons_pipe_num = port->ipa_consumer_ep;
			info.ipa_ep_pair.prod_pipe_num = port->ipa_producer_ep;
			break;
		default:
			ret = -ENODEV;
			pr_err("unknown transport\n");
			break;
		}

		ret = copy_to_user((void __user *)arg, &info,
			sizeof(info));
@@ -2072,12 +2113,6 @@ static int mbim_init(int instances)

	pr_info("Initialized %d ports\n", nr_mbim_ports);

	ret = mbim_bam_setup(nr_mbim_ports);
	if (ret) {
		pr_err("bam_data_setup failed err: %d\n", ret);
		return ret;
	}

	return ret;

fail_probe:
+45 −0
Original line number Diff line number Diff line
@@ -1105,6 +1105,7 @@ static void gbam_notify(void *p, int event, unsigned long data)
		if (test_bit(BAM_CH_OPENED, &d->flags))
			pr_warn("%s, BAM channel opened already", __func__);
		bam_mux_rx_req_size = data;
		pr_debug("%s rx_req_size: %lu", __func__, bam_mux_rx_req_size);
		break;
	}
}
@@ -2423,3 +2424,47 @@ void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)

	spin_unlock_irqrestore(&port->port_lock, flags);
}

int gbam_mbim_connect(struct usb_gadget *g, struct usb_ep *in,
			struct usb_ep *out)
{
	struct grmnet *gr;

	gr = kzalloc(sizeof(*gr), GFP_ATOMIC);
	if (!gr)
		return -ENOMEM;
	gr->in = in;
	gr->out = out;
	gr->gadget = g;

	return gbam_connect(gr, 0, USB_GADGET_XPORT_BAM, 0, 0);
}

void gbam_mbim_disconnect(void)
{
	struct gbam_port *port = bam_ports[0].port;
	struct grmnet *gr = port->port_usb;

	if (!gr) {
		pr_err("%s: port_usb is NULL\n", __func__);
		return;
	}

	gbam_disconnect(gr, 0, USB_GADGET_XPORT_BAM);
	kfree(gr);
}

int gbam_mbim_setup(void)
{
	int ret = 0;

	/*
	 * MBIM requires only 1 USB_GADGET_XPORT_BAM
	 * port. The port is always 0 and is shared
	 * between RMNET and MBIM.
	 */
	if (!n_bam_ports)
		ret = gbam_setup(1);

	return ret;
}
+5 −1
Original line number Diff line number Diff line
/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -68,6 +68,10 @@ void gbam_disconnect(struct grmnet *gr, u8 port_num,
	enum transport_type trans);
void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans);
void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans);
int gbam_mbim_setup(void);
int gbam_mbim_connect(struct usb_gadget *g, struct usb_ep *in,
					struct usb_ep *out);
void gbam_mbim_disconnect(void);
int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
int gsmd_ctrl_setup(enum ctrl_client client_num, unsigned int count,