usb: gadget: Add remote wakeup support to f_rmnet BAM-BAM
On USB suspend, f_rmnet with BAM-BAM transport will register for BAM
wakeup notification. Upon wakeup event from the BAM, the u_bam will
trigger a USB remote wakeup.
Change-Id: I1d1be987f225c0b2edf5bf9af75e2dd15f8c045a
Signed-off-by: Amit Blay <ablay@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index b128223..7e9833b 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -43,10 +43,11 @@
#include <mach/dma.h>
#include <mach/ion.h>
#include <mach/msm_memtypes.h>
+#include <mach/cpuidle.h>
+#include <mach/usb_bam.h>
#include "timer.h"
#include "devices.h"
#include "board-9615.h"
-#include <mach/cpuidle.h>
#include "pm.h"
#include "acpuclock.h"
#include "pm-boot.h"
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index 4caa71b..ec135a3 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* 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
@@ -14,6 +14,20 @@
#define _USB_BAM_H_
/**
+ * SPS Pipes direction.
+ *
+ * USB_TO_PEER_PERIPHERAL USB (as Producer) to other
+ * peer peripheral.
+ * PEER_PERIPHERAL_TO_USB Other Peripheral to
+ * USB (as consumer).
+ */
+enum usb_bam_pipe_dir {
+ USB_TO_PEER_PERIPHERAL,
+ PEER_PERIPHERAL_TO_USB,
+};
+
+#ifdef CONFIG_USB_BAM
+/**
* Connect USB-to-Periperal SPS connection.
*
* This function returns the allocated pipes number.
@@ -29,12 +43,31 @@
* @return 0 on success, negative value on error
*
*/
-#ifdef CONFIG_USB_BAM
int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx);
+
+/**
+ * Register a wakeup callback from peer BAM.
+ *
+ * @idx - Connection index.
+ *
+ * @callback - the callback function
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int usb_bam_register_wake_cb(u8 idx,
+ int (*callback)(void *), void* param);
#else
-int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+static inline int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+{
+ return -ENODEV;
+}
+
+static inline int usb_bam_register_wake_cb(u8 idx,
+ int (*callback)(void *), void* param)
{
return -ENODEV;
}
#endif
#endif /* _USB_BAM_H_ */
+
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 053b81f..e446c25 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -19,6 +19,7 @@
#include <linux/usb/msm_hsusb.h>
#include <mach/usb_bam.h>
#include <mach/sps.h>
+#include <linux/workqueue.h>
#define USB_SUMMING_THRESHOLD 512
#define CONNECTIONS_NUM 4
@@ -29,11 +30,20 @@
static struct sps_mem_buffer data_mem_buf[CONNECTIONS_NUM][2];
static struct sps_mem_buffer desc_mem_buf[CONNECTIONS_NUM][2];
static struct platform_device *usb_bam_pdev;
+static struct workqueue_struct *usb_bam_wq;
+
+struct usb_bam_wake_event_info {
+ struct sps_register_event event;
+ int (*callback)(void *);
+ void *param;
+ struct work_struct wake_w;
+};
struct usb_bam_connect_info {
u8 idx;
u8 *src_pipe;
u8 *dst_pipe;
+ struct usb_bam_wake_event_info peer_event;
bool enabled;
};
@@ -168,6 +178,58 @@
return 0;
}
+static void usb_bam_wake_work(struct work_struct *w)
+{
+ struct usb_bam_wake_event_info *wake_event_info =
+ container_of(w, struct usb_bam_wake_event_info, wake_w);
+
+ wake_event_info->callback(wake_event_info->param);
+}
+
+static void usb_bam_wake_cb(struct sps_event_notify *notify)
+{
+ struct usb_bam_wake_event_info *wake_event_info =
+ (struct usb_bam_wake_event_info *)notify->user;
+
+ queue_work(usb_bam_wq, &wake_event_info->wake_w);
+}
+
+int usb_bam_register_wake_cb(u8 idx,
+ int (*callback)(void *user), void* param)
+{
+ struct sps_pipe *pipe = sps_pipes[idx][PEER_PERIPHERAL_TO_USB];
+ struct sps_connect *sps_connection =
+ &sps_connections[idx][PEER_PERIPHERAL_TO_USB];
+ struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+ struct usb_bam_wake_event_info *wake_event_info =
+ &connection->peer_event;
+ int ret;
+
+ wake_event_info->param = param;
+ wake_event_info->callback = callback;
+ wake_event_info->event.mode = SPS_TRIGGER_CALLBACK;
+ wake_event_info->event.xfer_done = NULL;
+ wake_event_info->event.callback = callback ? usb_bam_wake_cb : NULL;
+ wake_event_info->event.user = wake_event_info;
+ wake_event_info->event.options = SPS_O_WAKEUP;
+ ret = sps_register_event(pipe, &wake_event_info->event);
+ if (ret) {
+ pr_err("%s: sps_register_event() failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ sps_connection->options = callback ?
+ (SPS_O_AUTO_ENABLE | SPS_O_WAKEUP | SPS_O_WAKEUP_IS_ONESHOT) :
+ SPS_O_AUTO_ENABLE;
+ ret = sps_set_config(pipe, sps_connection);
+ if (ret) {
+ pr_err("%s: sps_set_config() failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int usb_bam_init(void)
{
u32 h_usb;
@@ -275,8 +337,11 @@
dev_dbg(&pdev->dev, "usb_bam_probe\n");
- for (i = 0; i < CONNECTIONS_NUM; i++)
+ for (i = 0; i < CONNECTIONS_NUM; i++) {
usb_bam_connections[i].enabled = 0;
+ INIT_WORK(&usb_bam_connections[i].peer_event.wake_w,
+ usb_bam_wake_work);
+ }
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "missing platform_data\n");
@@ -288,11 +353,26 @@
if (ret)
dev_err(&pdev->dev, "failed to create device file\n");
+ usb_bam_wq = alloc_workqueue("usb_bam_wq",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+ if (!usb_bam_wq) {
+ pr_err("unable to create workqueue usb_bam_wq\n");
+ return -ENOMEM;
+ }
+
return ret;
}
+static int usb_bam_remove(struct platform_device *pdev)
+{
+ destroy_workqueue(usb_bam_wq);
+
+ return 0;
+}
+
static struct platform_driver usb_bam_driver = {
.probe = usb_bam_probe,
+ .remove = usb_bam_remove,
.driver = { .name = "usb_bam", },
};
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 958ed31..fcbc75c 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -414,6 +414,60 @@
kfree(f->name);
}
+static void frmnet_suspend(struct usb_function *f)
+{
+ struct f_rmnet *dev = func_to_rmnet(f);
+ unsigned port_num;
+ enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
+
+ pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+ __func__, xport_to_str(dxport),
+ dev, dev->port_num);
+
+ port_num = rmnet_ports[dev->port_num].data_xport_num;
+ switch (dxport) {
+ case USB_GADGET_XPORT_BAM:
+ break;
+ case USB_GADGET_XPORT_BAM2BAM:
+ gbam_suspend(&dev->port, port_num, dxport);
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %s\n", __func__,
+ xport_to_str(dxport));
+ }
+}
+
+static void frmnet_resume(struct usb_function *f)
+{
+ struct f_rmnet *dev = func_to_rmnet(f);
+ unsigned port_num;
+ enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
+
+ pr_debug("%s: data xport: %s dev: %p portno: %d\n",
+ __func__, xport_to_str(dxport),
+ dev, dev->port_num);
+
+ port_num = rmnet_ports[dev->port_num].data_xport_num;
+ switch (dxport) {
+ case USB_GADGET_XPORT_BAM:
+ break;
+ case USB_GADGET_XPORT_BAM2BAM:
+ gbam_resume(&dev->port, port_num, dxport);
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %s\n", __func__,
+ xport_to_str(dxport));
+ }
+}
+
static void frmnet_disable(struct usb_function *f)
{
struct f_rmnet *dev = func_to_rmnet(f);
@@ -912,6 +966,8 @@
f->disable = frmnet_disable;
f->set_alt = frmnet_set_alt;
f->setup = frmnet_setup;
+ f->suspend = frmnet_suspend;
+ f->resume = frmnet_resume;
dev->port.send_cpkt_response = frmnet_send_cpkt_response;
dev->port.disconnect = frmnet_disconnect;
dev->port.connect = frmnet_connect;
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 3113c45..d379c66 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -1214,3 +1214,49 @@
return ret;
}
+
+static int gbam_wake_cb(void *param)
+{
+ struct gbam_port *port = (struct gbam_port *)param;
+ struct bam_ch_info *d;
+ struct f_rmnet *dev;
+
+ dev = port_to_rmnet(port->gr);
+ d = &port->data_ch;
+
+ pr_debug("%s: woken up by peer\n", __func__);
+
+ return usb_gadget_wakeup(dev->cdev->gadget);
+}
+
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+
+ if (trans != USB_GADGET_XPORT_BAM2BAM)
+ return;
+
+ port = bam2bam_ports[port_num];
+ d = &port->data_ch;
+
+ pr_debug("%s: suspended port %d\n", __func__, port_num);
+
+ usb_bam_register_wake_cb(d->connection_idx, gbam_wake_cb, port);
+}
+
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+
+ if (trans != USB_GADGET_XPORT_BAM2BAM)
+ return;
+
+ port = bam2bam_ports[port_num];
+ d = &port->data_ch;
+
+ pr_debug("%s: resumed port %d\n", __func__, port_num);
+
+ usb_bam_register_wake_cb(d->connection_idx, NULL, NULL);
+}
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index 386101c..0f7c4fb 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* 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
@@ -50,6 +50,8 @@
int gbam_connect(struct grmnet *gr, u8 port_num,
enum transport_type trans, u8 connection_idx);
void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_suspend(struct grmnet *gr, u8 port_num, enum transport_type trans);
+void gbam_resume(struct grmnet *gr, u8 port_num, enum transport_type trans);
int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
int gsmd_ctrl_setup(unsigned int count);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 14d49eb..cf6bb4b 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -159,19 +159,6 @@
};
/**
- * SPS Pipes direction.
- *
- * USB_TO_PEER_PERIPHERAL USB (as Producer) to other
- * peer peripheral.
- * PEER_PERIPHERAL_TO_USB Other Peripheral to
- * USB (as consumer).
- */
-enum usb_bam_pipe_dir {
- USB_TO_PEER_PERIPHERAL,
- PEER_PERIPHERAL_TO_USB,
-};
-
-/**
* struct msm_otg_platform_data - platform device data
* for msm_otg driver.
* @phy_init_seq: PHY configuration sequence. val, reg pairs