diff options
Diffstat (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-usb-otg-and-still-image-driver.patch')
-rw-r--r-- | meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-usb-otg-and-still-image-driver.patch | 8395 |
1 files changed, 0 insertions, 8395 deletions
diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-usb-otg-and-still-image-driver.patch b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-usb-otg-and-still-image-driver.patch deleted file mode 100644 index 40eecf646..000000000 --- a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.34-moorestown-usb-otg-and-still-image-driver.patch +++ /dev/null @@ -1,8395 +0,0 @@ -From f12c49e8bf1ac056946bc3098c6c361d51891916 Mon Sep 17 00:00:00 2001 -From: Henry Yuan <hang.yuan@intel.com> -Date: Thu, 6 May 2010 19:30:00 +0800 -Subject: [PATCH] Moorestown USB-OTG drivers full patch 0.2 for MeeGo - -This is a consolidated full patch against K2.6.33. It -includes USB-OTG client controller driver, transceiver -driver, still image gadget driver and fixing for sighting -3469616: OTG driver hangs in suspend function. - -OTG host, client functions and role switch per cable -plugged are tested. -Known issue: HNP/SRP have problem. - -Kernel config: -CONFIG_USB_LANGWELL_OTG = y -CONFIG_USB_OTG_WHITELIST = n -CONFIG_USB_GADGET = y -CONFIG_USB_GADGET_LANGWELL = y - -CONFIG_USB_STILL_IMAGE = y -or select other gadget driver as needed. - -Signed-off-by: Henry Yuan <hang.yuan@intel.com> -Patch-mainline: 2.6.34 ---- - drivers/usb/gadget/Kconfig | 8 + - drivers/usb/gadget/Makefile | 2 + - drivers/usb/gadget/f_ecm.c | 22 + - drivers/usb/gadget/f_subset.c | 22 + - drivers/usb/gadget/langwell_udc.c | 582 ++++-- - drivers/usb/gadget/langwell_udc.h | 13 +- - drivers/usb/gadget/still_image.c | 4566 +++++++++++++++++++++++++++++++++++++ - drivers/usb/otg/Kconfig | 14 + - drivers/usb/otg/Makefile | 1 + - drivers/usb/otg/langwell_otg.c | 2260 ++++++++++++++++++ - include/linux/usb/langwell_otg.h | 201 ++ - include/linux/usb/langwell_udc.h | 13 + - 12 files changed, 7516 insertions(+), 188 deletions(-) - create mode 100644 drivers/usb/gadget/still_image.c - create mode 100644 drivers/usb/otg/langwell_otg.c - create mode 100644 include/linux/usb/langwell_otg.h - -diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig -index ee41120..94cc94f 100644 ---- a/drivers/usb/gadget/Kconfig -+++ b/drivers/usb/gadget/Kconfig -@@ -853,6 +853,14 @@ config USB_G_MULTI_CDC - - If unsure, say "y". - -+config USB_STILL_IMAGE -+ tristate "Lite Still Image Gadget" -+ help -+ The Lite Still Image Gadget implements object transfer based on -+ spec PIMA 15740:2000. -+ -+ Say "y" to link the driver statically, or "m" to build a dynamically -+ linked module called "g_still_image". - - # put drivers that need isochronous transfer support (for audio - # or video class gadget drivers), or specific hardware, here. -diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile -index 2e2c047..7ef974e 100644 ---- a/drivers/usb/gadget/Makefile -+++ b/drivers/usb/gadget/Makefile -@@ -43,6 +43,7 @@ g_mass_storage-objs := mass_storage.o - g_printer-objs := printer.o - g_cdc-objs := cdc2.o - g_multi-objs := multi.o -+g_still_image-objs := still_image.o - - obj-$(CONFIG_USB_ZERO) += g_zero.o - obj-$(CONFIG_USB_AUDIO) += g_audio.o -@@ -55,4 +56,5 @@ obj-$(CONFIG_USB_G_PRINTER) += g_printer.o - obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o - obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o - obj-$(CONFIG_USB_G_MULTI) += g_multi.o -+obj-$(CONFIG_USB_STILL_IMAGE) += g_still_image.o - -diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c -index ecf5bdd..d004328 100644 ---- a/drivers/usb/gadget/f_ecm.c -+++ b/drivers/usb/gadget/f_ecm.c -@@ -753,6 +753,26 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f) - kfree(ecm); - } - -+static void -+ecm_suspend(struct usb_function *f) -+{ -+ struct f_ecm *ecm = func_to_ecm(f); -+ struct eth_dev *dev = ecm->port.ioport; -+ -+ if (dev) -+ gether_disconnect(&ecm->port); -+} -+ -+static void -+ecm_resume(struct usb_function *f) -+{ -+ struct f_ecm *ecm = func_to_ecm(f); -+ struct eth_dev *dev = ecm->port.ioport; -+ -+ if (!dev) -+ gether_connect(&ecm->port); -+} -+ - /** - * ecm_bind_config - add CDC Ethernet network link to a configuration - * @c: the configuration to support the network link -@@ -821,6 +841,8 @@ int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) - ecm->port.func.get_alt = ecm_get_alt; - ecm->port.func.setup = ecm_setup; - ecm->port.func.disable = ecm_disable; -+ ecm->port.func.suspend = ecm_suspend; -+ ecm->port.func.resume = ecm_resume; - - status = usb_add_function(c, &ecm->port.func); - if (status) { -diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c -index a9c98fd..893816d 100644 ---- a/drivers/usb/gadget/f_subset.c -+++ b/drivers/usb/gadget/f_subset.c -@@ -353,6 +353,26 @@ geth_unbind(struct usb_configuration *c, struct usb_function *f) - kfree(func_to_geth(f)); - } - -+static void -+geth_suspend(struct usb_function *f) -+{ -+ struct f_gether *geth = func_to_geth(f); -+ struct eth_dev *dev = geth->port.ioport; -+ -+ if (dev) -+ gether_disconnect(&geth->port); -+} -+ -+static void -+geth_resume(struct usb_function *f) -+{ -+ struct f_gether *geth = func_to_geth(f); -+ struct eth_dev *dev = geth->port.ioport; -+ -+ if (!dev) -+ gether_connect(&geth->port); -+} -+ - /** - * geth_bind_config - add CDC Subset network link to a configuration - * @c: the configuration to support the network link -@@ -411,6 +431,8 @@ int __init geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) - geth->port.func.unbind = geth_unbind; - geth->port.func.set_alt = geth_set_alt; - geth->port.func.disable = geth_disable; -+ geth->port.func.resume = geth_resume; -+ geth->port.func.suspend = geth_suspend; - - status = usb_add_function(c, &geth->port.func); - if (status) { -diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c -index a391351..eb0e185 100644 ---- a/drivers/usb/gadget/langwell_udc.c -+++ b/drivers/usb/gadget/langwell_udc.c -@@ -54,7 +54,7 @@ - - - #define DRIVER_DESC "Intel Langwell USB Device Controller driver" --#define DRIVER_VERSION "16 May 2009" -+#define DRIVER_VERSION "Apr 30, 2010" - - static const char driver_name[] = "langwell_udc"; - static const char driver_desc[] = DRIVER_DESC; -@@ -73,7 +73,6 @@ langwell_ep0_desc = { - .wMaxPacketSize = EP0_MAX_PKT_SIZE, - }; - -- - /*-------------------------------------------------------------------------*/ - /* debugging */ - -@@ -114,104 +113,76 @@ static inline void print_all_registers(struct langwell_udc *dev) - int i; - - /* Capability Registers */ -- printk(KERN_DEBUG "Capability Registers (offset: " -- "0x%04x, length: 0x%08x)\n", -- CAP_REG_OFFSET, -- (u32)sizeof(struct langwell_cap_regs)); -- printk(KERN_DEBUG "caplength=0x%02x\n", -- readb(&dev->cap_regs->caplength)); -- printk(KERN_DEBUG "hciversion=0x%04x\n", -- readw(&dev->cap_regs->hciversion)); -- printk(KERN_DEBUG "hcsparams=0x%08x\n", -- readl(&dev->cap_regs->hcsparams)); -- printk(KERN_DEBUG "hccparams=0x%08x\n", -- readl(&dev->cap_regs->hccparams)); -- printk(KERN_DEBUG "dciversion=0x%04x\n", -- readw(&dev->cap_regs->dciversion)); -- printk(KERN_DEBUG "dccparams=0x%08x\n", -- readl(&dev->cap_regs->dccparams)); -+ DBG(dev, "Capability Registers (offset: 0x%04x, length: 0x%08x)\n", -+ CAP_REG_OFFSET, (u32)sizeof(struct langwell_cap_regs)); -+ DBG(dev, "caplength=0x%02x\n", readb(&dev->cap_regs->caplength)); -+ DBG(dev, "hciversion=0x%04x\n", readw(&dev->cap_regs->hciversion)); -+ DBG(dev, "hcsparams=0x%08x\n", readl(&dev->cap_regs->hcsparams)); -+ DBG(dev, "hccparams=0x%08x\n", readl(&dev->cap_regs->hccparams)); -+ DBG(dev, "dciversion=0x%04x\n", readw(&dev->cap_regs->dciversion)); -+ DBG(dev, "dccparams=0x%08x\n", readl(&dev->cap_regs->dccparams)); - - /* Operational Registers */ -- printk(KERN_DEBUG "Operational Registers (offset: " -- "0x%04x, length: 0x%08x)\n", -- OP_REG_OFFSET, -- (u32)sizeof(struct langwell_op_regs)); -- printk(KERN_DEBUG "extsts=0x%08x\n", -- readl(&dev->op_regs->extsts)); -- printk(KERN_DEBUG "extintr=0x%08x\n", -- readl(&dev->op_regs->extintr)); -- printk(KERN_DEBUG "usbcmd=0x%08x\n", -- readl(&dev->op_regs->usbcmd)); -- printk(KERN_DEBUG "usbsts=0x%08x\n", -- readl(&dev->op_regs->usbsts)); -- printk(KERN_DEBUG "usbintr=0x%08x\n", -- readl(&dev->op_regs->usbintr)); -- printk(KERN_DEBUG "frindex=0x%08x\n", -- readl(&dev->op_regs->frindex)); -- printk(KERN_DEBUG "ctrldssegment=0x%08x\n", -+ DBG(dev, "Operational Registers (offset: 0x%04x, length: 0x%08x)\n", -+ OP_REG_OFFSET, (u32)sizeof(struct langwell_op_regs)); -+ DBG(dev, "extsts=0x%08x\n", readl(&dev->op_regs->extsts)); -+ DBG(dev, "extintr=0x%08x\n", readl(&dev->op_regs->extintr)); -+ DBG(dev, "usbcmd=0x%08x\n", readl(&dev->op_regs->usbcmd)); -+ DBG(dev, "usbsts=0x%08x\n", readl(&dev->op_regs->usbsts)); -+ DBG(dev, "usbintr=0x%08x\n", readl(&dev->op_regs->usbintr)); -+ DBG(dev, "frindex=0x%08x\n", readl(&dev->op_regs->frindex)); -+ DBG(dev, "ctrldssegment=0x%08x\n", - readl(&dev->op_regs->ctrldssegment)); -- printk(KERN_DEBUG "deviceaddr=0x%08x\n", -- readl(&dev->op_regs->deviceaddr)); -- printk(KERN_DEBUG "endpointlistaddr=0x%08x\n", -+ DBG(dev, "deviceaddr=0x%08x\n", readl(&dev->op_regs->deviceaddr)); -+ DBG(dev, "endpointlistaddr=0x%08x\n", - readl(&dev->op_regs->endpointlistaddr)); -- printk(KERN_DEBUG "ttctrl=0x%08x\n", -- readl(&dev->op_regs->ttctrl)); -- printk(KERN_DEBUG "burstsize=0x%08x\n", -- readl(&dev->op_regs->burstsize)); -- printk(KERN_DEBUG "txfilltuning=0x%08x\n", -- readl(&dev->op_regs->txfilltuning)); -- printk(KERN_DEBUG "txttfilltuning=0x%08x\n", -+ DBG(dev, "ttctrl=0x%08x\n", readl(&dev->op_regs->ttctrl)); -+ DBG(dev, "burstsize=0x%08x\n", readl(&dev->op_regs->burstsize)); -+ DBG(dev, "txfilltuning=0x%08x\n", readl(&dev->op_regs->txfilltuning)); -+ DBG(dev, "txttfilltuning=0x%08x\n", - readl(&dev->op_regs->txttfilltuning)); -- printk(KERN_DEBUG "ic_usb=0x%08x\n", -- readl(&dev->op_regs->ic_usb)); -- printk(KERN_DEBUG "ulpi_viewport=0x%08x\n", -+ DBG(dev, "ic_usb=0x%08x\n", readl(&dev->op_regs->ic_usb)); -+ DBG(dev, "ulpi_viewport=0x%08x\n", - readl(&dev->op_regs->ulpi_viewport)); -- printk(KERN_DEBUG "configflag=0x%08x\n", -- readl(&dev->op_regs->configflag)); -- printk(KERN_DEBUG "portsc1=0x%08x\n", -- readl(&dev->op_regs->portsc1)); -- printk(KERN_DEBUG "devlc=0x%08x\n", -- readl(&dev->op_regs->devlc)); -- printk(KERN_DEBUG "otgsc=0x%08x\n", -- readl(&dev->op_regs->otgsc)); -- printk(KERN_DEBUG "usbmode=0x%08x\n", -- readl(&dev->op_regs->usbmode)); -- printk(KERN_DEBUG "endptnak=0x%08x\n", -- readl(&dev->op_regs->endptnak)); -- printk(KERN_DEBUG "endptnaken=0x%08x\n", -- readl(&dev->op_regs->endptnaken)); -- printk(KERN_DEBUG "endptsetupstat=0x%08x\n", -+ DBG(dev, "configflag=0x%08x\n", readl(&dev->op_regs->configflag)); -+ DBG(dev, "portsc1=0x%08x\n", readl(&dev->op_regs->portsc1)); -+ DBG(dev, "devlc=0x%08x\n", readl(&dev->op_regs->devlc)); -+ DBG(dev, "otgsc=0x%08x\n", readl(&dev->op_regs->otgsc)); -+ DBG(dev, "usbmode=0x%08x\n", readl(&dev->op_regs->usbmode)); -+ DBG(dev, "endptnak=0x%08x\n", readl(&dev->op_regs->endptnak)); -+ DBG(dev, "endptnaken=0x%08x\n", readl(&dev->op_regs->endptnaken)); -+ DBG(dev, "endptsetupstat=0x%08x\n", - readl(&dev->op_regs->endptsetupstat)); -- printk(KERN_DEBUG "endptprime=0x%08x\n", -- readl(&dev->op_regs->endptprime)); -- printk(KERN_DEBUG "endptflush=0x%08x\n", -- readl(&dev->op_regs->endptflush)); -- printk(KERN_DEBUG "endptstat=0x%08x\n", -- readl(&dev->op_regs->endptstat)); -- printk(KERN_DEBUG "endptcomplete=0x%08x\n", -+ DBG(dev, "endptprime=0x%08x\n", readl(&dev->op_regs->endptprime)); -+ DBG(dev, "endptflush=0x%08x\n", readl(&dev->op_regs->endptflush)); -+ DBG(dev, "endptstat=0x%08x\n", readl(&dev->op_regs->endptstat)); -+ DBG(dev, "endptcomplete=0x%08x\n", - readl(&dev->op_regs->endptcomplete)); - - for (i = 0; i < dev->ep_max / 2; i++) { -- printk(KERN_DEBUG "endptctrl[%d]=0x%08x\n", -+ DBG(dev, "endptctrl[%d]=0x%08x\n", - i, readl(&dev->op_regs->endptctrl[i])); - } - } -+#else -+ -+#define print_all_registers(dev) do { } while (0) -+ - #endif /* VERBOSE */ - - - /*-------------------------------------------------------------------------*/ - --#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") -+#define is_in(ep) (((ep)->ep_num == 0) ? ((ep)->dev->ep0_dir == \ -+ USB_DIR_IN) : (usb_endpoint_dir_in((ep)->desc))) - --#define is_in(ep) (((ep)->ep_num == 0) ? ((ep)->dev->ep0_dir == \ -- USB_DIR_IN) : ((ep)->desc->bEndpointAddress \ -- & USB_DIR_IN) == USB_DIR_IN) -+#define DIR_STRING(ep) (is_in(ep) ? "in" : "out") - - - #ifdef DEBUG --static char *type_string(u8 bmAttributes) -+static char *type_string(const struct usb_endpoint_descriptor *desc) - { -- switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { -+ switch (usb_endpoint_type(desc)) { - case USB_ENDPOINT_XFER_BULK: - return "bulk"; - case USB_ENDPOINT_XFER_ISOC: -@@ -274,11 +245,13 @@ static void ep0_reset(struct langwell_udc *dev) - ep->dqh->dqh_ios = 1; - ep->dqh->dqh_mpl = EP0_MAX_PKT_SIZE; - -- /* FIXME: enable ep0-in HW zero length termination select */ -+ /* enable ep0-in HW zero length termination select */ - if (is_in(ep)) - ep->dqh->dqh_zlt = 0; - ep->dqh->dqh_mult = 0; - -+ ep->dqh->dtd_next = DTD_TERM; -+ - /* configure ep0 control registers */ - ep_reset(&dev->ep[0], 0, i, USB_ENDPOINT_XFER_CONTROL); - } -@@ -300,7 +273,7 @@ static int langwell_ep_enable(struct usb_ep *_ep, - struct langwell_ep *ep; - u16 max = 0; - unsigned long flags; -- int retval = 0; -+ int i, retval = 0; - unsigned char zlt, ios = 0, mult = 0; - - ep = container_of(_ep, struct langwell_ep, ep); -@@ -326,7 +299,7 @@ static int langwell_ep_enable(struct usb_ep *_ep, - * sanity check type, direction, address, and then - * initialize the endpoint capabilities fields in dQH - */ -- switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { -+ switch (usb_endpoint_type(desc)) { - case USB_ENDPOINT_XFER_CONTROL: - ios = 1; - break; -@@ -386,28 +359,31 @@ static int langwell_ep_enable(struct usb_ep *_ep, - - spin_lock_irqsave(&dev->lock, flags); - -- /* configure endpoint capabilities in dQH */ -- ep->dqh->dqh_ios = ios; -- ep->dqh->dqh_mpl = cpu_to_le16(max); -- ep->dqh->dqh_zlt = zlt; -- ep->dqh->dqh_mult = mult; -- - ep->ep.maxpacket = max; - ep->desc = desc; - ep->stopped = 0; -- ep->ep_num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; -+ ep->ep_num = usb_endpoint_num(desc); - - /* ep_type */ -- ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; -+ ep->ep_type = usb_endpoint_type(desc); - - /* configure endpoint control registers */ - ep_reset(ep, ep->ep_num, is_in(ep), ep->ep_type); - -+ /* configure endpoint capabilities in dQH */ -+ i = ep->ep_num * 2 + is_in(ep); -+ ep->dqh = &dev->ep_dqh[i]; -+ ep->dqh->dqh_ios = ios; -+ ep->dqh->dqh_mpl = cpu_to_le16(max); -+ ep->dqh->dqh_zlt = zlt; -+ ep->dqh->dqh_mult = mult; -+ ep->dqh->dtd_next = DTD_TERM; -+ - DBG(dev, "enabled %s (ep%d%s-%s), max %04x\n", - _ep->name, - ep->ep_num, -- DIR_STRING(desc->bEndpointAddress), -- type_string(desc->bmAttributes), -+ DIR_STRING(ep), -+ type_string(desc), - max); - - spin_unlock_irqrestore(&dev->lock, flags); -@@ -617,7 +593,7 @@ static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req) - VDBG(dev, "%s\n", ep->name); - else - /* ep0 */ -- VDBG(dev, "%s-%s\n", ep->name, is_in(ep) ? "in" : "out"); -+ VDBG(dev, "%s-%s\n", ep->name, DIR_STRING(ep)); - - VDBG(dev, "ep_dqh[%d] addr: 0x%08x\n", i, (u32)&(dev->ep_dqh[i])); - -@@ -667,6 +643,9 @@ static int queue_dtd(struct langwell_ep *ep, struct langwell_request *req) - dqh->dtd_status &= dtd_status; - VDBG(dev, "dqh->dtd_status = 0x%x\n", dqh->dtd_status); - -+ /* ensure that updates to the dQH will occure before priming */ -+ wmb(); -+ - /* write 1 to endptprime register to PRIME endpoint */ - bit_mask = is_in(ep) ? (1 << (ep->ep_num + 16)) : (1 << ep->ep_num); - VDBG(dev, "endprime bit_mask = 0x%08x\n", bit_mask); -@@ -805,7 +784,7 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req, - req->ep = ep; - VDBG(dev, "---> %s()\n", __func__); - -- if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { -+ if (usb_endpoint_xfer_isoc(ep->desc)) { - if (req->req.length > ep->ep.maxpacket) - return -EMSGSIZE; - is_iso = 1; -@@ -844,7 +823,7 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req, - - DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n", - _ep->name, -- _req, _req->length, _req->buf, _req->dma); -+ _req, _req->length, _req->buf, (int)_req->dma); - - _req->status = -EINPROGRESS; - _req->actual = 0; -@@ -1024,8 +1003,7 @@ static int langwell_ep_set_halt(struct usb_ep *_ep, int value) - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - -- if (ep->desc && (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) -- == USB_ENDPOINT_XFER_ISOC) -+ if (usb_endpoint_xfer_isoc(ep->desc)) - return -EOPNOTSUPP; - - spin_lock_irqsave(&dev->lock, flags); -@@ -1094,7 +1072,7 @@ static void langwell_ep_fifo_flush(struct usb_ep *_ep) - return; - } - -- VDBG(dev, "%s-%s fifo flush\n", _ep->name, is_in(ep) ? "in" : "out"); -+ VDBG(dev, "%s-%s fifo flush\n", _ep->name, DIR_STRING(ep)); - - /* flush endpoint buffer */ - if (ep->ep_num == 0) -@@ -1181,6 +1159,7 @@ static int langwell_wakeup(struct usb_gadget *_gadget) - { - struct langwell_udc *dev; - u32 portsc1, devlc; -+ u8 devlc_byte2; - unsigned long flags; - - if (!_gadget) -@@ -1189,9 +1168,11 @@ static int langwell_wakeup(struct usb_gadget *_gadget) - dev = container_of(_gadget, struct langwell_udc, gadget); - VDBG(dev, "---> %s()\n", __func__); - -- /* Remote Wakeup feature not enabled by host */ -- if (!dev->remote_wakeup) -+ /* remote wakeup feature not enabled by host */ -+ if (!dev->remote_wakeup) { -+ INFO(dev, "remote wakeup is disabled\n"); - return -ENOTSUPP; -+ } - - spin_lock_irqsave(&dev->lock, flags); - -@@ -1201,23 +1182,25 @@ static int langwell_wakeup(struct usb_gadget *_gadget) - return 0; - } - -- /* LPM L1 to L0, remote wakeup */ -- if (dev->lpm && dev->lpm_state == LPM_L1) { -- portsc1 |= PORTS_SLP; -- writel(portsc1, &dev->op_regs->portsc1); -- } -- -- /* force port resume */ -- if (dev->usb_state == USB_STATE_SUSPENDED) { -- portsc1 |= PORTS_FPR; -- writel(portsc1, &dev->op_regs->portsc1); -- } -+ /* LPM L1 to L0 or legacy remote wakeup */ -+ if (dev->lpm && dev->lpm_state == LPM_L1) -+ INFO(dev, "LPM L1 to L0 remote wakeup\n"); -+ else -+ INFO(dev, "device remote wakeup\n"); - - /* exit PHY low power suspend */ - devlc = readl(&dev->op_regs->devlc); - VDBG(dev, "devlc = 0x%08x\n", devlc); - devlc &= ~LPM_PHCD; -- writel(devlc, &dev->op_regs->devlc); -+ /* FIXME: workaround for Langwell A1/A2/A3 sighting */ -+ devlc_byte2 = (devlc >> 16) & 0xff; -+ writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2); -+ devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "exit PHY low power suspend, devlc = 0x%08x\n", devlc); -+ -+ /* force port resume */ -+ portsc1 |= PORTS_FPR; -+ writel(portsc1, &dev->op_regs->portsc1); - - spin_unlock_irqrestore(&dev->lock, flags); - -@@ -1346,6 +1329,7 @@ static const struct usb_gadget_ops langwell_ops = { - static int langwell_udc_reset(struct langwell_udc *dev) - { - u32 usbcmd, usbmode, devlc, endpointlistaddr; -+ u8 devlc_byte0, devlc_byte2; - unsigned long timeout; - - if (!dev) -@@ -1390,9 +1374,16 @@ static int langwell_udc_reset(struct langwell_udc *dev) - /* if support USB LPM, ACK all LPM token */ - if (dev->lpm) { - devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "devlc = 0x%08x\n", devlc); -+ /* FIXME: workaround for Langwell A1/A2/A3 sighting */ - devlc &= ~LPM_STL; /* don't STALL LPM token */ - devlc &= ~LPM_NYT_ACK; /* ACK LPM token */ -- writel(devlc, &dev->op_regs->devlc); -+ devlc_byte0 = devlc & 0xff; -+ devlc_byte2 = (devlc >> 16) & 0xff; -+ writeb(devlc_byte0, (u8 *)&dev->op_regs->devlc); -+ writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2); -+ devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "ACK LPM token, devlc = 0x%08x\n", devlc); - } - - /* fill endpointlistaddr register */ -@@ -1449,8 +1440,6 @@ static int eps_reinit(struct langwell_udc *dev) - - INIT_LIST_HEAD(&ep->queue); - list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); -- -- ep->dqh = &dev->ep_dqh[i]; - } - - VDBG(dev, "<--- %s()\n", __func__); -@@ -1539,21 +1528,6 @@ static void stop_activity(struct langwell_udc *dev, - - /*-------------------------------------------------------------------------*/ - --/* device "function" sysfs attribute file */ --static ssize_t show_function(struct device *_dev, -- struct device_attribute *attr, char *buf) --{ -- struct langwell_udc *dev = the_controller; -- -- if (!dev->driver || !dev->driver->function -- || strlen(dev->driver->function) > PAGE_SIZE) -- return 0; -- -- return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); --} --static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); -- -- - /* device "langwell_udc" sysfs attribute file */ - static ssize_t show_langwell_udc(struct device *_dev, - struct device_attribute *attr, char *buf) -@@ -1659,13 +1633,15 @@ static ssize_t show_langwell_udc(struct device *_dev, - "Over-current Change: %s\n" - "Port Enable/Disable Change: %s\n" - "Port Enabled/Disabled: %s\n" -- "Current Connect Status: %s\n\n", -+ "Current Connect Status: %s\n" -+ "LPM Suspend Status: %s\n\n", - (tmp_reg & PORTS_PR) ? "Reset" : "Not Reset", - (tmp_reg & PORTS_SUSP) ? "Suspend " : "Not Suspend", - (tmp_reg & PORTS_OCC) ? "Detected" : "No", - (tmp_reg & PORTS_PEC) ? "Changed" : "Not Changed", - (tmp_reg & PORTS_PE) ? "Enable" : "Not Correct", -- (tmp_reg & PORTS_CCS) ? "Attached" : "Not Attached"); -+ (tmp_reg & PORTS_CCS) ? "Attached" : "Not Attached", -+ (tmp_reg & PORTS_SLP) ? "LPM L1" : "LPM L0"); - size -= t; - next += t; - -@@ -1676,7 +1652,7 @@ static ssize_t show_langwell_udc(struct device *_dev, - "Serial Transceiver : %d\n" - "Port Speed: %s\n" - "Port Force Full Speed Connenct: %s\n" -- "PHY Low Power Suspend Clock Disable: %s\n" -+ "PHY Low Power Suspend Clock: %s\n" - "BmAttributes: %d\n\n", - LPM_PTS(tmp_reg), - (tmp_reg & LPM_STS) ? 1 : 0, -@@ -1797,6 +1773,40 @@ static ssize_t show_langwell_udc(struct device *_dev, - static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL); - - -+/* device "remote_wakeup" sysfs attribute file */ -+static ssize_t store_remote_wakeup(struct device *_dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct langwell_udc *dev = the_controller; -+#if defined(CONFIG_USB_DEBUG) -+ unsigned long flags; -+#endif -+ ssize_t rc = count; -+ -+ if (count > 2) -+ return -EINVAL; -+ -+ if (count > 0 && buf[count-1] == '\n') -+ ((char *) buf)[count-1] = 0; -+ -+ if (buf[0] != '1') -+ return -EINVAL; -+ -+#if defined(CONFIG_USB_DEBUG) -+ /* force remote wakeup enabled in case gadget driver doesn't support */ -+ spin_lock_irqsave(&dev->lock, flags); -+ dev->remote_wakeup = 1; -+ dev->dev_status |= (1 << USB_DEVICE_REMOTE_WAKEUP); -+ spin_unlock_irqrestore(&dev->lock, flags); -+#endif -+ -+ langwell_wakeup(&dev->gadget); -+ -+ return rc; -+} -+static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup); -+ -+ - /*-------------------------------------------------------------------------*/ - - /* -@@ -1818,6 +1828,9 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) - - DBG(dev, "---> %s()\n", __func__); - -+ if (unlikely(!driver || !driver->bind)) -+ return -EINVAL; -+ - if (dev->driver) - return -EBUSY; - -@@ -1839,34 +1852,24 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) - return retval; - } - -- retval = device_create_file(&dev->pdev->dev, &dev_attr_function); -- if (retval) -- goto err_unbind; -- - dev->usb_state = USB_STATE_ATTACHED; - dev->ep0_state = WAIT_FOR_SETUP; - dev->ep0_dir = USB_DIR_OUT; - -+ /* bind OTG transceiver */ -+ if (dev->transceiver) -+ (void)otg_set_peripheral(dev->transceiver, &dev->gadget); -+ - /* enable interrupt and set controller to run state */ - if (dev->got_irq) - langwell_udc_start(dev); - - VDBG(dev, "After langwell_udc_start(), print all registers:\n"); --#ifdef VERBOSE - print_all_registers(dev); --#endif - - INFO(dev, "register driver: %s\n", driver->driver.name); -- VDBG(dev, "<--- %s()\n", __func__); -- return 0; -- --err_unbind: -- driver->unbind(&dev->gadget); -- dev->gadget.dev.driver = NULL; -- dev->driver = NULL; -- - DBG(dev, "<--- %s()\n", __func__); -- return retval; -+ return 0; - } - EXPORT_SYMBOL(usb_gadget_register_driver); - -@@ -1876,15 +1879,27 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) - { - struct langwell_udc *dev = the_controller; - unsigned long flags; -+ u32 devlc; -+ u8 devlc_byte2; - - if (!dev) - return -ENODEV; - - DBG(dev, "---> %s()\n", __func__); - -- if (unlikely(!driver || !driver->bind || !driver->unbind)) -+ if (unlikely(!driver || !driver->unbind || !driver->disconnect)) - return -EINVAL; - -+ /* exit PHY low power suspend */ -+ devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "devlc = 0x%08x\n", devlc); -+ devlc &= ~LPM_PHCD; -+ /* FIXME: workaround for Langwell A1/A2/A3 sighting */ -+ devlc_byte2 = (devlc >> 16) & 0xff; -+ writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2); -+ devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "exit PHY low power suspend, devlc = 0x%08x\n", devlc); -+ - /* unbind OTG transceiver */ - if (dev->transceiver) - (void)otg_set_peripheral(dev->transceiver, 0); -@@ -1908,8 +1923,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) - dev->gadget.dev.driver = NULL; - dev->driver = NULL; - -- device_remove_file(&dev->pdev->dev, &dev_attr_function); -- - INFO(dev, "unregistered driver '%s'\n", driver->driver.name); - DBG(dev, "<--- %s()\n", __func__); - return 0; -@@ -1917,6 +1930,55 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) - EXPORT_SYMBOL(usb_gadget_unregister_driver); - - -+/* gets the maximum power consumption */ -+int langwell_udc_maxpower(int *mA) -+{ -+ struct langwell_udc *dev = the_controller; -+ u32 usbmode, portsc1, usbcmd; -+ -+ /* fatal error */ -+ if (!dev) { -+ *mA = 0; -+ return -EOTGFAIL; -+ } -+ -+ DBG(dev, "---> %s()\n", __func__); -+ -+ /* contrller is not in device mode */ -+ usbmode = readl(&dev->op_regs->usbmode); -+ if (MODE_CM(usbmode) != MODE_DEVICE) { -+ *mA = 0; -+ return -EOTGNODEVICE; -+ } -+ -+ /* can't get maximum power */ -+ usbcmd = readl(&dev->op_regs->usbcmd); -+ if (!(usbcmd & CMD_RUNSTOP)) { -+ *mA = 0; -+ return -EOTGCHARGER; -+ } -+ -+ /* disconnect to USB host */ -+ portsc1 = readl(&dev->op_regs->portsc1); -+ if (!(portsc1 & PORTS_CCS)) { -+ *mA = 0; -+ return -EOTGDISCONN; -+ } -+ -+ /* set max power capability */ -+ *mA = CONFIG_USB_GADGET_VBUS_DRAW; -+ -+ if ((*mA < 8) || (*mA > 500)) { -+ *mA = 0; -+ return -EOTGINVAL; -+ } -+ -+ DBG(dev, "<--- %s()\n", __func__); -+ return 0; -+} -+EXPORT_SYMBOL(langwell_udc_maxpower); -+ -+ - /*-------------------------------------------------------------------------*/ - - /* -@@ -2113,8 +2175,7 @@ static void get_status(struct langwell_udc *dev, u8 request_type, u16 value, - - if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { - /* get device status */ -- status_data = 1 << USB_DEVICE_SELF_POWERED; -- status_data |= dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; -+ status_data = dev->dev_status; - } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { - /* get interface status */ - status_data = 0; -@@ -2129,6 +2190,8 @@ static void get_status(struct langwell_udc *dev, u8 request_type, u16 value, - status_data = ep_is_stall(epn) << USB_ENDPOINT_HALT; - } - -+ DBG(dev, "get status data: 0x%04x\n", status_data); -+ - dev->ep0_dir = USB_DIR_IN; - - /* borrow the per device status_req */ -@@ -2247,22 +2310,37 @@ static void handle_setup_packet(struct langwell_udc *dev, - } else if ((setup->bRequestType & (USB_RECIP_MASK - | USB_TYPE_MASK)) == (USB_RECIP_DEVICE - | USB_TYPE_STANDARD)) { -- if (!gadget_is_otg(&dev->gadget)) -+ rc = 0; -+ switch (wValue) { -+ case USB_DEVICE_REMOTE_WAKEUP: -+ if (setup->bRequest == USB_REQ_SET_FEATURE) { -+ dev->remote_wakeup = 1; -+ dev->dev_status |= (1 << wValue); -+ } else { -+ dev->remote_wakeup = 0; -+ dev->dev_status &= ~(1 << wValue); -+ } - break; -- else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) { -+ case USB_DEVICE_B_HNP_ENABLE: - dev->gadget.b_hnp_enable = 1; - #ifdef OTG_TRANSCEIVER - if (!dev->lotg->otg.default_a) - dev->lotg->hsm.b_hnp_enable = 1; - #endif -- } else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) -+ dev->dev_status |= (1 << wValue); -+ break; -+ case USB_DEVICE_A_HNP_SUPPORT: - dev->gadget.a_hnp_support = 1; -- else if (setup->bRequest == -- USB_DEVICE_A_ALT_HNP_SUPPORT) -+ dev->dev_status |= (1 << wValue); -+ break; -+ case USB_DEVICE_A_ALT_HNP_SUPPORT: - dev->gadget.a_alt_hnp_support = 1; -- else -+ dev->dev_status |= (1 << wValue); - break; -- rc = 0; -+ default: -+ rc = -EOPNOTSUPP; -+ break; -+ } - } else - break; - -@@ -2387,7 +2465,7 @@ static int process_ep_req(struct langwell_udc *dev, int index, - } else { - /* transfers completed with errors */ - if (dtd_status & DTD_STS_ACTIVE) { -- DBG(dev, "request not completed\n"); -+ DBG(dev, "dTD status ACTIVE dQH[%d]\n", index); - retval = 1; - return retval; - } else if (dtd_status & DTD_STS_HALTED) { -@@ -2586,18 +2664,14 @@ static void handle_port_change(struct langwell_udc *dev) - /* LPM L0 to L1 */ - if (dev->lpm && dev->lpm_state == LPM_L0) - if (portsc1 & PORTS_SUSP && portsc1 & PORTS_SLP) { -- INFO(dev, "LPM L0 to L1\n"); -- dev->lpm_state = LPM_L1; -+ INFO(dev, "LPM L0 to L1\n"); -+ dev->lpm_state = LPM_L1; - } - - /* LPM L1 to L0, force resume or remote wakeup finished */ - if (dev->lpm && dev->lpm_state == LPM_L1) - if (!(portsc1 & PORTS_SUSP)) { -- if (portsc1 & PORTS_SLP) -- INFO(dev, "LPM L1 to L0, force resume\n"); -- else -- INFO(dev, "LPM L1 to L0, remote wakeup\n"); -- -+ INFO(dev, "LPM L1 to L0\n"); - dev->lpm_state = LPM_L0; - } - -@@ -2634,7 +2708,10 @@ static void handle_usb_reset(struct langwell_udc *dev) - - dev->ep0_dir = USB_DIR_OUT; - dev->ep0_state = WAIT_FOR_SETUP; -- dev->remote_wakeup = 0; /* default to 0 on reset */ -+ -+ /* remote wakeup reset to 0 when the device is reset */ -+ dev->remote_wakeup = 0; -+ dev->dev_status = 1 << USB_DEVICE_SELF_POWERED; - dev->gadget.b_hnp_enable = 0; - dev->gadget.a_hnp_support = 0; - dev->gadget.a_alt_hnp_support = 0; -@@ -2699,6 +2776,7 @@ static void handle_usb_reset(struct langwell_udc *dev) - static void handle_bus_suspend(struct langwell_udc *dev) - { - u32 devlc; -+ u8 devlc_byte2; - DBG(dev, "---> %s()\n", __func__); - - dev->resume_state = dev->usb_state; -@@ -2706,7 +2784,8 @@ static void handle_bus_suspend(struct langwell_udc *dev) - - #ifdef OTG_TRANSCEIVER - if (dev->lotg->otg.default_a) { -- if (dev->lotg->hsm.b_bus_suspend_vld == 1) { -+ /* ignore host LPM capability checking during enumeration */ -+ if (dev->lotg->hsm.b_bus_suspend_vld == 2) { - dev->lotg->hsm.b_bus_suspend = 1; - /* notify transceiver the state changes */ - if (spin_trylock(&dev->lotg->wq_lock)) { -@@ -2741,7 +2820,11 @@ static void handle_bus_suspend(struct langwell_udc *dev) - devlc = readl(&dev->op_regs->devlc); - VDBG(dev, "devlc = 0x%08x\n", devlc); - devlc |= LPM_PHCD; -- writel(devlc, &dev->op_regs->devlc); -+ /* FIXME: workaround for Langwell A1/A2/A3 sighting */ -+ devlc_byte2 = (devlc >> 16) & 0xff; -+ writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2); -+ devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "enter PHY low power suspend, devlc = 0x%08x\n", devlc); - - DBG(dev, "<--- %s()\n", __func__); - } -@@ -2750,6 +2833,7 @@ static void handle_bus_suspend(struct langwell_udc *dev) - static void handle_bus_resume(struct langwell_udc *dev) - { - u32 devlc; -+ u8 devlc_byte2; - DBG(dev, "---> %s()\n", __func__); - - dev->usb_state = dev->resume_state; -@@ -2759,7 +2843,11 @@ static void handle_bus_resume(struct langwell_udc *dev) - devlc = readl(&dev->op_regs->devlc); - VDBG(dev, "devlc = 0x%08x\n", devlc); - devlc &= ~LPM_PHCD; -- writel(devlc, &dev->op_regs->devlc); -+ /* FIXME: workaround for Langwell A1/A2/A3 sighting */ -+ devlc_byte2 = (devlc >> 16) & 0xff; -+ writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2); -+ devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "exit PHY low power suspend, devlc = 0x%08x\n", devlc); - - #ifdef OTG_TRANSCEIVER - if (dev->lotg->otg.default_a == 0) -@@ -2898,6 +2986,50 @@ static void gadget_release(struct device *_dev) - } - - -+/* enable SRAM caching if SRAM detected */ -+static void sram_init(struct langwell_udc *dev) -+{ -+ struct pci_dev *pdev = dev->pdev; -+ -+ DBG(dev, "---> %s()\n", __func__); -+ -+ dev->sram_addr = pci_resource_start(pdev, 1); -+ dev->sram_size = pci_resource_len(pdev, 1); -+ INFO(dev, "Found private SRAM at %x size:%x\n", -+ dev->sram_addr, dev->sram_size); -+ dev->got_sram = 1; -+ -+ if (pci_request_region(pdev, 1, kobject_name(&pdev->dev.kobj))) { -+ WARNING(dev, "SRAM request failed\n"); -+ dev->got_sram = 0; -+ } else if (!dma_declare_coherent_memory(&pdev->dev, dev->sram_addr, -+ dev->sram_addr, dev->sram_size, DMA_MEMORY_MAP)) { -+ WARNING(dev, "SRAM DMA declare failed\n"); -+ pci_release_region(pdev, 1); -+ dev->got_sram = 0; -+ } -+ -+ DBG(dev, "<--- %s()\n", __func__); -+} -+ -+ -+/* release SRAM caching */ -+static void sram_deinit(struct langwell_udc *dev) -+{ -+ struct pci_dev *pdev = dev->pdev; -+ -+ DBG(dev, "---> %s()\n", __func__); -+ -+ dma_release_declared_memory(&pdev->dev); -+ pci_release_region(pdev, 1); -+ -+ dev->got_sram = 0; -+ -+ INFO(dev, "release SRAM caching\n"); -+ DBG(dev, "<--- %s()\n", __func__); -+} -+ -+ - /* tear down the binding between this driver and the pci device */ - static void langwell_udc_remove(struct pci_dev *pdev) - { -@@ -2910,19 +3042,25 @@ static void langwell_udc_remove(struct pci_dev *pdev) - - dev->done = &done; - -- /* free memory allocated in probe */ -+#ifndef OTG_TRANSCEIVER -+ /* free dTD dma_pool and dQH */ - if (dev->dtd_pool) - dma_pool_destroy(dev->dtd_pool); - -+ if (dev->ep_dqh) -+ dma_free_coherent(&pdev->dev, dev->ep_dqh_size, -+ dev->ep_dqh, dev->ep_dqh_dma); -+ -+ /* release SRAM caching */ -+ if (dev->has_sram && dev->got_sram) -+ sram_deinit(dev); -+#endif -+ - if (dev->status_req) { - kfree(dev->status_req->req.buf); - kfree(dev->status_req); - } - -- if (dev->ep_dqh) -- dma_free_coherent(&pdev->dev, dev->ep_dqh_size, -- dev->ep_dqh, dev->ep_dqh_dma); -- - kfree(dev->ep); - - /* diable IRQ handler */ -@@ -2954,6 +3092,7 @@ static void langwell_udc_remove(struct pci_dev *pdev) - - device_unregister(&dev->gadget.dev); - device_remove_file(&pdev->dev, &dev_attr_langwell_udc); -+ device_remove_file(&pdev->dev, &dev_attr_remote_wakeup); - - #ifndef OTG_TRANSCEIVER - pci_set_drvdata(pdev, NULL); -@@ -2976,9 +3115,9 @@ static int langwell_udc_probe(struct pci_dev *pdev, - struct langwell_udc *dev; - #ifndef OTG_TRANSCEIVER - unsigned long resource, len; -+ size_t size; - #endif - void __iomem *base = NULL; -- size_t size; - int retval; - - if (the_controller) { -@@ -3049,7 +3188,15 @@ static int langwell_udc_probe(struct pci_dev *pdev, - goto error; - } - -+ dev->has_sram = 1; -+ dev->got_sram = 0; -+ VDBG(dev, "dev->has_sram: %d\n", dev->has_sram); -+ - #ifndef OTG_TRANSCEIVER -+ /* enable SRAM caching if detected */ -+ if (dev->has_sram && !dev->got_sram) -+ sram_init(dev); -+ - INFO(dev, "irq %d, io mem: 0x%08lx, len: 0x%08lx, pci mem 0x%p\n", - pdev->irq, resource, len, base); - /* enables bus-mastering for device dev */ -@@ -3094,6 +3241,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, - goto error; - } - -+#ifndef OTG_TRANSCEIVER - /* allocate device dQH memory */ - size = dev->ep_max * sizeof(struct langwell_dqh); - VDBG(dev, "orig size = %d\n", size); -@@ -3112,6 +3260,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, - } - dev->ep_dqh_size = size; - VDBG(dev, "ep_dqh_size = %d\n", dev->ep_dqh_size); -+#endif - - /* initialize ep0 status request structure */ - dev->status_req = kzalloc(sizeof(struct langwell_request), GFP_KERNEL); -@@ -3129,7 +3278,10 @@ static int langwell_udc_probe(struct pci_dev *pdev, - dev->resume_state = USB_STATE_NOTATTACHED; - dev->usb_state = USB_STATE_POWERED; - dev->ep0_dir = USB_DIR_OUT; -- dev->remote_wakeup = 0; /* default to 0 on reset */ -+ -+ /* remote wakeup reset to 0 when the device is reset */ -+ dev->remote_wakeup = 0; -+ dev->dev_status = 1 << USB_DEVICE_SELF_POWERED; - - #ifndef OTG_TRANSCEIVER - /* reset device controller */ -@@ -3159,7 +3311,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, - #ifndef OTG_TRANSCEIVER - /* reset ep0 dQH and endptctrl */ - ep0_reset(dev); --#endif - - /* create dTD dma_pool resource */ - dev->dtd_pool = dma_pool_create("langwell_dtd", -@@ -3172,6 +3323,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, - retval = -ENOMEM; - goto error; - } -+#endif - - /* done */ - INFO(dev, "%s\n", driver_desc); -@@ -3183,9 +3335,7 @@ static int langwell_udc_probe(struct pci_dev *pdev, - INFO(dev, "Support USB LPM: %s\n", dev->lpm ? "Yes" : "No"); - - VDBG(dev, "After langwell_udc_probe(), print all registers:\n"); --#ifdef VERBOSE - print_all_registers(dev); --#endif - - the_controller = dev; - -@@ -3197,9 +3347,15 @@ static int langwell_udc_probe(struct pci_dev *pdev, - if (retval) - goto error; - -+ retval = device_create_file(&pdev->dev, &dev_attr_remote_wakeup); -+ if (retval) -+ goto error_attr1; -+ - VDBG(dev, "<--- %s()\n", __func__); - return 0; - -+error_attr1: -+ device_remove_file(&pdev->dev, &dev_attr_langwell_udc); - error: - if (dev) { - DBG(dev, "<--- %s()\n", __func__); -@@ -3215,6 +3371,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) - { - struct langwell_udc *dev = the_controller; - u32 devlc; -+ u8 devlc_byte2; - - DBG(dev, "---> %s()\n", __func__); - -@@ -3226,10 +3383,21 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) - free_irq(pdev->irq, dev); - dev->got_irq = 0; - -- - /* save PCI state */ - pci_save_state(pdev); - -+ /* free dTD dma_pool and dQH */ -+ if (dev->dtd_pool) -+ dma_pool_destroy(dev->dtd_pool); -+ -+ if (dev->ep_dqh) -+ dma_free_coherent(&pdev->dev, dev->ep_dqh_size, -+ dev->ep_dqh, dev->ep_dqh_dma); -+ -+ /* release SRAM caching */ -+ if (dev->has_sram && dev->got_sram) -+ sram_deinit(dev); -+ - /* set device power state */ - pci_set_power_state(pdev, PCI_D3hot); - -@@ -3237,7 +3405,11 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) - devlc = readl(&dev->op_regs->devlc); - VDBG(dev, "devlc = 0x%08x\n", devlc); - devlc |= LPM_PHCD; -- writel(devlc, &dev->op_regs->devlc); -+ /* FIXME: workaround for Langwell A1/A2/A3 sighting */ -+ devlc_byte2 = (devlc >> 16) & 0xff; -+ writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2); -+ devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "enter PHY low power suspend, devlc = 0x%08x\n", devlc); - - DBG(dev, "<--- %s()\n", __func__); - return 0; -@@ -3249,6 +3421,8 @@ static int langwell_udc_resume(struct pci_dev *pdev) - { - struct langwell_udc *dev = the_controller; - u32 devlc; -+ u8 devlc_byte2; -+ size_t size; - - DBG(dev, "---> %s()\n", __func__); - -@@ -3256,19 +3430,55 @@ static int langwell_udc_resume(struct pci_dev *pdev) - devlc = readl(&dev->op_regs->devlc); - VDBG(dev, "devlc = 0x%08x\n", devlc); - devlc &= ~LPM_PHCD; -- writel(devlc, &dev->op_regs->devlc); -+ /* FIXME: workaround for Langwell A1/A2/A3 sighting */ -+ devlc_byte2 = (devlc >> 16) & 0xff; -+ writeb(devlc_byte2, (u8 *)&dev->op_regs->devlc + 2); -+ devlc = readl(&dev->op_regs->devlc); -+ VDBG(dev, "exit PHY low power suspend, devlc = 0x%08x\n", devlc); - - /* set device D0 power state */ - pci_set_power_state(pdev, PCI_D0); - -+ /* enable SRAM caching if detected */ -+ if (dev->has_sram && !dev->got_sram) -+ sram_init(dev); -+ -+ /* allocate device dQH memory */ -+ size = dev->ep_max * sizeof(struct langwell_dqh); -+ VDBG(dev, "orig size = %d\n", size); -+ if (size < DQH_ALIGNMENT) -+ size = DQH_ALIGNMENT; -+ else if ((size % DQH_ALIGNMENT) != 0) { -+ size += DQH_ALIGNMENT + 1; -+ size &= ~(DQH_ALIGNMENT - 1); -+ } -+ dev->ep_dqh = dma_alloc_coherent(&pdev->dev, size, -+ &dev->ep_dqh_dma, GFP_KERNEL); -+ if (!dev->ep_dqh) { -+ ERROR(dev, "allocate dQH memory failed\n"); -+ return -ENOMEM; -+ } -+ dev->ep_dqh_size = size; -+ VDBG(dev, "ep_dqh_size = %d\n", dev->ep_dqh_size); -+ -+ /* create dTD dma_pool resource */ -+ dev->dtd_pool = dma_pool_create("langwell_dtd", -+ &dev->pdev->dev, -+ sizeof(struct langwell_dtd), -+ DTD_ALIGNMENT, -+ DMA_BOUNDARY); -+ -+ if (!dev->dtd_pool) -+ return -ENOMEM; -+ - /* restore PCI state */ - pci_restore_state(pdev); - - /* enable IRQ handler */ -- if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED, driver_name, dev) -- != 0) { -+ if (request_irq(pdev->irq, langwell_irq, IRQF_SHARED, -+ driver_name, dev) != 0) { - ERROR(dev, "request interrupt %d failed\n", pdev->irq); -- return -1; -+ return -EBUSY; - } - dev->got_irq = 1; - -diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h -index 9719934..323c574 100644 ---- a/drivers/usb/gadget/langwell_udc.h -+++ b/drivers/usb/gadget/langwell_udc.h -@@ -174,7 +174,7 @@ enum lpm_state { - struct langwell_udc { - /* each pci device provides one gadget, several endpoints */ - struct usb_gadget gadget; -- spinlock_t lock; /* device lock */ -+ spinlock_t lock; /* device lock */ - struct langwell_ep *ep; - struct usb_gadget_driver *driver; - struct otg_transceiver *transceiver; -@@ -199,7 +199,9 @@ struct langwell_udc { - vbus_active:1, - suspended:1, - stopped:1, -- lpm:1; /* LPM capability */ -+ lpm:1, /* LPM capability */ -+ has_sram:1, /* SRAM caching */ -+ got_sram:1; - - /* pci state used to access those endpoints */ - struct pci_dev *pdev; -@@ -224,5 +226,12 @@ struct langwell_udc { - - /* make sure release() is done */ - struct completion *done; -+ -+ /* for private SRAM caching */ -+ unsigned int sram_addr; -+ unsigned int sram_size; -+ -+ /* device status data for get_status request */ -+ u16 dev_status; - }; - -diff --git a/drivers/usb/gadget/still_image.c b/drivers/usb/gadget/still_image.c -new file mode 100644 -index 0000000..94c17ce ---- /dev/null -+++ b/drivers/usb/gadget/still_image.c -@@ -0,0 +1,4566 @@ -+/* -+ * still_image.c -- Lite USB Still Image Capture Gadget, for USB development -+ * Copyright (C) 2009, Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License along with -+ * this program; if not, write to the Free Software Foundation, Inc., -+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. -+ * -+ */ -+ -+ -+/* -+ * This code is partly based on: -+ * File-backed USB Storage Gadget driver, Copyright (C) 2003-2008 Alan Stern -+ * -+ * -+ * Refer to the USB Device Class Definition for Still Image Capture Device: -+ * http://www.usb.org/developers/devclass_docs/usb_still_img10.zip -+ * -+ * -+ * Supported PIMA 15740/PTP operations: -+ * - GetDeviceInfo -+ * - OpenSession -+ * - CloseSession -+ * - GetStorageIDs -+ * - GetStorageInfo -+ * - GetNumObjects -+ * - GetObjectHandles -+ * - GetObjectInfo -+ * - GetObject -+ * - DeleteObject -+ * - SendObjectInfo -+ * - SendObject -+ * - CopyObject -+ * - MoveObject -+ * -+ * Supported object formats: -+ * - EXIF/JPEG, JFIF -+ * - PNG -+ * - TIFF, TIFF/IT, TIFF/EP -+ * - BMP -+ * - GIF -+ * - Unknown image object -+ * - Undefined non-image object -+ * -+ * Supported PIMA 15740/PTP events: -+ * - N/A -+ * -+ * Storage filesystem type: -+ * - Generic hierarchical -+ * -+ * -+ * Module options: -+ * folder=foldername Default NULL, name of the backing folder -+ * vendor=0xVVVV Default 0x8087 (Intel), USB Vendor ID -+ * product=0xPPPP Default 0x811e, USB Product ID -+ * release=0xRRRR Override the USB release number (bcdDevice) -+ * buflen=N Default N=16384, buffer size used (will be -+ * rounded down to a multiple of -+ * PAGE_CACHE_SIZE) -+ * -+ * Sysfs attribute file: -+ * folder read/write the name of the backing folder -+ * -+ */ -+ -+ -+#define VERBOSE_DEBUG -+ -+#include <linux/blkdev.h> -+#include <linux/completion.h> -+#include <linux/dcache.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/fcntl.h> -+#include <linux/file.h> -+#include <linux/fs.h> -+#include <linux/vfs.h> -+#include <linux/namei.h> -+#include <linux/kref.h> -+#include <linux/kthread.h> -+#include <linux/limits.h> -+#include <linux/rwsem.h> -+#include <linux/slab.h> -+#include <linux/spinlock.h> -+#include <linux/string.h> -+#include <linux/freezer.h> -+#include <linux/utsname.h> -+#include <linux/sort.h> -+ -+#include <linux/usb/ch9.h> -+#include <linux/usb/gadget.h> -+ -+#include "gadget_chips.h" -+ -+#include "usbstring.c" -+#include "config.c" -+#include "epautoconf.c" -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define DRIVER_DESC "Still Image Gadget" -+#define DRIVER_NAME "g_still_image" -+#define DRIVER_VERSION "Apr 30, 2010" -+ -+ -+static const char longname[] = DRIVER_DESC; -+static const char shortname[] = DRIVER_NAME; -+ -+ -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_AUTHOR("Xiaochen Shen <xiaochen.shen@intel.com>; " -+ "Hang Yuan <hang.yuan@intel.com>"); -+MODULE_VERSION(DRIVER_VERSION); -+MODULE_LICENSE("GPL"); -+ -+ -+/* -+ * Intel Corporation donates this product ID. -+ * -+ * DO NOT REUSE THESE IDs with any other driver -+ * instead: allocate your own, using normal USB-IF procedures. -+ */ -+#define DRIVER_VENDOR_ID 0x8087 -+#define DRIVER_PRODUCT_ID 0x811e -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define MDBG(fmt, args...) \ -+ pr_debug(DRIVER_NAME ": " fmt, ## args) -+#define MINFO(fmt, args...) \ -+ pr_info(DRIVER_NAME ": " fmt, ## args) -+ -+#ifdef DEBUG -+#define DBG(d, fmt, args...) \ -+ dev_dbg(&(d)->gadget->dev, fmt, ## args) -+#else -+#define DBG(dev, fmt, args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+ -+ -+#ifndef DEBUG -+#undef VERBOSE_DEBUG -+#endif /* !DEBUG */ -+ -+#ifdef VERBOSE_DEBUG -+#define VDBG DBG -+#else -+#define VDBG(sti, fmt, args...) \ -+ do { } while (0) -+#endif /* VERBOSE_DEBUG */ -+ -+#define ERROR(d, fmt, args...) \ -+ dev_err(&(d)->gadget->dev, fmt, ## args) -+#define WARNING(d, fmt, args...) \ -+ dev_warn(&(d)->gadget->dev, fmt, ## args) -+#define INFO(d, fmt, args...) \ -+ dev_info(&(d)->gadget->dev, fmt, ## args) -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* encapsulate the module parameter settings */ -+ -+static struct { -+ char *folder; -+ unsigned short vendor; -+ unsigned short product; -+ unsigned short release; -+ unsigned int buflen; -+} mod_data = { /* default values */ -+ .vendor = DRIVER_VENDOR_ID, -+ .product = DRIVER_PRODUCT_ID, -+ .release = 0xffff, /* use controller chip type */ -+ .buflen = 16384, -+}; -+ -+ -+module_param_named(folder, mod_data.folder, charp, S_IRUGO); -+MODULE_PARM_DESC(folder, "name of the backing folder"); -+ -+module_param_named(vendor, mod_data.vendor, ushort, S_IRUGO); -+MODULE_PARM_DESC(vendor, "USB Vendor ID"); -+ -+module_param_named(product, mod_data.product, ushort, S_IRUGO); -+MODULE_PARM_DESC(product, "USB Product ID"); -+ -+module_param_named(release, mod_data.release, ushort, S_IRUGO); -+MODULE_PARM_DESC(release, "USB release number"); -+ -+module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); -+MODULE_PARM_DESC(buflen, "I/O buffer size"); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * DESCRIPTORS ... most are static, but strings and (full) configuration -+ * descriptors are built on demand. Also the (static) config and interface -+ * descriptors are adjusted during sti_bind(). -+ */ -+#define STRING_MANUFACTURER 1 -+#define STRING_PRODUCT 2 -+#define STRING_SERIAL 3 -+#define STRING_CONFIG 4 -+#define STRING_INTERFACE 5 -+ -+ -+/* only one configuration */ -+#define CONFIG_VALUE 1 -+ -+static struct usb_device_descriptor -+device_desc = { -+ .bLength = sizeof device_desc, -+ .bDescriptorType = USB_DT_DEVICE, -+ -+ .bcdUSB = cpu_to_le16(0x0200), -+ .bDeviceClass = USB_CLASS_PER_INTERFACE, -+ -+ /* the next three values can be overridden by module parameters */ -+ .idVendor = cpu_to_le16(DRIVER_VENDOR_ID), -+ .idProduct = cpu_to_le16(DRIVER_PRODUCT_ID), -+ .bcdDevice = cpu_to_le16(0xffff), -+ -+ .iManufacturer = STRING_MANUFACTURER, -+ .iProduct = STRING_PRODUCT, -+ .iSerialNumber = STRING_SERIAL, -+ .bNumConfigurations = 1, -+}; -+ -+static struct usb_config_descriptor -+config_desc = { -+ .bLength = sizeof config_desc, -+ .bDescriptorType = USB_DT_CONFIG, -+ -+ /* wTotalLength computed by usb_gadget_config_buf() */ -+ .bNumInterfaces = 1, -+ .bConfigurationValue = CONFIG_VALUE, -+ .iConfiguration = STRING_CONFIG, -+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -+ .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -+}; -+ -+static struct usb_otg_descriptor -+otg_desc = { -+ .bLength = sizeof(otg_desc), -+ .bDescriptorType = USB_DT_OTG, -+ -+ .bmAttributes = USB_OTG_SRP, -+}; -+ -+ -+/* one interface */ -+static struct usb_interface_descriptor -+intf_desc = { -+ .bLength = sizeof intf_desc, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bNumEndpoints = 3, /* adjusted during sti_bind() */ -+ .bInterfaceClass = USB_CLASS_STILL_IMAGE, -+ .bInterfaceSubClass = 0x01, /* Still Image Capture device */ -+ .bInterfaceProtocol = 0x01, /* Bulk-only protocol */ -+ .iInterface = STRING_INTERFACE, -+}; -+ -+ -+/* two full-speed endpoint descriptors: bulk-in, bulk-out */ -+ -+static struct usb_endpoint_descriptor -+fs_bulk_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_IN, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ /* wMaxPacketSize set by autoconfiguration */ -+}; -+ -+static struct usb_endpoint_descriptor -+fs_bulk_out_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_OUT, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ /* wMaxPacketSize set by autoconfiguration */ -+}; -+ -+static struct usb_endpoint_descriptor -+fs_intr_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_IN, -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .wMaxPacketSize = cpu_to_le16(2), -+ .bInterval = 32, /* frames -> 32 ms */ -+}; -+ -+static const struct usb_descriptor_header *fs_function[] = { -+ (struct usb_descriptor_header *) &otg_desc, -+ (struct usb_descriptor_header *) &intf_desc, -+ (struct usb_descriptor_header *) &fs_bulk_in_desc, -+ (struct usb_descriptor_header *) &fs_bulk_out_desc, -+ (struct usb_descriptor_header *) &fs_intr_in_desc, -+ NULL, -+}; -+ -+#define FS_FUNCTION_PRE_EP_ENTRIES 2 -+ -+ -+/* -+ * USB 2.0 devices need to expose both high speed and full speed -+ * descriptors, unless they only run at full speed. -+ * -+ * That means alternate endpoint descriptors (bigger packets) -+ * and a "device qualifier" ... plus more construction options -+ * for the config descriptor. -+ */ -+static struct usb_qualifier_descriptor -+dev_qualifier = { -+ .bLength = sizeof dev_qualifier, -+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, -+ -+ .bcdUSB = cpu_to_le16(0x0200), -+ .bDeviceClass = USB_CLASS_PER_INTERFACE, -+ -+ .bNumConfigurations = 1, -+}; -+ -+static struct usb_endpoint_descriptor -+hs_bulk_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ /* bEndpointAddress copied from fs_bulk_in_desc during sti_bind() */ -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = cpu_to_le16(512), -+}; -+ -+static struct usb_endpoint_descriptor -+hs_bulk_out_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ /* bEndpointAddress copied from fs_bulk_out_desc during sti_bind() */ -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = cpu_to_le16(512), -+ .bInterval = 1, /* NAK every 1 uframe */ -+}; -+ -+static struct usb_endpoint_descriptor -+hs_intr_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ /* bEndpointAddress copied from fs_intr_in_desc during sti_bind() */ -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .wMaxPacketSize = cpu_to_le16(2), -+ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ -+}; -+ -+static const struct usb_descriptor_header *hs_function[] = { -+ (struct usb_descriptor_header *) &otg_desc, -+ (struct usb_descriptor_header *) &intf_desc, -+ (struct usb_descriptor_header *) &hs_bulk_in_desc, -+ (struct usb_descriptor_header *) &hs_bulk_out_desc, -+ (struct usb_descriptor_header *) &hs_intr_in_desc, -+ NULL, -+}; -+ -+#define HS_FUNCTION_PRE_EP_ENTRIES 2 -+ -+ -+/* maxpacket and other transfer characteristics vary by speed. */ -+static struct usb_endpoint_descriptor * -+ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, -+ struct usb_endpoint_descriptor *hs) -+{ -+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) -+ return hs; -+ -+ return fs; -+} -+ -+static char manufacturer[64]; -+static char serial[13]; -+ -+/* static strings, in UTF-8 (for simplicity we use only ASCII characters) */ -+static struct usb_string strings[] = { -+ {STRING_MANUFACTURER, manufacturer}, -+ {STRING_PRODUCT, longname}, -+ {STRING_SERIAL, serial}, -+ {STRING_CONFIG, "Self-powered"}, -+ {STRING_INTERFACE, "Still Image"}, -+ {} -+}; -+ -+static struct usb_gadget_strings stringtab = { -+ .language = 0x0409, /* en-us */ -+ .strings = strings, -+}; -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* protocol, driver and device data structures */ -+ -+/* big enough to hold the biggest descriptor */ -+#define EP0_BUFSIZE 256 -+ -+#define DELAYED_STATUS (EP0_BUFSIZE + 999) -+ -+/* number of buffers we will use. 2 is enough for double-buffering */ -+#define NUM_BUFFERS 2 -+ -+/* PIMA 15740, operation and response datasets have at most 5 parameters */ -+#define PARAM_NUM_MAX 5 -+ -+/* PIMA 15740 generic container head length */ -+#define PIMA15740_CONTAINER_LEN 12 -+ -+/* storage id, description */ -+#define STORAGE_ID 0x00010001 -+#define STORAGE_DESCRIPTION "Built-in Storage" -+ -+/* Still Image class-specific requests */ -+#define STI_CANCEL_REQUEST 0x64 -+#define STI_GET_EXTENDED_EVENT_DATA 0x65 -+#define STI_DEVICE_RESET_REQUEST 0x66 -+#define STI_GET_DEVICE_STATUS 0x67 -+ -+#define STI_CANCEL_REQUEST_LENGTH 0x0006 -+#define STI_CANCEL_REQUEST_CODE 0x4001 -+ -+/* supported PIMA 15740 operations */ -+#define PIMA15740_OP_GET_DEVICE_INFO 0x1001 -+#define PIMA15740_OP_OPEN_SESSION 0x1002 -+#define PIMA15740_OP_CLOSE_SESSION 0x1003 -+#define PIMA15740_OP_GET_STORAGE_IDS 0x1004 -+#define PIMA15740_OP_GET_STORAGE_INFO 0x1005 -+#define PIMA15740_OP_GET_NUM_OBJECTS 0x1006 -+#define PIMA15740_OP_GET_OBJECT_HANDLES 0x1007 -+#define PIMA15740_OP_GET_OBJECT_INFO 0x1008 -+#define PIMA15740_OP_GET_OBJECT 0x1009 -+#define PIMA15740_OP_DELETE_OBJECT 0x100b -+#define PIMA15740_OP_SEND_OBJECT_INFO 0x100c -+#define PIMA15740_OP_SEND_OBJECT 0x100d -+#define PIMA15740_OP_MOVE_OBJECT 0x1019 -+#define PIMA15740_OP_COPY_OBJECT 0x101a -+ -+/* PIMA 15740 responses definition */ -+#define PIMA15740_RES_UNDEFINED 0x2000 -+#define PIMA15740_RES_OK 0x2001 -+#define PIMA15740_RES_GENERAL_ERROR 0x2002 -+#define PIMA15740_RES_SESSION_NOT_OPEN 0x2003 -+#define PIMA15740_RES_INVALID_TRANS_ID 0x2004 -+#define PIMA15740_RES_OPERATION_NOT_SUPPORTED 0x2005 -+#define PIMA15740_RES_PARAMETER_NOT_SUPPORTED 0x2006 -+#define PIMA15740_RES_INCOMPLETE_TRANSFER 0x2007 -+#define PIMA15740_RES_INVALID_STORAGE_ID 0x2008 -+#define PIMA15740_RES_INVALID_OBJECT_HANDLE 0x2009 -+#define PIMA15740_RES_DEVICE_PROP_NOT_SUPPORTED 0x200a -+#define PIMA15740_RES_INVALID_OBJECT_FORMAT 0x200b -+#define PIMA15740_RES_STORE_FULL 0x200c -+#define PIMA15740_RES_OBJECT_WRITE_PROTECTED 0x200d -+#define PIMA15740_RES_STORE_READ_ONLY 0x200e -+#define PIMA15740_RES_ACCESS_DENIED 0x200f -+#define PIMA15740_RES_NO_THUMBNAIL_PRESENT 0x2010 -+#define PIMA15740_RES_SELF_TEST_FAILED 0x2011 -+#define PIMA15740_RES_PARTIAL_DELETION 0x2012 -+#define PIMA15740_RES_STORE_NOT_AVAILABLE 0x2013 -+#define PIMA15740_RES_SPEC_BY_FORMAT_UNSUP 0x2014 -+#define PIMA15740_RES_NO_VALID_OBJECT_INFO 0x2015 -+#define PIMA15740_RES_INVALID_CODE_FORMAT 0x2016 -+#define PIMA15740_RES_UNKNOWN_VENDOR_CODE 0x2017 -+#define PIMA15740_RES_CAPTURE_ALREADY_TERM 0x2018 -+#define PIMA15740_RES_DEVICE_BUSY 0x2019 -+#define PIMA15740_RES_INVALID_PARENT_OBJECT 0x201a -+#define PIMA15740_RES_INVALID_DEV_PROP_FORMAT 0x201b -+#define PIMA15740_RES_INVALID_DEV_PROP_VALUE 0x201c -+#define PIMA15740_RES_INVALID_PARAMETER 0x201d -+#define PIMA15740_RES_SESSION_ALREADY_OPEN 0x201e -+#define PIMA15740_RES_TRANSACTION_CANCELLED 0x201f -+#define PIMA15740_RES_SPEC_OF_DESTINATION_UNSUP 0x2020 -+ -+/* PIMA 15740 functional mode */ -+#define PIMA15740_STANDARD_MODE 0x0000 -+#define PIMA15740_SLEEP_STATE_MODE 0x0001 -+ -+/* PIMA 15740 storage type */ -+#define PIMA15740_STOR_UNDEFINED 0x0000 -+#define PIMA15740_STOR_FIXED_ROM 0x0001 -+#define PIMA15740_STOR_REMOVABLE_ROM 0x0002 -+#define PIMA15740_STOR_FIXED_RAM 0x0003 -+#define PIMA15740_STOR_REMOVABLE_RAM 0x0004 -+ -+/* PIMA 15740 filesystem type */ -+#define PIMA15740_FS_UNDEFINED 0x0000 -+#define PIMA15740_FS_GENERIC_FLAT 0x0001 -+#define PIMA15740_FS_HIERARCHICAL 0x0002 -+#define PIMA15740_FS_DCF 0x0003 -+ -+/* PIMA 15740 access capability */ -+#define PIMA15740_ACCESS_CAP_RW 0x0000 -+#define PIMA15740_ACCESS_CAP_RO_WO_DELITION 0x0001 -+#define PIMA15740_ACCESS_CAP_RO_W_DELITION 0x0002 -+ -+/* PIMA 15740 object format codes */ -+#define PIMA15740_FMT_A_UNDEFINED 0x3000 -+#define PIMA15740_FMT_A_ASSOCIATION 0x3001 -+#define PIMA15740_FMT_I_UNDEFINED 0x3800 -+#define PIMA15740_FMT_I_EXIF_JPEG 0x3801 -+#define PIMA15740_FMT_I_TIFF_EP 0x3802 -+#define PIMA15740_FMT_I_FLASHPIX 0x3803 -+#define PIMA15740_FMT_I_BMP 0x3804 -+#define PIMA15740_FMT_I_CIFF 0x3805 -+#define PIMA15740_FMT_I_GIF 0x3807 -+#define PIMA15740_FMT_I_JFIF 0x3808 -+#define PIMA15740_FMT_I_PCD 0x3809 -+#define PIMA15740_FMT_I_PICT 0x380a -+#define PIMA15740_FMT_I_PNG 0x380b -+#define PIMA15740_FMT_I_TIFF 0x380d -+#define PIMA15740_FMT_I_TIFF_IT 0x380e -+#define PIMA15740_FMT_I_JP2 0x380f -+#define PIMA15740_FMT_I_JPX 0x3810 -+ -+/* PIMA 15740 object protection status */ -+#define PIMA15740_OBJECT_NO_PROTECTION 0x0000 -+#define PIMA15740_OBJECT_READ_ONLY 0x0001 -+ -+/* PIMA 15740 object association type */ -+#define PIMA15740_AS_UNDEFINED 0x0000 -+#define PIMA15740_AS_GENERIC_FOLDER 0x0001 -+ -+ -+static const char storage_desc[] = STORAGE_DESCRIPTION; -+static const char device_version[] = DRIVER_VERSION; -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* PIMA 15740 data structure */ -+ -+enum pima15740_container_type { -+ TYPE_UNDEFINED = 0, -+ TYPE_COMMAND_BLOCK = 1, -+ TYPE_DATA_BLOCK = 2, -+ TYPE_RESPONSE_BLOCK = 3, -+ TYPE_EVENT_BLOCK = 4, -+}; -+ -+/* PIMA15740 generic container structure, little endian */ -+struct pima15740_container { -+ __le32 container_len; -+ __le16 container_type; -+ __le16 code; -+ __le32 transaction_id; -+} __attribute__ ((packed)); -+ -+/* data stage of Get Extended Event Data */ -+struct sti_ext_event { -+ u16 event_code; -+ u32 transaction_id; -+ u16 param_num; -+} __attribute__ ((packed)); -+ -+/* data stage of Get Device Status Data */ -+struct sti_dev_status { -+ u16 wlength; -+ u16 code; -+} __attribute__ ((packed)); -+ -+ -+/* DeviceInfo Dataset */ -+struct pima15740_device_info { -+ u16 standard_version; -+ u32 vendor_extension_id; -+ u16 vendor_extension_version; -+ u8 vendor_extension_desc_len; -+ u8 vendor_extension_desc[0]; -+ u16 functional_mode; -+ u32 operations_supported_count; -+ u16 operations_supported[14]; -+ u32 events_supported_count; -+ u16 events_supported[0]; -+ u32 device_properties_count; -+ u16 device_properties_supported[0]; -+ u32 capture_formats_count; -+ u16 capture_formats[0]; -+ u32 image_formats_count; -+ u16 image_formats[10]; -+ u8 manufacturer_len; -+ u8 manufacturer[sizeof(manufacturer) * 2]; -+ u8 model_len; -+ u8 model[sizeof(longname) * 2]; -+ u8 device_version_len; -+ u8 device_version[sizeof(device_version) * 2]; -+ u8 serial_number_len; -+ u8 serial_number[sizeof(serial) * 2]; -+} __attribute__ ((packed)); -+ -+static struct pima15740_device_info sti_device_info = { -+ .standard_version = 100, -+ .vendor_extension_id = 0, -+ .vendor_extension_version = 0, -+ .vendor_extension_desc_len = 0, -+ .functional_mode = PIMA15740_STANDARD_MODE, -+ .operations_supported_count = 14, -+ .operations_supported = { -+ cpu_to_le16(PIMA15740_OP_GET_DEVICE_INFO), -+ cpu_to_le16(PIMA15740_OP_OPEN_SESSION), -+ cpu_to_le16(PIMA15740_OP_CLOSE_SESSION), -+ cpu_to_le16(PIMA15740_OP_GET_STORAGE_IDS), -+ cpu_to_le16(PIMA15740_OP_GET_STORAGE_INFO), -+ cpu_to_le16(PIMA15740_OP_GET_NUM_OBJECTS), -+ cpu_to_le16(PIMA15740_OP_GET_OBJECT_HANDLES), -+ cpu_to_le16(PIMA15740_OP_GET_OBJECT_INFO), -+ cpu_to_le16(PIMA15740_OP_GET_OBJECT), -+ cpu_to_le16(PIMA15740_OP_DELETE_OBJECT), -+ cpu_to_le16(PIMA15740_OP_SEND_OBJECT_INFO), -+ cpu_to_le16(PIMA15740_OP_SEND_OBJECT), -+ cpu_to_le16(PIMA15740_OP_COPY_OBJECT), -+ cpu_to_le16(PIMA15740_OP_MOVE_OBJECT) -+ }, -+ .events_supported_count = 0, -+ .device_properties_count = 0, -+ .capture_formats_count = 0, -+ .image_formats_count = 10, -+ .image_formats = { -+ cpu_to_le16(PIMA15740_FMT_I_EXIF_JPEG), -+ cpu_to_le16(PIMA15740_FMT_I_JFIF), -+ cpu_to_le16(PIMA15740_FMT_I_PNG), -+ cpu_to_le16(PIMA15740_FMT_I_TIFF), -+ cpu_to_le16(PIMA15740_FMT_I_TIFF_EP), -+ cpu_to_le16(PIMA15740_FMT_I_TIFF_IT), -+ cpu_to_le16(PIMA15740_FMT_I_BMP), -+ cpu_to_le16(PIMA15740_FMT_I_GIF), -+ cpu_to_le16(PIMA15740_FMT_I_UNDEFINED), -+ cpu_to_le16(PIMA15740_FMT_A_UNDEFINED) -+ }, -+ /* others will be filled in sti_bind() */ -+}; -+ -+ -+/* StorageInfo Dataset */ -+struct pima15740_storage_info { -+ u16 storage_type; -+ u16 filesystem_type; -+ u16 access_capability; -+ u64 max_capacity; -+ u64 free_space_in_bytes; -+ u32 free_space_in_images; -+ u8 storage_desc_len; -+ u8 storage_desc[sizeof(storage_desc) * 2]; -+ u8 volume_label_len; -+ u8 volume_label[0]; -+} __attribute__ ((packed)); -+ -+static struct pima15740_storage_info sti_storage_info = { -+ .storage_type = cpu_to_le16(PIMA15740_STOR_FIXED_RAM), -+ .filesystem_type = cpu_to_le16(PIMA15740_FS_HIERARCHICAL), -+ .access_capability = cpu_to_le16(PIMA15740_ACCESS_CAP_RW), -+ .storage_desc_len = sizeof(storage_desc), -+ .volume_label_len = 0, -+ /* others will be filled later */ -+}; -+ -+ -+/* ObjectInfo Dataset */ -+struct pima15740_object_info { -+ u32 storage_id; -+ u16 object_format; -+ u16 protection_status; -+ u32 object_compressed_size; -+ u16 thumb_format; -+ u32 thumb_compressed_size; -+ u32 thumb_pix_width; -+ u32 thumb_pix_height; -+ u32 image_pix_width; -+ u32 image_pix_height; -+ u32 image_bit_depth; -+ u32 parent_object; -+ u16 association_type; -+ u32 association_desc; -+ u32 sequence_number; -+ /* filename, capture date, modification date, keywords */ -+ u8 obj_strings[]; /* size will be fixed later */ -+} __attribute__ ((packed)); -+ -+/* object list element with object info data */ -+struct sti_object { -+ struct list_head list; -+ u32 obj_handle; -+ u32 parent_object; -+ u32 storage_id; -+ int is_dir; -+ int send_valid; -+ size_t obj_info_size; -+ char filename[NAME_MAX]; -+ char full_path[PATH_MAX]; -+ struct pima15740_object_info obj_info; -+}; -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* device data structure */ -+ -+enum sti_buffer_state { -+ BUF_STATE_EMPTY = 0, -+ BUF_STATE_FULL, -+ BUF_STATE_BUSY -+}; -+ -+struct sti_buffhd { -+ void *buf; -+ enum sti_buffer_state state; -+ struct sti_buffhd *next; -+ unsigned int bulk_out_intended_length; -+ struct usb_request *inreq; -+ int inreq_busy; -+ struct usb_request *outreq; -+ int outreq_busy; -+}; -+ -+enum sti_state { -+ STI_STATE_COMMAND_PHASE = -10, /* this one isn't used anywhere */ -+ STI_STATE_DATA_PHASE, -+ STI_STATE_STATUS_PHASE, -+ -+ STI_STATE_IDLE = 0, -+ STI_STATE_ABORT_BULK_OUT, -+ STI_STATE_CANCEL, -+ STI_STATE_RESET, -+ STI_STATE_INTERFACE_CHANGE, -+ STI_STATE_CONFIG_CHANGE, -+ STI_STATE_DISCONNECT, -+ STI_STATE_EXIT, -+ STI_STATE_TERMINATED -+}; -+ -+enum data_direction { -+ DATA_DIR_UNKNOWN = 0, -+ DATA_DIR_FROM_HOST, -+ DATA_DIR_TO_HOST, -+ DATA_DIR_NONE -+}; -+ -+struct sti_dev { -+ /* lock protects: device, req, endpoints states */ -+ spinlock_t lock; -+ -+ /* filesem protects: backing folder in use */ -+ struct rw_semaphore filesem; -+ -+ struct usb_gadget *gadget; -+ -+ /* reference counting: wait until released */ -+ struct kref ref; -+ -+ /* handy copy of gadget->ep0 */ -+ struct usb_ep *ep0; -+ -+ /* for control responses */ -+ struct usb_request *ep0req; -+ unsigned int ep0_req_tag; -+ const char *ep0req_name; -+ -+ /* for interrupt responses */ -+ struct usb_request *intreq; -+ int intreq_busy; -+ struct sti_buffhd *intr_buffhd; -+ -+ /* for exception handling */ -+ enum sti_state state; -+ unsigned int exception_req_tag; -+ -+ unsigned int bulk_out_maxpacket; -+ u8 config, new_config; -+ -+ unsigned int running:1; -+ unsigned int bulk_in_enabled:1; -+ unsigned int bulk_out_enabled:1; -+ unsigned int intr_in_enabled:1; -+ unsigned int registered:1; -+ unsigned int session_open:1; -+ -+ unsigned long atomic_bitflags; -+#define REGISTERED 0 -+#define CLEAR_BULK_HALTS 1 -+#define SUSPENDED 2 -+ -+ struct usb_ep *bulk_in; -+ struct usb_ep *bulk_out; -+ struct usb_ep *intr_in; -+ -+ struct sti_buffhd *next_buffhd_to_fill; -+ struct sti_buffhd *next_buffhd_to_drain; -+ struct sti_buffhd buffhds[NUM_BUFFERS]; -+ -+ int thread_wakeup_needed; -+ struct completion thread_notifier; -+ struct task_struct *thread_task; -+ -+ __le32 container_len; -+ __le16 container_type; -+ __le16 code; -+ __le32 transaction_id; -+ -+ __le16 response_code; -+ -+ u32 ops_params[PARAM_NUM_MAX]; -+ u32 session_id; -+ u32 storage_id; -+ u32 object_num; -+ u32 sub_object_num; -+ -+ char root_path[PATH_MAX]; -+ struct file *root_filp; -+ struct list_head obj_list; -+ struct list_head tmp_obj_list; -+ -+ struct sti_ext_event ext_event_data; -+ struct sti_dev_status status_data; -+ -+ struct device dev; -+}; -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define backing_folder_is_open(sti) ((sti)->root_filp != NULL) -+ -+ -+typedef void (*sti_routine_t)(struct sti_dev *); -+ -+static int exception_in_progress(struct sti_dev *sti) -+{ -+ return (sti->state > STI_STATE_IDLE); -+} -+ -+/* make bulk-out requests be divisible by the maxpacket size */ -+static void set_bulk_out_req_length(struct sti_dev *sti, -+ struct sti_buffhd *bh, unsigned int length) -+{ -+ unsigned int rem; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ bh->bulk_out_intended_length = length; -+ rem = length % sti->bulk_out_maxpacket; -+ if (rem > 0) -+ length += sti->bulk_out_maxpacket - rem; -+ bh->outreq->length = length; -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+/* global variables */ -+static struct sti_dev *the_sti; -+static struct usb_gadget_driver sti_driver; -+ -+static void close_backing_folder(struct sti_dev *sti); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifdef VERBOSE_DEBUG -+ -+static void dump_msg(struct sti_dev *sti, const char *label, -+ const u8 *buf, unsigned int length) -+{ -+ if (length < 512) { -+ DBG(sti, "%s, length %u:\n", label, length); -+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, -+ 16, 1, buf, length, 0); -+ } -+} -+ -+static void dump_cb(struct sti_dev *sti) -+{ -+ print_hex_dump(KERN_DEBUG, "PIMA15740 Command Block: ", -+ DUMP_PREFIX_NONE, 16, 1, &sti->container_len, -+ PIMA15740_CONTAINER_LEN, 0); -+} -+ -+static void dump_device_info(struct sti_dev *sti) -+{ -+ int i; -+ -+ VDBG(sti, "DeviceInfo Dataset:\n"); -+ VDBG(sti, "\tstandard_version: %u\n", -+ sti_device_info.standard_version); -+ VDBG(sti, "\tvendor_extension_id: %u\n", -+ sti_device_info.vendor_extension_id); -+ VDBG(sti, "\tvendor_extension_version: %u\n", -+ sti_device_info.vendor_extension_version); -+ VDBG(sti, "\tvendor_extension_desc_len: %u\n", -+ sti_device_info.vendor_extension_desc_len); -+ VDBG(sti, "\tfunctional_mode: 0x%04x\n", -+ sti_device_info.functional_mode); -+ VDBG(sti, "\toperations_supported_count: %u\n", -+ sti_device_info.operations_supported_count); -+ VDBG(sti, "\toperations_supported:\n"); -+ for (i = 0; i < sti_device_info.operations_supported_count; i++) -+ VDBG(sti, "\t\t0x%04x\n", -+ sti_device_info.operations_supported[i]); -+ VDBG(sti, "\tevents_supported_count: %u\n", -+ sti_device_info.events_supported_count); -+ VDBG(sti, "\tdevice_properties_count: %u\n", -+ sti_device_info.device_properties_count); -+ VDBG(sti, "\tcapture_formats_count: %u\n", -+ sti_device_info.capture_formats_count); -+ VDBG(sti, "\timage_formats_count: %u\n", -+ sti_device_info.image_formats_count); -+ VDBG(sti, "\tmanufacturer_len: %u\n", -+ sti_device_info.manufacturer_len); -+ VDBG(sti, "\tmanufacturer: %s\n", manufacturer); -+ VDBG(sti, "\tmodel_len: %u\n", -+ sti_device_info.model_len); -+ VDBG(sti, "\tmodel: %s\n", longname); -+ VDBG(sti, "\tdevice_version_len: %u\n", -+ sti_device_info.device_version_len); -+ VDBG(sti, "\tdevice_version: %s\n", device_version); -+ VDBG(sti, "\tserial_number_len: %u\n", -+ sti_device_info.serial_number_len); -+ VDBG(sti, "\tserial_number: %s\n", serial); -+} -+ -+static void dump_storage_info(struct sti_dev *sti) -+{ -+ VDBG(sti, "StorageInfo Dataset:\n"); -+ VDBG(sti, "\tstorage_type: 0x%04x\n", sti_storage_info.storage_type); -+ VDBG(sti, "\tfilesystem_type: 0x%04x\n", -+ sti_storage_info.filesystem_type); -+ VDBG(sti, "\taccess_capability: 0x%04x\n", -+ sti_storage_info.access_capability); -+ VDBG(sti, "\tmax_capacity: %llu\n", sti_storage_info.max_capacity); -+ VDBG(sti, "\tfree_space_in_bytes: %llu\n", -+ sti_storage_info.free_space_in_bytes); -+ VDBG(sti, "\tfree_space_in_images: %u\n", -+ sti_storage_info.free_space_in_images); -+ VDBG(sti, "\tstorage_desc_len: %u\n", -+ sti_storage_info.storage_desc_len); -+ VDBG(sti, "\tstorage_desc: %s\n", storage_desc); -+ VDBG(sti, "\tvolume_label_len: %u\n", -+ sti_storage_info.volume_label_len); -+} -+ -+static void dump_object_info(struct sti_dev *sti, struct sti_object *obj) -+{ -+ u8 filename_len; -+ -+ VDBG(sti, "ObjectInfo Dataset:\n"); -+ VDBG(sti, "\tstorage_id: 0x%08x\n", obj->obj_info.storage_id); -+ VDBG(sti, "\tobject_format: 0x%04x\n", obj->obj_info.object_format); -+ VDBG(sti, "\tprotection_status: 0x%04x\n", -+ obj->obj_info.protection_status); -+ VDBG(sti, "\tobject_compressed_size: %u\n", -+ obj->obj_info.object_compressed_size); -+ VDBG(sti, "\tthumb_format: %u\n", obj->obj_info.thumb_format); -+ VDBG(sti, "\tthumb_compressed_size: %u\n", -+ obj->obj_info.thumb_compressed_size); -+ VDBG(sti, "\tthumb_pix_width: %u\n", -+ obj->obj_info.thumb_pix_width); -+ VDBG(sti, "\tthumb_pix_height: %u\n", -+ obj->obj_info.thumb_pix_height); -+ VDBG(sti, "\timage_pix_width: %u\n", -+ obj->obj_info.image_pix_width); -+ VDBG(sti, "\timage_pix_height: %u\n", -+ obj->obj_info.image_pix_height); -+ VDBG(sti, "\timage_bit_depth: %u\n", -+ obj->obj_info.image_bit_depth); -+ VDBG(sti, "\tparent_object: 0x%08x\n", -+ obj->obj_info.parent_object); -+ VDBG(sti, "\tassociation_type: 0x%04x\n", -+ obj->obj_info.association_type); -+ VDBG(sti, "\tassociation_desc: 0x%08x\n", -+ obj->obj_info.association_desc); -+ VDBG(sti, "\tsequence_number: 0x%08x\n", -+ obj->obj_info.sequence_number); -+ VDBG(sti, "\tfilename_len: %u\n", obj->obj_info.obj_strings[0]); -+ filename_len = obj->obj_info.obj_strings[0]; -+ VDBG(sti, "\tfilename: %s\n", obj->filename); -+ VDBG(sti, "\tcapture_date_len: %u\n", -+ obj->obj_info.obj_strings[filename_len * 2 + 1]); -+ VDBG(sti, "\tmodification_date_len: %u\n", -+ obj->obj_info.obj_strings[filename_len * 2 + 2]); -+ VDBG(sti, "\tkeywords_len: %u\n", -+ obj->obj_info.obj_strings[filename_len * 2 + 3]); -+} -+ -+#else -+ -+static void dump_msg(struct sti_dev *sti, const char *label, -+ const u8 *buf, unsigned int length) -+{} -+ -+static void dump_cb(struct sti_dev *sti) -+{} -+ -+static void dump_device_info(struct sti_dev *sti) -+{} -+ -+static void dump_storage_info(struct sti_dev *sti) -+{} -+ -+static void dump_object_info(struct sti_dev *sti, struct sti_object *obj) -+{} -+ -+#endif /* VERBOSE_DEBUG */ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+ -+ -+/* -+ * Config descriptors must agree with the code that sets configurations -+ * and with code managing interfaces and their altsettings. They must -+ * also handle different speeds and other-speed requests. -+ */ -+static int populate_config_buf(struct usb_gadget *gadget, -+ u8 *buf, u8 type, unsigned index) -+{ -+ enum usb_device_speed speed = gadget->speed; -+ int len; -+ const struct usb_descriptor_header **function; -+ -+ if (index > 0) -+ return -EINVAL; -+ -+ if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) -+ speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; -+ if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH) -+ function = hs_function; -+ else -+ function = fs_function; -+ -+ /* for now, don't advertise srp-only devices */ -+ if (!gadget_is_otg(gadget)) -+ function++; -+ -+ len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); -+ ((struct usb_config_descriptor *) buf)->bDescriptorType = type; -+ -+ return len; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* these routines may be called in process context or in_irq */ -+ -+/* caller must hold sti->lock */ -+static void wakeup_thread(struct sti_dev *sti) -+{ -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* tell the main thread that something has happened */ -+ sti->thread_wakeup_needed = 1; -+ if (sti->thread_task) -+ wake_up_process(sti->thread_task); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+static void raise_exception(struct sti_dev *sti, enum sti_state new_state) -+{ -+ unsigned long flags; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* -+ * Do nothing if a higher-priority exception is already in progress. -+ * If a lower-or-equal priority exception is in progress, preempt it -+ * and notify the main thread by sending it a signal. -+ */ -+ spin_lock_irqsave(&sti->lock, flags); -+ if (sti->state <= new_state) { -+ sti->exception_req_tag = sti->ep0_req_tag; -+ sti->state = new_state; -+ if (sti->thread_task) -+ send_sig_info(SIGUSR1, SEND_SIG_FORCED, -+ sti->thread_task); -+ } -+ spin_unlock_irqrestore(&sti->lock, flags); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * The disconnect callback and ep0 routines. These always run in_irq, -+ * except that ep0_queue() is called in the main thread to acknowledge -+ * completion of various requests: set config, set interface, and -+ * Bulk-only device reset. -+ */ -+ -+static void sti_disconnect(struct usb_gadget *gadget) -+{ -+ struct sti_dev *sti = get_gadget_data(gadget); -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ DBG(sti, "disconnect or port reset\n"); -+ raise_exception(sti, STI_STATE_DISCONNECT); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+static int ep0_queue(struct sti_dev *sti) -+{ -+ int rc; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ rc = usb_ep_queue(sti->ep0, sti->ep0req, GFP_ATOMIC); -+ if (rc != 0 && rc != -ESHUTDOWN) { -+ /* we can't do much more than wait for a reset */ -+ WARNING(sti, "error in submission: %s --> %d\n", -+ sti->ep0->name, rc); -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+static void ep0_complete(struct usb_ep *ep, struct usb_request *req) -+{ -+ struct sti_dev *sti = ep->driver_data; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (req->actual > 0) -+ dump_msg(sti, sti->ep0req_name, req->buf, req->actual); -+ -+ if (req->status || req->actual != req->length) -+ VDBG(sti, "%s --> %d, %u/%u\n", __func__, -+ req->status, req->actual, req->length); -+ -+ /* request was cancelled */ -+ if (req->status == -ECONNRESET) -+ usb_ep_fifo_flush(ep); -+ -+ if (req->status == 0 && req->context) -+ ((sti_routine_t) (req->context))(sti); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* endpoint completion handlers, always run in_irq */ -+ -+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) -+{ -+ struct sti_dev *sti = ep->driver_data; -+ struct sti_buffhd *bh = req->context; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (req->status || req->actual != req->length) -+ VDBG(sti, "%s --> %d, %u/%u\n", __func__, -+ req->status, req->actual, req->length); -+ /* request was cancelled */ -+ if (req->status == -ECONNRESET) -+ usb_ep_fifo_flush(ep); -+ -+ /* hold the lock while we update the request and buffer states */ -+ smp_wmb(); -+ spin_lock(&sti->lock); -+ bh->inreq_busy = 0; -+ bh->state = BUF_STATE_EMPTY; -+ wakeup_thread(sti); -+ spin_unlock(&sti->lock); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) -+{ -+ struct sti_dev *sti = ep->driver_data; -+ struct sti_buffhd *bh = req->context; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ dump_msg(sti, "bulk-out", req->buf, req->actual); -+ if (req->status || req->actual != bh->bulk_out_intended_length) -+ VDBG(sti, "%s --> %d, %u/%u\n", __func__, -+ req->status, req->actual, -+ bh->bulk_out_intended_length); -+ -+ /* request was cancelled */ -+ if (req->status == -ECONNRESET) -+ usb_ep_fifo_flush(ep); -+ -+ /* hold the lock while we update the request and buffer states */ -+ smp_wmb(); -+ spin_lock(&sti->lock); -+ bh->outreq_busy = 0; -+ bh->state = BUF_STATE_FULL; -+ wakeup_thread(sti); -+ spin_unlock(&sti->lock); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int sti_set_halt(struct sti_dev *sti, struct usb_ep *ep) -+{ -+ const char *name; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (ep == sti->bulk_in) -+ name = "bulk-in"; -+ else if (ep == sti->bulk_out) -+ name = "bulk-out"; -+ else -+ name = ep->name; -+ -+ DBG(sti, "%s set halt\n", name); -+ VDBG(sti, "<--- %s()\n", __func__); -+ -+ return usb_ep_set_halt(ep); -+} -+ -+ -+static void received_cancel_request(struct sti_dev *sti) -+{ -+ struct usb_request *req = sti->ep0req; -+ u16 cancel_code; -+ u32 trans_id; -+ int rc; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* error in command transfer */ -+ if (req->status || req->length != req->actual) { -+ /* wait for reset */ -+ sti_set_halt(sti, sti->ep0); -+ return; -+ } -+ -+ VDBG(sti, "receive cancel request\n"); -+ -+ if (!req->buf) -+ return; -+ -+ cancel_code = get_unaligned_le16(req->buf); -+ if (cancel_code != cpu_to_le16(STI_CANCEL_REQUEST_CODE)) { -+ VDBG(sti, "invalid cancel_code: 0x%04x\n", cancel_code); -+ goto out; -+ } -+ -+ trans_id = get_unaligned_le32(req->buf + 2); -+ if (trans_id != sti->transaction_id) { -+ VDBG(sti, "invalid trans_id:0x%04x\n", trans_id); -+ goto out; -+ } -+ -+ /* stall bulk endpoints */ -+ sti_set_halt(sti, sti->bulk_out); -+ -+ rc = sti_set_halt(sti, sti->bulk_in); -+ if (rc == -EAGAIN) -+ VDBG(sti, "delayed bulk-in endpoint halt\n"); -+ -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+out: -+ raise_exception(sti, STI_STATE_CANCEL); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+/* ep0 class-specific request handlers, always run in_irq */ -+static int class_setup_req(struct sti_dev *sti, -+ const struct usb_ctrlrequest *ctrl) -+{ -+ struct usb_request *req = sti->ep0req; -+ int value = -EOPNOTSUPP; -+ u16 w_index = le16_to_cpu(ctrl->wIndex); -+ u16 w_value = le16_to_cpu(ctrl->wValue); -+ u16 w_length = le16_to_cpu(ctrl->wLength); -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->config) -+ return value; -+ -+ /* handle class-specific requests */ -+ switch (ctrl->bRequest) { -+ -+ case STI_CANCEL_REQUEST: -+ if (ctrl->bRequestType != (USB_DIR_OUT | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE)) -+ break; -+ if (w_index != 0 || w_value != 0 || w_length != 6) { -+ value = -EDOM; -+ break; -+ } -+ -+ DBG(sti, "cancel request\n"); -+ -+ value = w_length; -+ sti->ep0req->context = received_cancel_request; -+ break; -+ -+ case STI_GET_EXTENDED_EVENT_DATA: -+ /* asynchronous events by interrupt endpoint */ -+ if (ctrl->bRequestType != (USB_DIR_IN | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE)) -+ break; -+ if (w_index != 0 || w_value != 0) { -+ value = -EDOM; -+ break; -+ } -+ -+ DBG(sti, "get extended event data\n"); -+ -+ sti->ext_event_data.event_code = PIMA15740_RES_OK; -+ sti->ext_event_data.transaction_id = sti->transaction_id; -+ sti->ext_event_data.param_num = 0; -+ -+ value = min_t(unsigned, w_length, -+ sizeof(struct sti_ext_event)); -+ memcpy(req->buf, &sti->ext_event_data, value); -+ break; -+ -+ case STI_DEVICE_RESET_REQUEST: -+ if (ctrl->bRequestType != (USB_DIR_OUT | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE)) -+ break; -+ if (w_index != 0 || w_value != 0 || w_length != 0) { -+ value = -EDOM; -+ break; -+ } -+ -+ /* Raise an exception to stop the current operation -+ * and reinitialize our state. */ -+ DBG(sti, "device reset request\n"); -+ -+ sti->response_code = PIMA15740_RES_OK; -+ sti->session_open = 1; -+ -+ raise_exception(sti, STI_STATE_RESET); -+ value = DELAYED_STATUS; -+ break; -+ -+ case STI_GET_DEVICE_STATUS: -+ if (ctrl->bRequestType != (USB_DIR_IN | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE)) -+ break; -+ if (w_index != 0 || w_value != 0) { -+ value = -EDOM; -+ break; -+ } -+ -+ DBG(sti, "get device status\n"); -+ sti->status_data.wlength = 4; -+ sti->status_data.code = sti->response_code; -+ -+ value = min_t(unsigned, w_length, -+ sizeof(struct sti_dev_status)); -+ memcpy(req->buf, &sti->status_data, value); -+ break; -+ -+ default: -+ DBG(sti, "unknown class-specific control req " -+ "%02x.%02x v%04x i%04x l%u\n", -+ ctrl->bRequestType, ctrl->bRequest, -+ le16_to_cpu(ctrl->wValue), w_index, w_length); -+ break; -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return value; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* ep0 standard request handlers, always run in_irq */ -+ -+static int standard_setup_req(struct sti_dev *sti, -+ const struct usb_ctrlrequest *ctrl) -+{ -+ struct usb_request *req = sti->ep0req; -+ int value = -EOPNOTSUPP; -+ u16 w_index = le16_to_cpu(ctrl->wIndex); -+ u16 w_value = le16_to_cpu(ctrl->wValue); -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* usually this just stores reply data in the pre-allocated ep0 buffer, -+ * but config change events will also reconfigure hardware */ -+ switch (ctrl->bRequest) { -+ -+ case USB_REQ_GET_DESCRIPTOR: -+ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | -+ USB_RECIP_DEVICE)) -+ break; -+ switch (w_value >> 8) { -+ -+ case USB_DT_DEVICE: -+ VDBG(sti, "get device descriptor\n"); -+ value = sizeof device_desc; -+ memcpy(req->buf, &device_desc, value); -+ break; -+ case USB_DT_DEVICE_QUALIFIER: -+ VDBG(sti, "get device qualifier\n"); -+ if (!gadget_is_dualspeed(sti->gadget)) -+ break; -+ value = sizeof dev_qualifier; -+ memcpy(req->buf, &dev_qualifier, value); -+ break; -+ -+ case USB_DT_OTHER_SPEED_CONFIG: -+ VDBG(sti, "get other-speed config descriptor\n"); -+ if (!gadget_is_dualspeed(sti->gadget)) -+ break; -+ goto get_config; -+ case USB_DT_CONFIG: -+ VDBG(sti, "get configuration descriptor\n"); -+get_config: -+ value = populate_config_buf(sti->gadget, -+ req->buf, -+ w_value >> 8, -+ w_value & 0xff); -+ break; -+ -+ case USB_DT_STRING: -+ VDBG(sti, "get string descriptor\n"); -+ -+ /* wIndex == language code */ -+ value = usb_gadget_get_string(&stringtab, -+ w_value & 0xff, req->buf); -+ break; -+ } -+ break; -+ -+ /* one config, two speeds */ -+ case USB_REQ_SET_CONFIGURATION: -+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | -+ USB_RECIP_DEVICE)) -+ break; -+ VDBG(sti, "set configuration\n"); -+ if (w_value == CONFIG_VALUE || w_value == 0) { -+ sti->new_config = w_value; -+ -+ /* Raise an exception to wipe out previous transaction -+ * state (queued bufs, etc) and set the new config. */ -+ raise_exception(sti, STI_STATE_CONFIG_CHANGE); -+ value = DELAYED_STATUS; -+ } -+ break; -+ -+ case USB_REQ_GET_CONFIGURATION: -+ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | -+ USB_RECIP_DEVICE)) -+ break; -+ VDBG(sti, "get configuration\n"); -+ *(u8 *) req->buf = sti->config; -+ value = 1; -+ break; -+ -+ case USB_REQ_SET_INTERFACE: -+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | -+ USB_RECIP_INTERFACE)) -+ break; -+ if (sti->config && w_index == 0) { -+ -+ /* Raise an exception to wipe out previous transaction -+ * state (queued bufs, etc) and install the new -+ * interface altsetting. */ -+ raise_exception(sti, STI_STATE_INTERFACE_CHANGE); -+ value = DELAYED_STATUS; -+ } -+ break; -+ -+ case USB_REQ_GET_INTERFACE: -+ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | -+ USB_RECIP_INTERFACE)) -+ break; -+ if (!sti->config) -+ break; -+ if (w_index != 0) { -+ value = -EDOM; -+ break; -+ } -+ VDBG(sti, "get interface\n"); -+ *(u8 *) req->buf = 0; -+ value = 1; -+ break; -+ -+ default: -+ VDBG(sti, "unknown control req %02x.%02x v%04x i%04x l%u\n", -+ ctrl->bRequestType, ctrl->bRequest, -+ w_value, w_index, le16_to_cpu(ctrl->wLength)); -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return value; -+} -+ -+static int sti_setup(struct usb_gadget *gadget, -+ const struct usb_ctrlrequest *ctrl) -+{ -+ struct sti_dev *sti = get_gadget_data(gadget); -+ int rc; -+ int w_length = le16_to_cpu(ctrl->wLength); -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* record arrival of a new request */ -+ ++sti->ep0_req_tag; -+ sti->ep0req->context = NULL; -+ sti->ep0req->length = 0; -+ dump_msg(sti, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); -+ -+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) -+ rc = class_setup_req(sti, ctrl); -+ else -+ rc = standard_setup_req(sti, ctrl); -+ -+ /* respond with data/status or defer until later */ -+ if (rc >= 0 && rc != DELAYED_STATUS) { -+ rc = min(rc, w_length); -+ sti->ep0req->length = rc; -+ sti->ep0req->zero = rc < w_length; -+ sti->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? -+ "ep0-in" : "ep0-out"); -+ rc = ep0_queue(sti); -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ /* device either stalls (rc < 0) or reports success */ -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* all the following routines run in process context */ -+ -+/* use this for bulk or interrupt transfers, not ep0 */ -+static void start_transfer(struct sti_dev *sti, struct usb_ep *ep, -+ struct usb_request *req, int *pbusy, -+ enum sti_buffer_state *state) -+{ -+ int rc; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (ep == sti->bulk_in) -+ dump_msg(sti, "bulk-in", req->buf, req->length); -+ else if (ep == sti->intr_in) -+ dump_msg(sti, "intr-in", req->buf, req->length); -+ -+ spin_lock_irq(&sti->lock); -+ *pbusy = 1; -+ *state = BUF_STATE_BUSY; -+ spin_unlock_irq(&sti->lock); -+ -+ rc = usb_ep_queue(ep, req, GFP_KERNEL); -+ VDBG(sti, "start_transfer, rc: %d\n", rc); -+ if (rc != 0) { -+ *pbusy = 0; -+ *state = BUF_STATE_EMPTY; -+ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && -+ req->length == 0)) -+ WARNING(sti, "error in submission: %s --> %d\n", -+ ep->name, rc); -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+static int sleep_thread(struct sti_dev *sti) -+{ -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* wait until a signal arrives or we are woken up */ -+ for (;;) { -+ try_to_freeze(); -+ set_current_state(TASK_INTERRUPTIBLE); -+ if (signal_pending(current)) { -+ rc = -EINTR; -+ break; -+ } -+ if (sti->thread_wakeup_needed) -+ break; -+ -+ schedule(); -+ } -+ -+ __set_current_state(TASK_RUNNING); -+ sti->thread_wakeup_needed = 0; -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int fill_data_container(struct sti_buffhd *bh, -+ struct sti_dev *sti, unsigned int size) -+{ -+ struct pima15740_container *rb; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ rb = bh->buf; -+ -+ rb->container_len = size; -+ rb->container_type = TYPE_DATA_BLOCK; -+ rb->code = sti->code; -+ rb->transaction_id = sti->transaction_id; -+ -+ bh->inreq->zero = 0; -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return 0; -+} -+ -+ -+static int send_response(struct sti_dev *sti, unsigned int code) -+{ -+ struct sti_buffhd *bh; -+ struct pima15740_container *rb; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* wait for the next buffer to become available */ -+ bh = sti->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ rc = sleep_thread(sti); -+ if (rc) -+ return rc; -+ } -+ -+ rb = bh->buf; -+ -+ rb->container_len = PIMA15740_CONTAINER_LEN; -+ rb->container_type = TYPE_RESPONSE_BLOCK; -+ rb->code = code; -+ rb->transaction_id = sti->transaction_id; -+ -+ bh->inreq->length = PIMA15740_CONTAINER_LEN; -+ bh->state = BUF_STATE_FULL; -+ bh->inreq->zero = 0; -+ -+ start_transfer(sti, sti->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ -+ sti->next_buffhd_to_fill = bh->next; -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int send_params_response(struct sti_dev *sti, unsigned int code, -+ u32 p1, u32 p2, u32 p3, unsigned p_num) -+{ -+ struct sti_buffhd *bh; -+ struct pima15740_container *rb; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* wait for the next buffer to become available */ -+ bh = sti->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ rc = sleep_thread(sti); -+ if (rc) -+ return rc; -+ } -+ -+ rb = bh->buf; -+ -+ rb->container_len = PIMA15740_CONTAINER_LEN + p_num * 4; -+ rb->container_type = TYPE_RESPONSE_BLOCK; -+ rb->code = code; -+ rb->transaction_id = sti->transaction_id; -+ -+ switch (p_num) { -+ case 3: -+ memcpy((u8 *)rb + PIMA15740_CONTAINER_LEN, &p1, 4); -+ memcpy((u8 *)rb + PIMA15740_CONTAINER_LEN + 4, &p2, 4); -+ memcpy((u8 *)rb + PIMA15740_CONTAINER_LEN + 8, &p3, 4); -+ break; -+ case 2: -+ memcpy((u8 *)rb + PIMA15740_CONTAINER_LEN, &p1, 4); -+ memcpy((u8 *)rb + PIMA15740_CONTAINER_LEN + 4, &p2, 4); -+ break; -+ case 1: -+ memcpy((u8 *)rb + PIMA15740_CONTAINER_LEN, &p1, 4); -+ break; -+ default: -+ break; -+ } -+ -+ bh->inreq->length = PIMA15740_CONTAINER_LEN + p_num * 4; -+ bh->state = BUF_STATE_FULL; -+ bh->inreq->zero = 0; -+ -+ start_transfer(sti, sti->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ -+ sti->next_buffhd_to_fill = bh->next; -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+/* ISO-8859-1 to UTF-16LE */ -+static unsigned short str_to_uni16(const char *src, char *dest) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < strlen(src); i++) { -+ dest[i * 2] = src[i]; -+ dest[i * 2 + 1] = '\0'; -+ } -+ -+ /* null-terminated string */ -+ dest[i * 2] = dest[i * 2 + 1] = '\0'; -+ -+ return (i + 1) * 2; -+} -+ -+/* UTF-16LE to ISO-8859-1 */ -+static void uni16_to_str(const char *src, char *dest, unsigned short len) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < len; i++) -+ dest[i] = src[i * 2]; -+} -+ -+ -+static int do_get_device_info(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ size_t size; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* dump DeviceInfo Dataset */ -+ dump_device_info(sti); -+ -+ size = sizeof sti_device_info; -+ fill_data_container(bh, sti, PIMA15740_CONTAINER_LEN + size); -+ -+ memcpy(bh->buf + PIMA15740_CONTAINER_LEN, &sti_device_info, size); -+ -+ bh->inreq->length = PIMA15740_CONTAINER_LEN + size; -+ bh->state = BUF_STATE_FULL; -+ start_transfer(sti, sti->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ sti->next_buffhd_to_fill = bh->next; -+ -+ /* send response */ -+ rc = send_response(sti, PIMA15740_RES_OK); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int filldir_all(void *__buf, const char *name, int len, -+ loff_t pos, u64 ino, unsigned int d_type) -+{ -+ struct sti_dev *sti = __buf; -+ struct sti_object *obj; -+ char *ext; -+ u8 filename_len; -+ char filename_utf16le[NAME_MAX * 2]; -+ size_t obj_size; -+ u16 object_format = PIMA15740_FMT_A_UNDEFINED; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ VDBG(sti, "name: %s, len: %d, pos: %lu, ino: %llu, d_type: %u\n", -+ name, len, (unsigned long)pos, ino, d_type); -+ -+ /* ignore "." and ".." directories */ -+ if (!strcmp(name, ".") || !strcmp(name, "..")) -+ goto out; -+ -+ if (d_type != DT_DIR && d_type != DT_REG) -+ goto out; -+ -+ /* filename strings length */ -+ filename_len = len + 1; -+ VDBG(sti, "filename_len: %u\n", filename_len); -+ -+ /* sti_object size */ -+ obj_size = sizeof(struct sti_object) + 2 * filename_len + 4; -+ VDBG(sti, "obj_size: %u\n", obj_size); -+ /* obj_size > sizeof(struct sti_object) */ -+ obj = kzalloc(obj_size, GFP_KERNEL); -+ if (!obj) { -+ rc = -ENOMEM; -+ goto out; -+ } -+ -+ /* fill part of sti_object info */ -+ obj->storage_id = STORAGE_ID; -+ obj->send_valid = 0; -+ -+ /* ObjectInfo Dataset size */ -+ obj->obj_info_size = sizeof(struct pima15740_object_info) -+ + 2 * filename_len + 4; -+ VDBG(sti, "obj_info_size: %u\n", obj->obj_info_size); -+ -+ /* filename */ -+ memset(obj->filename, 0, sizeof(obj->filename)); -+ strncpy(obj->filename, name, len); -+ -+ /* fill ObjectInfo Dataset */ -+ obj->obj_info.storage_id = cpu_to_le32(STORAGE_ID); -+ -+ if (d_type == DT_DIR) { /* association */ -+ object_format = PIMA15740_FMT_A_ASSOCIATION; -+ obj->obj_info.association_type = -+ cpu_to_le16(PIMA15740_AS_GENERIC_FOLDER); -+ obj->is_dir = 1; -+ } else if (d_type == DT_REG) { /* regular file */ -+ ext = strrchr(obj->filename, '.'); -+ if (ext) { -+ /* image object */ -+ if (!strcasecmp(ext, ".jpg") || -+ !strcasecmp(ext, ".jpeg") || -+ !strcasecmp(ext, ".jpe")) -+ object_format = PIMA15740_FMT_I_EXIF_JPEG; -+ else if (!strcasecmp(ext, ".jfif")) -+ object_format = PIMA15740_FMT_I_JFIF; -+ else if (!strcasecmp(ext, ".tif") || -+ !strcasecmp(ext, ".tiff")) -+ object_format = PIMA15740_FMT_I_TIFF; -+ else if (!strcasecmp(ext, ".png")) -+ object_format = PIMA15740_FMT_I_PNG; -+ else if (!strcasecmp(ext, ".bmp")) -+ object_format = PIMA15740_FMT_I_BMP; -+ else if (!strcasecmp(ext, ".gif")) -+ object_format = PIMA15740_FMT_I_GIF; -+ else /* undefined non-image object */ -+ object_format = PIMA15740_FMT_A_UNDEFINED; -+ } else /* file without extension */ -+ object_format = PIMA15740_FMT_A_UNDEFINED; -+ obj->obj_info.association_type = -+ cpu_to_le16(PIMA15740_AS_UNDEFINED); -+ obj->is_dir = 0; -+ } -+ obj->obj_info.object_format = cpu_to_le16(object_format); -+ -+ /* protection_status, object_compressed_size will be filled later */ -+ obj->obj_info.thumb_format = cpu_to_le16(0); -+ obj->obj_info.thumb_compressed_size = cpu_to_le32(0); -+ obj->obj_info.thumb_pix_width = cpu_to_le32(0); -+ obj->obj_info.thumb_pix_height = cpu_to_le32(0); -+ obj->obj_info.image_pix_width = cpu_to_le32(0); -+ obj->obj_info.image_pix_height = cpu_to_le32(0); -+ obj->obj_info.image_bit_depth = cpu_to_le32(0); -+ -+ obj->obj_info.association_desc = cpu_to_le32(0); -+ obj->obj_info.sequence_number = cpu_to_le32(0); -+ -+ /* filename_utf16le: UTF-16LE unicode string */ -+ obj->obj_info.obj_strings[0] = filename_len; -+ memset(filename_utf16le, 0, sizeof(filename_utf16le)); -+ str_to_uni16(obj->filename, filename_utf16le); -+ memcpy(obj->obj_info.obj_strings + 1, filename_utf16le, -+ filename_len * 2); -+ -+ /* capture date */ -+ obj->obj_info.obj_strings[filename_len * 2 + 1] = 0; -+ -+ /* modification date */ -+ obj->obj_info.obj_strings[filename_len * 2 + 2] = 0; -+ -+ /* keywords */ -+ obj->obj_info.obj_strings[filename_len * 2 + 3] = 0; -+ -+ /* increase object number */ -+ sti->sub_object_num++; -+ -+ /* add to temp object list */ -+ list_add_tail(&obj->list, &sti->tmp_obj_list); -+out: -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+/* alphabetic sort function */ -+static int alnumsort(const void *a, const void *b) -+{ -+ const struct sti_object *oa = *(const struct sti_object **)a; -+ const struct sti_object *ob = *(const struct sti_object **)b; -+ return strcmp(oa->filename, ob->filename); -+} -+ -+ -+/* descend through the hierarchical folder recursively */ -+static int list_objects(struct sti_dev *sti, const char *folder_name, -+ struct sti_object *folder_obj, bool recursive) -+{ -+ struct file *filp; -+ struct dentry *dentry; -+ struct sti_object *obj = NULL; -+ struct sti_object *tmp_obj; -+ struct sti_object **pobj, **temp_pobj = NULL; -+ struct kstat stat; -+ u32 parent_object; -+ int i, rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* root directory */ -+ if (!strcmp(folder_name, sti->root_path)) { -+ filp = sti->root_filp; -+ parent_object = 0; -+ VDBG(sti, "root directory\n"); -+ } else { /* subdirectory */ -+ filp = filp_open(folder_name, O_RDONLY | O_DIRECTORY, 0); -+ if (IS_ERR(filp)) { -+ ERROR(sti, "unable to open folder: %s\n", -+ folder_name); -+ return PTR_ERR(filp); -+ } -+ VDBG(sti, "folder_name: %s\n", folder_name); -+ parent_object = folder_obj->obj_handle; -+ } -+ dentry = filp->f_dentry; -+ -+ sti->sub_object_num = 0; -+ filp->f_pos = 0; -+ rc = vfs_readdir(filp, filldir_all, sti); -+ if (rc) -+ ERROR(sti, "vfs_readdir %s error: %d\n", -+ folder_name, rc); -+ VDBG(sti, "%d objects in folder %s\n", -+ sti->sub_object_num, folder_name); -+ -+ /* no file in the directory */ -+ if (!sti->sub_object_num) -+ goto out; -+ -+ /* pre-allocated objects array */ -+ pobj = kzalloc((sti->sub_object_num + 1) * sizeof(struct sti_object *), -+ GFP_KERNEL); -+ if (!pobj) { -+ rc = -ENOMEM; -+ goto out; -+ } -+ -+ temp_pobj = pobj; -+ -+ i = 0; -+ list_for_each_entry_safe(obj, tmp_obj, &sti->tmp_obj_list, list) { -+ pobj[i] = obj; -+ /* remove from temp object list */ -+ list_del_init(&obj->list); -+ i++; -+ } -+ VDBG(sti, "i = %d\n", i); -+ pobj[i] = NULL; -+ -+ /* sort the objects array */ -+ sort(pobj, sti->sub_object_num, sizeof(struct sti_object *), -+ alnumsort, NULL); -+ -+ while (*pobj) { -+ /* increase total object number */ -+ sti->object_num++; -+ -+ /* fill object handle */ -+ (*pobj)->obj_handle = sti->object_num; -+ -+ /* fill parent object */ -+ (*pobj)->parent_object = cpu_to_le32(parent_object); -+ (*pobj)->obj_info.parent_object = cpu_to_le32(parent_object); -+ -+ /* object full path */ -+ memset((*pobj)->full_path, 0, sizeof((*pobj)->full_path)); -+ snprintf((*pobj)->full_path, sizeof((*pobj)->full_path), -+ "%s/%s", folder_name, (*pobj)->filename); -+ -+ VDBG(sti, "full_path: %s, obj_handle: 0x%08x, " -+ "parent_object: 0x%08x\n", -+ (*pobj)->full_path, (*pobj)->obj_handle, -+ parent_object); -+ -+ /* get file statistics info */ -+ rc = vfs_stat((char __user *)(*pobj)->full_path, &stat); -+ if (rc) { -+ ERROR(sti, "vfs_stat error: %d\n", rc); -+ goto out; -+ } -+ -+ /* fill remained ObjectInfo Dataset */ -+ if (stat.mode & S_IWUSR) -+ (*pobj)->obj_info.protection_status = -+ cpu_to_le16(PIMA15740_OBJECT_NO_PROTECTION); -+ else -+ (*pobj)->obj_info.protection_status = -+ cpu_to_le16(PIMA15740_OBJECT_READ_ONLY); -+ -+ (*pobj)->obj_info.object_compressed_size = -+ cpu_to_le32((u32)stat.size); -+ -+ /* add to object list */ -+ list_add_tail(&(*pobj)->list, &sti->obj_list); -+ -+ if ((*pobj)->is_dir && recursive) -+ list_objects(sti, (*pobj)->full_path, *pobj, true); -+ -+ pobj++; -+ } -+ -+out: -+ /* free pre-allocated objects array */ -+ kfree(temp_pobj); -+ -+ if (strcmp(folder_name, sti->root_path)) -+ filp_close(filp, current->files); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_open_session(struct sti_dev *sti) -+{ -+ struct sti_object *obj; -+ u8 filename_len; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_ALREADY_OPEN; -+ goto out; -+ } -+ -+ sti->session_id = sti->ops_params[0]; -+ VDBG(sti, "session_id: 0x%08x\n", sti->session_id); -+ if (sti->session_id) { -+ sti->response_code = PIMA15740_RES_OK; -+ sti->session_open = 1; -+ } else { -+ sti->response_code = PIMA15740_RES_INVALID_PARAMETER; -+ sti->session_open = 0; -+ goto out; -+ } -+ -+ /* reset total object number */ -+ sti->object_num = 0; -+ -+ /* root object init */ -+ filename_len = strlen(sti->root_filp->f_dentry->d_name.name) + 1; -+ VDBG(sti, "root object: %s\n", sti->root_path); -+ VDBG(sti, "filename_len: %u\n", filename_len); -+ obj = kzalloc(sizeof(*obj), GFP_KERNEL); -+ if (!obj) { -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ goto out; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ obj->obj_handle = 0; -+ obj->parent_object = 0; -+ obj->storage_id = STORAGE_ID; -+ obj->is_dir = 1; -+ obj->send_valid = 0; -+ obj->obj_info_size = sizeof(struct pima15740_object_info); -+ -+ /* root object filename */ -+ memset(obj->filename, 0, sizeof(obj->filename)); -+ strncpy(obj->filename, sti->root_filp->f_dentry->d_name.name, -+ sizeof(obj->filename)); -+ VDBG(sti, "root object filename: %s\n", obj->filename); -+ -+ /* root object full path */ -+ memset(obj->full_path, 0, sizeof(obj->full_path)); -+ strncpy(obj->full_path, sti->root_path, sizeof(obj->full_path)); -+ VDBG(sti, "root object full path: %s\n", obj->full_path); -+ -+ /* add to object list */ -+ list_add_tail(&obj->list, &sti->obj_list); -+ -+ spin_unlock_irq(&sti->lock); -+out: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_close_session(struct sti_dev *sti) -+{ -+ struct sti_object *obj, *tmp_obj; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (sti->session_open) { -+ sti->response_code = PIMA15740_RES_OK; -+ sti->session_open = 0; -+ } else { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* release object list */ -+ list_for_each_entry_safe(obj, tmp_obj, &sti->obj_list, list) { -+ list_del_init(&obj->list); -+ kfree(obj); -+ } -+ -+ spin_unlock_irq(&sti->lock); -+ -+ DBG(sti, "release object list\n"); -+out: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_get_storage_ids(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ size_t size; -+ u32 i; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out; -+ } -+ -+ sti->storage_id = cpu_to_le32(STORAGE_ID); -+ DBG(sti, "storage_id: 0x%08x\n", sti->storage_id); -+ -+ /* 4 bytes array number and 4 bytes storage id */ -+ size = 8; -+ fill_data_container(bh, sti, PIMA15740_CONTAINER_LEN + size); -+ -+ /* support one storage id */ -+ i = 1; -+ memcpy(bh->buf + PIMA15740_CONTAINER_LEN, &i, 4); -+ memcpy(bh->buf + PIMA15740_CONTAINER_LEN + 4, &sti->storage_id, 4); -+ -+ bh->inreq->length = PIMA15740_CONTAINER_LEN + size; -+ bh->state = BUF_STATE_FULL; -+ start_transfer(sti, sti->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ sti->next_buffhd_to_fill = bh->next; -+ -+ sti->response_code = PIMA15740_RES_OK; -+out: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_get_storage_info(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ size_t size; -+ u32 storage_id; -+ u64 sbytes_max, sbytes_free; -+ struct kstatfs sbuf; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out; -+ } -+ -+ /* storage id */ -+ storage_id = sti->ops_params[0]; -+ if (storage_id != sti->storage_id) { -+ WARNING(sti, "invalid storage id: 0x%08x\n", storage_id); -+ sti->response_code = PIMA15740_RES_INVALID_STORAGE_ID; -+ goto out; -+ } -+ -+ /* get filesystem statistics info */ -+ rc = vfs_statfs(sti->root_filp->f_dentry, &sbuf); -+ if (rc) { -+ sti->response_code = PIMA15740_RES_ACCESS_DENIED; -+ goto out; -+ } -+ -+ /* fill remained items in StorageInfo Dataset */ -+ sbytes_max = (u64) sbuf.f_bsize * sbuf.f_blocks; -+ sbytes_free = (u64) sbuf.f_bsize * sbuf.f_bfree; -+ sti_storage_info.max_capacity = cpu_to_le64(sbytes_max); -+ sti_storage_info.free_space_in_bytes = cpu_to_le64(sbytes_free); -+ sti_storage_info.free_space_in_images = cpu_to_le32((u32)~0); -+ str_to_uni16(storage_desc, sti_storage_info.storage_desc); -+ -+ /* dump StorageInfo Dataset */ -+ dump_storage_info(sti); -+ -+ memcpy(bh->buf + PIMA15740_CONTAINER_LEN, &sti_storage_info, -+ sizeof(sti_storage_info)); -+ -+ size = PIMA15740_CONTAINER_LEN + sizeof(sti_storage_info); -+ fill_data_container(bh, sti, size); -+ -+ bh->inreq->length = size; -+ bh->state = BUF_STATE_FULL; -+ start_transfer(sti, sti->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ sti->next_buffhd_to_fill = bh->next; -+ -+ sti->response_code = PIMA15740_RES_OK; -+out: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_get_num_objects(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ int i; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out; -+ } -+ -+ for (i = 0; i < PARAM_NUM_MAX; i++) -+ VDBG(sti, "parameter[%u]: 0x%08x\n", -+ i + 1, sti->ops_params[i]); -+ -+ if (!backing_folder_is_open(sti)) { -+ ERROR(sti, "backing folder is not open\n"); -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ goto out; -+ } -+ -+ DBG(sti, "total object number: %u\n", sti->object_num); -+ -+ sti->response_code = PIMA15740_RES_OK; -+out: -+ /* send response */ -+ rc = send_params_response(sti, sti->response_code, -+ sti->object_num, 0, 0, -+ 1); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_get_object_handles(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ size_t size; -+ u32 storage_id, obj_handle; -+ u32 new_obj_num, old_obj_num, tmp_obj_num; -+ char *cur_path = NULL; -+ struct sti_object *obj; -+ int i, rc = 0; -+ -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out; -+ } -+ -+ for (i = 0; i < PARAM_NUM_MAX; i++) -+ VDBG(sti, "parameter[%u]: 0x%08x\n", -+ i + 1, sti->ops_params[i]); -+ -+ if (!backing_folder_is_open(sti)) { -+ ERROR(sti, "backing folder is not open\n"); -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ goto out; -+ } -+ -+ storage_id = sti->ops_params[0]; -+ obj_handle = sti->ops_params[2]; -+ old_obj_num = sti->object_num; -+ -+ if (storage_id == 0xffffffff) { -+ /* list all objects recursive */ -+ rc = list_objects(sti, sti->root_path, NULL, true); -+ new_obj_num = sti->object_num; -+ } else { -+ /* list objects of current folder */ -+ list_for_each_entry(obj, &sti->obj_list, list) { -+ if (obj->obj_handle == obj_handle) -+ break; -+ } -+ -+ if (obj_handle == 0xffffffff) -+ cur_path = sti->root_path; -+ else -+ cur_path = obj->full_path; -+ VDBG(sti, "current path: %s\n", cur_path); -+ -+ if (cur_path) -+ rc = list_objects(sti, cur_path, obj, false); -+ else { -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ goto out; -+ } -+ -+ new_obj_num = sti->sub_object_num; -+ } -+ -+ if (rc) { -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ goto out; -+ } -+ -+ /* 4 bytes array number plus object handles size */ -+ size = 4 + new_obj_num * 4; -+ VDBG(sti, "object number: %u, payload size: %u\n", -+ new_obj_num, size); -+ fill_data_container(bh, sti, PIMA15740_CONTAINER_LEN + size); -+ -+ /* fill object handles array */ -+ memcpy(bh->buf + PIMA15740_CONTAINER_LEN, &new_obj_num, 4); -+ for (i = 1; i <= new_obj_num; i++) { -+ tmp_obj_num = old_obj_num + i; -+ memcpy(bh->buf + PIMA15740_CONTAINER_LEN + i * 4, -+ &tmp_obj_num, 4); -+ } -+ -+ bh->inreq->length = PIMA15740_CONTAINER_LEN + size; -+ bh->state = BUF_STATE_FULL; -+ start_transfer(sti, sti->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ sti->next_buffhd_to_fill = bh->next; -+ -+ sti->response_code = PIMA15740_RES_OK; -+out: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_get_object_info(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ size_t size = 0; -+ u32 obj_handle; -+ struct sti_object *obj; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out; -+ } -+ -+ obj_handle = sti->ops_params[0]; -+ if (obj_handle == 0 || obj_handle > sti->object_num) { -+ WARNING(sti, "invalid object handle: 0x%08x\n", obj_handle); -+ sti->response_code = PIMA15740_RES_INVALID_OBJECT_HANDLE; -+ goto out; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* find the object */ -+ list_for_each_entry(obj, &sti->obj_list, list) { -+ if (obj->obj_handle == obj_handle) -+ break; -+ } -+ -+ memcpy(bh->buf + PIMA15740_CONTAINER_LEN, &obj->obj_info, -+ obj->obj_info_size); -+ size = PIMA15740_CONTAINER_LEN + obj->obj_info_size; -+ fill_data_container(bh, sti, size); -+ -+ bh->inreq->length = size; -+ bh->state = BUF_STATE_FULL; -+ -+ spin_unlock_irq(&sti->lock); -+ -+ start_transfer(sti, sti->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ sti->next_buffhd_to_fill = bh->next; -+ -+ DBG(sti, "get object info: %s\n", obj->full_path); -+ VDBG(sti, "obj_handle: 0x%08x\n", obj->obj_handle); -+ -+ /* dump ObjectInfo Dataset */ -+ dump_object_info(sti, obj); -+ -+ sti->response_code = PIMA15740_RES_OK; -+out: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_get_object(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ u32 obj_handle; -+ loff_t file_size, file_offset, file_offset_tmp; -+ unsigned int amount_left, amount; -+ ssize_t nread; -+ struct sti_object *obj; -+ struct file *filp = NULL; -+ struct inode *inode = NULL; -+ char __user *buf; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out1; -+ } -+ -+ obj_handle = sti->ops_params[0]; -+ if (obj_handle == 0 || obj_handle > sti->object_num) { -+ WARNING(sti, "invalid object handle: 0x%08x\n", obj_handle); -+ sti->response_code = PIMA15740_RES_INVALID_OBJECT_HANDLE; -+ goto out1; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* find the object */ -+ list_for_each_entry(obj, &sti->obj_list, list) { -+ if (obj->obj_handle == obj_handle) -+ break; -+ } -+ -+ spin_unlock_irq(&sti->lock); -+ -+ /* open object file */ -+ filp = filp_open(obj->full_path, O_RDONLY | O_LARGEFILE, 0); -+ if (IS_ERR(filp)) { -+ ERROR(sti, "unable to open file: %s. Err = %d\n", -+ obj->full_path, (int) PTR_ERR(filp)); -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ goto out1; -+ } -+ -+ /* figure out the size and read the remaining amount */ -+ inode = filp->f_dentry->d_inode; -+ file_size = i_size_read(inode->i_mapping->host); -+ VDBG(sti, "object file size: %llu\n", (unsigned long long) file_size); -+ if (unlikely(file_size == 0)) { -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ goto out2; -+ } -+ -+ DBG(sti, "get object: %s\n", obj->full_path); -+ -+ file_offset = 0; -+ amount_left = file_size; -+ -+ while (amount_left > 0) { -+ bh = sti->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ rc = sleep_thread(sti); -+ if (rc) { -+ filp_close(filp, current->files); -+ return rc; -+ } -+ } -+ -+ /* don't read more than the buffer size */ -+ if (file_offset == 0) { -+ fill_data_container(bh, sti, -+ file_size + PIMA15740_CONTAINER_LEN); -+ buf = (char __user *) bh->buf + -+ PIMA15740_CONTAINER_LEN; -+ amount = min((unsigned int) amount_left, -+ mod_data.buflen - PIMA15740_CONTAINER_LEN); -+ } else { -+ buf = (char __user *) bh->buf; -+ amount = min((unsigned int) amount_left, -+ mod_data.buflen); -+ } -+ -+ /* no more left to read */ -+ if (amount == 0) -+ break; -+ -+ /* perform the read */ -+ file_offset_tmp = file_offset; -+ nread = vfs_read(filp, buf, amount, &file_offset_tmp); -+ VDBG(sti, "file read %u @ %llu -> %d\n", amount, -+ (unsigned long long) file_offset, -+ (int) nread); -+ -+ if (signal_pending(current)) { -+ filp_close(filp, current->files); -+ return -EINTR; -+ } -+ -+ if (nread < 0) { -+ WARNING(sti, "error in file read: %d\n", -+ (int) nread); -+ nread = 0; -+ } else if (nread < amount) { -+ WARNING(sti, "partial file read: %d/%u\n", -+ (int) nread, amount); -+ /* round down to a block */ -+ nread -= (nread & 511); -+ } -+ -+ /* -+ * PIMA 15740 generic container head resides in -+ * first data block payload -+ */ -+ if (file_offset == 0) -+ bh->inreq->length = nread + PIMA15740_CONTAINER_LEN; -+ else -+ bh->inreq->length = nread; -+ bh->state = BUF_STATE_FULL; -+ bh->inreq->zero = 0; -+ -+ file_offset += nread; -+ amount_left -= nread; -+ -+ /* send this buffer and go read some more */ -+ start_transfer(sti, sti->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ sti->next_buffhd_to_fill = bh->next; -+ } -+ -+ sti->response_code = PIMA15740_RES_OK; -+out2: -+ filp_close(filp, current->files); -+out1: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_delete_object(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ u32 obj_handle; -+ struct sti_object *obj, *tmp_obj; -+ struct nameidata nd; -+ int i; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out; -+ } -+ -+ for (i = 0; i < PARAM_NUM_MAX; i++) -+ VDBG(sti, "parameter[%u]: 0x%08x\n", -+ i + 1, sti->ops_params[i]); -+ -+ obj_handle = sti->ops_params[0]; -+ if (obj_handle == 0 || obj_handle > sti->object_num) { -+ WARNING(sti, "invalid object handle: 0x%08x\n", obj_handle); -+ sti->response_code = PIMA15740_RES_INVALID_OBJECT_HANDLE; -+ goto out; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* find the object */ -+ list_for_each_entry_safe(obj, tmp_obj, &sti->obj_list, list) { -+ if (obj->obj_handle == obj_handle) { -+ list_del_init(&obj->list); -+ kfree(obj); -+ break; -+ } -+ } -+ -+ spin_unlock_irq(&sti->lock); -+ -+ /* lookup the object file */ -+ rc = path_lookup(obj->full_path, 0, &nd); -+ if (rc) { -+ ERROR(sti, "invalid object file path: %s\n", obj->full_path); -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ goto out; -+ } -+ -+ /* unlink the file */ -+ rc = vfs_unlink(nd.path.dentry->d_parent->d_inode, nd.path.dentry); -+ if (rc) { -+ ERROR(sti, "can't delete object\n"); -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ goto out; -+ } -+ -+ DBG(sti, "delete object: %s\n", obj->full_path); -+ -+ sti->response_code = PIMA15740_RES_OK; -+out: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_send_object_info(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ u8 filename_len; -+ u32 storage_id; -+ u32 parent_object = 0xffffffff; -+ unsigned int offset; -+ struct sti_object *obj, *parent_obj; -+ size_t obj_size; -+ int i; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out2; -+ } -+ -+ for (i = 0; i < PARAM_NUM_MAX; i++) -+ VDBG(sti, "parameter[%u]: 0x%08x\n", -+ i + 1, sti->ops_params[i]); -+ -+ /* destination storage id */ -+ storage_id = sti->ops_params[0]; -+ if (storage_id != STORAGE_ID) { -+ WARNING(sti, "invalid storage id: 0x%08x\n", storage_id); -+ sti->response_code = PIMA15740_RES_INVALID_STORAGE_ID; -+ goto out2; -+ } -+ -+ /* parent object handle where object should be placed */ -+ parent_object = sti->ops_params[1]; -+ -+ /* if root directory, parent object is 0xffffffff */ -+ if (parent_object == 0 || (parent_object > sti->object_num -+ && parent_object != 0xffffffff)) { -+ WARNING(sti, "invalid parent handle: 0x%08x\n", -+ parent_object); -+ sti->response_code = PIMA15740_RES_INVALID_PARENT_OBJECT; -+ goto out2; -+ } -+ -+ /* queue a request to read ObjectInfo Dataset */ -+ set_bulk_out_req_length(sti, bh, 512); -+ bh->outreq->short_not_ok = 1; -+ start_transfer(sti, sti->bulk_out, bh->outreq, -+ &bh->outreq_busy, &bh->state); -+ -+ /* wait for the ObjectInfo Dataset to arrive */ -+ while (bh->state != BUF_STATE_FULL) { -+ rc = sleep_thread(sti); -+ if (rc) -+ goto out1; -+ } -+ -+ /* filename strings length */ -+ offset = offsetof(struct pima15740_object_info, obj_strings[0]); -+ filename_len = *(u8 *)(bh->outreq->buf + PIMA15740_CONTAINER_LEN -+ + offset); -+ VDBG(sti, "filename_len: %u\n", filename_len); -+ -+ /* sti_object size */ -+ obj_size = sizeof(*obj) + 2 * filename_len + 4; -+ VDBG(sti, "obj_size: %u\n", obj_size); -+ obj = kzalloc(obj_size, GFP_KERNEL); -+ if (!obj) { -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ goto out2; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* increase total object number */ -+ sti->object_num++; -+ -+ /* fill sti_object info */ -+ obj->obj_handle = sti->object_num; -+ VDBG(sti, "obj_handle: 0x%08x\n", obj->obj_handle); -+ -+ if (parent_object == 0xffffffff) -+ obj->parent_object = 0; -+ else -+ obj->parent_object = parent_object; -+ VDBG(sti, "parent_object: 0x%08x\n", obj->parent_object); -+ -+ obj->storage_id = storage_id; -+ -+ /* mark object ready to send */ -+ obj->send_valid = 1; -+ -+ /* ObjectInfo Dataset size */ -+ obj->obj_info_size = sizeof(struct pima15740_object_info) -+ + 2 * filename_len + 4; -+ VDBG(sti, "obj_info_size: %u\n", obj->obj_info_size); -+ -+ /* filename */ -+ offset = offsetof(struct pima15740_object_info, obj_strings[1]); -+ uni16_to_str(bh->outreq->buf + PIMA15740_CONTAINER_LEN + offset, -+ obj->filename, filename_len); -+ -+ /* object full path */ -+ memset(obj->full_path, 0, sizeof(obj->full_path)); -+ if (parent_object == 0xffffffff) { -+ snprintf(obj->full_path, sizeof(obj->full_path), "%s/%s", -+ sti->root_path, obj->filename); -+ } else { -+ /* find the parent object */ -+ list_for_each_entry(parent_obj, &sti->obj_list, list) { -+ if (parent_obj->obj_handle == parent_object) -+ break; -+ } -+ snprintf(obj->full_path, sizeof(obj->full_path), "%s/%s", -+ parent_obj->full_path, obj->filename); -+ } -+ VDBG(sti, "full_path: %s\n", obj->full_path); -+ -+ /* fetch ObjectInfo Dataset from buffer */ -+ memcpy(&obj->obj_info, bh->outreq->buf + PIMA15740_CONTAINER_LEN, -+ obj->obj_info_size); -+ -+ /* root directory, modify parent object */ -+ if (parent_object == 0xffffffff) -+ obj->obj_info.parent_object = cpu_to_le32(0); -+ else -+ obj->obj_info.parent_object = parent_object; -+ -+ obj->obj_info.storage_id = storage_id; -+ -+ /* capture date */ -+ obj->obj_info.obj_strings[filename_len * 2 + 1] = 0; -+ -+ /* modification date */ -+ obj->obj_info.obj_strings[filename_len * 2 + 2] = 0; -+ -+ /* keywords */ -+ obj->obj_info.obj_strings[filename_len * 2 + 3] = 0; -+ -+ bh->state = BUF_STATE_EMPTY; -+ -+ /* add to object list */ -+ list_add_tail(&obj->list, &sti->obj_list); -+ -+ spin_unlock_irq(&sti->lock); -+ -+ DBG(sti, "send object info: %s\n", obj->filename); -+ -+ /* dump ObjectInfo Dataset */ -+ dump_object_info(sti, obj); -+out2: -+ /* send response */ -+ rc = send_params_response(sti, PIMA15740_RES_OK, -+ sti->storage_id, parent_object, sti->object_num, -+ 3); -+out1: -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_send_object(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ int rc = -EINVAL; -+ int get_some_more; -+ u32 amount_left_to_req, amount_left_to_write; -+ loff_t file_size, file_offset, file_offset_tmp, -+ usb_offset; -+ unsigned int amount; -+ ssize_t nwritten; -+ struct sti_object *obj; -+ struct file *filp = NULL; -+ char __user *buf; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* find the object */ -+ list_for_each_entry(obj, &sti->obj_list, list) { -+ if (obj->send_valid) -+ break; -+ } -+ -+ /* mark object already sent */ -+ obj->send_valid = 0; -+ -+ spin_unlock_irq(&sti->lock); -+ -+ /* open object file */ -+ filp = filp_open(obj->full_path, O_CREAT | O_RDWR | O_LARGEFILE, 0666); -+ if (IS_ERR(filp)) { -+ ERROR(sti, "unable to open file: %s. Err = %d\n", -+ obj->full_path, (int) PTR_ERR(filp)); -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ goto out1; -+ } -+ -+ file_size = obj->obj_info.object_compressed_size; -+ VDBG(sti, "object file size: %llu\n", -+ (unsigned long long) file_size); -+ if (unlikely(file_size == 0)) { -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ goto out2; -+ } -+ -+ DBG(sti, "send object: %s\n", obj->full_path); -+ -+ /* carry out the file writes */ -+ get_some_more = 1; -+ file_offset = usb_offset = 0; -+ -+ amount_left_to_req = file_size + PIMA15740_CONTAINER_LEN; -+ amount_left_to_write = file_size; -+ VDBG(sti, "in total: amount_left_to_req: %u\n", -+ amount_left_to_req); -+ VDBG(sti, "in total: amount_left_to_write: %u\n", -+ amount_left_to_write); -+ -+ while (amount_left_to_write > 0) { -+ bh = sti->next_buffhd_to_fill; -+ if (bh->state == BUF_STATE_EMPTY && get_some_more) { -+ amount = min(amount_left_to_req, mod_data.buflen); -+ amount = min((loff_t) amount, file_size -+ + PIMA15740_CONTAINER_LEN - usb_offset); -+ VDBG(sti, "usb amount: %u\n", amount); -+ -+ /* no left data request to transfer */ -+ if (amount == 0) { -+ get_some_more = 0; -+ continue; -+ } -+ -+ /* get the next buffer */ -+ usb_offset += amount; -+ amount_left_to_req -= amount; -+ -+ if (amount_left_to_req == 0) -+ get_some_more = 0; -+ -+ /* amount is always divisible by bulk-out -+ maxpacket size */ -+ bh->outreq->length = bh->bulk_out_intended_length = -+ amount; -+ bh->outreq->short_not_ok = 1; -+ start_transfer(sti, sti->bulk_out, bh->outreq, -+ &bh->outreq_busy, &bh->state); -+ sti->next_buffhd_to_fill = bh->next; -+ continue; -+ } -+ -+ /* write the received data to the backing folder */ -+ bh = sti->next_buffhd_to_drain; -+ -+ /* host stopped early */ -+ if (bh->state == BUF_STATE_EMPTY && !get_some_more) { -+ WARNING(sti, "host stops early, bh->state: %d\n", -+ bh->state); -+ sti->response_code = PIMA15740_RES_INCOMPLETE_TRANSFER; -+ goto out2; -+ } -+ -+ if (bh->state == BUF_STATE_FULL) { -+ smp_rmb(); -+ sti->next_buffhd_to_drain = bh->next; -+ bh->state = BUF_STATE_EMPTY; -+ -+ /* something go wrong with the transfer */ -+ if (bh->outreq->status != 0) { -+ sti->response_code = -+ PIMA15740_RES_INCOMPLETE_TRANSFER; -+ goto out2; -+ } -+ -+ /* -+ * PIMA 15740 generic container head resides in -+ * first data block payload -+ */ -+ if (file_offset == 0) { -+ buf = (char __user *) bh->buf + -+ PIMA15740_CONTAINER_LEN; -+ amount = bh->outreq->actual - -+ PIMA15740_CONTAINER_LEN; -+ } else { -+ buf = (char __user *) bh->buf; -+ amount = bh->outreq->actual; -+ } -+ amount = min((loff_t) amount, -+ file_size - file_offset); -+ -+ /* across page boundary, recalculate the length */ -+ if (amount == 0) { -+ INFO(sti, "extra bulk out zlp packets\n"); -+ usb_offset -= bh->outreq->length; -+ amount_left_to_req += bh->outreq->length; -+ continue; -+ } -+ -+ /* perform the write */ -+ file_offset_tmp = file_offset; -+ nwritten = vfs_write(filp, (char __user *) buf, -+ amount, &file_offset_tmp); -+ VDBG(sti, "file write %u @ %llu -> %d\n", amount, -+ (unsigned long long) file_offset, -+ (int) nwritten); -+ -+ if (signal_pending(current)) { -+ filp_close(filp, current->files); -+ return -EINTR; -+ } -+ -+ if (nwritten < 0) { -+ VDBG(sti, "error in file write: %d\n", -+ (int) nwritten); -+ nwritten = 0; -+ } else if (nwritten < amount) { -+ VDBG(sti, "partial file write: %d/%u\n", -+ (int) nwritten, amount); -+ /* round down to a block */ -+ nwritten -= (nwritten & 511); -+ } -+ -+ file_offset += nwritten; -+ amount_left_to_write -= nwritten; -+ -+ VDBG(sti, "file_offset: %llu, " -+ "amount_left_to_write: %u\n", -+ (unsigned long long) file_offset, -+ amount_left_to_write); -+ -+ /* error occurred */ -+ if (nwritten < amount) { -+ sti->response_code = -+ PIMA15740_RES_INCOMPLETE_TRANSFER; -+ goto out2; -+ } -+ continue; -+ } -+ -+ /* wait for something to happen */ -+ rc = sleep_thread(sti); -+ if (rc) { -+ filp_close(filp, current->files); -+ return rc; -+ } -+ } -+ -+ /* fsync object file */ -+ vfs_fsync(filp, filp->f_path.dentry, 1); -+ -+ sti->response_code = PIMA15740_RES_OK; -+out2: -+ filp_close(filp, current->files); -+out1: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_copy_object(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ int rc = 0, i; -+ size_t size = 0; -+ unsigned int old_obj_handle, new_obj_parent_handle; -+ unsigned int new_storage_id, amount, amount_left; -+ struct sti_object *old_obj = NULL, *new_obj_parent = NULL; -+ struct sti_object *new_obj, *tmp_obj; -+ char *new_obj_fname; -+ struct file *old_fp, *new_fp; -+ struct inode *inode = NULL; -+ char __user *buf; -+ loff_t file_size, file_offset, file_offset_tmp; -+ ssize_t nread, nwritten; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out1; -+ } -+ -+ old_obj_handle = sti->ops_params[0]; -+ new_storage_id = sti->ops_params[1]; -+ new_obj_parent_handle = sti->ops_params[2]; -+ -+ if ((old_obj_handle == 0) || (old_obj_handle > sti->object_num)) { -+ WARNING(sti, "invalid object handle: %u\n", old_obj_handle); -+ sti->response_code = PIMA15740_RES_INVALID_OBJECT_HANDLE; -+ goto out1; -+ } -+ -+ if (new_storage_id != sti->storage_id) { -+ WARNING(sti, "invalid storage id: %u\n", new_storage_id); -+ sti->response_code = PIMA15740_RES_INVALID_STORAGE_ID; -+ goto out1; -+ } -+ -+ if (new_obj_parent_handle > sti->object_num -+ && new_obj_parent_handle != 0xffffffff) { -+ WARNING(sti, "invalid parent object handle: %u\n", -+ new_obj_parent_handle); -+ sti->response_code = PIMA15740_RES_INVALID_PARENT_OBJECT; -+ goto out1; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* find the old object to be copied */ -+ i = 0; -+ list_for_each_entry(tmp_obj, &sti->obj_list, list) { -+ if (tmp_obj->obj_handle == old_obj_handle) { -+ i++; -+ old_obj = tmp_obj; -+ } -+ -+ if (tmp_obj->obj_handle == new_obj_parent_handle) { -+ i++; -+ new_obj_parent = tmp_obj; -+ } -+ -+ if (i == 2) -+ break; -+ } -+ -+ spin_unlock_irq(&sti->lock); -+ -+ if (i != 2 || !old_obj || !new_obj_parent) { -+ WARNING(sti, "invalid objects %u or %u\n", -+ old_obj_handle, new_obj_parent_handle); -+ sti->response_code = PIMA15740_RES_INVALID_PARENT_OBJECT; -+ goto out1; -+ } -+ -+ size = strlen(new_obj_parent->full_path) + -+ strlen(old_obj->filename) + 2; -+ new_obj_fname = kzalloc(size, GFP_KERNEL); -+ if (!new_obj_fname) { -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ rc = -EINVAL; -+ goto out1; -+ } -+ strncpy(new_obj_fname, new_obj_parent->full_path, size); -+ strncat(new_obj_fname, "/", size); -+ strncat(new_obj_fname, old_obj->filename, size); -+ -+ VDBG(sti, "copy object: from [%s] to [%s]\n", -+ old_obj->full_path, new_obj_fname); -+ -+ old_fp = filp_open(old_obj->full_path, O_RDONLY | O_LARGEFILE, 0); -+ if (IS_ERR(old_fp)) { -+ ERROR(sti, "unable to open file: %s. Err = %d\n", -+ old_obj->full_path, (int) PTR_ERR(old_fp)); -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ rc = -EINVAL; -+ goto out2; -+ } -+ -+ new_fp = filp_open(new_obj_fname, O_CREAT | O_RDWR | O_LARGEFILE, 0666); -+ if (IS_ERR(new_fp)) { -+ ERROR(sti, "unable to create file: %s. Err = %d\n", -+ new_obj_fname, (int) PTR_ERR(new_fp)); -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ rc = -EINVAL; -+ goto out3; -+ } -+ -+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL); -+ if (!buf) { -+ sti->response_code = PIMA15740_RES_OPERATION_NOT_SUPPORTED; -+ rc = -EINVAL; -+ goto out4; -+ } -+ -+ inode = old_fp->f_dentry->d_inode; -+ file_size = i_size_read(inode->i_mapping->host); -+ VDBG(sti, "object file size: %llu\n", (unsigned long long) file_size); -+ -+ if (unlikely(file_size == 0)) { -+ sti->response_code = PIMA15740_RES_STORE_NOT_AVAILABLE; -+ rc = -EIO; -+ goto out5; -+ } -+ -+ file_offset = 0; -+ amount_left = file_size; -+ -+ while (amount_left > 0) { -+ amount = min(amount_left, (unsigned int) PAGE_SIZE); -+ if (amount == 0) -+ break; -+ -+ file_offset_tmp = file_offset; -+ nread = vfs_read(old_fp, buf, amount, &file_offset_tmp); -+ -+ if (signal_pending(current)) { -+ rc = -EINTR; -+ goto out5; -+ } -+ -+ if (nread < 0) { -+ DBG(sti, "error in file read: %d\n", -+ (int) nread); -+ nread = 0; -+ } else if (nread < amount) { -+ DBG(sti, "partial file read: %d/%u\n", -+ (int) nread, amount); -+ /* round down to a block */ -+ nread -= (nread & 511); -+ } -+ -+ amount = min(amount, (unsigned int) nread); -+ file_offset_tmp = file_offset; -+ nwritten = vfs_write(new_fp, buf, amount, &file_offset_tmp); -+ -+ if (signal_pending(current)) { -+ rc = -EINTR; -+ goto out5; -+ } -+ -+ if (nwritten < 0) { -+ VDBG(sti, "error in file write: %d\n", -+ (int) nwritten); -+ nwritten = 0; -+ } else if (nwritten < amount) { -+ VDBG(sti, "partial file write: %d/%u\n", -+ (int) nwritten, amount); -+ /* round down to a block */ -+ nwritten -= (nwritten & 511); -+ } -+ -+ amount = min(amount, (unsigned int) nwritten); -+ file_offset += amount; -+ amount_left -= amount; -+ } -+ -+ size = sizeof(*old_obj); -+ new_obj = kzalloc(size, GFP_KERNEL); -+ if (!new_obj) { -+ rc = -ENOMEM; -+ goto out5; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ sti->object_num++; -+ -+ /* change obj_handle */ -+ new_obj->obj_handle = sti->object_num; -+ -+ /* change parent object */ -+ if (new_obj_parent_handle == 0xffffffff) -+ new_obj->parent_object = 0; -+ else -+ new_obj->parent_object = new_obj_parent_handle; -+ -+ new_obj->storage_id = old_obj->storage_id; -+ new_obj->is_dir = old_obj->is_dir; -+ new_obj->send_valid = old_obj->send_valid; -+ new_obj->obj_info_size = old_obj->obj_info_size; -+ strncpy(new_obj->filename, old_obj->filename, -+ sizeof(new_obj->filename)); -+ -+ /* change full path name */ -+ strncpy(new_obj->full_path, new_obj_fname, sizeof(new_obj->full_path)); -+ -+ /* copy object_info */ -+ memcpy(&new_obj->obj_info, &old_obj->obj_info, old_obj->obj_info_size); -+ -+ /* fill parent_object in object_info */ -+ new_obj->obj_info.parent_object = new_obj->parent_object; -+ -+ /* add to object list */ -+ list_add_tail(&new_obj->list, &sti->obj_list); -+ -+ spin_unlock_irq(&sti->lock); -+ -+ sti->response_code = PIMA15740_RES_OK; -+out5: -+ kfree(buf); -+out4: -+ filp_close(new_fp, current->files); -+out3: -+ filp_close(old_fp, current->files); -+out2: -+ kfree(new_obj_fname); -+out1: -+ /* send response */ -+ rc = send_params_response(sti, sti->response_code, -+ sti->object_num, 0, 0, -+ 1); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+static int do_move_object(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ int i, rc = 0; -+ size_t size = 0; -+ unsigned int old_obj_handle, new_obj_parent_handle; -+ unsigned int new_storage_id; -+ char *new_obj_fname; -+ struct file *old_fp, *new_fp; -+ struct inode *old_dir, *new_dir; -+ struct dentry *old_dentry, *new_dentry; -+ struct sti_object *old_obj = NULL; -+ struct sti_object *new_obj = NULL; -+ struct sti_object *new_obj_parent = NULL; -+ struct sti_object *tmp_obj = NULL; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (!sti->session_open) { -+ sti->response_code = PIMA15740_RES_SESSION_NOT_OPEN; -+ goto out1; -+ } -+ -+ old_obj_handle = sti->ops_params[0]; -+ new_storage_id = sti->ops_params[1]; -+ new_obj_parent_handle = sti->ops_params[2]; -+ -+ if ((old_obj_handle == 0) || (old_obj_handle > sti->object_num)) { -+ WARNING(sti, "invalid object handle: %u\n", old_obj_handle); -+ sti->response_code = PIMA15740_RES_INVALID_OBJECT_HANDLE; -+ goto out1; -+ } -+ -+ if (new_storage_id != sti->storage_id) { -+ WARNING(sti, "invalid storage id: %u\n", new_storage_id); -+ sti->response_code = PIMA15740_RES_INVALID_STORAGE_ID; -+ goto out1; -+ } -+ -+ if (new_obj_parent_handle > sti->object_num -+ && new_obj_parent_handle != 0xffffffff) { -+ WARNING(sti, "invalid parent object handle: %u\n", -+ new_obj_parent_handle); -+ sti->response_code = PIMA15740_RES_INVALID_PARENT_OBJECT; -+ goto out1; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* find the old object to be moved */ -+ i = 0; -+ list_for_each_entry(tmp_obj, &sti->obj_list, list) { -+ if (tmp_obj->obj_handle == old_obj_handle) { -+ i++; -+ old_obj = tmp_obj; -+ } -+ -+ if (tmp_obj->obj_handle == new_obj_parent_handle) { -+ i++; -+ new_obj_parent = tmp_obj; -+ } -+ -+ if (i == 2) -+ break; -+ } -+ -+ spin_unlock_irq(&sti->lock); -+ -+ if (i != 2 || !old_obj || !new_obj_parent) { -+ WARNING(sti, "invalid objects %u or %u\n", -+ old_obj_handle, new_obj_parent_handle); -+ sti->response_code = PIMA15740_RES_INVALID_PARENT_OBJECT; -+ goto out1; -+ } -+ -+ size = strlen(new_obj_parent->full_path) + -+ strlen(old_obj->filename) + 2; -+ new_obj_fname = kzalloc(size, GFP_KERNEL); -+ if (!new_obj_fname) { -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ rc = -EINVAL; -+ goto out1; -+ } -+ strncpy(new_obj_fname, new_obj_parent->full_path, size); -+ strncat(new_obj_fname, "/", size); -+ strncat(new_obj_fname, old_obj->filename, size); -+ -+ VDBG(sti, "move object: from [%s] to [%s]\n", -+ old_obj->full_path, new_obj_fname); -+ -+ old_fp = filp_open(old_obj->full_path, O_RDONLY | O_LARGEFILE, 0); -+ if (IS_ERR(old_fp)) { -+ ERROR(sti, "unable to open file: %s. Err = %d\n", -+ old_obj->full_path, (int) PTR_ERR(old_fp)); -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ rc = -EINVAL; -+ goto out2; -+ } -+ -+ new_fp = filp_open(new_obj_fname, O_CREAT | O_RDWR | O_LARGEFILE, 0666); -+ if (IS_ERR(new_fp)) { -+ ERROR(sti, "unable to create file: %s. Err = %d\n", -+ new_obj_fname, (int) PTR_ERR(new_fp)); -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ rc = -EINVAL; -+ goto out3; -+ } -+ -+ old_dir = old_fp->f_dentry->d_parent->d_inode; -+ new_dir = new_fp->f_dentry->d_parent->d_inode; -+ old_dentry = old_fp->f_dentry; -+ new_dentry = new_fp->f_dentry; -+ -+ rc = vfs_rename(old_dir, old_dentry, new_dir, new_dentry); -+ -+ if (rc) { -+ sti->response_code = PIMA15740_RES_OPERATION_NOT_SUPPORTED; -+ goto out4; -+ } else -+ sti->response_code = PIMA15740_RES_OK; -+ -+ size = sizeof(*old_obj); -+ new_obj = kzalloc(size, GFP_KERNEL); -+ if (!new_obj) { -+ rc = -ENOMEM; -+ goto out4; -+ } -+ -+ spin_lock_irq(&sti->lock); -+ -+ /* change parent object */ -+ if (new_obj_parent_handle == 0xffffffff) -+ new_obj->parent_object = 0; -+ else -+ new_obj->parent_object = new_obj_parent_handle; -+ -+ new_obj->obj_handle = old_obj->obj_handle; -+ new_obj->storage_id = old_obj->storage_id; -+ new_obj->is_dir = old_obj->is_dir; -+ new_obj->send_valid = old_obj->send_valid; -+ new_obj->obj_info_size = old_obj->obj_info_size; -+ strncpy(new_obj->filename, old_obj->filename, -+ sizeof(new_obj->filename)); -+ -+ /* change full path name */ -+ strncpy(new_obj->full_path, new_obj_fname, sizeof(new_obj->full_path)); -+ -+ /* copy object_info */ -+ memcpy(&new_obj->obj_info, &old_obj->obj_info, old_obj->obj_info_size); -+ -+ /* fill parent_object in object_info */ -+ new_obj->obj_info.parent_object = new_obj->parent_object; -+ -+ /* add to object list */ -+ list_add_tail(&new_obj->list, &sti->obj_list); -+ -+ /* remove from object list */ -+ list_del_init(&old_obj->list); -+ -+ spin_unlock_irq(&sti->lock); -+ -+ kfree(old_obj); -+out4: -+ filp_close(new_fp, current->files); -+out3: -+ filp_close(old_fp, current->files); -+out2: -+ kfree(new_obj_fname); -+out1: -+ /* send response */ -+ rc = send_response(sti, sti->response_code); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+/* TODO: PIMA 15740 Event handling via interrupt endpoint */ -+static int send_status(struct sti_dev *sti) -+{ -+ VDBG(sti, "---> %s()\n", __func__); -+ VDBG(sti, "<--- %s()\n", __func__); -+ return 0; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* handle supported PIMA 15740 operations */ -+static int do_still_image_command(struct sti_dev *sti) -+{ -+ struct sti_buffhd *bh; -+ int rc = -EINVAL; -+ int reply = -EINVAL; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ dump_cb(sti); -+ -+ if (!backing_folder_is_open(sti)) { -+ ERROR(sti, "backing folder is not open\n"); -+ return rc; -+ } -+ -+ /* wait for the next buffer to become available for data or status */ -+ bh = sti->next_buffhd_to_drain = sti->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ rc = sleep_thread(sti); -+ if (rc) -+ return rc; -+ } -+ -+ down_read(&sti->filesem); -+ switch (sti->code) { -+ -+ case PIMA15740_OP_GET_DEVICE_INFO: -+ DBG(sti, "PIMA15740 OPS: get device info\n"); -+ reply = do_get_device_info(sti, bh); -+ break; -+ -+ case PIMA15740_OP_OPEN_SESSION: -+ DBG(sti, "PIMA15740 OPS: open session\n"); -+ reply = do_open_session(sti); -+ break; -+ -+ case PIMA15740_OP_CLOSE_SESSION: -+ DBG(sti, "PIMA15740 OPS: close session\n"); -+ reply = do_close_session(sti); -+ break; -+ -+ case PIMA15740_OP_GET_STORAGE_IDS: -+ DBG(sti, "PIMA15740 OPS: get storage ids\n"); -+ reply = do_get_storage_ids(sti, bh); -+ break; -+ -+ case PIMA15740_OP_GET_STORAGE_INFO: -+ DBG(sti, "PIMA15740 OPS: get storage info\n"); -+ reply = do_get_storage_info(sti, bh); -+ break; -+ -+ case PIMA15740_OP_GET_NUM_OBJECTS: -+ DBG(sti, "PIMA15740 OPS: get num objects\n"); -+ reply = do_get_num_objects(sti, bh); -+ break; -+ -+ case PIMA15740_OP_GET_OBJECT_HANDLES: -+ DBG(sti, "PIMA15740 OPS: get object handles\n"); -+ reply = do_get_object_handles(sti, bh); -+ break; -+ -+ case PIMA15740_OP_GET_OBJECT_INFO: -+ DBG(sti, "PIMA15740 OPS: get object info\n"); -+ reply = do_get_object_info(sti, bh); -+ break; -+ -+ case PIMA15740_OP_GET_OBJECT: -+ DBG(sti, "PIMA15740 OPS: get object\n"); -+ reply = do_get_object(sti, bh); -+ break; -+ -+ case PIMA15740_OP_DELETE_OBJECT: -+ DBG(sti, "PIMA15740 OPS: delete object\n"); -+ reply = do_delete_object(sti, bh); -+ break; -+ -+ case PIMA15740_OP_SEND_OBJECT_INFO: -+ DBG(sti, "PIMA15740 OPS: send object info\n"); -+ reply = do_send_object_info(sti, bh); -+ break; -+ -+ case PIMA15740_OP_SEND_OBJECT: -+ DBG(sti, "PIMA15740 OPS: send object\n"); -+ reply = do_send_object(sti, bh); -+ break; -+ -+ case PIMA15740_OP_COPY_OBJECT: -+ DBG(sti, "PIMA15740 OPS: copy object\n"); -+ reply = do_copy_object(sti, bh); -+ break; -+ -+ case PIMA15740_OP_MOVE_OBJECT: -+ DBG(sti, "PIMA15740 OPS: move object\n"); -+ reply = do_move_object(sti, bh); -+ break; -+ -+ default: -+ WARNING(sti, "unknown PIMA15740 OPS 0x%04x\n", sti->code); -+ break; -+ } -+ up_read(&sti->filesem); -+ -+ if (reply == -EINTR || signal_pending(current)) -+ rc = -EINTR; -+ -+ if (reply == -EINVAL) -+ rc = 0; -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* received PIMA 15740 Command Blocks */ -+static int received_cb(struct sti_dev *sti, struct sti_buffhd *bh) -+{ -+ struct usb_request *req = bh->outreq; -+ struct pima15740_container *cb = req->buf; -+ unsigned short n; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* this is not a real packet */ -+ if (req->status) -+ return -EINVAL; -+ -+ /* save the command for later */ -+ sti->container_len = cb->container_len; -+ sti->container_type = cb->container_type; -+ sti->code = cb->code; -+ sti->transaction_id = cb->transaction_id; -+ -+ /* get Command Block Parameters 1..N */ -+ n = sti->container_len - PIMA15740_CONTAINER_LEN; -+ if (n != 0) -+ memcpy(sti->ops_params, cb + 1, n); -+ -+ VDBG(sti, "Command Block: len=%u, type=0x%04x, " -+ "code=0x%04x, trans_id=0x%08x\n", -+ sti->container_len, sti->container_type, -+ sti->code, sti->transaction_id); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return 0; -+} -+ -+ -+static int get_next_command(struct sti_dev *sti) -+{ -+ struct sti_buffhd *bh; -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* wait for the next buffer to become available */ -+ bh = sti->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ rc = sleep_thread(sti); -+ if (rc) -+ return rc; -+ } -+ -+ /* queue a request to read a Bulk-only Command Block */ -+ set_bulk_out_req_length(sti, bh, 512); -+ bh->outreq->short_not_ok = 1; -+ start_transfer(sti, sti->bulk_out, bh->outreq, -+ &bh->outreq_busy, &bh->state); -+ -+ /* we will drain the buffer in software, which means we -+ * can reuse it for the next filling. No need to advance -+ * next_buffhd_to_fill. */ -+ -+ /* wait for the Command Block to arrive */ -+ while (bh->state != BUF_STATE_FULL) { -+ rc = sleep_thread(sti); -+ if (rc) -+ return rc; -+ } -+ smp_rmb(); -+ rc = received_cb(sti, bh); -+ bh->state = BUF_STATE_EMPTY; -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int enable_endpoint(struct sti_dev *sti, struct usb_ep *ep, -+ const struct usb_endpoint_descriptor *d) -+{ -+ int rc; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ ep->driver_data = sti; -+ rc = usb_ep_enable(ep, d); -+ if (rc) -+ ERROR(sti, "can't enable %s, result %d\n", ep->name, rc); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+static int alloc_request(struct sti_dev *sti, struct usb_ep *ep, -+ struct usb_request **preq) -+{ -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); -+ if (*preq) -+ return 0; -+ -+ ERROR(sti, "can't allocate request for %s\n", ep->name); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return -ENOMEM; -+} -+ -+/* -+ * Reset interface setting and re-init endpoint state (toggle etc). -+ * Call with altsetting < 0 to disable the interface. The only other -+ * available altsetting is 0, which enables the interface. -+ */ -+static int do_set_interface(struct sti_dev *sti, int altsetting) -+{ -+ int rc = 0; -+ int i; -+ const struct usb_endpoint_descriptor *d; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (sti->running) -+ DBG(sti, "reset interface\n"); -+ -+reset: -+ /* deallocate the requests */ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ struct sti_buffhd *bh = &sti->buffhds[i]; -+ -+ if (bh->inreq) { -+ usb_ep_free_request(sti->bulk_in, bh->inreq); -+ bh->inreq = NULL; -+ } -+ if (bh->outreq) { -+ usb_ep_free_request(sti->bulk_out, bh->outreq); -+ bh->outreq = NULL; -+ } -+ } -+ if (sti->intreq) { -+ usb_ep_free_request(sti->intr_in, sti->intreq); -+ sti->intreq = NULL; -+ } -+ -+ /* disable the endpoints */ -+ if (sti->bulk_in_enabled) { -+ usb_ep_disable(sti->bulk_in); -+ sti->bulk_in_enabled = 0; -+ } -+ if (sti->bulk_out_enabled) { -+ usb_ep_disable(sti->bulk_out); -+ sti->bulk_out_enabled = 0; -+ } -+ if (sti->intr_in_enabled) { -+ usb_ep_disable(sti->intr_in); -+ sti->intr_in_enabled = 0; -+ } -+ -+ sti->running = 0; -+ if (altsetting < 0 || rc != 0) -+ return rc; -+ -+ DBG(sti, "set interface %d\n", altsetting); -+ -+ /* enable the endpoints */ -+ d = ep_desc(sti->gadget, &fs_bulk_in_desc, &hs_bulk_in_desc); -+ rc = enable_endpoint(sti, sti->bulk_in, d); -+ if (rc) -+ goto reset; -+ sti->bulk_in_enabled = 1; -+ -+ d = ep_desc(sti->gadget, &fs_bulk_out_desc, &hs_bulk_out_desc); -+ rc = enable_endpoint(sti, sti->bulk_out, d); -+ if (rc) -+ goto reset; -+ sti->bulk_out_enabled = 1; -+ sti->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); -+ clear_bit(CLEAR_BULK_HALTS, &sti->atomic_bitflags); -+ -+ d = ep_desc(sti->gadget, &fs_intr_in_desc, &hs_intr_in_desc); -+ rc = enable_endpoint(sti, sti->intr_in, d); -+ if (rc) -+ goto reset; -+ sti->intr_in_enabled = 1; -+ -+ /* allocate the requests */ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ struct sti_buffhd *bh = &sti->buffhds[i]; -+ -+ rc = alloc_request(sti, sti->bulk_in, &bh->inreq); -+ if (rc) -+ goto reset; -+ -+ rc = alloc_request(sti, sti->bulk_out, &bh->outreq); -+ if (rc) -+ goto reset; -+ -+ bh->inreq->buf = bh->outreq->buf = bh->buf; -+ bh->inreq->context = bh->outreq->context = bh; -+ bh->inreq->complete = bulk_in_complete; -+ bh->outreq->complete = bulk_out_complete; -+ } -+ -+ rc = alloc_request(sti, sti->intr_in, &sti->intreq); -+ if (rc) -+ goto reset; -+ -+ sti->running = 1; -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+/* -+ * Change our operational configuration. This code must agree with the code -+ * that returns config descriptors, and with interface altsetting code. -+ * -+ * It's also responsible for power management interactions. Some -+ * configurations might not work with our current power sources. -+ * For now we just assume the gadget is always self-powered. -+ */ -+static int do_set_config(struct sti_dev *sti, u8 new_config) -+{ -+ int rc = 0; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* disable the single interface */ -+ if (sti->config != 0) { -+ DBG(sti, "reset config\n"); -+ sti->config = 0; -+ rc = do_set_interface(sti, -1); -+ } -+ -+ /* enable the interface */ -+ if (new_config != 0) { -+ sti->config = new_config; -+ rc = do_set_interface(sti, 0); -+ if (rc) -+ sti->config = 0; /* reset on errors */ -+ else { -+ char *speed; -+ -+ switch (sti->gadget->speed) { -+ case USB_SPEED_LOW: -+ speed = "low"; -+ break; -+ case USB_SPEED_FULL: -+ speed = "full"; -+ break; -+ case USB_SPEED_HIGH: -+ speed = "high"; -+ break; -+ default: -+ speed = "?"; -+ break; -+ } -+ INFO(sti, "%s speed config #%d\n", -+ speed, sti->config); -+ } -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void handle_exception(struct sti_dev *sti) -+{ -+ siginfo_t info; -+ int sig; -+ int i; -+ int num_active; -+ struct sti_buffhd *bh; -+ enum sti_state old_state; -+ u8 new_config; -+ unsigned int exception_req_tag; -+ int rc; -+ -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* Clear the existing signals. Anything but SIGUSR1 is converted -+ * into a high-priority EXIT exception. */ -+ for (;;) { -+ sig = dequeue_signal_lock(current, ¤t->blocked, &info); -+ if (!sig) -+ break; -+ -+ if (sig != SIGUSR1) { -+ if (sti->state < STI_STATE_EXIT) -+ DBG(sti, "main thread exiting on signal\n"); -+ raise_exception(sti, STI_STATE_EXIT); -+ } -+ } -+ -+ /* cancel all the pending transfers */ -+ if (sti->intreq_busy) -+ usb_ep_dequeue(sti->intr_in, sti->intreq); -+ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ bh = &sti->buffhds[i]; -+ if (bh->inreq_busy) -+ usb_ep_dequeue(sti->bulk_in, bh->inreq); -+ if (bh->outreq_busy) -+ usb_ep_dequeue(sti->bulk_out, bh->outreq); -+ } -+ -+ /* wait until everything is idle */ -+ for (;;) { -+ num_active = sti->intreq_busy; -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ bh = &sti->buffhds[i]; -+ num_active += bh->inreq_busy + bh->outreq_busy; -+ } -+ -+ if (num_active == 0) -+ break; -+ -+ if (sleep_thread(sti)) -+ return; -+ } -+ -+ /* clear out the controller's fifos */ -+ if (sti->bulk_in_enabled) -+ usb_ep_fifo_flush(sti->bulk_in); -+ if (sti->bulk_out_enabled) -+ usb_ep_fifo_flush(sti->bulk_out); -+ if (sti->intr_in_enabled) -+ usb_ep_fifo_flush(sti->intr_in); -+ -+ /* -+ * Reset the I/O buffer states and pointers, the device -+ * state, and the exception. Then invoke the handler. -+ */ -+ spin_lock_irq(&sti->lock); -+ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ bh = &sti->buffhds[i]; -+ bh->state = BUF_STATE_EMPTY; -+ } -+ sti->next_buffhd_to_fill = sti->next_buffhd_to_drain = -+ &sti->buffhds[0]; -+ -+ exception_req_tag = sti->exception_req_tag; -+ new_config = sti->new_config; -+ old_state = sti->state; -+ -+ if (old_state == STI_STATE_ABORT_BULK_OUT) -+ sti->state = STI_STATE_STATUS_PHASE; -+ else -+ sti->state = STI_STATE_IDLE; -+ spin_unlock_irq(&sti->lock); -+ -+ /* carry out any extra actions required for the exception */ -+ switch (old_state) { -+ default: -+ break; -+ -+ case STI_STATE_CANCEL: -+ if (usb_ep_clear_halt(sti->bulk_out) || -+ usb_ep_clear_halt(sti->bulk_in)) -+ sti->response_code = PIMA15740_RES_DEVICE_BUSY; -+ else -+ sti->response_code = PIMA15740_RES_OK; -+ break; -+ -+ case STI_STATE_ABORT_BULK_OUT: -+ send_status(sti); -+ spin_lock_irq(&sti->lock); -+ if (sti->state == STI_STATE_STATUS_PHASE) -+ sti->state = STI_STATE_IDLE; -+ spin_unlock_irq(&sti->lock); -+ break; -+ -+ case STI_STATE_RESET: -+ /* in case we were forced against our will to halt a -+ * bulk endpoint, clear the halt now */ -+ if (test_and_clear_bit(CLEAR_BULK_HALTS, -+ &sti->atomic_bitflags)) { -+ usb_ep_clear_halt(sti->bulk_in); -+ usb_ep_clear_halt(sti->bulk_out); -+ } -+ -+ if (sti->ep0_req_tag == exception_req_tag) -+ /* complete the status stage */ -+ ep0_queue(sti); -+ break; -+ -+ case STI_STATE_INTERFACE_CHANGE: -+ rc = do_set_interface(sti, 0); -+ if (sti->ep0_req_tag != exception_req_tag) -+ break; -+ if (rc != 0) /* STALL on errors */ -+ sti_set_halt(sti, sti->ep0); -+ else /* complete the status stage */ -+ ep0_queue(sti); -+ break; -+ -+ case STI_STATE_CONFIG_CHANGE: -+ rc = do_set_config(sti, new_config); -+ if (sti->ep0_req_tag != exception_req_tag) -+ break; -+ if (rc != 0) /* STALL on errors */ -+ sti_set_halt(sti, sti->ep0); -+ else /* complete the status stage */ -+ ep0_queue(sti); -+ break; -+ -+ case STI_STATE_DISCONNECT: -+ do_set_config(sti, 0); /* unconfigured state */ -+ break; -+ -+ case STI_STATE_EXIT: -+ case STI_STATE_TERMINATED: -+ do_set_config(sti, 0); /* free resources */ -+ spin_lock_irq(&sti->lock); -+ sti->state = STI_STATE_TERMINATED; /* stop the thread */ -+ spin_unlock_irq(&sti->lock); -+ break; -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int sti_main_thread(void *sti_) -+{ -+ struct sti_dev *sti = sti_; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* -+ * allow the thread to be killed by a signal, but set the signal mask -+ * to block everything but INT, TERM, KILL, and USR1 -+ */ -+ allow_signal(SIGINT); -+ allow_signal(SIGTERM); -+ allow_signal(SIGKILL); -+ allow_signal(SIGUSR1); -+ -+ /* allow the thread to be frozen */ -+ set_freezable(); -+ -+ /* -+ * arrange for userspace references to be interpreted as kernel -+ * pointers. That way we can pass a kernel pointer to a routine -+ * that expects a __user pointer and it will work okay. -+ */ -+ set_fs(get_ds()); -+ -+ /* the main loop */ -+ while (sti->state != STI_STATE_TERMINATED) { -+ if (exception_in_progress(sti) || signal_pending(current)) { -+ handle_exception(sti); -+ continue; -+ } -+ -+ if (!sti->running) { -+ sleep_thread(sti); -+ continue; -+ } -+ -+ if (get_next_command(sti)) -+ continue; -+ -+ spin_lock_irq(&sti->lock); -+ if (!exception_in_progress(sti)) -+ sti->state = STI_STATE_DATA_PHASE; -+ spin_unlock_irq(&sti->lock); -+ -+ if (do_still_image_command(sti)) -+ continue; -+ -+ spin_lock_irq(&sti->lock); -+ if (!exception_in_progress(sti)) -+ sti->state = STI_STATE_STATUS_PHASE; -+ spin_unlock_irq(&sti->lock); -+ -+ if (send_status(sti)) -+ continue; -+ -+ spin_lock_irq(&sti->lock); -+ if (!exception_in_progress(sti)) -+ sti->state = STI_STATE_IDLE; -+ spin_unlock_irq(&sti->lock); -+ } -+ -+ spin_lock_irq(&sti->lock); -+ sti->thread_task = NULL; -+ spin_unlock_irq(&sti->lock); -+ -+ /* in case we are exiting because of a signal, unregister the -+ * gadget driver */ -+ if (test_and_clear_bit(REGISTERED, &sti->atomic_bitflags)) -+ usb_gadget_unregister_driver(&sti_driver); -+ -+ /* let the unbind and cleanup routines know the thread has exited */ -+ complete_and_exit(&sti->thread_notifier, 0); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int open_backing_folder(struct sti_dev *sti, const char *folder_name) -+{ -+ struct file *filp = NULL; -+ int rc = -EINVAL; -+ struct inode *inode = NULL; -+ size_t len; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* remove the trailing path sign */ -+ len = strlen(folder_name); -+ if (len > 1 && folder_name[len-1] == '/') -+ ((char *) folder_name)[len-1] = 0; -+ -+ memset(sti->root_path, 0, sizeof(sti->root_path)); -+ strncpy(sti->root_path, folder_name, sizeof(sti->root_path)); -+ -+ filp = filp_open(sti->root_path, O_RDONLY | O_DIRECTORY, 0); -+ if (IS_ERR(filp)) { -+ ERROR(sti, "unable to open backing folder: %s\n", -+ sti->root_path); -+ return PTR_ERR(filp); -+ } -+ -+ if (filp->f_path.dentry) -+ inode = filp->f_dentry->d_inode; -+ -+ if (!inode || !S_ISDIR(inode->i_mode)) { -+ ERROR(sti, "%s is not a directory\n", sti->root_path); -+ goto out; -+ } -+ -+ get_file(filp); -+ -+ sti->root_filp = filp; -+ -+ INFO(sti, "open backing folder: %s\n", folder_name); -+ rc = 0; -+out: -+ filp_close(filp, current->files); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return rc; -+} -+ -+static void close_backing_folder(struct sti_dev *sti) -+{ -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ if (sti->root_filp) { -+ INFO(sti, "close backing folder\n"); -+ fput(sti->root_filp); -+ sti->root_filp = NULL; -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* sysfs attribute files */ -+static ssize_t show_folder(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct sti_dev *sti = dev_get_drvdata(dev); -+ char *p; -+ ssize_t rc; -+ -+ down_read(&sti->filesem); -+ if (backing_folder_is_open(sti)) { -+ /* get the complete pathname */ -+ p = d_path(&sti->root_filp->f_path, buf, PAGE_SIZE - 1); -+ if (IS_ERR(p)) -+ rc = PTR_ERR(p); -+ else { -+ rc = strlen(p); -+ memmove(buf, p, rc); -+ -+ /* add a newline */ -+ buf[rc] = '\n'; -+ buf[++rc] = 0; -+ } -+ } else { /* no file */ -+ *buf = 0; -+ rc = 0; -+ } -+ up_read(&sti->filesem); -+ -+ return rc; -+} -+ -+ -+static ssize_t store_folder(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct sti_dev *sti = dev_get_drvdata(dev); -+ int rc = 0; -+ -+ /* remove a trailing newline */ -+ if (count > 0 && buf[count-1] == '\n') -+ ((char *) buf)[count-1] = 0; -+ -+ /* eject current medium */ -+ down_write(&sti->filesem); -+ if (backing_folder_is_open(sti)) -+ close_backing_folder(sti); -+ -+ /* load new medium */ -+ if (count > 0 && buf[0]) -+ rc = open_backing_folder(sti, buf); -+ -+ up_write(&sti->filesem); -+ -+ return (rc < 0 ? rc : count); -+} -+ -+/* the write permissions and store_xxx pointers are set in sti_bind() */ -+static DEVICE_ATTR(folder, 0444, show_folder, NULL); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void sti_release(struct kref *ref) -+{ -+ struct sti_dev *sti = container_of(ref, struct sti_dev, ref); -+ -+ while (!list_empty(&sti->obj_list)) { -+ struct sti_object *obj = NULL; -+ obj = list_entry(sti->obj_list.next, struct sti_object, list); -+ list_del_init(&obj->list); -+ kfree(obj); -+ } -+ -+ while (!list_empty(&sti->tmp_obj_list)) { -+ struct sti_object *obj = NULL; -+ obj = list_entry(sti->tmp_obj_list.next, struct sti_object, -+ list); -+ list_del_init(&obj->list); -+ kfree(obj); -+ } -+ -+ kfree(sti); -+} -+ -+static void gadget_release(struct device *dev) -+{ -+ struct sti_dev *sti = dev_get_drvdata(dev); -+ VDBG(sti, "---> %s()\n", __func__); -+ VDBG(sti, "<--- %s()\n", __func__); -+ -+ kref_put(&sti->ref, sti_release); -+} -+ -+ -+static void /* __init_or_exit */ sti_unbind(struct usb_gadget *gadget) -+{ -+ struct sti_dev *sti = get_gadget_data(gadget); -+ int i; -+ struct usb_request *req = sti->ep0req; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ DBG(sti, "unbind\n"); -+ clear_bit(REGISTERED, &sti->atomic_bitflags); -+ -+ /* unregister the sysfs attribute files */ -+ if (sti->registered) { -+ device_remove_file(&sti->dev, &dev_attr_folder); -+ close_backing_folder(sti); -+ device_unregister(&sti->dev); -+ sti->registered = 0; -+ } -+ -+ /* if the thread isn't already dead, tell it to exit now */ -+ if (sti->state != STI_STATE_TERMINATED) { -+ raise_exception(sti, STI_STATE_EXIT); -+ wait_for_completion(&sti->thread_notifier); -+ -+ /* the cleanup routine waits for this completion also */ -+ complete(&sti->thread_notifier); -+ } -+ -+ /* free the data buffers */ -+ for (i = 0; i < NUM_BUFFERS; ++i) -+ kfree(sti->buffhds[i].buf); -+ -+ /* free the request and buffer for endpoint 0 */ -+ if (req) { -+ kfree(req->buf); -+ usb_ep_free_request(sti->ep0, req); -+ } -+ -+ set_gadget_data(gadget, NULL); -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+} -+ -+ -+static int __init check_parameters(struct sti_dev *sti) -+{ -+ int gcnum; -+ VDBG(sti, "---> %s()\n", __func__); -+ -+ /* parameter wasn't set */ -+ if (mod_data.release == 0xffff) { -+ gcnum = usb_gadget_controller_number(sti->gadget); -+ if (gcnum >= 0) -+ mod_data.release = 0x0300 + gcnum; -+ else { -+ WARNING(sti, "controller '%s' not recognized\n", -+ sti->gadget->name); -+ mod_data.release = 0x0399; -+ } -+ } -+ -+ mod_data.buflen &= PAGE_CACHE_MASK; -+ if (mod_data.buflen <= 0) { -+ ERROR(sti, "invalid buflen\n"); -+ return -ETOOSMALL; -+ } -+ -+ VDBG(sti, "<--- %s()\n", __func__); -+ return 0; -+} -+ -+ -+static int __init sti_bind(struct usb_gadget *gadget) -+{ -+ struct sti_dev *sti = the_sti; -+ int rc; -+ int i; -+ struct usb_ep *ep; -+ struct usb_request *req; -+ -+ sti->gadget = gadget; -+ set_gadget_data(gadget, sti); -+ sti->ep0 = gadget->ep0; -+ sti->ep0->driver_data = sti; -+ -+ rc = check_parameters(sti); -+ if (rc) -+ goto out; -+ -+ /* enable store_xxx attributes */ -+ dev_attr_folder.attr.mode = 0644; -+ dev_attr_folder.store = store_folder; -+ -+ sti->dev.release = gadget_release; -+ sti->dev.parent = &gadget->dev; -+ sti->dev.driver = &sti_driver.driver; -+ dev_set_drvdata(&sti->dev, sti); -+ dev_set_name(&sti->dev, "%s", sti_driver.driver.name); -+ -+ rc = device_register(&sti->dev); -+ if (rc) { -+ INFO(sti, "failed to register sti: %d\n", rc); -+ goto out; -+ } -+ -+ rc = device_create_file(&sti->dev, &dev_attr_folder); -+ if (rc) { -+ device_unregister(&sti->dev); -+ goto out; -+ } -+ -+ sti->registered = 1; -+ kref_get(&sti->ref); -+ -+ /* initialize object list */ -+ INIT_LIST_HEAD(&sti->obj_list); -+ INIT_LIST_HEAD(&sti->tmp_obj_list); -+ -+ if (mod_data.folder && *mod_data.folder) -+ rc = open_backing_folder(sti, mod_data.folder); -+ if (rc) -+ goto out; -+ -+ /* find all the endpoints we will use */ -+ usb_ep_autoconfig_reset(gadget); -+ ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc); -+ if (!ep) -+ goto autoconf_fail; -+ -+ /* claim bulk-in endpoint */ -+ ep->driver_data = sti; -+ sti->bulk_in = ep; -+ -+ ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc); -+ if (!ep) -+ goto autoconf_fail; -+ -+ /* claim bulk-out endpoint */ -+ ep->driver_data = sti; -+ sti->bulk_out = ep; -+ -+ ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc); -+ if (!ep) -+ goto autoconf_fail; -+ -+ /* claim intr-in endpoint */ -+ ep->driver_data = sti; -+ sti->intr_in = ep; -+ -+ /* fix up the descriptors */ -+ device_desc.bMaxPacketSize0 = sti->ep0->maxpacket; -+ device_desc.idVendor = cpu_to_le16(mod_data.vendor); -+ device_desc.idProduct = cpu_to_le16(mod_data.product); -+ device_desc.bcdDevice = cpu_to_le16(mod_data.release); -+ -+ fs_function[3 + FS_FUNCTION_PRE_EP_ENTRIES] = NULL; -+ -+ if (gadget_is_dualspeed(gadget)) { -+ hs_function[3 + HS_FUNCTION_PRE_EP_ENTRIES] = NULL; -+ -+ /* assume ep0 uses the same maxpacket value for both speeds */ -+ dev_qualifier.bMaxPacketSize0 = sti->ep0->maxpacket; -+ -+ /* assume endpoint addresses are the same for both speeds */ -+ hs_bulk_in_desc.bEndpointAddress = -+ fs_bulk_in_desc.bEndpointAddress; -+ hs_bulk_out_desc.bEndpointAddress = -+ fs_bulk_out_desc.bEndpointAddress; -+ hs_intr_in_desc.bEndpointAddress = -+ fs_intr_in_desc.bEndpointAddress; -+ } -+ -+ if (gadget_is_otg(gadget)) -+ otg_desc.bmAttributes |= USB_OTG_HNP; -+ -+ rc = -ENOMEM; -+ -+ /* allocate the request and buffer for endpoint 0 */ -+ sti->ep0req = req = usb_ep_alloc_request(sti->ep0, GFP_KERNEL); -+ if (!req) -+ goto autoconf_fail; -+ -+ req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); -+ if (!req->buf) -+ goto autoconf_fail; -+ -+ req->complete = ep0_complete; -+ -+ /* allocate the data buffers */ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ struct sti_buffhd *bh = &sti->buffhds[i]; -+ -+ /* -+ * Allocate for the bulk-in endpoint. We assume that -+ * the buffer will also work with the bulk-out (and -+ * interrupt-in) endpoint. -+ */ -+ bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL); -+ if (!bh->buf) -+ goto autoconf_fail; -+ -+ bh->next = bh + 1; -+ } -+ sti->buffhds[NUM_BUFFERS - 1].next = &sti->buffhds[0]; -+ -+ /* this should reflect the actual gadget power source */ -+ usb_gadget_set_selfpowered(gadget); -+ -+ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", -+ init_utsname()->sysname, init_utsname()->release, -+ gadget->name); -+ -+ DBG(sti, "manufacturer: %s\n", manufacturer); -+ -+ /* -+ * on a real device, serial[] would be loaded from permanent -+ * storage. We just encode it from the driver version string. -+ */ -+ for (i = 0; i < sizeof(serial) - 2; i += 2) { -+ unsigned char c = DRIVER_VERSION[i / 2]; -+ -+ if (!c) -+ break; -+ -+ snprintf(&serial[i], sizeof(&serial[i]), "%02X", c); -+ } -+ -+ /* fill remained device info */ -+ sti_device_info.manufacturer_len = sizeof(manufacturer); -+ str_to_uni16(manufacturer, sti_device_info.manufacturer); -+ -+ sti_device_info.model_len = sizeof(longname); -+ str_to_uni16(longname, sti_device_info.model); -+ -+ sti_device_info.device_version_len = sizeof(device_version); -+ str_to_uni16(device_version, sti_device_info.device_version); -+ -+ sti_device_info.serial_number_len = sizeof(serial); -+ str_to_uni16(serial, sti_device_info.serial_number); -+ -+ /* create main kernel thread */ -+ sti->thread_task = kthread_create(sti_main_thread, sti, -+ "still-image-gadget"); -+ -+ if (IS_ERR(sti->thread_task)) { -+ rc = PTR_ERR(sti->thread_task); -+ goto autoconf_fail; -+ } -+ -+ INFO(sti, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); -+ INFO(sti, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", -+ mod_data.vendor, mod_data.product, mod_data.release); -+ INFO(sti, "I/O thread pid: %d, buflen=%u\n", -+ task_pid_nr(sti->thread_task), mod_data.buflen); -+ -+ set_bit(REGISTERED, &sti->atomic_bitflags); -+ -+ /* tell the thread to start working */ -+ wake_up_process(sti->thread_task); -+ -+ DBG(sti, "bind\n"); -+ return 0; -+ -+autoconf_fail: -+ ERROR(sti, "unable to autoconfigure all endpoints\n"); -+ rc = -ENOTSUPP; -+out: -+ /* the thread is dead */ -+ sti->state = STI_STATE_TERMINATED; -+ -+ sti_unbind(gadget); -+ complete(&sti->thread_notifier); -+ -+ VDBG(sti, "<---> %s()\n", __func__); -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void sti_suspend(struct usb_gadget *gadget) -+{ -+ struct sti_dev *sti = get_gadget_data(gadget); -+ -+ DBG(sti, "suspend\n"); -+ set_bit(SUSPENDED, &sti->atomic_bitflags); -+} -+ -+ -+static void sti_resume(struct usb_gadget *gadget) -+{ -+ struct sti_dev *sti = get_gadget_data(gadget); -+ -+ DBG(sti, "resume\n"); -+ clear_bit(SUSPENDED, &sti->atomic_bitflags); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_gadget_driver sti_driver = { -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ .speed = USB_SPEED_HIGH, -+#else -+ .speed = USB_SPEED_FULL, -+#endif -+ .function = (char *) longname, -+ .bind = sti_bind, -+ .unbind = sti_unbind, -+ .disconnect = sti_disconnect, -+ .setup = sti_setup, -+ .suspend = sti_suspend, -+ .resume = sti_resume, -+ -+ .driver = { -+ .name = (char *) shortname, -+ .owner = THIS_MODULE, -+ /* .release = ... */ -+ /* .suspend = ... */ -+ /* .resume = ... */ -+ }, -+}; -+ -+ -+static int __init sti_alloc(void) -+{ -+ struct sti_dev *sti; -+ -+ sti = kzalloc(sizeof *sti, GFP_KERNEL); -+ if (!sti) -+ return -ENOMEM; -+ -+ spin_lock_init(&sti->lock); -+ init_rwsem(&sti->filesem); -+ kref_init(&sti->ref); -+ init_completion(&sti->thread_notifier); -+ -+ the_sti = sti; -+ -+ return 0; -+} -+ -+ -+static int __init sti_init(void) -+{ -+ int rc; -+ struct sti_dev *sti; -+ -+ rc = sti_alloc(); -+ if (rc) -+ return rc; -+ -+ sti = the_sti; -+ -+ rc = usb_gadget_register_driver(&sti_driver); -+ if (rc) -+ kref_put(&sti->ref, sti_release); -+ -+ return rc; -+} -+module_init(sti_init); -+ -+ -+static void __exit sti_cleanup(void) -+{ -+ struct sti_dev *sti = the_sti; -+ -+ /* unregister the driver if the thread hasn't already done */ -+ if (test_and_clear_bit(REGISTERED, &sti->atomic_bitflags)) -+ usb_gadget_unregister_driver(&sti_driver); -+ -+ /* wait for the thread to finish up */ -+ wait_for_completion(&sti->thread_notifier); -+ -+ kref_put(&sti->ref, sti_release); -+} -+module_exit(sti_cleanup); -diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig -index 3d2d3e5..69ff37b 100644 ---- a/drivers/usb/otg/Kconfig -+++ b/drivers/usb/otg/Kconfig -@@ -69,4 +69,18 @@ config NOP_USB_XCEIV - built-in with usb ip or which are autonomous and doesn't require any - phy programming such as ISP1x04 etc. - -+config USB_LANGWELL_OTG -+ tristate "Intel Langwell USB OTG dual-role support" -+ depends on USB && X86_MRST -+ select USB_OTG -+ select USB_OTG_UTILS -+ help -+ Say Y here if you want to build Intel Langwell USB OTG -+ transciever driver in kernel. This driver implements role -+ switch between EHCI host driver and Langwell USB OTG -+ client driver. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called langwell_otg. -+ - endif # USB || OTG -diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile -index aeb49a8..b6609db 100644 ---- a/drivers/usb/otg/Makefile -+++ b/drivers/usb/otg/Makefile -@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o - obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o - obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o - obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o -+obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o - obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o - obj-$(CONFIG_USB_ULPI) += ulpi.o - -diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c -new file mode 100644 -index 0000000..46ae881 ---- /dev/null -+++ b/drivers/usb/otg/langwell_otg.c -@@ -0,0 +1,2260 @@ -+/* -+ * Intel Langwell USB OTG transceiver driver -+ * Copyright (C) 2008 - 2009, Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License along with -+ * this program; if not, write to the Free Software Foundation, Inc., -+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. -+ * -+ */ -+/* This driver helps to switch Langwell OTG controller function between host -+ * and peripheral. It works with EHCI driver and Langwell client controller -+ * driver together. -+ */ -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/pci.h> -+#include <linux/errno.h> -+#include <linux/interrupt.h> -+#include <linux/kernel.h> -+#include <linux/device.h> -+#include <linux/moduleparam.h> -+#include <linux/usb/ch9.h> -+#include <linux/usb/gadget.h> -+#include <linux/usb.h> -+#include <linux/usb/otg.h> -+#include <linux/notifier.h> -+#include <asm/ipc_defs.h> -+#include <linux/delay.h> -+#include "../core/hcd.h" -+ -+#include <linux/usb/langwell_otg.h> -+ -+#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" -+#define DRIVER_VERSION "March 19, 2010" -+ -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_AUTHOR("Henry Yuan <hang.yuan@intel.com>, Hao Wu <hao.wu@intel.com>"); -+MODULE_VERSION(DRIVER_VERSION); -+MODULE_LICENSE("GPL"); -+ -+static const char driver_name[] = "langwell_otg"; -+ -+static int langwell_otg_probe(struct pci_dev *pdev, -+ const struct pci_device_id *id); -+static void langwell_otg_remove(struct pci_dev *pdev); -+static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message); -+static int langwell_otg_resume(struct pci_dev *pdev); -+ -+static int langwell_otg_set_host(struct otg_transceiver *otg, -+ struct usb_bus *host); -+static int langwell_otg_set_peripheral(struct otg_transceiver *otg, -+ struct usb_gadget *gadget); -+static int langwell_otg_start_srp(struct otg_transceiver *otg); -+ -+static const struct pci_device_id pci_ids[] = {{ -+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), -+ .class_mask = ~0, -+ .vendor = 0x8086, -+ .device = 0x0811, -+ .subvendor = PCI_ANY_ID, -+ .subdevice = PCI_ANY_ID, -+}, { /* end: all zeroes */ } -+}; -+ -+static struct pci_driver otg_pci_driver = { -+ .name = (char *) driver_name, -+ .id_table = pci_ids, -+ -+ .probe = langwell_otg_probe, -+ .remove = langwell_otg_remove, -+ -+ .suspend = langwell_otg_suspend, -+ .resume = langwell_otg_resume, -+}; -+ -+static const char *state_string(enum usb_otg_state state) -+{ -+ switch (state) { -+ case OTG_STATE_A_IDLE: -+ return "a_idle"; -+ case OTG_STATE_A_WAIT_VRISE: -+ return "a_wait_vrise"; -+ case OTG_STATE_A_WAIT_BCON: -+ return "a_wait_bcon"; -+ case OTG_STATE_A_HOST: -+ return "a_host"; -+ case OTG_STATE_A_SUSPEND: -+ return "a_suspend"; -+ case OTG_STATE_A_PERIPHERAL: -+ return "a_peripheral"; -+ case OTG_STATE_A_WAIT_VFALL: -+ return "a_wait_vfall"; -+ case OTG_STATE_A_VBUS_ERR: -+ return "a_vbus_err"; -+ case OTG_STATE_B_IDLE: -+ return "b_idle"; -+ case OTG_STATE_B_SRP_INIT: -+ return "b_srp_init"; -+ case OTG_STATE_B_PERIPHERAL: -+ return "b_peripheral"; -+ case OTG_STATE_B_WAIT_ACON: -+ return "b_wait_acon"; -+ case OTG_STATE_B_HOST: -+ return "b_host"; -+ default: -+ return "UNDEFINED"; -+ } -+} -+ -+/* HSM timers */ -+static inline struct langwell_otg_timer *otg_timer_initializer -+(void (*function)(unsigned long), unsigned long expires, unsigned long data) -+{ -+ struct langwell_otg_timer *timer; -+ timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL); -+ timer->function = function; -+ timer->expires = expires; -+ timer->data = data; -+ return timer; -+} -+ -+static struct langwell_otg_timer *a_wait_vrise_tmr, *a_aidl_bdis_tmr, -+ *b_se0_srp_tmr, *b_srp_init_tmr; -+ -+static struct list_head active_timers; -+ -+static struct langwell_otg *the_transceiver; -+ -+/* host/client notify transceiver when event affects HNP state */ -+void langwell_update_transceiver() -+{ -+ struct langwell_otg *langwell = the_transceiver; -+ -+ otg_dbg("transceiver is updated\n"); -+ -+ if (!langwell->qwork) -+ return ; -+ -+ queue_work(langwell->qwork, &langwell->work); -+} -+EXPORT_SYMBOL(langwell_update_transceiver); -+ -+static int langwell_otg_set_host(struct otg_transceiver *otg, -+ struct usb_bus *host) -+{ -+ otg->host = host; -+ -+ return 0; -+} -+ -+static int langwell_otg_set_peripheral(struct otg_transceiver *otg, -+ struct usb_gadget *gadget) -+{ -+ otg->gadget = gadget; -+ -+ return 0; -+} -+ -+static int langwell_otg_set_power(struct otg_transceiver *otg, -+ unsigned mA) -+{ -+ return 0; -+} -+ -+/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/ -+static void langwell_otg_drv_vbus(int on) -+{ -+ struct ipc_pmic_reg_data pmic_data = {0}; -+ struct ipc_pmic_reg_data data = {0}; -+ -+ data.pmic_reg_data[0].register_address = 0xd2; -+ data.ioc = 0; -+ data.num_entries = 1; -+ -+ if (ipc_pmic_register_read(&data)) { -+ otg_dbg("Failed to read PMIC register 0x00.\n"); -+ return; -+ } -+ -+ if (data.pmic_reg_data[0].value & 0x20) -+ otg_dbg("battery attached(%x)\n", data.pmic_reg_data[0].value); -+ else { -+ otg_dbg("no battery detected\n"); -+ return; -+ } -+ -+ pmic_data.ioc = 0; -+ pmic_data.pmic_reg_data[0].register_address = 0xd4; -+ pmic_data.num_entries = 1; -+ if (on) -+ pmic_data.pmic_reg_data[0].value = 0x20; -+ else -+ pmic_data.pmic_reg_data[0].value = 0xc0; -+ -+ if (ipc_pmic_register_write(&pmic_data, TRUE)) -+ otg_dbg("Failed to write PMIC.\n"); -+} -+ -+/* charge vbus or discharge vbus through a resistor to ground */ -+static void langwell_otg_chrg_vbus(int on) -+{ -+ -+ u32 val; -+ -+ val = readl(the_transceiver->regs + CI_OTGSC); -+ -+ if (on) -+ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC, -+ the_transceiver->regs + CI_OTGSC); -+ else -+ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD, -+ the_transceiver->regs + CI_OTGSC); -+ -+} -+ -+/* Start SRP */ -+static int langwell_otg_start_srp(struct otg_transceiver *otg) -+{ -+ u32 val; -+ -+ otg_dbg("Start SRP ->\n"); -+ -+ val = readl(the_transceiver->regs + CI_OTGSC); -+ -+ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, -+ the_transceiver->regs + CI_OTGSC); -+ -+ /* Check if the data plus is finished or not */ -+ msleep(8); -+ val = readl(the_transceiver->regs + CI_OTGSC); -+ if (val & (OTGSC_HADP | OTGSC_DP)) -+ otg_dbg("DataLine SRP Error\n"); -+ -+ /* Disable interrupt - b_sess_vld */ -+ val = readl(the_transceiver->regs + CI_OTGSC); -+ val &= (~(OTGSC_BSVIE | OTGSC_BSEIE)); -+ writel(val, the_transceiver->regs + CI_OTGSC); -+ -+ /* Start VBus SRP */ -+ langwell_otg_drv_vbus(1); -+ msleep(15); -+ langwell_otg_drv_vbus(0); -+ -+ /* Enable interrupt - b_sess_vld*/ -+ val = readl(the_transceiver->regs + CI_OTGSC); -+ val |= (OTGSC_BSVIE | OTGSC_BSEIE); -+ writel(val, the_transceiver->regs + CI_OTGSC); -+ -+ otg_dbg("Start SRP <-\n"); -+ return 0; -+} -+ -+/* stop SOF via bus_suspend */ -+static void langwell_otg_loc_sof(int on) -+{ -+ struct usb_hcd *hcd; -+ int err; -+ -+ otg_dbg("loc_sof -> %d\n", on); -+ -+ hcd = bus_to_hcd(the_transceiver->otg.host); -+ if (on) -+ err = hcd->driver->bus_resume(hcd); -+ else -+ err = hcd->driver->bus_suspend(hcd); -+ -+ if (err) -+ otg_dbg("Failed to resume/suspend bus - %d\n", err); -+} -+ -+static int langwell_otg_check_otgsc(void) -+{ -+ struct langwell_otg *langwell; -+ u32 val_otgsc, val_usbcfg; -+ -+ langwell = the_transceiver; -+ -+ val_otgsc = readl(langwell->regs + CI_OTGSC); -+ val_usbcfg = readl(langwell->usbcfg); -+ -+ otg_dbg("check sync OTGSC and USBCFG\n"); -+ otg_dbg("OTGSC = %08x, USBCFG = %08x\n", val_otgsc, val_usbcfg); -+ otg_dbg("OTGSC_AVV = %d\n", !!(val_otgsc & OTGSC_AVV)); -+ otg_dbg("USBCFG.VBUSVAL = %d\n", !!(val_usbcfg & USBCFG_VBUSVAL)); -+ otg_dbg("OTGSC_ASV = %d\n", !!(val_otgsc & OTGSC_ASV)); -+ otg_dbg("USBCFG.AVALID = %d\n", !!(val_usbcfg & USBCFG_AVALID)); -+ otg_dbg("OTGSC_BSV = %d\n", !!(val_otgsc & OTGSC_BSV)); -+ otg_dbg("USBCFG.BVALID = %d\n", !!(val_usbcfg & USBCFG_BVALID)); -+ otg_dbg("OTGSC_BSE = %d\n", !!(val_otgsc & OTGSC_BSE)); -+ otg_dbg("USBCFG.SESEND = %d\n", !!(val_usbcfg & USBCFG_SESEND)); -+ -+ /* Check USBCFG VBusValid/AValid/BValid/SessEnd */ -+ if (!!(val_otgsc & OTGSC_AVV) ^ !!(val_usbcfg & USBCFG_VBUSVAL)) { -+ otg_dbg("OTGSC AVV and USBCFG VBUSVAL are not sync.\n"); -+ return -1; -+ } else if (!!(val_otgsc & OTGSC_ASV) ^ !!(val_usbcfg & USBCFG_AVALID)) { -+ otg_dbg("OTGSC ASV and USBCFG AVALID are not sync.\n"); -+ return -1; -+ } else if (!!(val_otgsc & OTGSC_BSV) ^ !!(val_usbcfg & USBCFG_BVALID)) { -+ otg_dbg("OTGSC BSV and USBCFG BVALID are not sync.\n"); -+ return -1; -+ } else if (!!(val_otgsc & OTGSC_BSE) ^ !!(val_usbcfg & USBCFG_SESEND)) { -+ otg_dbg("OTGSC BSE and USBCFG SESSEN are not sync.\n"); -+ return -1; -+ } -+ -+ otg_dbg("OTGSC and USBCFG are synced\n"); -+ -+ return 0; -+} -+ -+static void langwell_otg_phy_low_power(int on) -+{ -+ u8 val, phcd; -+ int retval; -+ -+ otg_dbg("phy low power mode-> %d start\n", on); -+ -+ phcd = 0x40; -+ -+ val = readb(the_transceiver->regs + CI_HOSTPC1 + 2); -+ -+ if (on) { -+ /* Due to hardware issue, after set PHCD, sync will failed -+ * between USBCFG and OTGSC, so before set PHCD, check if -+ * sync is in process now. If the answer is "yes", then do -+ * not touch PHCD bit */ -+ retval = langwell_otg_check_otgsc(); -+ if (retval) { -+ otg_dbg("Skip PHCD programming..\n"); -+ return ; -+ } -+ -+ writeb(val | phcd, the_transceiver->regs + CI_HOSTPC1 + 2); -+ } else -+ writeb(val & ~phcd, the_transceiver->regs + CI_HOSTPC1 + 2); -+ -+ otg_dbg("phy low power mode<- %d done\n", on); -+} -+ -+/* After drv vbus, add 2 ms delay to set PHCD */ -+static void langwell_otg_phy_low_power_wait(int on) -+{ -+ otg_dbg("2 ms delay before set PHY low power mode\n"); -+ -+ mdelay(2); -+ langwell_otg_phy_low_power(on); -+} -+ -+/* Enable/Disable OTG interrupts */ -+static void langwell_otg_intr(int on) -+{ -+ u32 val; -+ -+ otg_dbg("interrupt -> %d\n", on); -+ -+ val = readl(the_transceiver->regs + CI_OTGSC); -+ -+ /* OTGSC_INT_MASK doesn't contains 1msInt */ -+ if (on) { -+ val = val | (OTGSC_INT_MASK); -+ writel(val, the_transceiver->regs + CI_OTGSC); -+ } else { -+ val = val & ~(OTGSC_INT_MASK); -+ writel(val, the_transceiver->regs + CI_OTGSC); -+ } -+} -+ -+/* set HAAR: Hardware Assist Auto-Reset */ -+static void langwell_otg_HAAR(int on) -+{ -+ u32 val; -+ -+ otg_dbg("HAAR -> %d\n", on); -+ -+ val = readl(the_transceiver->regs + CI_OTGSC); -+ if (on) -+ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR, -+ the_transceiver->regs + CI_OTGSC); -+ else -+ writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR, -+ the_transceiver->regs + CI_OTGSC); -+} -+ -+/* set HABA: Hardware Assist B-Disconnect to A-Connect */ -+static void langwell_otg_HABA(int on) -+{ -+ u32 val; -+ -+ otg_dbg("HABA -> %d\n", on); -+ -+ val = readl(the_transceiver->regs + CI_OTGSC); -+ if (on) -+ writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, -+ the_transceiver->regs + CI_OTGSC); -+ else -+ writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, -+ the_transceiver->regs + CI_OTGSC); -+} -+ -+static int langwell_otg_check_se0_srp(int on) -+{ -+ u32 val; -+ -+ int delay_time = TB_SE0_SRP * 10; /* step is 100us */ -+ -+ otg_dbg("check_se0_srp -> \n"); -+ -+ do { -+ udelay(100); -+ if (!delay_time--) -+ break; -+ val = readl(the_transceiver->regs + CI_PORTSC1); -+ val &= PORTSC_LS; -+ } while (!val); -+ -+ otg_dbg("check_se0_srp <- \n"); -+ return val; -+} -+ -+/* The timeout callback function to set time out bit */ -+static void set_tmout(unsigned long indicator) -+{ -+ *(int *)indicator = 1; -+} -+ -+void langwell_otg_nsf_msg(unsigned long indicator) -+{ -+ switch (indicator) { -+ case 2: -+ case 4: -+ case 6: -+ case 7: -+ printk(KERN_ERR "OTG:NSF-%lu - deivce not responding\n", -+ indicator); -+ break; -+ case 3: -+ printk(KERN_ERR "OTG:NSF-%lu - deivce not supported\n", -+ indicator); -+ break; -+ default: -+ printk(KERN_ERR "Do not have this kind of NSF\n"); -+ break; -+ } -+} -+ -+/* Initialize timers */ -+static void langwell_otg_init_timers(struct otg_hsm *hsm) -+{ -+ /* HSM used timers */ -+ a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, -+ (unsigned long)&hsm->a_wait_vrise_tmout); -+ a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, -+ (unsigned long)&hsm->a_aidl_bdis_tmout); -+ b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, -+ (unsigned long)&hsm->b_se0_srp); -+ b_srp_init_tmr = otg_timer_initializer(&set_tmout, TB_SRP_INIT, -+ (unsigned long)&hsm->b_srp_init_tmout); -+} -+ -+/* Free timers */ -+static void langwell_otg_free_timers(void) -+{ -+ kfree(a_wait_vrise_tmr); -+ kfree(a_aidl_bdis_tmr); -+ kfree(b_se0_srp_tmr); -+ kfree(b_srp_init_tmr); -+} -+ -+/* The timeout callback function to set time out bit */ -+static void langwell_otg_timer_fn(unsigned long indicator) -+{ -+ struct langwell_otg *langwell; -+ -+ langwell = the_transceiver; -+ -+ *(int *)indicator = 1; -+ -+ otg_dbg("kernel timer - timeout\n"); -+ -+ queue_work(langwell->qwork, &langwell->work); -+} -+ -+/* kernel timer used instead of HW based interrupt */ -+static void langwell_otg_add_ktimer(enum langwell_otg_timer_type timers) -+{ -+ struct langwell_otg *langwell; -+ unsigned long j = jiffies; -+ unsigned long data, time; -+ -+ langwell = the_transceiver; -+ -+ switch (timers) { -+ case TA_WAIT_VRISE_TMR: -+ langwell->hsm.a_wait_vrise_tmout = 0; -+ data = (unsigned long)&langwell->hsm.a_wait_vrise_tmout; -+ time = TA_WAIT_VRISE; -+ break; -+ case TA_WAIT_BCON_TMR: -+ langwell->hsm.a_wait_bcon_tmout = 0; -+ data = (unsigned long)&langwell->hsm.a_wait_bcon_tmout; -+ time = TA_WAIT_BCON; -+ break; -+ case TA_AIDL_BDIS_TMR: -+ langwell->hsm.a_aidl_bdis_tmout = 0; -+ data = (unsigned long)&langwell->hsm.a_aidl_bdis_tmout; -+ time = TA_AIDL_BDIS; -+ break; -+ case TB_ASE0_BRST_TMR: -+ langwell->hsm.b_ase0_brst_tmout = 0; -+ data = (unsigned long)&langwell->hsm.b_ase0_brst_tmout; -+ time = TB_ASE0_BRST; -+ break; -+ case TB_SRP_INIT_TMR: -+ langwell->hsm.b_srp_init_tmout = 0; -+ data = (unsigned long)&langwell->hsm.b_srp_init_tmout; -+ time = TB_SRP_INIT; -+ break; -+ case TB_SRP_FAIL_TMR: -+ langwell->hsm.b_srp_fail_tmout = 0; -+ data = (unsigned long)&langwell->hsm.b_srp_fail_tmout; -+ time = TB_SRP_FAIL; -+ break; -+ case TB_BUS_SUSPEND_TMR: -+ langwell->hsm.b_bus_suspend_tmout = 0; -+ data = (unsigned long)&langwell->hsm.b_bus_suspend_tmout; -+ time = TB_BUS_SUSPEND; -+ break; -+ default: -+ otg_dbg("OTG: unkown timer, can not enable such timer\n"); -+ return; -+ } -+ -+ langwell->hsm_timer.data = data; -+ langwell->hsm_timer.function = langwell_otg_timer_fn; -+ langwell->hsm_timer.expires = j + time * HZ / 1000; /* milliseconds */ -+ -+ add_timer(&langwell->hsm_timer); -+ -+ otg_dbg("OTG: add timer successfully\n"); -+} -+ -+/* Add timer to timer list */ -+static void langwell_otg_add_timer(void *gtimer) -+{ -+ struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; -+ struct langwell_otg_timer *tmp_timer; -+ u32 val32; -+ -+ /* Check if the timer is already in the active list, -+ * if so update timer count -+ */ -+ list_for_each_entry(tmp_timer, &active_timers, list) -+ if (tmp_timer == timer) { -+ timer->count = timer->expires; -+ return; -+ } -+ timer->count = timer->expires; -+ -+ if (list_empty(&active_timers)) { -+ val32 = readl(the_transceiver->regs + CI_OTGSC); -+ writel(val32 | OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); -+ } -+ -+ list_add_tail(&timer->list, &active_timers); -+} -+ -+/* Remove timer from the timer list; clear timeout status */ -+static void langwell_otg_del_timer(void *gtimer) -+{ -+ struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; -+ struct langwell_otg_timer *tmp_timer, *del_tmp; -+ u32 val32; -+ -+ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) -+ if (tmp_timer == timer) -+ list_del(&timer->list); -+ -+ if (list_empty(&active_timers)) { -+ val32 = readl(the_transceiver->regs + CI_OTGSC); -+ writel(val32 & ~OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); -+ } -+} -+ -+/* Reduce timer count by 1, and find timeout conditions.*/ -+static int langwell_otg_tick_timer(u32 *int_sts) -+{ -+ struct langwell_otg_timer *tmp_timer, *del_tmp; -+ int expired = 0; -+ -+ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { -+ tmp_timer->count--; -+ /* check if timer expires */ -+ if (!tmp_timer->count) { -+ list_del(&tmp_timer->list); -+ tmp_timer->function(tmp_timer->data); -+ expired = 1; -+ } -+ } -+ -+ if (list_empty(&active_timers)) { -+ otg_dbg("tick timer: disable 1ms int\n"); -+ *int_sts = *int_sts & ~OTGSC_1MSE; -+ } -+ return expired; -+} -+ -+static void reset_otg(void) -+{ -+ u32 val; -+ int delay_time = 1000; -+ -+ otg_dbg("reseting OTG controller ...\n"); -+ val = readl(the_transceiver->regs + CI_USBCMD); -+ writel(val | USBCMD_RST, the_transceiver->regs + CI_USBCMD); -+ do { -+ udelay(100); -+ if (!delay_time--) -+ otg_dbg("reset timeout\n"); -+ val = readl(the_transceiver->regs + CI_USBCMD); -+ val &= USBCMD_RST; -+ } while (val != 0); -+ otg_dbg("reset done.\n"); -+} -+ -+static void set_host_mode(void) -+{ -+ u32 val; -+ -+ reset_otg(); -+ val = readl(the_transceiver->regs + CI_USBMODE); -+ val = (val & (~USBMODE_CM)) | USBMODE_HOST; -+ writel(val, the_transceiver->regs + CI_USBMODE); -+} -+ -+static void set_client_mode(void) -+{ -+ u32 val; -+ -+ reset_otg(); -+ val = readl(the_transceiver->regs + CI_USBMODE); -+ val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; -+ writel(val, the_transceiver->regs + CI_USBMODE); -+} -+ -+static void init_hsm(void) -+{ -+ struct langwell_otg *langwell = the_transceiver; -+ u32 val32; -+ -+ /* read OTGSC after reset */ -+ val32 = readl(langwell->regs + CI_OTGSC); -+ otg_dbg("%s: OTGSC init value = 0x%x\n", __func__, val32); -+ -+ /* set init state */ -+ if (val32 & OTGSC_ID) { -+ langwell->hsm.id = 1; -+ langwell->otg.default_a = 0; -+ set_client_mode(); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ langwell_otg_drv_vbus(0); -+ } else { -+ langwell->hsm.id = 0; -+ langwell->otg.default_a = 1; -+ set_host_mode(); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ } -+ -+ /* set session indicator */ -+ if (val32 & OTGSC_BSE) -+ langwell->hsm.b_sess_end = 1; -+ if (val32 & OTGSC_BSV) -+ langwell->hsm.b_sess_vld = 1; -+ if (val32 & OTGSC_ASV) -+ langwell->hsm.a_sess_vld = 1; -+ if (val32 & OTGSC_AVV) -+ langwell->hsm.a_vbus_vld = 1; -+ -+ /* defautly power the bus */ -+ langwell->hsm.a_bus_req = 1; -+ langwell->hsm.a_bus_drop = 0; -+ /* defautly don't request bus as B device */ -+ langwell->hsm.b_bus_req = 0; -+ /* no system error */ -+ langwell->hsm.a_clr_err = 0; -+ -+ langwell_otg_phy_low_power_wait(1); -+} -+ -+static void update_hsm(void) -+{ -+ struct langwell_otg *langwell = the_transceiver; -+ u32 val32; -+ -+ /* read OTGSC */ -+ val32 = readl(langwell->regs + CI_OTGSC); -+ otg_dbg("%s: OTGSC current value = 0x%x\n", __func__, val32); -+ -+ langwell->hsm.id = !!(val32 & OTGSC_ID); -+ langwell->hsm.b_sess_end = !!(val32 & OTGSC_BSE); -+ langwell->hsm.b_sess_vld = !!(val32 & OTGSC_BSV); -+ langwell->hsm.a_sess_vld = !!(val32 & OTGSC_ASV); -+ langwell->hsm.a_vbus_vld = !!(val32 & OTGSC_AVV); -+} -+ -+static irqreturn_t otg_dummy_irq(int irq, void *_dev) -+{ -+ void __iomem *reg_base = _dev; -+ u32 val; -+ u32 int_mask = 0; -+ -+ val = readl(reg_base + CI_USBMODE); -+ if ((val & USBMODE_CM) != USBMODE_DEVICE) -+ return IRQ_NONE; -+ -+ val = readl(reg_base + CI_USBSTS); -+ int_mask = val & INTR_DUMMY_MASK; -+ -+ if (int_mask == 0) -+ return IRQ_NONE; -+ -+ /* clear hsm.b_conn here since host driver can't detect it -+ * otg_dummy_irq called means B-disconnect happened. -+ */ -+ if (the_transceiver->hsm.b_conn) { -+ the_transceiver->hsm.b_conn = 0; -+ if (spin_trylock(&the_transceiver->wq_lock)) { -+ queue_work(the_transceiver->qwork, -+ &the_transceiver->work); -+ spin_unlock(&the_transceiver->wq_lock); -+ } -+ } -+ /* Clear interrupts */ -+ writel(int_mask, reg_base + CI_USBSTS); -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t otg_irq(int irq, void *_dev) -+{ -+ struct langwell_otg *langwell = _dev; -+ u32 int_sts, int_en; -+ u32 int_mask = 0; -+ int flag = 0; -+ -+ int_sts = readl(langwell->regs + CI_OTGSC); -+ int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; -+ int_mask = int_sts & int_en; -+ if (int_mask == 0) -+ return IRQ_NONE; -+ -+ if (int_mask & OTGSC_IDIS) { -+ otg_dbg("%s: id change int\n", __func__); -+ langwell->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; -+ flag = 1; -+ } -+ if (int_mask & OTGSC_DPIS) { -+ otg_dbg("%s: data pulse int\n", __func__); -+ langwell->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; -+ flag = 1; -+ } -+ if (int_mask & OTGSC_BSEIS) { -+ otg_dbg("%s: b session end int\n", __func__); -+ langwell->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; -+ flag = 1; -+ } -+ if (int_mask & OTGSC_BSVIS) { -+ otg_dbg("%s: b session valid int\n", __func__); -+ langwell->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; -+ flag = 1; -+ } -+ if (int_mask & OTGSC_ASVIS) { -+ otg_dbg("%s: a session valid int\n", __func__); -+ langwell->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; -+ flag = 1; -+ } -+ if (int_mask & OTGSC_AVVIS) { -+ otg_dbg("%s: a vbus valid int\n", __func__); -+ langwell->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; -+ flag = 1; -+ } -+ -+ if (int_mask & OTGSC_1MSS) { -+ /* need to schedule otg_work if any timer is expired */ -+ if (langwell_otg_tick_timer(&int_sts)) -+ flag = 1; -+ } -+ -+ writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, -+ langwell->regs + CI_OTGSC); -+ if (flag) -+ queue_work(langwell->qwork, &langwell->work); -+ -+ return IRQ_HANDLED; -+} -+ -+static void langwell_otg_work(struct work_struct *work) -+{ -+ struct langwell_otg *langwell = container_of(work, -+ struct langwell_otg, work); -+ int retval; -+ -+ otg_dbg("%s: old state = %s\n", __func__, -+ state_string(langwell->otg.state)); -+ -+ switch (langwell->otg.state) { -+ case OTG_STATE_UNDEFINED: -+ case OTG_STATE_B_IDLE: -+ if (!langwell->hsm.id) { -+ langwell_otg_del_timer(b_srp_init_tmr); -+ del_timer_sync(&langwell->hsm_timer); -+ langwell->otg.default_a = 1; -+ langwell->hsm.a_srp_det = 0; -+ langwell_otg_chrg_vbus(0); -+ set_host_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (langwell->hsm.b_srp_init_tmout) { -+ langwell->hsm.b_srp_init_tmout = 0; -+ printk(KERN_WARNING "USB OTG: SRP init timeout\n"); -+ } else if (langwell->hsm.b_srp_fail_tmout) { -+ langwell->hsm.b_srp_fail_tmout = 0; -+ langwell->hsm.b_bus_req = 0; -+ langwell_otg_nsf_msg(6); -+ } else if (langwell->hsm.b_sess_vld) { -+ langwell_otg_del_timer(b_srp_init_tmr); -+ del_timer_sync(&langwell->hsm_timer); -+ langwell->hsm.b_sess_end = 0; -+ langwell->hsm.a_bus_suspend = 0; -+ langwell_otg_chrg_vbus(0); -+ if (langwell->client_ops) { -+ langwell->client_ops->resume(langwell->pdev); -+ langwell->otg.state = OTG_STATE_B_PERIPHERAL; -+ } else -+ otg_dbg("client driver not loaded.\n"); -+ -+ } else if (langwell->hsm.b_bus_req && -+ (langwell->hsm.b_sess_end)) { -+ del_timer_sync(&langwell->hsm_timer); -+ /* workaround for b_se0_srp detection */ -+ retval = langwell_otg_check_se0_srp(0); -+ if (retval) { -+ langwell->hsm.b_bus_req = 0; -+ otg_dbg("LS is not SE0, try again later\n"); -+ } else { -+ /* clear the PHCD before start srp */ -+ langwell_otg_phy_low_power(0); -+ -+ /* Start SRP */ -+ langwell_otg_add_timer(b_srp_init_tmr); -+ langwell_otg_start_srp(&langwell->otg); -+ langwell_otg_del_timer(b_srp_init_tmr); -+ langwell_otg_add_ktimer(TB_SRP_FAIL_TMR); -+ -+ /* reset PHY low power mode here */ -+ langwell_otg_phy_low_power_wait(1); -+ } -+ } -+ break; -+ case OTG_STATE_B_SRP_INIT: -+ if (!langwell->hsm.id) { -+ langwell->otg.default_a = 1; -+ langwell->hsm.a_srp_det = 0; -+ langwell_otg_drv_vbus(0); -+ langwell_otg_chrg_vbus(0); -+ set_host_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (langwell->hsm.b_sess_vld) { -+ langwell_otg_chrg_vbus(0); -+ if (langwell->client_ops) { -+ langwell->client_ops->resume(langwell->pdev); -+ langwell->otg.state = OTG_STATE_B_PERIPHERAL; -+ } else -+ otg_dbg("client driver not loaded.\n"); -+ } -+ break; -+ case OTG_STATE_B_PERIPHERAL: -+ if (!langwell->hsm.id) { -+ langwell->otg.default_a = 1; -+ langwell->hsm.a_srp_det = 0; -+ -+ langwell_otg_chrg_vbus(0); -+ -+ if (langwell->client_ops) { -+ langwell->client_ops->suspend(langwell->pdev, -+ PMSG_FREEZE); -+ } else -+ otg_dbg("client driver has been removed.\n"); -+ -+ set_host_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (!langwell->hsm.b_sess_vld) { -+ langwell->hsm.b_hnp_enable = 0; -+ -+ if (langwell->client_ops) { -+ langwell->client_ops->suspend(langwell->pdev, -+ PMSG_FREEZE); -+ } else -+ otg_dbg("client driver has been removed.\n"); -+ -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ } else if (langwell->hsm.b_bus_req && langwell->hsm.b_hnp_enable -+ && langwell->hsm.a_bus_suspend) { -+ -+ if (langwell->client_ops) { -+ langwell->client_ops->suspend(langwell->pdev, -+ PMSG_FREEZE); -+ } else -+ otg_dbg("client driver has been removed.\n"); -+ -+ langwell_otg_HAAR(1); -+ langwell->hsm.a_conn = 0; -+ -+ if (langwell->host_ops) { -+ langwell->host_ops->probe(langwell->pdev, -+ langwell->host_ops->id_table); -+ langwell->otg.state = OTG_STATE_B_WAIT_ACON; -+ } else -+ otg_dbg("host driver not loaded.\n"); -+ -+ langwell->hsm.a_bus_resume = 0; -+ langwell_otg_add_ktimer(TB_ASE0_BRST_TMR); -+ } -+ break; -+ -+ case OTG_STATE_B_WAIT_ACON: -+ if (!langwell->hsm.id) { -+ /* delete hsm timer for a_wait_bcon_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ langwell->otg.default_a = 1; -+ langwell->hsm.a_srp_det = 0; -+ -+ langwell_otg_chrg_vbus(0); -+ -+ langwell_otg_HAAR(0); -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ set_host_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (!langwell->hsm.b_sess_vld) { -+ /* delete hsm timer for a_wait_bcon_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ langwell->hsm.b_hnp_enable = 0; -+ langwell->hsm.b_bus_req = 0; -+ langwell_otg_chrg_vbus(0); -+ langwell_otg_HAAR(0); -+ -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ set_client_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ } else if (langwell->hsm.a_conn) { -+ /* delete hsm timer for a_wait_bcon_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ langwell_otg_HAAR(0); -+ langwell->otg.state = OTG_STATE_B_HOST; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (langwell->hsm.a_bus_resume || -+ langwell->hsm.b_ase0_brst_tmout) { -+ /* delete hsm timer for a_wait_bcon_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ langwell_otg_HAAR(0); -+ langwell_otg_nsf_msg(7); -+ -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ langwell->hsm.a_bus_suspend = 0; -+ langwell->hsm.b_bus_req = 0; -+ -+ if (langwell->client_ops) -+ langwell->client_ops->resume(langwell->pdev); -+ else -+ otg_dbg("client driver not loaded.\n"); -+ -+ langwell->otg.state = OTG_STATE_B_PERIPHERAL; -+ } -+ break; -+ -+ case OTG_STATE_B_HOST: -+ if (!langwell->hsm.id) { -+ langwell->otg.default_a = 1; -+ langwell->hsm.a_srp_det = 0; -+ -+ langwell_otg_chrg_vbus(0); -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ set_host_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (!langwell->hsm.b_sess_vld) { -+ langwell->hsm.b_hnp_enable = 0; -+ langwell->hsm.b_bus_req = 0; -+ langwell_otg_chrg_vbus(0); -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ set_client_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ } else if ((!langwell->hsm.b_bus_req) || -+ (!langwell->hsm.a_conn)) { -+ langwell->hsm.b_bus_req = 0; -+ langwell_otg_loc_sof(0); -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ langwell->hsm.a_bus_suspend = 0; -+ -+ if (langwell->client_ops) -+ langwell->client_ops->resume(langwell->pdev); -+ else -+ otg_dbg("client driver not loaded.\n"); -+ -+ langwell->otg.state = OTG_STATE_B_PERIPHERAL; -+ } -+ break; -+ -+ case OTG_STATE_A_IDLE: -+ langwell->otg.default_a = 1; -+ if (langwell->hsm.id) { -+ langwell->otg.default_a = 0; -+ langwell->hsm.b_bus_req = 0; -+ langwell->hsm.vbus_srp_up = 0; -+ langwell_otg_chrg_vbus(0); -+ set_client_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (!langwell->hsm.a_bus_drop && -+ (langwell->hsm.a_srp_det || langwell->hsm.a_bus_req)) { -+ langwell_otg_phy_low_power(0); -+ langwell_otg_drv_vbus(1); -+ langwell->hsm.a_srp_det = 1; -+ langwell->hsm.vbus_srp_up = 0; -+ langwell->hsm.a_wait_vrise_tmout = 0; -+ langwell_otg_add_timer(a_wait_vrise_tmr); -+ langwell->otg.state = OTG_STATE_A_WAIT_VRISE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (!langwell->hsm.a_bus_drop && -+ langwell->hsm.a_sess_vld) { -+ langwell->hsm.vbus_srp_up = 1; -+ } else if (!langwell->hsm.a_sess_vld && -+ langwell->hsm.vbus_srp_up) { -+ msleep(10); -+ langwell_otg_phy_low_power(0); -+ langwell_otg_drv_vbus(1); -+ langwell->hsm.a_srp_det = 1; -+ langwell->hsm.vbus_srp_up = 0; -+ langwell->hsm.a_wait_vrise_tmout = 0; -+ langwell_otg_add_timer(a_wait_vrise_tmr); -+ langwell->otg.state = OTG_STATE_A_WAIT_VRISE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (!langwell->hsm.a_sess_vld && -+ !langwell->hsm.vbus_srp_up) { -+ langwell_otg_phy_low_power(1); -+ } -+ break; -+ case OTG_STATE_A_WAIT_VRISE: -+ if (langwell->hsm.id) { -+ langwell_otg_del_timer(a_wait_vrise_tmr); -+ langwell->hsm.b_bus_req = 0; -+ langwell->otg.default_a = 0; -+ langwell_otg_drv_vbus(0); -+ set_client_mode(); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ } else if (langwell->hsm.a_vbus_vld) { -+ langwell_otg_del_timer(a_wait_vrise_tmr); -+ if (langwell->host_ops) -+ langwell->host_ops->probe(langwell->pdev, -+ langwell->host_ops->id_table); -+ else { -+ otg_dbg("host driver not loaded.\n"); -+ break; -+ } -+ langwell->hsm.b_conn = 0; -+ /* Replace HW timer with kernel timer */ -+ langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); -+ langwell->otg.state = OTG_STATE_A_WAIT_BCON; -+ } else if (langwell->hsm.a_wait_vrise_tmout) { -+ if (langwell->hsm.a_vbus_vld) { -+ if (langwell->host_ops) -+ langwell->host_ops->probe( -+ langwell->pdev, -+ langwell->host_ops->id_table); -+ else { -+ otg_dbg("host driver not loaded.\n"); -+ break; -+ } -+ langwell->hsm.b_conn = 0; -+ /* change to kernel timer */ -+ langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); -+ langwell->otg.state = OTG_STATE_A_WAIT_BCON; -+ } else { -+ langwell_otg_drv_vbus(0); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_A_VBUS_ERR; -+ } -+ } -+ break; -+ case OTG_STATE_A_WAIT_BCON: -+ if (langwell->hsm.id) { -+ /* delete hsm timer for a_wait_bcon_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ langwell->otg.default_a = 0; -+ langwell->hsm.b_bus_req = 0; -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ set_client_mode(); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (!langwell->hsm.a_vbus_vld) { -+ /* delete hsm timer for a_wait_bcon_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_A_VBUS_ERR; -+ } else if (langwell->hsm.a_bus_drop || -+ (langwell->hsm.a_wait_bcon_tmout && -+ !langwell->hsm.a_bus_req)) { -+ /* delete hsm timer for a_wait_bcon_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; -+ } else if (langwell->hsm.b_conn) { -+ /* delete hsm timer for a_wait_bcon_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ langwell->hsm.a_suspend_req = 0; -+ langwell->otg.state = OTG_STATE_A_HOST; -+ if (langwell->hsm.a_srp_det && -+ !langwell->otg.host->b_hnp_enable) { -+ /* SRP capable peripheral-only device */ -+ langwell->hsm.a_bus_req = 1; -+ langwell->hsm.a_srp_det = 0; -+ } else if (!langwell->hsm.a_bus_req && -+ langwell->otg.host->b_hnp_enable) { -+ /* It is not safe enough to do a fast -+ * transistion from A_WAIT_BCON to -+ * A_SUSPEND */ -+ msleep(10000); -+ if (langwell->hsm.a_bus_req) -+ break; -+ -+ if (request_irq(langwell->pdev->irq, -+ otg_dummy_irq, IRQF_SHARED, -+ driver_name, langwell->regs) != 0) { -+ otg_dbg("request interrupt %d fail\n", -+ langwell->pdev->irq); -+ } -+ -+ langwell_otg_HABA(1); -+ langwell->hsm.b_bus_resume = 0; -+ langwell->hsm.a_aidl_bdis_tmout = 0; -+ langwell_otg_add_timer(a_aidl_bdis_tmr); -+ -+ langwell_otg_loc_sof(0); -+ /* clear PHCD to enable HW timer */ -+ langwell_otg_phy_low_power(0); -+ langwell->otg.state = OTG_STATE_A_SUSPEND; -+ } else if (!langwell->hsm.a_bus_req && -+ !langwell->otg.host->b_hnp_enable) { -+ struct pci_dev *pdev = langwell->pdev; -+ if (langwell->host_ops) -+ langwell->host_ops->remove(pdev); -+ else -+ otg_dbg("host driver removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; -+ } -+ } -+ break; -+ case OTG_STATE_A_HOST: -+ if (langwell->hsm.id) { -+ langwell->otg.default_a = 0; -+ langwell->hsm.b_bus_req = 0; -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ set_client_mode(); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (langwell->hsm.a_bus_drop || -+ (!langwell->otg.host->b_hnp_enable && -+ !langwell->hsm.a_bus_req)) { -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; -+ } else if (!langwell->hsm.a_vbus_vld) { -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_A_VBUS_ERR; -+ } else if (langwell->otg.host->b_hnp_enable -+ && !langwell->hsm.a_bus_req) { -+ /* Set HABA to enable hardware assistance to signal -+ * A-connect after receiver B-disconnect. Hardware -+ * will then set client mode and enable URE, SLE and -+ * PCE after the assistance. otg_dummy_irq is used to -+ * clean these ints when client driver is not resumed. -+ */ -+ if (request_irq(langwell->pdev->irq, -+ otg_dummy_irq, IRQF_SHARED, driver_name, -+ langwell->regs) != 0) { -+ otg_dbg("request interrupt %d failed\n", -+ langwell->pdev->irq); -+ } -+ -+ /* set HABA */ -+ langwell_otg_HABA(1); -+ langwell->hsm.b_bus_resume = 0; -+ langwell->hsm.a_aidl_bdis_tmout = 0; -+ langwell_otg_add_timer(a_aidl_bdis_tmr); -+ langwell_otg_loc_sof(0); -+ /* clear PHCD to enable HW timer */ -+ langwell_otg_phy_low_power(0); -+ langwell->otg.state = OTG_STATE_A_SUSPEND; -+ } else if (!langwell->hsm.b_conn || !langwell->hsm.a_bus_req) { -+ langwell->hsm.a_wait_bcon_tmout = 0; -+ /* add kernel timer */ -+ langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); -+ langwell->otg.state = OTG_STATE_A_WAIT_BCON; -+ } -+ break; -+ case OTG_STATE_A_SUSPEND: -+ if (langwell->hsm.id) { -+ langwell_otg_del_timer(a_aidl_bdis_tmr); -+ langwell_otg_HABA(0); -+ free_irq(langwell->pdev->irq, langwell->regs); -+ langwell->otg.default_a = 0; -+ langwell->hsm.b_bus_req = 0; -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ set_client_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (langwell->hsm.a_bus_req || -+ langwell->hsm.b_bus_resume) { -+ langwell_otg_del_timer(a_aidl_bdis_tmr); -+ langwell_otg_HABA(0); -+ free_irq(langwell->pdev->irq, langwell->regs); -+ langwell->hsm.a_suspend_req = 0; -+ langwell_otg_loc_sof(1); -+ langwell->otg.state = OTG_STATE_A_HOST; -+ } else if (langwell->hsm.a_aidl_bdis_tmout || -+ langwell->hsm.a_bus_drop) { -+ langwell_otg_del_timer(a_aidl_bdis_tmr); -+ langwell_otg_HABA(0); -+ free_irq(langwell->pdev->irq, langwell->regs); -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; -+ } else if (!langwell->hsm.b_conn && -+ langwell->otg.host->b_hnp_enable) { -+ langwell_otg_del_timer(a_aidl_bdis_tmr); -+ langwell_otg_HABA(0); -+ free_irq(langwell->pdev->irq, langwell->regs); -+ -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ langwell->hsm.b_bus_suspend = 0; -+ langwell->hsm.b_bus_suspend_vld = 0; -+ -+ /* msleep(200); */ -+ if (langwell->client_ops) -+ langwell->client_ops->resume(langwell->pdev); -+ else -+ otg_dbg("client driver not loaded.\n"); -+ -+ langwell_otg_add_ktimer(TB_BUS_SUSPEND_TMR); -+ langwell->otg.state = OTG_STATE_A_PERIPHERAL; -+ break; -+ } else if (!langwell->hsm.a_vbus_vld) { -+ langwell_otg_del_timer(a_aidl_bdis_tmr); -+ langwell_otg_HABA(0); -+ free_irq(langwell->pdev->irq, langwell->regs); -+ if (langwell->host_ops) -+ langwell->host_ops->remove(langwell->pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_A_VBUS_ERR; -+ } -+ break; -+ case OTG_STATE_A_PERIPHERAL: -+ if (langwell->hsm.id) { -+ /* delete hsm timer for b_bus_suspend_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ langwell->otg.default_a = 0; -+ langwell->hsm.b_bus_req = 0; -+ if (langwell->client_ops) -+ langwell->client_ops->suspend(langwell->pdev, -+ PMSG_FREEZE); -+ else -+ otg_dbg("client driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ set_client_mode(); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (!langwell->hsm.a_vbus_vld) { -+ /* delete hsm timer for b_bus_suspend_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ if (langwell->client_ops) -+ langwell->client_ops->suspend(langwell->pdev, -+ PMSG_FREEZE); -+ else -+ otg_dbg("client driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell_otg_phy_low_power_wait(1); -+ langwell->otg.state = OTG_STATE_A_VBUS_ERR; -+ } else if (langwell->hsm.a_bus_drop) { -+ /* delete hsm timer for b_bus_suspend_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ if (langwell->client_ops) -+ langwell->client_ops->suspend(langwell->pdev, -+ PMSG_FREEZE); -+ else -+ otg_dbg("client driver has been removed.\n"); -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_WAIT_VFALL; -+ } else if (langwell->hsm.b_bus_suspend) { -+ /* delete hsm timer for b_bus_suspend_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ if (langwell->client_ops) -+ langwell->client_ops->suspend(langwell->pdev, -+ PMSG_FREEZE); -+ else -+ otg_dbg("client driver has been removed.\n"); -+ if (langwell->host_ops) -+ langwell->host_ops->probe(langwell->pdev, -+ langwell->host_ops->id_table); -+ else -+ otg_dbg("host driver not loaded.\n"); -+ langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); -+ langwell->otg.state = OTG_STATE_A_WAIT_BCON; -+ } else if (langwell->hsm.b_bus_suspend_tmout) { -+ u32 val; -+ val = readl(langwell->regs + CI_PORTSC1); -+ if (!(val & PORTSC_SUSP)) -+ break; -+ if (langwell->client_ops) -+ langwell->client_ops->suspend(langwell->pdev, -+ PMSG_FREEZE); -+ else -+ otg_dbg("client driver has been removed.\n"); -+ if (langwell->host_ops) -+ langwell->host_ops->probe(langwell->pdev, -+ langwell->host_ops->id_table); -+ else -+ otg_dbg("host driver not loaded.\n"); -+ /* replaced with kernel timer */ -+ langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); -+ langwell->otg.state = OTG_STATE_A_WAIT_BCON; -+ } -+ break; -+ case OTG_STATE_A_VBUS_ERR: -+ if (langwell->hsm.id) { -+ langwell->otg.default_a = 0; -+ langwell->hsm.a_clr_err = 0; -+ langwell->hsm.a_srp_det = 0; -+ set_client_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (langwell->hsm.a_clr_err) { -+ langwell->hsm.a_clr_err = 0; -+ langwell->hsm.a_srp_det = 0; -+ reset_otg(); -+ init_hsm(); -+ if (langwell->otg.state == OTG_STATE_A_IDLE) -+ queue_work(langwell->qwork, &langwell->work); -+ } else { -+ /* FIXME: Because FW will clear PHCD bit when any VBus -+ * event detected. Reset PHCD to 1 again */ -+ langwell_otg_phy_low_power(1); -+ } -+ break; -+ case OTG_STATE_A_WAIT_VFALL: -+ if (langwell->hsm.id) { -+ langwell->otg.default_a = 0; -+ set_client_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ queue_work(langwell->qwork, &langwell->work); -+ } else if (langwell->hsm.a_bus_req) { -+ langwell_otg_drv_vbus(1); -+ langwell->hsm.a_wait_vrise_tmout = 0; -+ langwell_otg_add_timer(a_wait_vrise_tmr); -+ langwell->otg.state = OTG_STATE_A_WAIT_VRISE; -+ } else if (!langwell->hsm.a_sess_vld) { -+ langwell->hsm.a_srp_det = 0; -+ set_host_mode(); -+ langwell_otg_phy_low_power(1); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ } -+ break; -+ default: -+ ; -+ } -+ -+ otg_dbg("%s: new state = %s\n", __func__, -+ state_string(langwell->otg.state)); -+} -+ -+ static ssize_t -+show_registers(struct device *_dev, struct device_attribute *attr, char *buf) -+{ -+ struct langwell_otg *langwell; -+ char *next; -+ unsigned size; -+ unsigned t; -+ -+ langwell = the_transceiver; -+ next = buf; -+ size = PAGE_SIZE; -+ -+ t = scnprintf(next, size, -+ "\n" -+ "USBCMD = 0x%08x \n" -+ "USBSTS = 0x%08x \n" -+ "USBINTR = 0x%08x \n" -+ "ASYNCLISTADDR = 0x%08x \n" -+ "PORTSC1 = 0x%08x \n" -+ "HOSTPC1 = 0x%08x \n" -+ "OTGSC = 0x%08x \n" -+ "USBMODE = 0x%08x \n", -+ readl(langwell->regs + 0x30), -+ readl(langwell->regs + 0x34), -+ readl(langwell->regs + 0x38), -+ readl(langwell->regs + 0x48), -+ readl(langwell->regs + 0x74), -+ readl(langwell->regs + 0xb4), -+ readl(langwell->regs + 0xf4), -+ readl(langwell->regs + 0xf8) -+ ); -+ size -= t; -+ next += t; -+ -+ return PAGE_SIZE - size; -+} -+static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); -+ -+static ssize_t -+show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) -+{ -+ struct langwell_otg *langwell; -+ char *next; -+ unsigned size; -+ unsigned t; -+ enum usb_otg_state state; -+ -+ langwell = the_transceiver; -+ next = buf; -+ size = PAGE_SIZE; -+ state = langwell->otg.state; -+ -+ /* Add a_set_b_hnp_en */ -+ if (state == OTG_STATE_A_HOST || state == OTG_STATE_A_SUSPEND) -+ langwell->hsm.a_set_b_hnp_en = langwell->otg.host->b_hnp_enable; -+ else -+ langwell->hsm.a_set_b_hnp_en = 0; -+ -+ t = scnprintf(next, size, -+ "\n" -+ "current state = %s\n" -+ "a_bus_resume = \t%d\n" -+ "a_bus_suspend = \t%d\n" -+ "a_conn = \t%d\n" -+ "a_sess_vld = \t%d\n" -+ "a_srp_det = \t%d\n" -+ "a_vbus_vld = \t%d\n" -+ "b_bus_resume = \t%d\n" -+ "b_bus_suspend = \t%d\n" -+ "b_conn = \t%d\n" -+ "b_se0_srp = \t%d\n" -+ "b_sess_end = \t%d\n" -+ "b_sess_vld = \t%d\n" -+ "id = \t%d\n" -+ "a_set_b_hnp_en = \t%d\n" -+ "b_srp_done = \t%d\n" -+ "b_hnp_enable = \t%d\n" -+ "a_wait_vrise_tmout = \t%d\n" -+ "a_wait_bcon_tmout = \t%d\n" -+ "a_aidl_bdis_tmout = \t%d\n" -+ "b_ase0_brst_tmout = \t%d\n" -+ "a_bus_drop = \t%d\n" -+ "a_bus_req = \t%d\n" -+ "a_clr_err = \t%d\n" -+ "a_suspend_req = \t%d\n" -+ "b_bus_req = \t%d\n" -+ "b_bus_suspend_tmout = \t%d\n" -+ "b_bus_suspend_vld = \t%d\n", -+ state_string(langwell->otg.state), -+ langwell->hsm.a_bus_resume, -+ langwell->hsm.a_bus_suspend, -+ langwell->hsm.a_conn, -+ langwell->hsm.a_sess_vld, -+ langwell->hsm.a_srp_det, -+ langwell->hsm.a_vbus_vld, -+ langwell->hsm.b_bus_resume, -+ langwell->hsm.b_bus_suspend, -+ langwell->hsm.b_conn, -+ langwell->hsm.b_se0_srp, -+ langwell->hsm.b_sess_end, -+ langwell->hsm.b_sess_vld, -+ langwell->hsm.id, -+ langwell->hsm.a_set_b_hnp_en, -+ langwell->hsm.b_srp_done, -+ langwell->hsm.b_hnp_enable, -+ langwell->hsm.a_wait_vrise_tmout, -+ langwell->hsm.a_wait_bcon_tmout, -+ langwell->hsm.a_aidl_bdis_tmout, -+ langwell->hsm.b_ase0_brst_tmout, -+ langwell->hsm.a_bus_drop, -+ langwell->hsm.a_bus_req, -+ langwell->hsm.a_clr_err, -+ langwell->hsm.a_suspend_req, -+ langwell->hsm.b_bus_req, -+ langwell->hsm.b_bus_suspend_tmout, -+ langwell->hsm.b_bus_suspend_vld -+ ); -+ size -= t; -+ next += t; -+ -+ return PAGE_SIZE - size; -+} -+static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); -+ -+static ssize_t -+get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct langwell_otg *langwell; -+ char *next; -+ unsigned size; -+ unsigned t; -+ -+ langwell = the_transceiver; -+ next = buf; -+ size = PAGE_SIZE; -+ -+ t = scnprintf(next, size, "%d", langwell->hsm.a_bus_req); -+ size -= t; -+ next += t; -+ -+ return PAGE_SIZE - size; -+} -+ -+static ssize_t -+set_a_bus_req(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct langwell_otg *langwell; -+ langwell = the_transceiver; -+ if (!langwell->otg.default_a) -+ return -1; -+ if (count > 2) -+ return -1; -+ -+ if (buf[0] == '0') { -+ langwell->hsm.a_bus_req = 0; -+ otg_dbg("a_bus_req = 0\n"); -+ } else if (buf[0] == '1') { -+ /* If a_bus_drop is TRUE, a_bus_req can't be set */ -+ if (langwell->hsm.a_bus_drop) -+ return -1; -+ langwell->hsm.a_bus_req = 1; -+ otg_dbg("a_bus_req = 1\n"); -+ } -+ -+ langwell_update_transceiver(); -+ -+ return count; -+} -+static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req); -+ -+static ssize_t -+get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct langwell_otg *langwell; -+ char *next; -+ unsigned size; -+ unsigned t; -+ -+ langwell = the_transceiver; -+ next = buf; -+ size = PAGE_SIZE; -+ -+ t = scnprintf(next, size, "%d", langwell->hsm.a_bus_drop); -+ size -= t; -+ next += t; -+ -+ return PAGE_SIZE - size; -+} -+ -+static ssize_t -+set_a_bus_drop(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct langwell_otg *langwell; -+ langwell = the_transceiver; -+ if (!langwell->otg.default_a) -+ return -1; -+ if (count > 2) -+ return -1; -+ -+ if (buf[0] == '0') { -+ langwell->hsm.a_bus_drop = 0; -+ otg_dbg("a_bus_drop = 0\n"); -+ } else if (buf[0] == '1') { -+ langwell->hsm.a_bus_drop = 1; -+ langwell->hsm.a_bus_req = 0; -+ otg_dbg("a_bus_drop = 1, then a_bus_req = 0\n"); -+ } -+ -+ langwell_update_transceiver(); -+ -+ return count; -+} -+static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO, -+ get_a_bus_drop, set_a_bus_drop); -+ -+static ssize_t -+get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct langwell_otg *langwell; -+ char *next; -+ unsigned size; -+ unsigned t; -+ -+ langwell = the_transceiver; -+ next = buf; -+ size = PAGE_SIZE; -+ -+ t = scnprintf(next, size, "%d", langwell->hsm.b_bus_req); -+ size -= t; -+ next += t; -+ -+ return PAGE_SIZE - size; -+} -+ -+static ssize_t -+set_b_bus_req(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct langwell_otg *langwell; -+ langwell = the_transceiver; -+ -+ if (langwell->otg.default_a) -+ return -1; -+ -+ if (count > 2) -+ return -1; -+ -+ if (buf[0] == '0') { -+ langwell->hsm.b_bus_req = 0; -+ otg_dbg("b_bus_req = 0\n"); -+ } else if (buf[0] == '1') { -+ langwell->hsm.b_bus_req = 1; -+ otg_dbg("b_bus_req = 1\n"); -+ } -+ -+ langwell_update_transceiver(); -+ -+ return count; -+} -+static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req); -+ -+static ssize_t -+set_a_clr_err(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct langwell_otg *langwell; -+ langwell = the_transceiver; -+ -+ if (!langwell->otg.default_a) -+ return -1; -+ if (count > 2) -+ return -1; -+ -+ if (buf[0] == '1') { -+ langwell->hsm.a_clr_err = 1; -+ otg_dbg("a_clr_err = 1\n"); -+ } -+ -+ langwell_update_transceiver(); -+ -+ return count; -+} -+static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err); -+ -+static struct attribute *inputs_attrs[] = { -+ &dev_attr_a_bus_req.attr, -+ &dev_attr_a_bus_drop.attr, -+ &dev_attr_b_bus_req.attr, -+ &dev_attr_a_clr_err.attr, -+ NULL, -+}; -+ -+static struct attribute_group debug_dev_attr_group = { -+ .name = "inputs", -+ .attrs = inputs_attrs, -+}; -+ -+int langwell_register_host(struct pci_driver *host_driver) -+{ -+ int ret = 0; -+ -+ the_transceiver->host_ops = host_driver; -+ queue_work(the_transceiver->qwork, &the_transceiver->work); -+ otg_dbg("host controller driver is registered\n"); -+ -+ return ret; -+} -+EXPORT_SYMBOL(langwell_register_host); -+ -+void langwell_unregister_host(struct pci_driver *host_driver) -+{ -+ if (the_transceiver->host_ops) -+ the_transceiver->host_ops->remove(the_transceiver->pdev); -+ the_transceiver->host_ops = NULL; -+ the_transceiver->hsm.a_bus_drop = 1; -+ queue_work(the_transceiver->qwork, &the_transceiver->work); -+ otg_dbg("host controller driver is unregistered\n"); -+} -+EXPORT_SYMBOL(langwell_unregister_host); -+ -+int langwell_register_peripheral(struct pci_driver *client_driver) -+{ -+ int ret = 0; -+ -+ if (client_driver) -+ ret = client_driver->probe(the_transceiver->pdev, -+ client_driver->id_table); -+ if (!ret) { -+ the_transceiver->client_ops = client_driver; -+ queue_work(the_transceiver->qwork, &the_transceiver->work); -+ otg_dbg("client controller driver is registered\n"); -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL(langwell_register_peripheral); -+ -+void langwell_unregister_peripheral(struct pci_driver *client_driver) -+{ -+ if (the_transceiver->client_ops) -+ the_transceiver->client_ops->remove(the_transceiver->pdev); -+ the_transceiver->client_ops = NULL; -+ the_transceiver->hsm.b_bus_req = 0; -+ queue_work(the_transceiver->qwork, &the_transceiver->work); -+ otg_dbg("client controller driver is unregistered\n"); -+} -+EXPORT_SYMBOL(langwell_unregister_peripheral); -+ -+static int langwell_otg_probe(struct pci_dev *pdev, -+ const struct pci_device_id *id) -+{ -+ unsigned long resource, len; -+ void __iomem *base = NULL; -+ int retval; -+ u32 val32; -+ struct langwell_otg *langwell; -+ char qname[] = "langwell_otg_queue"; -+ -+ retval = 0; -+ otg_dbg("\notg controller is detected.\n"); -+ if (pci_enable_device(pdev) < 0) { -+ retval = -ENODEV; -+ goto done; -+ } -+ -+ langwell = kzalloc(sizeof *langwell, GFP_KERNEL); -+ if (langwell == NULL) { -+ retval = -ENOMEM; -+ goto done; -+ } -+ the_transceiver = langwell; -+ -+ /* control register: BAR 0 */ -+ resource = pci_resource_start(pdev, 0); -+ len = pci_resource_len(pdev, 0); -+ if (!request_mem_region(resource, len, driver_name)) { -+ retval = -EBUSY; -+ goto err; -+ } -+ langwell->region = 1; -+ -+ base = ioremap_nocache(resource, len); -+ if (base == NULL) { -+ retval = -EFAULT; -+ goto err; -+ } -+ langwell->regs = base; -+ -+ if (!request_mem_region(USBCFG_ADDR, USBCFG_LEN, driver_name)) { -+ retval = -EBUSY; -+ goto err; -+ } -+ langwell->cfg_region = 1; -+ -+ /* For the SCCB.USBCFG register */ -+ base = ioremap_nocache(USBCFG_ADDR, USBCFG_LEN); -+ if (base == NULL) { -+ retval = -EFAULT; -+ goto err; -+ } -+ langwell->usbcfg = base; -+ -+ if (!pdev->irq) { -+ otg_dbg("No IRQ.\n"); -+ retval = -ENODEV; -+ goto err; -+ } -+ -+ langwell->qwork = create_singlethread_workqueue(qname); -+ if (!langwell->qwork) { -+ otg_dbg("cannot create workqueue %s\n", qname); -+ retval = -ENOMEM; -+ goto err; -+ } -+ INIT_WORK(&langwell->work, langwell_otg_work); -+ -+ /* OTG common part */ -+ langwell->pdev = pdev; -+ langwell->otg.dev = &pdev->dev; -+ langwell->otg.label = driver_name; -+ langwell->otg.set_host = langwell_otg_set_host; -+ langwell->otg.set_peripheral = langwell_otg_set_peripheral; -+ langwell->otg.set_power = langwell_otg_set_power; -+ langwell->otg.start_srp = langwell_otg_start_srp; -+ langwell->otg.state = OTG_STATE_UNDEFINED; -+ if (otg_set_transceiver(&langwell->otg)) { -+ otg_dbg("can't set transceiver\n"); -+ retval = -EBUSY; -+ goto err; -+ } -+ -+ reset_otg(); -+ init_hsm(); -+ -+ spin_lock_init(&langwell->lock); -+ spin_lock_init(&langwell->wq_lock); -+ INIT_LIST_HEAD(&active_timers); -+ langwell_otg_init_timers(&langwell->hsm); -+ init_timer(&langwell->hsm_timer); -+ -+ if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, -+ driver_name, langwell) != 0) { -+ otg_dbg("request interrupt %d failed\n", pdev->irq); -+ retval = -EBUSY; -+ goto err; -+ } -+ -+ /* enable OTGSC int */ -+ val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | -+ OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; -+ writel(val32, langwell->regs + CI_OTGSC); -+ -+ retval = device_create_file(&pdev->dev, &dev_attr_registers); -+ if (retval < 0) { -+ otg_dbg("Can't register sysfs attribute: %d\n", retval); -+ goto err; -+ } -+ -+ retval = device_create_file(&pdev->dev, &dev_attr_hsm); -+ if (retval < 0) { -+ otg_dbg("Can't hsm sysfs attribute: %d\n", retval); -+ goto err; -+ } -+ -+ retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); -+ if (retval < 0) { -+ otg_dbg("Can't register sysfs attr group: %d\n", retval); -+ goto err; -+ } -+ -+ if (langwell->otg.state == OTG_STATE_A_IDLE) -+ queue_work(langwell->qwork, &langwell->work); -+ -+ return 0; -+ -+err: -+ if (the_transceiver) -+ langwell_otg_remove(pdev); -+done: -+ return retval; -+} -+ -+static void langwell_otg_remove(struct pci_dev *pdev) -+{ -+ struct langwell_otg *langwell; -+ -+ langwell = the_transceiver; -+ -+ if (langwell->qwork) { -+ flush_workqueue(langwell->qwork); -+ destroy_workqueue(langwell->qwork); -+ } -+ langwell_otg_free_timers(); -+ -+ /* disable OTGSC interrupt as OTGSC doesn't change in reset */ -+ writel(0, langwell->regs + CI_OTGSC); -+ -+ if (pdev->irq) -+ free_irq(pdev->irq, langwell); -+ if (langwell->usbcfg) -+ iounmap(langwell->usbcfg); -+ if (langwell->cfg_region) -+ release_mem_region(USBCFG_ADDR, USBCFG_LEN); -+ if (langwell->regs) -+ iounmap(langwell->regs); -+ if (langwell->region) -+ release_mem_region(pci_resource_start(pdev, 0), -+ pci_resource_len(pdev, 0)); -+ -+ otg_set_transceiver(NULL); -+ pci_disable_device(pdev); -+ sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); -+ device_remove_file(&pdev->dev, &dev_attr_hsm); -+ device_remove_file(&pdev->dev, &dev_attr_registers); -+ kfree(langwell); -+ langwell = NULL; -+} -+ -+static void transceiver_suspend(struct pci_dev *pdev) -+{ -+ pci_save_state(pdev); -+ pci_set_power_state(pdev, PCI_D3hot); -+ langwell_otg_phy_low_power(1); -+} -+ -+static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) -+{ -+ struct langwell_otg *langwell; -+ struct pci_driver *ops; -+ int ret = 0; -+ -+ langwell = the_transceiver; -+ -+ /* Disbale OTG interrupts */ -+ langwell_otg_intr(0); -+ -+ if (pdev->irq) -+ free_irq(pdev->irq, langwell); -+ -+ /* Prevent more otg_work */ -+ flush_workqueue(langwell->qwork); -+ destroy_workqueue(langwell->qwork); -+ langwell->qwork = NULL; -+ -+ /* start actions */ -+ switch (langwell->otg.state) { -+ case OTG_STATE_A_WAIT_VFALL: -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ case OTG_STATE_A_IDLE: -+ case OTG_STATE_A_VBUS_ERR: -+ case OTG_STATE_B_IDLE: -+ transceiver_suspend(pdev); -+ break; -+ case OTG_STATE_A_WAIT_VRISE: -+ langwell_otg_del_timer(a_wait_vrise_tmr); -+ langwell->hsm.a_srp_det = 0; -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ transceiver_suspend(pdev); -+ break; -+ case OTG_STATE_A_WAIT_BCON: -+ del_timer_sync(&langwell->hsm_timer); -+ ops = langwell->host_ops; -+ -+ switch (message.event) { -+ case PM_EVENT_SUSPEND: -+ if (ops && ops->driver.pm && ops->driver.pm->suspend) -+ ret = ops->driver.pm->suspend(&pdev->dev); -+ break; -+ case PM_EVENT_FREEZE: -+ if (ops && ops->driver.pm && ops->driver.pm->freeze) -+ ret = ops->driver.pm->freeze(&pdev->dev); -+ break; -+ case PM_EVENT_HIBERNATE: -+ if (ops && ops->driver.pm && ops->driver.pm->poweroff) -+ ret = ops->driver.pm->poweroff(&pdev->dev); -+ break; -+ default: -+ otg_dbg("not suspend/freeze/hibernate pm event\n"); -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (ret) { -+ otg_dbg("pm suspend function error = %d\n", ret); -+ /* restart timer */ -+ langwell_otg_add_ktimer(TA_WAIT_BCON_TMR); -+ goto error; -+ } -+ -+ if (ops && ops->remove) -+ ops->remove(pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ langwell->hsm.a_srp_det = 0; -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ transceiver_suspend(pdev); -+ break; -+ case OTG_STATE_A_HOST: -+ ops = langwell->host_ops; -+ -+ switch (message.event) { -+ case PM_EVENT_SUSPEND: -+ if (ops && ops->driver.pm && ops->driver.pm->suspend) -+ ret = ops->driver.pm->suspend(&pdev->dev); -+ break; -+ case PM_EVENT_FREEZE: -+ if (ops && ops->driver.pm && ops->driver.pm->freeze) -+ ret = ops->driver.pm->freeze(&pdev->dev); -+ break; -+ case PM_EVENT_HIBERNATE: -+ if (ops && ops->driver.pm && ops->driver.pm->poweroff) -+ ret = ops->driver.pm->poweroff(&pdev->dev); -+ break; -+ default: -+ otg_dbg("not suspend/freeze/hibernate pm event\n"); -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (ret) { -+ otg_dbg("pm suspend function error = %d\n", ret); -+ goto error; -+ } -+ -+ if (ops && ops->remove) -+ ops->remove(pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ -+ langwell->hsm.a_srp_det = 0; -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ transceiver_suspend(pdev); -+ break; -+ case OTG_STATE_A_SUSPEND: -+ langwell_otg_del_timer(a_aidl_bdis_tmr); -+ langwell_otg_HABA(0); -+ if (langwell->host_ops && langwell->host_ops->remove) -+ langwell->host_ops->remove(pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell->hsm.a_srp_det = 0; -+ langwell_otg_drv_vbus(0); -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ transceiver_suspend(pdev); -+ break; -+ case OTG_STATE_A_PERIPHERAL: -+ del_timer_sync(&langwell->hsm_timer); -+ if (langwell->client_ops && langwell->client_ops->suspend) -+ ret = langwell->client_ops->suspend(pdev, message); -+ else -+ otg_dbg("client driver has been removed.\n"); -+ -+ if (ret) { -+ otg_dbg("pm suspend function error = %d\n", ret); -+ goto error; -+ } -+ -+ langwell_otg_drv_vbus(0); -+ langwell->hsm.a_srp_det = 0; -+ langwell->otg.state = OTG_STATE_A_IDLE; -+ transceiver_suspend(pdev); -+ break; -+ case OTG_STATE_B_HOST: -+ if (langwell->host_ops && langwell->host_ops->remove) -+ langwell->host_ops->remove(pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell->hsm.b_bus_req = 0; -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ transceiver_suspend(pdev); -+ break; -+ case OTG_STATE_B_PERIPHERAL: -+ if (langwell->client_ops && langwell->client_ops->suspend) -+ ret = langwell->client_ops->suspend(pdev, message); -+ else -+ otg_dbg("client driver has been removed.\n"); -+ -+ if (ret) { -+ otg_dbg("pm suspend function error = %d\n", ret); -+ goto error; -+ } -+ -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ transceiver_suspend(pdev); -+ break; -+ case OTG_STATE_B_WAIT_ACON: -+ /* delete hsm timer for b_ase0_brst_tmr */ -+ del_timer_sync(&langwell->hsm_timer); -+ -+ langwell_otg_HAAR(0); -+ if (langwell->host_ops && langwell->host_ops->remove) -+ langwell->host_ops->remove(pdev); -+ else -+ otg_dbg("host driver has been removed.\n"); -+ langwell->hsm.b_bus_req = 0; -+ langwell->otg.state = OTG_STATE_B_IDLE; -+ transceiver_suspend(pdev); -+ break; -+ default: -+ otg_dbg("error state before suspend\n "); -+ break; -+ } -+ -+ return ret; -+error: -+ langwell->qwork = create_singlethread_workqueue("langwell_otg_queue"); -+ if (!langwell->qwork) { -+ otg_dbg("cannot create workqueue langwell_otg_queue\n"); -+ return -ENOMEM; -+ } -+ -+ if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, -+ driver_name, the_transceiver) != 0) { -+ otg_dbg("request interrupt %d failed\n", pdev->irq); -+ return -EBUSY; -+ } -+ -+ /* enable OTG interrupts */ -+ langwell_otg_intr(1); -+ -+ return ret; -+} -+ -+static void transceiver_resume(struct pci_dev *pdev) -+{ -+ pci_restore_state(pdev); -+ pci_set_power_state(pdev, PCI_D0); -+} -+ -+static int langwell_otg_resume(struct pci_dev *pdev) -+{ -+ struct langwell_otg *langwell; -+ int ret = 0; -+ -+ langwell = the_transceiver; -+ -+ transceiver_resume(pdev); -+ -+ langwell->qwork = create_singlethread_workqueue("langwell_otg_queue"); -+ if (!langwell->qwork) { -+ otg_dbg("cannot create workqueue langwell_otg_queue\n"); -+ ret = -ENOMEM; -+ goto error; -+ } -+ -+ if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, -+ driver_name, the_transceiver) != 0) { -+ otg_dbg("request interrupt %d failed\n", pdev->irq); -+ ret = -EBUSY; -+ goto error; -+ } -+ -+ /* enable OTG interrupts */ -+ langwell_otg_intr(1); -+ -+ update_hsm(); -+ -+ langwell_update_transceiver(); -+ -+ return ret; -+error: -+ langwell_otg_intr(0); -+ transceiver_suspend(pdev); -+ return ret; -+} -+ -+static int __init langwell_otg_init(void) -+{ -+ return pci_register_driver(&otg_pci_driver); -+} -+module_init(langwell_otg_init); -+ -+static void __exit langwell_otg_cleanup(void) -+{ -+ pci_unregister_driver(&otg_pci_driver); -+} -+module_exit(langwell_otg_cleanup); -diff --git a/include/linux/usb/langwell_otg.h b/include/linux/usb/langwell_otg.h -new file mode 100644 -index 0000000..cbb204b ---- /dev/null -+++ b/include/linux/usb/langwell_otg.h -@@ -0,0 +1,201 @@ -+/* -+ * Intel Langwell USB OTG transceiver driver -+ * Copyright (C) 2008, Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License along with -+ * this program; if not, write to the Free Software Foundation, Inc., -+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. -+ * -+ */ -+ -+#ifndef __LANGWELL_OTG_H__ -+#define __LANGWELL_OTG_H__ -+ -+/* notify transceiver driver about OTG events */ -+extern void langwell_update_transceiver(void); -+/* HCD register bus driver */ -+extern int langwell_register_host(struct pci_driver *host_driver); -+/* HCD unregister bus driver */ -+extern void langwell_unregister_host(struct pci_driver *host_driver); -+/* DCD register bus driver */ -+extern int langwell_register_peripheral(struct pci_driver *client_driver); -+/* DCD unregister bus driver */ -+extern void langwell_unregister_peripheral(struct pci_driver *client_driver); -+/* No silent failure, output warning message */ -+extern void langwell_otg_nsf_msg(unsigned long message); -+ -+#define CI_USBCMD 0x30 -+# define USBCMD_RST BIT(1) -+# define USBCMD_RS BIT(0) -+#define CI_USBSTS 0x34 -+# define USBSTS_SLI BIT(8) -+# define USBSTS_URI BIT(6) -+# define USBSTS_PCI BIT(2) -+#define CI_PORTSC1 0x74 -+# define PORTSC_PP BIT(12) -+# define PORTSC_LS (BIT(11) | BIT(10)) -+# define PORTSC_SUSP BIT(7) -+# define PORTSC_CCS BIT(0) -+#define CI_HOSTPC1 0xb4 -+# define HOSTPC1_PHCD BIT(22) -+#define CI_OTGSC 0xf4 -+# define OTGSC_DPIE BIT(30) -+# define OTGSC_1MSE BIT(29) -+# define OTGSC_BSEIE BIT(28) -+# define OTGSC_BSVIE BIT(27) -+# define OTGSC_ASVIE BIT(26) -+# define OTGSC_AVVIE BIT(25) -+# define OTGSC_IDIE BIT(24) -+# define OTGSC_DPIS BIT(22) -+# define OTGSC_1MSS BIT(21) -+# define OTGSC_BSEIS BIT(20) -+# define OTGSC_BSVIS BIT(19) -+# define OTGSC_ASVIS BIT(18) -+# define OTGSC_AVVIS BIT(17) -+# define OTGSC_IDIS BIT(16) -+# define OTGSC_DPS BIT(14) -+# define OTGSC_1MST BIT(13) -+# define OTGSC_BSE BIT(12) -+# define OTGSC_BSV BIT(11) -+# define OTGSC_ASV BIT(10) -+# define OTGSC_AVV BIT(9) -+# define OTGSC_ID BIT(8) -+# define OTGSC_HABA BIT(7) -+# define OTGSC_HADP BIT(6) -+# define OTGSC_IDPU BIT(5) -+# define OTGSC_DP BIT(4) -+# define OTGSC_OT BIT(3) -+# define OTGSC_HAAR BIT(2) -+# define OTGSC_VC BIT(1) -+# define OTGSC_VD BIT(0) -+# define OTGSC_INTEN_MASK (0x7f << 24) -+# define OTGSC_INT_MASK (0x5f << 24) -+# define OTGSC_INTSTS_MASK (0x7f << 16) -+#define CI_USBMODE 0xf8 -+# define USBMODE_CM (BIT(1) | BIT(0)) -+# define USBMODE_IDLE 0 -+# define USBMODE_DEVICE 0x2 -+# define USBMODE_HOST 0x3 -+#define USBCFG_ADDR 0xff10801c -+#define USBCFG_LEN 4 -+# define USBCFG_VBUSVAL BIT(14) -+# define USBCFG_AVALID BIT(13) -+# define USBCFG_BVALID BIT(12) -+# define USBCFG_SESEND BIT(11) -+ -+#define INTR_DUMMY_MASK (USBSTS_SLI | USBSTS_URI | USBSTS_PCI) -+ -+struct otg_hsm { -+ /* Input */ -+ int a_bus_resume; -+ int a_bus_suspend; -+ int a_conn; -+ int a_sess_vld; -+ int a_srp_det; -+ int a_vbus_vld; -+ int b_bus_resume; -+ int b_bus_suspend; -+ int b_conn; -+ int b_se0_srp; -+ int b_sess_end; -+ int b_sess_vld; -+ int id; -+ -+ /* Internal variables */ -+ int a_set_b_hnp_en; -+ int b_srp_done; -+ int b_hnp_enable; -+ -+ /* Timeout indicator for timers */ -+ int a_wait_vrise_tmout; -+ int a_wait_bcon_tmout; -+ int a_aidl_bdis_tmout; -+ int b_ase0_brst_tmout; -+ int b_bus_suspend_tmout; -+ int b_srp_init_tmout; -+ int b_srp_fail_tmout; -+ -+ /* Informative variables */ -+ int a_bus_drop; -+ int a_bus_req; -+ int a_clr_err; -+ int a_suspend_req; -+ int b_bus_req; -+ -+ /* Output */ -+ int drv_vbus; -+ int loc_conn; -+ int loc_sof; -+ -+ /* Others */ -+ int b_bus_suspend_vld; -+ int vbus_srp_up; -+}; -+ -+enum langwell_otg_timer_type { -+ TA_WAIT_VRISE_TMR, -+ TA_WAIT_BCON_TMR, -+ TA_AIDL_BDIS_TMR, -+ TB_ASE0_BRST_TMR, -+ TB_SE0_SRP_TMR, -+ TB_SRP_INIT_TMR, -+ TB_SRP_FAIL_TMR, -+ TB_BUS_SUSPEND_TMR -+}; -+ -+#define TA_WAIT_VRISE 100 -+#define TA_WAIT_BCON 30000 -+#define TA_AIDL_BDIS 15000 -+#define TB_ASE0_BRST 5000 -+#define TB_SE0_SRP 2 -+#define TB_SRP_INIT 100 -+#define TB_SRP_FAIL 5500 -+#define TB_BUS_SUSPEND 500 -+ -+struct langwell_otg_timer { -+ unsigned long expires; /* Number of count increase to timeout */ -+ unsigned long count; /* Tick counter */ -+ void (*function)(unsigned long); /* Timeout function */ -+ unsigned long data; /* Data passed to function */ -+ struct list_head list; -+}; -+ -+struct langwell_otg { -+ struct otg_transceiver otg; -+ struct otg_hsm hsm; -+ void __iomem *regs; -+ void __iomem *usbcfg; /* SCCB USB config Reg */ -+ unsigned region; -+ unsigned cfg_region; -+ struct pci_driver *host_ops; -+ struct pci_driver *client_ops; -+ struct pci_dev *pdev; -+ struct work_struct work; -+ struct workqueue_struct *qwork; -+ struct timer_list hsm_timer; -+ spinlock_t lock; -+ spinlock_t wq_lock; -+}; -+ -+static inline struct langwell_otg *otg_to_langwell(struct otg_transceiver *otg) -+{ -+ return container_of(otg, struct langwell_otg, otg); -+} -+ -+#ifdef DEBUG -+#define otg_dbg(fmt, args...) \ -+ printk(KERN_DEBUG fmt , ## args) -+#else -+#define otg_dbg(fmt, args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+#endif /* __LANGWELL_OTG_H__ */ -diff --git a/include/linux/usb/langwell_udc.h b/include/linux/usb/langwell_udc.h -index c949178..fe2c698 100644 ---- a/include/linux/usb/langwell_udc.h -+++ b/include/linux/usb/langwell_udc.h -@@ -306,5 +306,18 @@ struct langwell_op_regs { - #define EPCTRL_RXS BIT(0) /* RX endpoint STALL */ - } __attribute__ ((packed)); - -+ -+/* export function declaration */ -+ -+/* gets the maximum power consumption */ -+extern int langwell_udc_maxpower(int *mA); -+ -+/* return errors of langwell_udc_maxpower() */ -+#define EOTGFAIL 1 -+#define EOTGNODEVICE 2 -+#define EOTGCHARGER 3 -+#define EOTGDISCONN 4 -+#define EOTGINVAL 5 -+ - #endif /* __LANGWELL_UDC_H */ - --- -1.5.4.5 - |