msm: usb: fix mdm bridge channel mismatch when disconnecting

The bridge_disconnect assumed the internal channel id in pre-defined order,
which was not matched with the actual usb_interface to be disconnected.
Due to this, other internal resources were freed unexpectedly and caused
occasional crash during reset and power off.

Implemented new routines for matching usb_interface and internal channel id
to free proper resources when bridge_disconnect is called.

Change-Id: I0ca604423dd36928f99fad3e21a24ed4699f96c3
This commit is contained in:
Jongrak Kwon
2012-09-05 09:32:35 -07:00
committed by Iliyan Malchev
parent 5f7f224e7b
commit becc299c0d

View File

@@ -109,6 +109,25 @@ static unsigned int get_timestamp(void);
static void dbg_timestamp(char *, struct sk_buff *);
static int submit_rx_urb(struct data_bridge *dev, struct urb *urb,
gfp_t flags);
static void* interface[MAX_BRIDGE_DEVICES];
static inline int get_chid(struct usb_interface *iface)
{
int i;
for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
if (interface[i] == iface)
return i;
}
return -1;
}
static inline void set_chid(struct usb_interface *iface, int chid)
{
if (chid >= 0 && chid < MAX_BRIDGE_DEVICES) {
interface[chid] = iface;
}
}
static inline bool rx_halted(struct data_bridge *dev)
{
@@ -962,6 +981,8 @@ bridge_probe(struct usb_interface *iface, const struct usb_device_id *id)
goto free_data_bridge;
}
set_chid(iface, ch_id);
ch_id++;
return 0;
@@ -983,17 +1004,26 @@ static void bridge_disconnect(struct usb_interface *intf)
struct list_head *head;
struct urb *rx_urb;
unsigned long flags;
int chid;
if (!dev) {
err("%s: data device not found\n", __func__);
return;
}
ch_id--;
ctrl_bridge_disconnect(ch_id);
ch_id--; /* leave it for now */
chid = get_chid(intf);
if (chid < 0) {
err("%s: invalid interface\n", __func__);
return;
}
ctrl_bridge_disconnect(chid);
platform_device_unregister(dev->pdev);
usb_set_intfdata(intf, NULL);
__dev[ch_id] = NULL;
__dev[chid] = NULL;
cancel_work_sync(&dev->process_rx_w);
cancel_work_sync(&dev->kevent);
@@ -1010,6 +1040,8 @@ static void bridge_disconnect(struct usb_interface *intf)
usb_put_dev(dev->udev);
kfree(dev);
set_chid(NULL, chid);
}
/*bit position represents interface number*/