usb: gadget: f_qdss: Add support for mdm qdss channel

Driver needs to pass qdss data received from mdm qdss
bridge driver to host PC. This requires a software data
path using IN endpoint. Bridge driver needs to open
qdss_mdm channel and use exported write() API to pass
the qdss data received from mdm. Driver calls the notify
call back upon write completion.

Change-Id: I4d8ceaed0bf9c85aa17d3f49503cd690917cc117
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
This commit is contained in:
Hemant Kumar 2017-03-02 15:30:08 -08:00
parent c745d32a3d
commit 5d13333ba0
3 changed files with 126 additions and 25 deletions

View File

@ -183,15 +183,28 @@ struct usb_qdss_opts *to_fi_usb_qdss_opts(struct usb_function_instance *fi)
}
/*----------------------------------------------------------------------*/
static void qdss_ctrl_write_complete(struct usb_ep *ep,
static void qdss_write_complete(struct usb_ep *ep,
struct usb_request *req)
{
struct f_qdss *qdss = ep->driver_data;
struct qdss_request *d_req = req->context;
struct usb_ep *in;
struct list_head *list_pool;
enum qdss_state state;
unsigned long flags;
pr_debug("%s\n", __func__);
if (qdss->debug_inface_enabled) {
in = qdss->port.ctrl_in;
list_pool = &qdss->ctrl_write_pool;
state = USB_QDSS_CTRL_WRITE_DONE;
} else {
in = qdss->port.data;
list_pool = &qdss->data_write_pool;
state = USB_QDSS_DATA_WRITE_DONE;
}
if (!req->status) {
/* send zlp */
if ((req->length >= ep->maxpacket) &&
@ -199,13 +212,13 @@ static void qdss_ctrl_write_complete(struct usb_ep *ep,
req->length = 0;
d_req->actual = req->actual;
d_req->status = req->status;
if (!usb_ep_queue(qdss->port.ctrl_in, req, GFP_ATOMIC))
if (!usb_ep_queue(in, req, GFP_ATOMIC))
return;
}
}
spin_lock_irqsave(&qdss->lock, flags);
list_add_tail(&req->list, &qdss->ctrl_write_pool);
list_add_tail(&req->list, list_pool);
if (req->length != 0) {
d_req->actual = req->actual;
d_req->status = req->status;
@ -213,8 +226,7 @@ static void qdss_ctrl_write_complete(struct usb_ep *ep,
spin_unlock_irqrestore(&qdss->lock, flags);
if (qdss->ch.notify)
qdss->ch.notify(qdss->ch.priv, USB_QDSS_CTRL_WRITE_DONE, d_req,
NULL);
qdss->ch.notify(qdss->ch.priv, state, d_req, NULL);
}
static void qdss_ctrl_read_complete(struct usb_ep *ep,
@ -252,6 +264,12 @@ void usb_qdss_free_req(struct usb_qdss_ch *ch)
return;
}
list_for_each_safe(act, tmp, &qdss->data_write_pool) {
req = list_entry(act, struct usb_request, list);
list_del(&req->list);
usb_ep_free_request(qdss->port.data, req);
}
list_for_each_safe(act, tmp, &qdss->ctrl_write_pool) {
req = list_entry(act, struct usb_request, list);
list_del(&req->list);
@ -271,23 +289,41 @@ int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf,
{
struct f_qdss *qdss = ch->priv_usb;
struct usb_request *req;
struct usb_ep *in;
struct list_head *list_pool;
int i;
pr_debug("%s\n", __func__);
if (no_write_buf <= 0 || no_read_buf <= 0 || !qdss) {
if (!qdss) {
pr_err("%s: %s closed\n", __func__, ch->name);
return -ENODEV;
}
if ((qdss->debug_inface_enabled &&
(no_write_buf <= 0 || no_read_buf <= 0)) ||
(!qdss->debug_inface_enabled &&
(no_write_buf <= 0 || no_read_buf))) {
pr_err("%s: missing params\n", __func__);
return -ENODEV;
}
if (qdss->debug_inface_enabled) {
in = qdss->port.ctrl_in;
list_pool = &qdss->ctrl_write_pool;
} else {
in = qdss->port.data;
list_pool = &qdss->data_write_pool;
}
for (i = 0; i < no_write_buf; i++) {
req = usb_ep_alloc_request(qdss->port.ctrl_in, GFP_ATOMIC);
req = usb_ep_alloc_request(in, GFP_ATOMIC);
if (!req) {
pr_err("%s: ctrl_in allocation err\n", __func__);
goto fail;
}
req->complete = qdss_ctrl_write_complete;
list_add_tail(&req->list, &qdss->ctrl_write_pool);
req->complete = qdss_write_complete;
list_add_tail(&req->list, list_pool);
}
for (i = 0; i < no_read_buf; i++) {
@ -378,6 +414,10 @@ static int qdss_bind(struct usb_configuration *c, struct usb_function *f)
qdss_ctrl_intf_desc.iInterface = id;
}
/* for non-accelerated path keep tx fifo size 1k */
if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM))
qdss_data_ep_comp_desc.bMaxBurst = 0;
ep = usb_ep_autoconfig_ss(gadget, &qdss_ss_data_desc,
&qdss_data_ep_comp_desc);
if (!ep) {
@ -491,21 +531,20 @@ static void usb_qdss_disconnect_work(struct work_struct *work)
qdss = container_of(work, struct f_qdss, disconnect_w);
pr_debug("%s\n", __func__);
/*
* Uninitialized init data i.e. ep specific operation.
* Notify qdss to cancel all active transfers.
*/
if (qdss->ch.app_conn) {
/* Notify qdss to cancel all active transfers */
if (qdss->ch.notify)
qdss->ch.notify(qdss->ch.priv,
USB_QDSS_DISCONNECT,
NULL,
NULL);
/* Uninitialized init data i.e. ep specific operation */
if (qdss->ch.app_conn && !strcmp(qdss->ch.name, USB_QDSS_CH_MSM)) {
status = uninit_data(qdss->port.data);
if (status)
pr_err("%s: uninit_data error\n", __func__);
if (qdss->ch.notify)
qdss->ch.notify(qdss->ch.priv,
USB_QDSS_DISCONNECT,
NULL,
NULL);
status = set_qdss_data_connection(qdss, 0);
if (status)
pr_err("qdss_disconnect error");
@ -562,15 +601,16 @@ static void usb_qdss_connect_work(struct work_struct *work)
}
pr_debug("%s\n", __func__);
if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM))
goto notify;
status = set_qdss_data_connection(qdss, 1);
if (status) {
pr_err("set_qdss_data_connection error(%d)", status);
return;
}
if (qdss->ch.notify)
qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT,
NULL, &qdss->ch);
spin_lock_irqsave(&qdss->lock, flags);
req = qdss->endless_req;
spin_unlock_irqrestore(&qdss->lock, flags);
@ -578,8 +618,15 @@ static void usb_qdss_connect_work(struct work_struct *work)
return;
status = usb_ep_queue(qdss->port.data, req, GFP_ATOMIC);
if (status)
if (status) {
pr_err("%s: usb_ep_queue error (%d)\n", __func__, status);
return;
}
notify:
if (qdss->ch.notify)
qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT,
NULL, &qdss->ch);
}
static int qdss_set_alt(struct usb_function *f, unsigned int intf,
@ -722,6 +769,7 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name)
spin_lock_init(&qdss->lock);
INIT_LIST_HEAD(&qdss->ctrl_read_pool);
INIT_LIST_HEAD(&qdss->ctrl_write_pool);
INIT_LIST_HEAD(&qdss->data_write_pool);
INIT_WORK(&qdss->connect_w, usb_qdss_connect_work);
INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work);
@ -817,6 +865,50 @@ int usb_qdss_ctrl_write(struct usb_qdss_ch *ch, struct qdss_request *d_req)
}
EXPORT_SYMBOL(usb_qdss_ctrl_write);
int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req)
{
struct f_qdss *qdss = ch->priv_usb;
unsigned long flags;
struct usb_request *req = NULL;
pr_debug("usb_qdss_ctrl_write\n");
if (!qdss)
return -ENODEV;
spin_lock_irqsave(&qdss->lock, flags);
if (qdss->usb_connected == 0) {
spin_unlock_irqrestore(&qdss->lock, flags);
return -EIO;
}
if (list_empty(&qdss->data_write_pool)) {
pr_err("error: usb_qdss_data_write list is empty\n");
spin_unlock_irqrestore(&qdss->lock, flags);
return -EAGAIN;
}
req = list_first_entry(&qdss->data_write_pool, struct usb_request,
list);
list_del(&req->list);
spin_unlock_irqrestore(&qdss->lock, flags);
req->buf = d_req->buf;
req->length = d_req->length;
req->context = d_req;
if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) {
spin_lock_irqsave(&qdss->lock, flags);
list_add_tail(&req->list, &qdss->data_write_pool);
spin_unlock_irqrestore(&qdss->lock, flags);
pr_err("qdss usb_ep_queue failed\n");
return -EIO;
}
return 0;
}
EXPORT_SYMBOL(usb_qdss_write);
struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
void (*notify)(void *priv, unsigned int event,
struct qdss_request *d_req, struct usb_qdss_ch *))
@ -874,7 +966,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
pr_debug("%s\n", __func__);
spin_lock_irqsave(&qdss_lock, flags);
if (!qdss || !qdss->usb_connected) {
ch->priv_usb = NULL;
if (!qdss || !qdss->usb_connected ||
!strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) {
ch->app_conn = 0;
spin_unlock_irqrestore(&qdss_lock, flags);
return;

View File

@ -59,6 +59,10 @@ struct f_qdss {
struct usb_qdss_ch ch;
struct list_head ctrl_read_pool;
struct list_head ctrl_write_pool;
/* for mdm channel SW path */
struct list_head data_write_pool;
struct work_struct connect_w;
struct work_struct disconnect_w;
spinlock_t lock;

View File

@ -15,6 +15,9 @@
#include <linux/kernel.h>
#define USB_QDSS_CH_MDM "qdss_mdm"
#define USB_QDSS_CH_MSM "qdss"
struct qdss_request {
char *buf;
int length;