Loading drivers/usb/gadget/function/f_gsi.c +155 −118 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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( Loading @@ -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( Loading @@ -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", Loading @@ -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: Loading @@ -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__); Loading @@ -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); Loading @@ -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); } } Loading Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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", Loading @@ -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) Loading Loading @@ -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) Loading @@ -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"); } Loading Loading @@ -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__); } Loading Loading @@ -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; } Loading drivers/usb/gadget/function/f_gsi.h +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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading Loading
drivers/usb/gadget/function/f_gsi.c +155 −118 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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) Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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( Loading @@ -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( Loading @@ -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", Loading @@ -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: Loading @@ -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__); Loading @@ -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); Loading @@ -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); } } Loading Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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", Loading @@ -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) Loading Loading @@ -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) Loading @@ -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"); } Loading Loading @@ -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__); } Loading Loading @@ -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; } Loading
drivers/usb/gadget/function/f_gsi.h +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 Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; Loading