usb: gadget: f_gsi: Fix NULL ptr dereference

When usb composition switch happens upon cable disconnect and
function bind fails, function's config pointer is set to NULL.
This is resulting in to NULL ptr dereference when config is
accessed from APIs which are called asynchronously and out
side of the usb core driver(i.e. dwc3) context. Fix the
issue by directly accessing gadget pointer from gsi driver
context in those APIs.

Change-Id: I1006881ae1838e8ddc8fa5e9ef501f4c658b54e7
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
This commit is contained in:
Hemant Kumar 2017-02-21 15:49:21 -08:00
parent 74efdfc1b9
commit e45656bfce
2 changed files with 30 additions and 36 deletions

View file

@ -148,15 +148,10 @@ int gsi_wakeup_host(struct f_gsi *gsi)
struct usb_function *func; struct usb_function *func;
func = &gsi->function; func = &gsi->function;
gadget = gsi->function.config->cdev->gadget; gadget = gsi->gadget;
log_event_dbg("Entering %s", __func__); log_event_dbg("Entering %s", __func__);
if (!gadget) {
log_event_err("FAILED: d_port->cdev->gadget == NULL");
return -ENODEV;
}
/* /*
* In Super-Speed mode, remote wakeup is not allowed for suspended * In Super-Speed mode, remote wakeup is not allowed for suspended
* functions which have been disallowed by the host to issue Function * functions which have been disallowed by the host to issue Function
@ -276,7 +271,7 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
&d_port->ipa_out_channel_params; &d_port->ipa_out_channel_params;
struct ipa_usb_xdci_connect_params *conn_params = struct ipa_usb_xdci_connect_params *conn_params =
&d_port->ipa_conn_pms; &d_port->ipa_conn_pms;
struct usb_composite_dev *cdev = gsi->function.config->cdev; struct usb_gadget *gadget = gsi->gadget;
struct gsi_channel_info gsi_channel_info; struct gsi_channel_info gsi_channel_info;
struct ipa_req_chan_out_params ipa_in_channel_out_params; struct ipa_req_chan_out_params ipa_in_channel_out_params;
struct ipa_req_chan_out_params ipa_out_channel_out_params; struct ipa_req_chan_out_params ipa_out_channel_out_params;
@ -366,7 +361,7 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
/* Populate connection params */ /* Populate connection params */
conn_params->max_pkt_size = conn_params->max_pkt_size =
(cdev->gadget->speed == USB_SPEED_SUPER) ? (gadget->speed == USB_SPEED_SUPER) ?
IPA_USB_SUPER_SPEED_1024B : IPA_USB_HIGH_SPEED_512B; IPA_USB_SUPER_SPEED_1024B : IPA_USB_HIGH_SPEED_512B;
conn_params->ipa_to_usb_xferrscidx = conn_params->ipa_to_usb_xferrscidx =
d_port->in_xfer_rsc_index; d_port->in_xfer_rsc_index;
@ -392,7 +387,7 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
conn_params->teth_prot_params.max_packet_number_to_dev = conn_params->teth_prot_params.max_packet_number_to_dev =
DEFAULT_MAX_PKT_PER_XFER; DEFAULT_MAX_PKT_PER_XFER;
conn_params->max_supported_bandwidth_mbps = conn_params->max_supported_bandwidth_mbps =
(cdev->gadget->speed == USB_SPEED_SUPER) ? 3600 : 400; (gadget->speed == USB_SPEED_SUPER) ? 3600 : 400;
memset(&ipa_in_channel_out_params, 0x0, memset(&ipa_in_channel_out_params, 0x0,
sizeof(ipa_in_channel_out_params)); sizeof(ipa_in_channel_out_params));
@ -604,12 +599,12 @@ static void ipa_work_handler(struct work_struct *w)
{ {
struct gsi_data_port *d_port = container_of(w, struct gsi_data_port, struct gsi_data_port *d_port = container_of(w, struct gsi_data_port,
usb_ipa_w); usb_ipa_w);
struct f_gsi *gsi = d_port_to_gsi(d_port);
u8 event; u8 event;
int ret = 0; int ret = 0;
struct usb_gadget *gadget = d_port->gadget; struct usb_gadget *gadget = gsi->gadget;
struct device *dev; struct device *dev;
struct device *gad_dev; struct device *gad_dev;
struct f_gsi *gsi;
event = read_event(d_port); event = read_event(d_port);
@ -636,7 +631,7 @@ static void ipa_work_handler(struct work_struct *w)
break; break;
case STATE_INITIALIZED: case STATE_INITIALIZED:
if (event == EVT_CONNECT_IN_PROGRESS) { if (event == EVT_CONNECT_IN_PROGRESS) {
usb_gadget_autopm_get(d_port->gadget); usb_gadget_autopm_get(gadget);
log_event_dbg("%s: get = %d", __func__, log_event_dbg("%s: get = %d", __func__,
atomic_read(&gad_dev->power.usage_count)); atomic_read(&gad_dev->power.usage_count));
/* allocate buffers used with each TRB */ /* allocate buffers used with each TRB */
@ -661,7 +656,7 @@ static void ipa_work_handler(struct work_struct *w)
* EVT_HOST_READY is posted to the state machine * EVT_HOST_READY is posted to the state machine
* in the handler for this msg. * in the handler for this msg.
*/ */
usb_gadget_autopm_get(d_port->gadget); usb_gadget_autopm_get(gadget);
log_event_dbg("%s: get = %d", __func__, log_event_dbg("%s: get = %d", __func__,
atomic_read(&gad_dev->power.usage_count)); atomic_read(&gad_dev->power.usage_count));
/* allocate buffers used with each TRB */ /* allocate buffers used with each TRB */
@ -716,7 +711,7 @@ static void ipa_work_handler(struct work_struct *w)
read_event(d_port); read_event(d_port);
ipa_disconnect_work_handler(d_port); ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED; d_port->sm_state = STATE_INITIALIZED;
usb_gadget_autopm_put_async(d_port->gadget); usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS_DIS", log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS_DIS",
__func__); __func__);
log_event_dbg("%s: put_async1 = %d", __func__, log_event_dbg("%s: put_async1 = %d", __func__,
@ -726,7 +721,7 @@ static void ipa_work_handler(struct work_struct *w)
} }
ret = ipa_suspend_work_handler(d_port); ret = ipa_suspend_work_handler(d_port);
if (!ret) { if (!ret) {
usb_gadget_autopm_put_async(d_port->gadget); usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS", log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS",
__func__); __func__);
log_event_dbg("%s: put_async2 = %d", __func__, log_event_dbg("%s: put_async2 = %d", __func__,
@ -736,7 +731,7 @@ static void ipa_work_handler(struct work_struct *w)
} else if (event == EVT_DISCONNECTED) { } else if (event == EVT_DISCONNECTED) {
ipa_disconnect_work_handler(d_port); ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED; d_port->sm_state = STATE_INITIALIZED;
usb_gadget_autopm_put_async(d_port->gadget); usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_IN_PROG_EVT_DIS", log_event_dbg("%s: ST_CON_IN_PROG_EVT_DIS",
__func__); __func__);
log_event_dbg("%s: put_async3 = %d", log_event_dbg("%s: put_async3 = %d",
@ -760,7 +755,7 @@ static void ipa_work_handler(struct work_struct *w)
ipa_disconnect_work_handler(d_port); ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED; d_port->sm_state = STATE_INITIALIZED;
usb_gadget_autopm_put_async(d_port->gadget); usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_EVT_DIS", __func__); log_event_dbg("%s: ST_CON_EVT_DIS", __func__);
log_event_dbg("%s: put_async4 = %d", log_event_dbg("%s: put_async4 = %d",
__func__, atomic_read( __func__, atomic_read(
@ -770,7 +765,7 @@ static void ipa_work_handler(struct work_struct *w)
read_event(d_port); read_event(d_port);
ipa_disconnect_work_handler(d_port); ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED; d_port->sm_state = STATE_INITIALIZED;
usb_gadget_autopm_put_async(d_port->gadget); usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_EVT_SUS_DIS", log_event_dbg("%s: ST_CON_EVT_SUS_DIS",
__func__); __func__);
log_event_dbg("%s: put_async5 = %d", log_event_dbg("%s: put_async5 = %d",
@ -780,7 +775,7 @@ static void ipa_work_handler(struct work_struct *w)
} }
ret = ipa_suspend_work_handler(d_port); ret = ipa_suspend_work_handler(d_port);
if (!ret) { if (!ret) {
usb_gadget_autopm_put_async(d_port->gadget); usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_EVT_SUS", log_event_dbg("%s: ST_CON_EVT_SUS",
__func__); __func__);
log_event_dbg("%s: put_async6 = %d", log_event_dbg("%s: put_async6 = %d",
@ -805,7 +800,7 @@ static void ipa_work_handler(struct work_struct *w)
case STATE_SUSPEND_IN_PROGRESS: case STATE_SUSPEND_IN_PROGRESS:
if (event == EVT_IPA_SUSPEND) { if (event == EVT_IPA_SUSPEND) {
d_port->sm_state = STATE_SUSPENDED; d_port->sm_state = STATE_SUSPENDED;
usb_gadget_autopm_put_async(d_port->gadget); usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_SUS_IN_PROG_EVT_IPA_SUS", log_event_dbg("%s: ST_SUS_IN_PROG_EVT_IPA_SUS",
__func__); __func__);
log_event_dbg("%s: put_async6 = %d", log_event_dbg("%s: put_async6 = %d",
@ -820,7 +815,7 @@ static void ipa_work_handler(struct work_struct *w)
* after IPA disconnect is done in disconnect work * after IPA disconnect is done in disconnect work
* (due to cable disconnect) or in suspended state. * (due to cable disconnect) or in suspended state.
*/ */
usb_gadget_autopm_get_noresume(d_port->gadget); usb_gadget_autopm_get_noresume(gadget);
log_event_dbg("%s: ST_SUS_IN_PROG_EVT_RES", __func__); log_event_dbg("%s: ST_SUS_IN_PROG_EVT_RES", __func__);
log_event_dbg("%s: get_nores1 = %d", __func__, log_event_dbg("%s: get_nores1 = %d", __func__,
atomic_read( atomic_read(
@ -828,7 +823,7 @@ static void ipa_work_handler(struct work_struct *w)
} else if (event == EVT_DISCONNECTED) { } else if (event == EVT_DISCONNECTED) {
ipa_disconnect_work_handler(d_port); ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED; d_port->sm_state = STATE_INITIALIZED;
usb_gadget_autopm_put_async(d_port->gadget); usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_SUS_IN_PROG_EVT_DIS", __func__); log_event_dbg("%s: ST_SUS_IN_PROG_EVT_DIS", __func__);
log_event_dbg("%s: put_async7 = %d", __func__, log_event_dbg("%s: put_async7 = %d", __func__,
atomic_read( atomic_read(
@ -838,7 +833,7 @@ static void ipa_work_handler(struct work_struct *w)
case STATE_SUSPENDED: case STATE_SUSPENDED:
if (event == EVT_RESUMED) { if (event == EVT_RESUMED) {
usb_gadget_autopm_get(d_port->gadget); usb_gadget_autopm_get(gadget);
log_event_dbg("%s: ST_SUS_EVT_RES", __func__); log_event_dbg("%s: ST_SUS_EVT_RES", __func__);
log_event_dbg("%s: get = %d", __func__, log_event_dbg("%s: get = %d", __func__,
atomic_read(&gad_dev->power.usage_count)); atomic_read(&gad_dev->power.usage_count));
@ -1425,7 +1420,6 @@ static int gsi_ctrl_send_notification(struct f_gsi *gsi)
__le32 *data; __le32 *data;
struct usb_cdc_notification *event; struct usb_cdc_notification *event;
struct usb_request *req = gsi->c_port.notify_req; struct usb_request *req = gsi->c_port.notify_req;
struct usb_composite_dev *cdev = gsi->function.config->cdev;
struct gsi_ctrl_pkt *cpkt; struct gsi_ctrl_pkt *cpkt;
unsigned long flags; unsigned long flags;
bool del_free_cpkt = false; bool del_free_cpkt = false;
@ -1472,11 +1466,11 @@ static int gsi_ctrl_send_notification(struct f_gsi *gsi)
/* SPEED_CHANGE data is up/down speeds in bits/sec */ /* SPEED_CHANGE data is up/down speeds in bits/sec */
data = req->buf + sizeof(*event); data = req->buf + sizeof(*event);
data[0] = cpu_to_le32(gsi_xfer_bitrate(cdev->gadget)); data[0] = cpu_to_le32(gsi_xfer_bitrate(gsi->gadget));
data[1] = data[0]; data[1] = data[0];
log_event_dbg("notify speed %d", log_event_dbg("notify speed %d",
gsi_xfer_bitrate(cdev->gadget)); gsi_xfer_bitrate(gsi->gadget));
break; break;
case GSI_CTRL_NOTIFY_OFFLINE: case GSI_CTRL_NOTIFY_OFFLINE:
del_free_cpkt = true; del_free_cpkt = true;
@ -1868,10 +1862,10 @@ static int gsi_alloc_trb_buffer(struct f_gsi *gsi)
len_in = gsi->d_port.in_request.buf_len * len_in = gsi->d_port.in_request.buf_len *
gsi->d_port.in_request.num_bufs; gsi->d_port.in_request.num_bufs;
gsi->d_port.in_request.buf_base_addr = gsi->d_port.in_request.buf_base_addr =
dma_zalloc_coherent(gsi->d_port.gadget->dev.parent, dma_zalloc_coherent(gsi->gadget->dev.parent,
len_in, &gsi->d_port.in_request.dma, GFP_KERNEL); len_in, &gsi->d_port.in_request.dma, GFP_KERNEL);
if (!gsi->d_port.in_request.buf_base_addr) { if (!gsi->d_port.in_request.buf_base_addr) {
dev_err(&gsi->d_port.gadget->dev, dev_err(&gsi->gadget->dev,
"IN buf_base_addr allocate failed %s\n", "IN buf_base_addr allocate failed %s\n",
gsi->function.name); gsi->function.name);
ret = -ENOMEM; ret = -ENOMEM;
@ -1887,10 +1881,10 @@ static int gsi_alloc_trb_buffer(struct f_gsi *gsi)
len_out = gsi->d_port.out_request.buf_len * len_out = gsi->d_port.out_request.buf_len *
gsi->d_port.out_request.num_bufs; gsi->d_port.out_request.num_bufs;
gsi->d_port.out_request.buf_base_addr = gsi->d_port.out_request.buf_base_addr =
dma_zalloc_coherent(gsi->d_port.gadget->dev.parent, dma_zalloc_coherent(gsi->gadget->dev.parent,
len_out, &gsi->d_port.out_request.dma, GFP_KERNEL); len_out, &gsi->d_port.out_request.dma, GFP_KERNEL);
if (!gsi->d_port.out_request.buf_base_addr) { if (!gsi->d_port.out_request.buf_base_addr) {
dev_err(&gsi->d_port.gadget->dev, dev_err(&gsi->gadget->dev,
"OUT buf_base_addr allocate failed %s\n", "OUT buf_base_addr allocate failed %s\n",
gsi->function.name); gsi->function.name);
ret = -ENOMEM; ret = -ENOMEM;
@ -1903,7 +1897,7 @@ static int gsi_alloc_trb_buffer(struct f_gsi *gsi)
fail: fail:
if (len_in && gsi->d_port.in_request.buf_base_addr) { if (len_in && gsi->d_port.in_request.buf_base_addr) {
dma_free_coherent(gsi->d_port.gadget->dev.parent, len_in, dma_free_coherent(gsi->gadget->dev.parent, len_in,
gsi->d_port.in_request.buf_base_addr, gsi->d_port.in_request.buf_base_addr,
gsi->d_port.in_request.dma); gsi->d_port.in_request.dma);
gsi->d_port.in_request.buf_base_addr = NULL; gsi->d_port.in_request.buf_base_addr = NULL;
@ -1922,7 +1916,7 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi)
gsi->d_port.out_request.buf_base_addr) { gsi->d_port.out_request.buf_base_addr) {
len = gsi->d_port.out_request.buf_len * len = gsi->d_port.out_request.buf_len *
gsi->d_port.out_request.num_bufs; gsi->d_port.out_request.num_bufs;
dma_free_coherent(gsi->d_port.gadget->dev.parent, len, dma_free_coherent(gsi->gadget->dev.parent, len,
gsi->d_port.out_request.buf_base_addr, gsi->d_port.out_request.buf_base_addr,
gsi->d_port.out_request.dma); gsi->d_port.out_request.dma);
gsi->d_port.out_request.buf_base_addr = NULL; gsi->d_port.out_request.buf_base_addr = NULL;
@ -1932,7 +1926,7 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi)
gsi->d_port.in_request.buf_base_addr) { gsi->d_port.in_request.buf_base_addr) {
len = gsi->d_port.in_request.buf_len * len = gsi->d_port.in_request.buf_len *
gsi->d_port.in_request.num_bufs; gsi->d_port.in_request.num_bufs;
dma_free_coherent(gsi->d_port.gadget->dev.parent, len, dma_free_coherent(gsi->gadget->dev.parent, len,
gsi->d_port.in_request.buf_base_addr, gsi->d_port.in_request.buf_base_addr,
gsi->d_port.in_request.dma); gsi->d_port.in_request.dma);
gsi->d_port.in_request.buf_base_addr = NULL; gsi->d_port.in_request.buf_base_addr = NULL;
@ -2035,7 +2029,7 @@ static int gsi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
GSI_EP_OP_CONFIG); GSI_EP_OP_CONFIG);
} }
gsi->d_port.gadget = cdev->gadget; gsi->gadget = cdev->gadget;
if (gsi->prot_id == IPA_USB_RNDIS) { if (gsi->prot_id == IPA_USB_RNDIS) {
gsi_rndis_open(gsi); gsi_rndis_open(gsi);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. * Copyright (c) 2015-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
@ -192,7 +192,6 @@ struct gsi_data_port {
struct usb_ep *out_ep; struct usb_ep *out_ep;
struct usb_gsi_request in_request; struct usb_gsi_request in_request;
struct usb_gsi_request out_request; struct usb_gsi_request out_request;
struct usb_gadget *gadget;
int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *driver_data); int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *driver_data);
struct ipa_usb_teth_params ipa_init_params; struct ipa_usb_teth_params ipa_init_params;
int in_channel_handle; int in_channel_handle;
@ -228,6 +227,7 @@ struct gsi_data_port {
struct f_gsi { struct f_gsi {
struct usb_function function; struct usb_function function;
struct usb_gadget *gadget;
enum ipa_usb_teth_prot prot_id; enum ipa_usb_teth_prot prot_id;
int ctrl_id; int ctrl_id;
int data_id; int data_id;