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

Commit 3f883d7d authored by Hemant Kumar's avatar Hemant Kumar
Browse files

usb: gadget: f_gsi: Enable/Disable eps in workqueue context



For sequence of events : set_alt(1) -> bus suspend ->bus
reset->re-enumeration. Bus suspend handler queues EVT_SUSPEND
event. Between bus suspend and bus reset ipa_work_handler
runs in STATE_CONNECT_IN_PROGRESS and allocates TRB buffers.
Bus reset interrupt on same core schedules out worker thread
and performs end xfer cmd on all eps. As part of bus reset
gsi function disable queues EVT_DISCONNECT. Upon return from
bus reset handling worker thread schedules in and prepares TRBs
and starts xfer on gsi eps. Since event queue is not empty
worker thread runs again and handles EVT_SUSPEND and EVT_DISCONNECT
in STATE_CONNECT_IN_PROGRESS which calls ipa_disconnect_work_handler().
This unmaps and frees TRBs and buffers without stopping transfers on
gsi eps. Later this is resulting into SMMU page fault because
USB controller attempts to access unmapped stale buffer as gsi ep is
active. Fix this issue by disabling eps in ipa_disconnect_work_handler()
before unmap and free of TRBs and buffers instead of doing that
in gsi_disable(). Move ep config ops to ipa_work_handler() in
STATE_INITIALIZED. Return USB_GADGET_DELAYED_STATUS for data interface
to make sure ep is enabled before starting protocol specific transfers.
Add STATE_HOST_NRDY to handle RNDIS flow control enable/disable with
disconnect. As part of clean up remove unreachable state and event
handling. Also rename some states and events for clarity.

Change-Id: Iba19100fcc10addf3668b91d0a6f36c9e7aee1a8
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent 7b5291b3
Loading
Loading
Loading
Loading
+155 −118
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ static DEFINE_IDA(gsi_ida);
/* Deregister misc device and free instance structures */
static void gsi_inst_clean(struct gsi_opts *opts);
static void gsi_rndis_ipa_reset_trigger(struct gsi_data_port *d_port);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static int gsi_ctrl_send_notification(struct f_gsi *gsi);
static int gsi_alloc_trb_buffer(struct f_gsi *gsi);
static void gsi_free_trb_buffer(struct f_gsi *gsi);
@@ -75,7 +74,7 @@ static void __maybe_unused post_event_to_evt_queue(struct gsi_data_port *port,
								u8 event)
{
	post_event(port, event);
	queue_work(port->ipa_usb_wq, &port->usb_ipa_w);
	queue_delayed_work(port->ipa_usb_wq, &port->usb_ipa_w, 0);
}

static u8 read_event(struct gsi_data_port *port)
@@ -239,9 +238,9 @@ int ipa_usb_notify_cb(enum ipa_usb_notify_event event,
		 * Data path for RNDIS is enabled on EVT_HOST_READY.
		 */
		if (gsi->prot_id != IPA_USB_RNDIS) {
			post_event(&gsi->d_port, EVT_CONNECTED);
			queue_work(gsi->d_port.ipa_usb_wq,
					&gsi->d_port.usb_ipa_w);
			post_event(&gsi->d_port, EVT_IPA_READY);
			queue_delayed_work(gsi->d_port.ipa_usb_wq,
					&gsi->d_port.usb_ipa_w, 0);
		}
		break;

@@ -251,7 +250,8 @@ int ipa_usb_notify_cb(enum ipa_usb_notify_event event,

	case IPA_USB_SUSPEND_COMPLETED:
		post_event(&gsi->d_port, EVT_IPA_SUSPEND);
		queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
		queue_delayed_work(gsi->d_port.ipa_usb_wq,
				&gsi->d_port.usb_ipa_w, 0);
		break;
	}

@@ -463,32 +463,27 @@ static void ipa_data_path_enable(struct gsi_data_port *d_port)
						GSI_EP_OP_RING_DB);
}

