diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 4d05d5cb3756..ed45212fc218 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -19,7 +19,7 @@ static struct gsi_inst_status { struct gsi_opts *opts; } inst_status[IPA_USB_MAX_TETH_PROT_SIZE]; -#define MAX_CDEV_INSTANCES 3 +#define MAX_CDEV_INSTANCES 4 static int major; static struct class *gsi_class; @@ -1533,6 +1533,7 @@ static long gsi_ctrl_dev_ioctl(struct file *fp, unsigned int cmd, atomic_set(&c_port->ctrl_online, 1); break; case QTI_CTRL_GET_LINE_STATE: + case GSI_MBIM_GPS_USB_STATUS: val = atomic_read(&gsi->connected); if (gsi->prot_id == IPA_USB_RMNET) val = gsi->rmnet_dtr_status; @@ -1742,6 +1743,8 @@ static int gsi_function_ctrl_port_init(struct f_gsi *gsi) strlcat(gsi->c_port.name, GSI_MBIM_CTRL_NAME, sz); else if (gsi->prot_id == IPA_USB_DIAG) strlcat(gsi->c_port.name, GSI_DPL_CTRL_NAME, sz); + else if (gsi->prot_id == IPA_USB_GPS) + strlcat(gsi->c_port.name, GSI_GPS_CTRL_NAME, sz); else ctrl_dev_create = false; @@ -2497,7 +2500,9 @@ static int gsi_set_alt(struct usb_function *f, unsigned int intf, atomic_set(&gsi->connected, 1); /* send 0 len pkt to qti to notify state change */ - if (gsi->prot_id == IPA_USB_DIAG) + if (gsi->prot_id == IPA_USB_DIAG || + gsi->prot_id == IPA_USB_GPS || + gsi->prot_id == IPA_USB_MBIM) gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0); return ret; @@ -2529,7 +2534,7 @@ static void gsi_disable(struct usb_function *f) } gsi_ctrl_clear_cpkt_queues(gsi, false); - /* send 0 len pkt to qti/qbi to notify state change */ + /* send 0 len pkt to qti/qbi/gps to notify state change */ gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0); gsi->c_port.notify_req_queued = false; /* Disable Data Path - only if it was initialized already (alt=1) */ @@ -2559,6 +2564,11 @@ static void gsi_suspend(struct usb_function *f) return; } + if (!gsi->data_interface_up) { + log_event_dbg("%s: suspend done\n", __func__); + return; + } + block_db = true; usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db, GSI_EP_OP_SET_CLR_BLOCK_DBL); @@ -2588,6 +2598,11 @@ static void gsi_resume(struct usb_function *f) /* Check any pending cpkt, and queue immediately on resume */ gsi_ctrl_send_notification(gsi); + if (!gsi->data_interface_up) { + log_event_dbg("%s: resume done\n", __func__); + return; + } + /* * Linux host does not send RNDIS_MSG_INIT or non-zero * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume. @@ -2849,9 +2864,11 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) goto fail; } - status = gsi->data_id = usb_interface_id(c, f); - if (status < 0) - goto fail; + if (gsi->prot_id != IPA_USB_GPS) { + status = gsi->data_id = usb_interface_id(c, f); + if (status < 0) + goto fail; + } switch (gsi->prot_id) { case IPA_USB_RNDIS: @@ -3137,6 +3154,18 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) info.in_req_num_buf = GSI_NUM_IN_BUFFERS; info.notify_buf_len = sizeof(struct usb_cdc_notification); break; + case IPA_USB_GPS: + info.string_defs = gps_string_defs; + info.ctrl_str_idx = 0; + info.ctrl_desc = &gps_interface_desc; + info.fs_notify_desc = &gps_fs_notify_desc; + info.hs_notify_desc = &gps_hs_notify_desc; + info.ss_notify_desc = &gps_ss_notify_desc; + info.fs_desc_hdr = gps_fs_function; + info.hs_desc_hdr = gps_hs_function; + info.ss_desc_hdr = gps_ss_function; + info.notify_buf_len = sizeof(struct usb_cdc_notification); + break; default: log_event_err("%s: Invalid prot id %d", __func__, gsi->prot_id); @@ -3147,6 +3176,9 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto dereg_rndis; + if (gsi->prot_id == IPA_USB_GPS) + goto skip_ipa_init; + status = ipa_register_ipa_ready_cb(ipa_ready_callback, gsi); if (!status) { log_event_info("%s: ipa is not ready", __func__); @@ -3172,6 +3204,7 @@ static int gsi_bind(struct usb_configuration *c, struct usb_function *f) gsi->d_port.sm_state = STATE_INITIALIZED; +skip_ipa_init: DBG(cdev, "%s: %s speed IN/%s OUT/%s NOTIFY/%s\n", f->name, gadget_is_superspeed(c->cdev->gadget) ? "super" : @@ -3265,6 +3298,10 @@ static int gsi_bind_config(struct f_gsi *gsi) gsi->function.name = "dpl"; gsi->function.strings = qdss_gsi_strings; break; + case IPA_USB_GPS: + gsi->function.name = "gps"; + gsi->function.strings = gps_strings; + break; default: log_event_err("%s: invalid prot id %d", __func__, prot_id); return -EINVAL; diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h index 8b6ae9697630..11de421a6e18 100644 --- a/drivers/usb/gadget/function/f_gsi.h +++ b/drivers/usb/gadget/function/f_gsi.h @@ -24,6 +24,7 @@ #define GSI_RMNET_CTRL_NAME "rmnet_ctrl" #define GSI_MBIM_CTRL_NAME "android_mbim" #define GSI_DPL_CTRL_NAME "dpl_ctrl" +#define GSI_GPS_CTRL_NAME "gps" #define GSI_CTRL_NAME_LEN (sizeof(GSI_MBIM_CTRL_NAME)+2) #define GSI_MAX_CTRL_PKT_SIZE 8192 #define GSI_CTRL_DTR (1 << 0) @@ -331,7 +332,8 @@ static enum ipa_usb_teth_prot name_to_prot_id(const char *name) return IPA_USB_MBIM; if (!strncasecmp(name, "dpl", strlen("dpl"))) return IPA_USB_DIAG; - + if (!strncasecmp(name, "gps", strlen("gps"))) + return IPA_USB_GPS; error: return -EINVAL; } @@ -1430,4 +1432,91 @@ static struct usb_gadget_strings *qdss_gsi_strings[] = { &qdss_gsi_string_table, NULL, }; + +/* gps device descriptor */ +static struct usb_interface_descriptor gps_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + +/* Full speed support */ +static struct usb_endpoint_descriptor gps_fs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(MAX_NOTIFY_SIZE), + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, +}; + +static struct usb_descriptor_header *gps_fs_function[] = { + (struct usb_descriptor_header *) &gps_interface_desc, + (struct usb_descriptor_header *) &gps_fs_notify_desc, + NULL, +}; + +/* High speed support */ +static struct usb_endpoint_descriptor gps_hs_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(MAX_NOTIFY_SIZE), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; + +static struct usb_descriptor_header *gps_hs_function[] = { + (struct usb_descriptor_header *) &gps_interface_desc, + (struct usb_descriptor_header *) &gps_hs_notify_desc, + NULL, +}; + +/* Super speed support */ +static struct usb_endpoint_descriptor gps_ss_notify_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(MAX_NOTIFY_SIZE), + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, +}; + +static struct usb_ss_ep_comp_descriptor gps_ss_notify_comp_desc = { + .bLength = sizeof(gps_ss_notify_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 3 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(MAX_NOTIFY_SIZE), +}; + +static struct usb_descriptor_header *gps_ss_function[] = { + (struct usb_descriptor_header *) &gps_interface_desc, + (struct usb_descriptor_header *) &gps_ss_notify_desc, + (struct usb_descriptor_header *) &gps_ss_notify_comp_desc, + NULL, +}; + +/* String descriptors */ + +static struct usb_string gps_string_defs[] = { + [0].s = "GPS", + { } /* end of list */ +}; + +static struct usb_gadget_strings gps_string_table = { + .language = 0x0409, /* en-us */ + .strings = gps_string_defs, +}; + +static struct usb_gadget_strings *gps_strings[] = { + &gps_string_table, + NULL, +}; #endif diff --git a/include/linux/ipa_usb.h b/include/linux/ipa_usb.h index 0ec03d7fddc9..c0c250e4709a 100644 --- a/include/linux/ipa_usb.h +++ b/include/linux/ipa_usb.h @@ -19,6 +19,7 @@ enum ipa_usb_teth_prot { IPA_USB_MBIM = 3, IPA_USB_DIAG = 4, IPA_USB_RMNET_CV2X = 5, + IPA_USB_GPS = 6, IPA_USB_MAX_TETH_PROT_SIZE };