Merge "usb: gadget: f_qdss: Add support for mdm qdss channel"
This commit is contained in:
commit
cb08d85f02
3 changed files with 123 additions and 26 deletions
|
@ -183,15 +183,28 @@ static struct usb_qdss_opts *to_fi_usb_qdss_opts(struct usb_function_instance *f
|
||||||
}
|
}
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
|
|
||||||
static void qdss_ctrl_write_complete(struct usb_ep *ep,
|
static void qdss_write_complete(struct usb_ep *ep,
|
||||||
struct usb_request *req)
|
struct usb_request *req)
|
||||||
{
|
{
|
||||||
struct f_qdss *qdss = ep->driver_data;
|
struct f_qdss *qdss = ep->driver_data;
|
||||||
struct qdss_request *d_req = req->context;
|
struct qdss_request *d_req = req->context;
|
||||||
|
struct usb_ep *in;
|
||||||
|
struct list_head *list_pool;
|
||||||
|
enum qdss_state state;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
pr_debug("qdss_ctrl_write_complete\n");
|
pr_debug("qdss_ctrl_write_complete\n");
|
||||||
|
|
||||||
|
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) {
|
if (!req->status) {
|
||||||
/* send zlp */
|
/* send zlp */
|
||||||
if ((req->length >= ep->maxpacket) &&
|
if ((req->length >= ep->maxpacket) &&
|
||||||
|
@ -199,13 +212,13 @@ static void qdss_ctrl_write_complete(struct usb_ep *ep,
|
||||||
req->length = 0;
|
req->length = 0;
|
||||||
d_req->actual = req->actual;
|
d_req->actual = req->actual;
|
||||||
d_req->status = req->status;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&qdss->lock, flags);
|
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) {
|
if (req->length != 0) {
|
||||||
d_req->actual = req->actual;
|
d_req->actual = req->actual;
|
||||||
d_req->status = req->status;
|
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);
|
spin_unlock_irqrestore(&qdss->lock, flags);
|
||||||
|
|
||||||
if (qdss->ch.notify)
|
if (qdss->ch.notify)
|
||||||
qdss->ch.notify(qdss->ch.priv, USB_QDSS_CTRL_WRITE_DONE, d_req,
|
qdss->ch.notify(qdss->ch.priv, state, d_req, NULL);
|
||||||
NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qdss_ctrl_read_complete(struct usb_ep *ep,
|
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;
|
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) {
|
list_for_each_safe(act, tmp, &qdss->ctrl_write_pool) {
|
||||||
req = list_entry(act, struct usb_request, list);
|
req = list_entry(act, struct usb_request, list);
|
||||||
list_del(&req->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 f_qdss *qdss = ch->priv_usb;
|
||||||
struct usb_request *req;
|
struct usb_request *req;
|
||||||
|
struct usb_ep *in;
|
||||||
|
struct list_head *list_pool;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
pr_debug("usb_qdss_alloc_req\n");
|
pr_debug("usb_qdss_alloc_req\n");
|
||||||
|
|
||||||
if (no_write_buf <= 0 || no_read_buf <= 0 || !qdss) {
|
if (!qdss) {
|
||||||
|
pr_err("usb_qdss_alloc_req: channel %s closed\n", 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("usb_qdss_alloc_req: missing params\n");
|
pr_err("usb_qdss_alloc_req: missing params\n");
|
||||||
return -ENODEV;
|
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++) {
|
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) {
|
if (!req) {
|
||||||
pr_err("usb_qdss_alloc_req: ctrl_in allocation err\n");
|
pr_err("usb_qdss_alloc_req: ctrl_in allocation err\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
req->complete = qdss_ctrl_write_complete;
|
req->complete = qdss_write_complete;
|
||||||
list_add_tail(&req->list, &qdss->ctrl_write_pool);
|
list_add_tail(&req->list, list_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < no_read_buf; i++) {
|
for (i = 0; i < no_read_buf; i++) {
|
||||||
|
@ -479,21 +515,20 @@ static void usb_qdss_disconnect_work(struct work_struct *work)
|
||||||
qdss = container_of(work, struct f_qdss, disconnect_w);
|
qdss = container_of(work, struct f_qdss, disconnect_w);
|
||||||
pr_debug("usb_qdss_disconnect_work\n");
|
pr_debug("usb_qdss_disconnect_work\n");
|
||||||
|
|
||||||
/*
|
|
||||||
* Uninitialized init data i.e. ep specific operation.
|
/* Notify qdss to cancel all active transfers */
|
||||||
* Notify qdss to cancel all active transfers.
|
if (qdss->ch.notify)
|
||||||
*/
|
qdss->ch.notify(qdss->ch.priv,
|
||||||
if (qdss->ch.app_conn) {
|
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);
|
status = uninit_data(qdss->port.data);
|
||||||
if (status)
|
if (status)
|
||||||
pr_err("%s: uninit_data error\n", __func__);
|
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);
|
status = set_qdss_data_connection(qdss, 0);
|
||||||
if (status)
|
if (status)
|
||||||
pr_err("qdss_disconnect error");
|
pr_err("qdss_disconnect error");
|
||||||
|
@ -550,15 +585,16 @@ static void usb_qdss_connect_work(struct work_struct *work)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_debug("usb_qdss_connect_work\n");
|
pr_debug("usb_qdss_connect_work\n");
|
||||||
|
|
||||||
|
if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM))
|
||||||
|
goto notify;
|
||||||
|
|
||||||
status = set_qdss_data_connection(qdss, 1);
|
status = set_qdss_data_connection(qdss, 1);
|
||||||
if (status) {
|
if (status) {
|
||||||
pr_err("set_qdss_data_connection error(%d)", status);
|
pr_err("set_qdss_data_connection error(%d)", status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qdss->ch.notify)
|
|
||||||
qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT,
|
|
||||||
NULL, &qdss->ch);
|
|
||||||
spin_lock_irqsave(&qdss->lock, flags);
|
spin_lock_irqsave(&qdss->lock, flags);
|
||||||
req = qdss->endless_req;
|
req = qdss->endless_req;
|
||||||
spin_unlock_irqrestore(&qdss->lock, flags);
|
spin_unlock_irqrestore(&qdss->lock, flags);
|
||||||
|
@ -566,8 +602,15 @@ static void usb_qdss_connect_work(struct work_struct *work)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
status = usb_ep_queue(qdss->port.data, req, GFP_ATOMIC);
|
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);
|
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 intf, unsigned alt)
|
static int qdss_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||||
|
@ -706,6 +749,7 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name)
|
||||||
spin_lock_init(&qdss->lock);
|
spin_lock_init(&qdss->lock);
|
||||||
INIT_LIST_HEAD(&qdss->ctrl_read_pool);
|
INIT_LIST_HEAD(&qdss->ctrl_read_pool);
|
||||||
INIT_LIST_HEAD(&qdss->ctrl_write_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->connect_w, usb_qdss_connect_work);
|
||||||
INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work);
|
INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work);
|
||||||
|
|
||||||
|
@ -801,6 +845,50 @@ int usb_qdss_ctrl_write(struct usb_qdss_ch *ch, struct qdss_request *d_req)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(usb_qdss_ctrl_write);
|
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,
|
struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
|
||||||
void (*notify)(void *, unsigned, struct qdss_request *,
|
void (*notify)(void *, unsigned, struct qdss_request *,
|
||||||
struct usb_qdss_ch *))
|
struct usb_qdss_ch *))
|
||||||
|
@ -859,7 +947,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
|
||||||
pr_debug("usb_qdss_close\n");
|
pr_debug("usb_qdss_close\n");
|
||||||
|
|
||||||
spin_lock_irqsave(&qdss_lock, flags);
|
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;
|
ch->app_conn = 0;
|
||||||
spin_unlock_irqrestore(&qdss_lock, flags);
|
spin_unlock_irqrestore(&qdss_lock, flags);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -53,6 +53,10 @@ struct f_qdss {
|
||||||
struct usb_qdss_ch ch;
|
struct usb_qdss_ch ch;
|
||||||
struct list_head ctrl_read_pool;
|
struct list_head ctrl_read_pool;
|
||||||
struct list_head ctrl_write_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 connect_w;
|
||||||
struct work_struct disconnect_w;
|
struct work_struct disconnect_w;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
|
/* Copyright (c) 2012-2013, 2017, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -15,6 +15,9 @@
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
|
#define USB_QDSS_CH_MDM "qdss_mdm"
|
||||||
|
#define USB_QDSS_CH_MSM "qdss"
|
||||||
|
|
||||||
struct qdss_request {
|
struct qdss_request {
|
||||||
char *buf;
|
char *buf;
|
||||||
int length;
|
int length;
|
||||||
|
|
Loading…
Add table
Reference in a new issue