static void ipa_disconnect_handler(struct gsi_data_port *d_port)
static void ipa_data_path_disable(struct gsi_data_port *d_port)
{
	struct f_gsi *gsi = d_port_to_gsi(d_port);
	bool block_db = true;

	log_event_dbg("%s: EP Disable for data", __func__);
	log_event_dbg("%s: Disable eps", __func__);

	if (gsi->d_port.in_ep) {
		/*
		 * Block doorbell to GSI to avoid USB wrapper from
		 * ringing doorbell in case IPA clocks are OFF.
		 */
		usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);
	if (gsi->d_port.in_ep)
		usb_gsi_ep_op(gsi->d_port.in_ep,
				&gsi->d_port.in_request, GSI_EP_OP_DISABLE);
	}

	if (gsi->d_port.out_ep)
		usb_gsi_ep_op(gsi->d_port.out_ep,
				&gsi->d_port.out_request, GSI_EP_OP_DISABLE);

	gsi->d_port.net_ready_trigger = false;
	if (gsi->d_port.in_ep)
		usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
				GSI_EP_OP_SET_CLR_BLOCK_DBL);
}

static void ipa_disconnect_work_handler(struct gsi_data_port *d_port)
static void ipa_disconnect_channel(struct gsi_data_port *d_port)
{
	int ret;
	struct f_gsi *gsi = d_port_to_gsi(d_port);
@@ -507,6 +502,7 @@ static void ipa_disconnect_work_handler(struct gsi_data_port *d_port)
	gsi->d_port.in_channel_handle = -EINVAL;
	gsi->d_port.out_channel_handle = -EINVAL;

	if (gsi->d_port.in_ep)
		usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request,
							GSI_EP_OP_FREE_TRBS);

@@ -584,10 +580,39 @@ static void ipa_resume_work_handler(struct gsi_data_port *d_port)
			GSI_EP_OP_SET_CLR_BLOCK_DBL);
}

static int gsi_ep_enable(struct f_gsi *gsi)
{
	int ret;

	if (gsi->d_port.in_ep && !gsi->d_port.in_ep->desc) {
		ret = config_ep_by_speed(gsi->d_port.gadget, &gsi->function,
						gsi->d_port.in_ep);
		if (ret)
			return ret;

		log_event_dbg("%s: Enable IN ep", __func__);
		usb_gsi_ep_op(gsi->d_port.in_ep,
			&gsi->d_port.in_request, GSI_EP_OP_CONFIG);
	}

	if (gsi->d_port.out_ep && !gsi->d_port.out_ep->desc) {
		ret = config_ep_by_speed(gsi->d_port.gadget, &gsi->function,
						gsi->d_port.out_ep);
		if (ret)
			return ret;

		log_event_dbg("%s: Enable OUT ep", __func__);
		usb_gsi_ep_op(gsi->d_port.out_ep,
				&gsi->d_port.out_request, GSI_EP_OP_CONFIG);
	}

	return 0;
}

static void ipa_work_handler(struct work_struct *w)
{
	struct gsi_data_port *d_port = container_of(w, struct gsi_data_port,
						  usb_ipa_w);
						  usb_ipa_w.work);
	u8 event;
	int ret = 0;
	struct usb_gadget *gadget = d_port->gadget;
