From 33be9e30f19bcf899bf933853b9a9f4eee434451 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Thu, 15 Feb 2018 18:48:27 -0800 Subject: [PATCH 1/2] usb: gadget: f_gsi: Add support for gps function driver gps usb functionality is implemented using qmi packets. User space entity sends and receives QMI packets using misc device named /dev/gps. Change-Id: I73af1caa9b272597909199022739390274a4b034 Signed-off-by: Hemant Kumar Signed-off-by: Elson Roy Serrao --- drivers/usb/gadget/function/f_gsi.c | 42 +++++++++++-- drivers/usb/gadget/function/f_gsi.h | 91 ++++++++++++++++++++++++++++- include/linux/ipa_usb.h | 1 + 3 files changed, 129 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 4d05d5cb3756..52f5835e60f6 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; @@ -1742,6 +1742,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; @@ -2559,6 +2561,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 +2595,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 +2861,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 +3151,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 +3173,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 +3201,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 +3295,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 }; From 640176fca2fcc846a72b4475fac0896fb2d13f8d Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 31 Jan 2019 10:53:23 -0800 Subject: [PATCH 2/2] usb: f_gsi: Send zero length packet for MBIM/GPS on cable connect Qmuxd bridge daemon is opening /dev/gps before USB enumerated with USB composition having GPS interface as part of it. This daemon performs EP_LOOKUP ioctl and encounters failure. It keeps retrying for some iterations, and later goes into sleep and doesn't perform any IOs. This results into no GPS related IOs between daemon and host side QMUXD driver. Hence this change sends zero length packet for MBIM (as QBI also faces same issue) and GPS interface when USB cable is connected and those interfaces are part of USB composition. Both QBI and Qmuxd bridge daemon are now wait for zero length packet as indication for change in USB cable status, and queries using GSI_MBIM_GPS_USB_STATUS IOCTL (newly added). Based on this IOCTL related response, both daemons performs read/write with created control devices. Change-Id: Ib47443fe0e473ab4e27facb3f2927b8c13966158 Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_gsi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 52f5835e60f6..ed45212fc218 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -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; @@ -2499,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; @@ -2531,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) */