@@ -618,35 +643,33 @@ static void ipa_work_handler(struct work_struct *w)
	case STATE_UNINITIALIZED:
		break;
	case STATE_INITIALIZED:
		if (event == EVT_CONNECT_IN_PROGRESS) {
		if (event == EVT_SET_ALT) {
			usb_gadget_autopm_get(d_port->gadget);
			log_event_dbg("%s: get = %d", __func__,
				atomic_read(&gad_dev->power.usage_count));
			/* allocate buffers used with each TRB */
			ret = gsi_alloc_trb_buffer(gsi);

			/* Configure EPs for GSI */
			ret = gsi_ep_enable(gsi);
			if (ret) {
				log_event_err("%s: gsi_alloc_trb_failed\n",
								__func__);
				log_event_err("%s:ep enable err %d", __func__);
				usb_composite_setup_continue(gsi->d_port.cdev);
				usb_gadget_autopm_put_async(d_port->gadget);
				break;
			}
			ipa_connect_channels(d_port);
			d_port->sm_state = STATE_CONNECT_IN_PROGRESS;
			log_event_dbg("%s: ST_INIT_EVT_CONN_IN_PROG",
					__func__);
		} else if (event == EVT_HOST_READY) {

			usb_composite_setup_continue(gsi->d_port.cdev);

			/*
			 * When in a composition such as RNDIS + ADB,
			 * RNDIS host sends a GEN_CURRENT_PACKET_FILTER msg
			 * to enable/disable flow control eg. during RNDIS
			 * adaptor disable/enable from device manager.
			 * In the case of the msg to disable flow control,
			 * connect IPA channels and enable data path.
			 * EVT_HOST_READY is posted to the state machine
			 * in the handler for this msg.
			 * Skip rest for RNDIS and wait for EVT_HOST_READY
			 * which is invoked when the host sends the
			 * GEN_CURRENT_PACKET_FILTER message.
			 */
			usb_gadget_autopm_get(d_port->gadget);
			log_event_dbg("%s: get = %d", __func__,
				atomic_read(&gad_dev->power.usage_count));
			if (gsi->prot_id == IPA_USB_RNDIS) {
				d_port->sm_state = STATE_HOST_NRDY;
				usb_gadget_autopm_put_async(d_port->gadget);
				break;
			}

			/* allocate buffers used with each TRB */
			ret = gsi_alloc_trb_buffer(gsi);
			if (ret) {
@@ -654,36 +677,31 @@ static void ipa_work_handler(struct work_struct *w)
								__func__);
				break;
			}

			ipa_connect_channels(d_port);
			ipa_data_path_enable(d_port);
			d_port->sm_state = STATE_CONNECTED;
			log_event_dbg("%s: ST_INIT_EVT_HOST_READY", __func__);
			d_port->sm_state = STATE_WAIT_FOR_IPA_RDY;
			log_event_dbg("%s: ST_INIT_EVT_SET_ALT",
					__func__);
		}
		break;
	case STATE_CONNECT_IN_PROGRESS:
		if (event == EVT_HOST_READY) {
			ipa_data_path_enable(d_port);
			d_port->sm_state = STATE_CONNECTED;
			log_event_dbg("%s: ST_CON_IN_PROG_EVT_HOST_READY",
					 __func__);
		} else if (event == EVT_CONNECTED) {
	case STATE_WAIT_FOR_IPA_RDY:
		if (event == EVT_IPA_READY) {
			if (peek_event(d_port) == EVT_SUSPEND) {
				log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUSPEND",
				log_event_dbg("%s: ST_WAIT_IPARDY_EVT_SUSPEND",
					 __func__);
				break;
			}
			ipa_data_path_enable(d_port);
			d_port->sm_state = STATE_CONNECTED;
			log_event_dbg("%s: ST_CON_IN_PROG_EVT_CON %d",
			log_event_dbg("%s: ST_WAIT_IPARDY_EVT_IPARDY %d",
					__func__, __LINE__);
		} else if (event == EVT_SUSPEND) {
			if (peek_event(d_port) == EVT_DISCONNECTED) {
				read_event(d_port);
				ipa_disconnect_work_handler(d_port);
				ipa_data_path_disable(d_port);
				ipa_disconnect_channel(d_port);
				d_port->sm_state = STATE_INITIALIZED;
				usb_gadget_autopm_put_async(d_port->gadget);
				log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS_DIS",
				log_event_dbg("%s: ST_WAIT_IPARDY_EVT_SUS_DIS",
						__func__);
				log_event_dbg("%s: put_async1 = %d", __func__,
						atomic_read(
@@ -693,17 +711,18 @@ static void ipa_work_handler(struct work_struct *w)
			ret = ipa_suspend_work_handler(d_port);
			if (!ret) {
				usb_gadget_autopm_put_async(d_port->gadget);
				log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS",
				log_event_dbg("%s: ST_WAIT_IPARDY_EVT_SUS",
						__func__);
				log_event_dbg("%s: put_async2 = %d", __func__,
						atomic_read(
						&gad_dev->power.usage_count));
			}
		} else if (event == EVT_DISCONNECTED) {
			ipa_disconnect_work_handler(d_port);
			ipa_data_path_disable(d_port);
			ipa_disconnect_channel(d_port);
			d_port->sm_state = STATE_INITIALIZED;
			usb_gadget_autopm_put_async(d_port->gadget);
			log_event_dbg("%s: ST_CON_IN_PROG_EVT_DIS",
			log_event_dbg("%s: ST_WAIT_IPARDY_EVT_DIS",
						__func__);
			log_event_dbg("%s: put_async3 = %d",
					__func__, atomic_read(
@@ -730,19 +749,28 @@ static void ipa_work_handler(struct work_struct *w)
						GSI_EP_OP_ENDXFER);
				usb_gsi_ep_op(d_port->out_ep, NULL,
						GSI_EP_OP_ENDXFER);
			}

			ipa_disconnect_work_handler(d_port);
				/*
				 * don't disable endpoints for RNDIS flow
				 * control enable
				 */
				ipa_disconnect_channel(d_port);
				d_port->sm_state = STATE_HOST_NRDY;
				log_event_dbg("%s: ST_CON_EVT_HNRDY", __func__);
			} else {
				ipa_data_path_disable(d_port);
				ipa_disconnect_channel(d_port);
				d_port->sm_state = STATE_INITIALIZED;
			usb_gadget_autopm_put_async(d_port->gadget);
				log_event_dbg("%s: ST_CON_EVT_DIS", __func__);
			}
			usb_gadget_autopm_put_async(d_port->gadget);
			log_event_dbg("%s: put_async4 = %d",
					__func__, atomic_read(
						&gad_dev->power.usage_count));
		} else if (event == EVT_SUSPEND) {
			if (peek_event(d_port) == EVT_DISCONNECTED) {
				read_event(d_port);
				ipa_disconnect_work_handler(d_port);
				ipa_data_path_disable(d_port);
				ipa_disconnect_channel(d_port);
				d_port->sm_state = STATE_INITIALIZED;
				usb_gadget_autopm_put_async(d_port->gadget);
				log_event_dbg("%s: ST_CON_EVT_SUS_DIS",
@@ -761,19 +789,44 @@ static void ipa_work_handler(struct work_struct *w)
						__func__, atomic_read(
						&gad_dev->power.usage_count));
			}
		} else if (event == EVT_CONNECTED) {
			d_port->sm_state = STATE_CONNECTED;
			log_event_dbg("%s: ST_CON_EVT_CON", __func__);
		}
		break;
	case STATE_DISCONNECTED:
		if (event == EVT_CONNECT_IN_PROGRESS) {
	case STATE_HOST_NRDY:
		if (event == EVT_DISCONNECTED) {
			usb_gadget_autopm_get(d_port->gadget);
			ipa_data_path_disable(d_port);
			d_port->sm_state = STATE_INITIALIZED;
			usb_gadget_autopm_put_async(d_port->gadget);
			log_event_dbg("%s: ST_HOST_NRDY_EVT_DIS", __func__);
			log_event_dbg("%s: put_async = %d",
					__func__, atomic_read(
						&gad_dev->power.usage_count));
		} else if (event == EVT_HOST_READY) {
			/*
			 * When in a composition such as RNDIS + ADB,
			 * RNDIS host sends a GEN_CURRENT_PACKET_FILTER msg
			 * to enable/disable flow control eg. during RNDIS
			 * adaptor disable/enable from device manager.
			 * In the case of the msg to disable flow control,
			 * connect IPA channels and enable data path.
			 * EVT_HOST_READY is posted to the state machine
			 * in the handler for this msg.
			 */
			usb_gadget_autopm_get(d_port->gadget);
			log_event_dbg("%s: get = %d", __func__,
				atomic_read(&gad_dev->power.usage_count));
			/* allocate buffers used with each TRB */
			ret = gsi_alloc_trb_buffer(gsi);
			if (ret) {
				log_event_err("%s: gsi_alloc_trb_failed\n",
								__func__);
				break;
			}

			ipa_connect_channels(d_port);
			d_port->sm_state = STATE_CONNECT_IN_PROGRESS;
			log_event_dbg("%s: ST_DIS_EVT_CON_IN_PROG", __func__);
		} else if (event == EVT_UNINITIALIZED) {
			d_port->sm_state = STATE_UNINITIALIZED;
			log_event_dbg("%s: ST_DIS_EVT_UNINIT", __func__);
			ipa_data_path_enable(d_port);
			d_port->sm_state = STATE_CONNECTED;
			log_event_dbg("%s: ST_HOST_NRDY_EVT_HRDY_", __func__);
		}
		break;
	case STATE_SUSPEND_IN_PROGRESS:
@@ -800,7 +853,8 @@ static void ipa_work_handler(struct work_struct *w)
					atomic_read(
						&gad_dev->power.usage_count));
		} else if (event == EVT_DISCONNECTED) {
			ipa_disconnect_work_handler(d_port);
			ipa_data_path_disable(d_port);
			ipa_disconnect_channel(d_port);
			d_port->sm_state = STATE_INITIALIZED;
			usb_gadget_autopm_put_async(d_port->gadget);
			log_event_dbg("%s: ST_SUS_IN_PROG_EVT_DIS", __func__);
@@ -820,7 +874,8 @@ static void ipa_work_handler(struct work_struct *w)
			d_port->sm_state = STATE_CONNECTED;
		} else if (event == EVT_DISCONNECTED) {
			usb_gadget_autopm_get(d_port->gadget);
			ipa_disconnect_work_handler(d_port);
			ipa_data_path_disable(d_port);
			ipa_disconnect_channel(d_port);
			d_port->sm_state = STATE_INITIALIZED;
			log_event_dbg("%s: ST_SUS_EVT_DIS", __func__);
			usb_gadget_autopm_put_async(d_port->gadget);
@@ -832,7 +887,7 @@ static void ipa_work_handler(struct work_struct *w)

	if (peek_event(d_port) != EVT_NONE) {
		log_event_dbg("%s: New events to process", __func__);
		queue_work(d_port->ipa_usb_wq, &d_port->usb_ipa_w);
		queue_delayed_work(d_port->ipa_usb_wq, &d_port->usb_ipa_w, 0);
	}
}

@@ -1521,7 +1576,7 @@ void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param)
		post_event(d_port, EVT_HOST_READY);
	}

	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
	queue_delayed_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w, 0);
}

static int queue_notification_request(struct f_gsi *gsi)
@@ -2098,7 +2153,7 @@ static int gsi_set_alt(struct usb_function *f, unsigned int intf,
	struct f_gsi	 *gsi = func_to_gsi(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	struct net_device	*net;
	int ret;
	int ret = 0;

	log_event_dbg("intf=%u, alt=%u", intf, alt);

@@ -2157,39 +2212,18 @@ static int gsi_set_alt(struct usb_function *f, unsigned int intf,
			gsi->d_port.ntb_info.ntb_input_size =
				MBIM_NTB_DEFAULT_IN_SIZE;
		if (alt == 1) {
			if (gsi->d_port.in_ep && !gsi->d_port.in_ep->desc
				&& config_ep_by_speed(cdev->gadget, f,
					gsi->d_port.in_ep)) {
				gsi->d_port.in_ep->desc = NULL;
				goto notify_ep_disable;
			}

			if (gsi->d_port.out_ep && !gsi->d_port.out_ep->desc
				&& config_ep_by_speed(cdev->gadget, f,
					gsi->d_port.out_ep)) {
				gsi->d_port.out_ep->desc = NULL;
				goto notify_ep_disable;
			}

			/* Configure EPs for GSI */
			if (gsi->d_port.in_ep) {
				if (gsi->prot_id == IPA_USB_DIAG)
					gsi->d_port.in_ep->ep_intr_num = 3;
				else
					gsi->d_port.in_ep->ep_intr_num = 2;
				usb_gsi_ep_op(gsi->d_port.in_ep,
					&gsi->d_port.in_request,
						GSI_EP_OP_CONFIG);
			}

			if (gsi->d_port.out_ep) {
			if (gsi->d_port.out_ep)
				gsi->d_port.out_ep->ep_intr_num = 1;
				usb_gsi_ep_op(gsi->d_port.out_ep,
					&gsi->d_port.out_request,
						GSI_EP_OP_CONFIG);
			}

			gsi->d_port.gadget = cdev->gadget;
			gsi->d_port.cdev = cdev;

			if (gsi->prot_id == IPA_USB_RNDIS) {
				gsi_rndis_open(gsi);
@@ -2206,23 +2240,26 @@ static int gsi_set_alt(struct usb_function *f, unsigned int intf,
			if (gsi->prot_id == IPA_USB_ECM)
				gsi->d_port.cdc_filter = DEFAULT_FILTER;

			post_event(&gsi->d_port, EVT_SET_ALT);
			/*
			 * For RNDIS the event is posted from the flow control
			 * handler which is invoked when the host sends the
			 * GEN_CURRENT_PACKET_FILTER message.
			 * delay until delayed status is returned to
			 * composite layer.
			 */
			if (gsi->prot_id != IPA_USB_RNDIS)
				post_event(&gsi->d_port,
						EVT_CONNECT_IN_PROGRESS);
			queue_work(gsi->d_port.ipa_usb_wq,
					&gsi->d_port.usb_ipa_w);
			queue_delayed_work(gsi->d_port.ipa_usb_wq,
					&gsi->d_port.usb_ipa_w,
					msecs_to_jiffies(1));
			ret = USB_GADGET_DELAYED_STATUS;
		}

		if (alt == 0 && ((gsi->d_port.in_ep &&
				!gsi->d_port.in_ep->driver_data) ||
				(gsi->d_port.out_ep &&
				!gsi->d_port.out_ep->driver_data)))
			ipa_disconnect_handler(&gsi->d_port);
				!gsi->d_port.out_ep->driver_data))) {
			post_event(&gsi->d_port, EVT_DISCONNECTED);
			queue_delayed_work(gsi->d_port.ipa_usb_wq,
						&gsi->d_port.usb_ipa_w, 0);
			log_event_dbg("%s: Disconnecting\n", __func__);
		}

		gsi->data_interface_up = alt;
		log_event_dbg("DATA_INTERFACE id = %d, status = %d",
@@ -2235,7 +2272,7 @@ static int gsi_set_alt(struct usb_function *f, unsigned int intf,
	if (gsi->prot_id == IPA_USB_DIAG)
		gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);

	return 0;
	return ret;

notify_ep_disable:
	if (gsi->c_port.notify && gsi->c_port.notify->driver_data)
@@ -2276,9 +2313,9 @@ static void gsi_disable(struct usb_function *f)
	gsi->data_interface_up = false;

	log_event_dbg("%s deactivated", gsi->function.name);
	ipa_disconnect_handler(&gsi->d_port);
	gsi->d_port.net_ready_trigger = false;
	post_event(&gsi->d_port, EVT_DISCONNECTED);
	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
	queue_delayed_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w, 0);
}

static void gsi_suspend(struct usb_function *f)
@@ -2296,7 +2333,7 @@ static void gsi_suspend(struct usb_function *f)
	usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
			GSI_EP_OP_SET_CLR_BLOCK_DBL);
	post_event(&gsi->d_port, EVT_SUSPEND);
	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
	queue_delayed_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w, 0);
	log_event_dbg("gsi suspended");
}

@@ -2331,7 +2368,7 @@ static void gsi_resume(struct usb_function *f)
		rndis_flow_control(gsi->params, false);

	post_event(&gsi->d_port, EVT_RESUMED);
	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
	queue_delayed_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w, 0);

	log_event_dbg("%s: completed", __func__);
}
@@ -3004,7 +3041,7 @@ static int gsi_bind_config(struct f_gsi *gsi)
	gsi->function.func_suspend = gsi_func_suspend;
	gsi->function.resume = gsi_resume;

	INIT_WORK(&gsi->d_port.usb_ipa_w, ipa_work_handler);
	INIT_DELAYED_WORK(&gsi->d_port.usb_ipa_w, ipa_work_handler);

	return status;
}
+7 −5
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
 */

#ifndef _F_GSI_H
@@ -68,8 +68,8 @@
#define EVT_NONE			0
#define EVT_UNINITIALIZED		1
#define EVT_INITIALIZED			2
#define EVT_CONNECT_IN_PROGRESS		3
#define EVT_CONNECTED			4
#define EVT_SET_ALT		3
#define EVT_IPA_READY			4
#define EVT_HOST_NRDY			5
#define EVT_HOST_READY			6
#define EVT_DISCONNECTED		7
@@ -102,8 +102,9 @@
enum connection_state {
	STATE_UNINITIALIZED,
	STATE_INITIALIZED,
	STATE_CONNECT_IN_PROGRESS,
	STATE_WAIT_FOR_IPA_RDY,
	STATE_CONNECTED,
	STATE_HOST_NRDY,
	STATE_DISCONNECTED,
	STATE_SUSPEND_IN_PROGRESS,
	STATE_SUSPENDED
@@ -223,6 +224,7 @@ struct gsi_data_port {
	struct usb_gsi_request in_request;
	struct usb_gsi_request out_request;
	struct usb_gadget *gadget;
	struct usb_composite_dev *cdev;
	int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *driver_data);
	struct ipa_usb_teth_params ipa_init_params;
	int in_channel_handle;
@@ -240,7 +242,7 @@ struct gsi_data_port {

	spinlock_t lock;

	struct work_struct usb_ipa_w;
	struct delayed_work usb_ipa_w;
	struct workqueue_struct *ipa_usb_wq;
	enum connection_state sm_state;
	struct event_queue evt_q;