提交 1c41a957 编写于 作者: G Greg Kroah-Hartman

Merge tag 'usb-for-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next

Felipe writes:

usb: patches for v4.1 merge window

As usual, a big pile of commits. This time a total
of 111 non-merge commits.

Other than the usual set of cleanups and non-critical
fixes, we have some interesting work for AM335x's MUSB
babble recovery. Now that takes a lot less time and we
don't have to Reset MUSB all the time.

The printer gadget has been converted to configfs interface
and the atmel udc has learned suspend/resume with wakeup.
Signed-off-by: NFelipe Balbi <balbi@ti.com>
What: /config/usb-gadget/gadget/functions/printer.name
Date: Apr 2015
KernelVersion: 4.1
Description:
The attributes:
pnp_string - Data to be passed to the host in pnp string
q_len - Number of requests per endpoint
......@@ -14,6 +14,7 @@ Optional properties:
- phys: from the *Generic PHY* bindings
- phy-names: from the *Generic PHY* bindings
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
- snps,disable_scramble_quirk: true when SW should disable data scrambling.
Only really useful for FPGA builds.
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
......
......@@ -15,7 +15,10 @@ Optional properties:
- phys: phandle + phy specifier pair
- phy-names: must be "usb"
- dmas: Must contain a list of references to DMA specifiers.
- dma-names : Must contain a list of DMA names, "tx" or "rx".
- dma-names : Must contain a list of DMA names:
- tx0 ... tx<n>
- rx0 ... rx<n>
- This <n> means DnFIFO in USBHS module.
Example:
usbhs: usb@e6590000 {
......
......@@ -19,6 +19,7 @@ provided by gadgets.
16. UAC1 function
17. UAC2 function
18. UVC function
19. PRINTER function
1. ACM function
......@@ -726,3 +727,49 @@ with these patches:
http://www.spinics.net/lists/linux-usb/msg99220.html
host: luvcview -f yuv
19. PRINTER function
====================
The function is provided by usb_f_printer.ko module.
Function-specific configfs interface
------------------------------------
The function name to use when creating the function directory is "printer".
The printer function provides these attributes in its function directory:
pnp_string - Data to be passed to the host in pnp string
q_len - Number of requests per endpoint
Testing the PRINTER function
----------------------------
The most basic testing:
device: run the gadget
# ls -l /devices/virtual/usb_printer_gadget/
should show g_printer<number>.
If udev is active, then /dev/g_printer<number> should appear automatically.
host:
If udev is active, then e.g. /dev/usb/lp0 should appear.
host->device transmission:
device:
# cat /dev/g_printer<number>
host:
# cat > /dev/usb/lp0
device->host transmission:
# cat > /dev/g_printer<number>
host:
# cat /dev/usb/lp0
More advanced testing can be done with the prn_example
described in Documentation/usb/gadget-printer.txt.
......@@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
/* interrupt, error, port change, reset, sleep/suspend */
hw_write(ci, OP_USBINTR, ~0,
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
} else {
hw_write(ci, OP_USBINTR, ~0, 0);
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
}
return 0;
}
......@@ -1508,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
hw_device_reset(ci);
hw_device_state(ci, ci->ep0out->qh.dma);
usb_gadget_set_state(_gadget, USB_STATE_POWERED);
usb_udc_vbus_handler(_gadget, true);
} else {
usb_udc_vbus_handler(_gadget, false);
if (ci->driver)
ci->driver->disconnect(&ci->gadget);
hw_device_state(ci, 0);
......@@ -1574,13 +1574,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
{
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
if (!ci->vbus_active)
return -EOPNOTSUPP;
pm_runtime_get_sync(&ci->gadget.dev);
if (is_on)
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
else
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
pm_runtime_put_sync(&ci->gadget.dev);
return 0;
}
......@@ -1710,6 +1709,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci);
} else {
usb_udc_vbus_handler(&ci->gadget, false);
pm_runtime_put_sync(&ci->gadget.dev);
return retval;
}
......
......@@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM
config USB_DWC2_PCI
tristate "DWC2 PCI"
depends on USB_DWC2_HOST && PCI
default USB_DWC2_HOST
depends on PCI
default n
select USB_DWC2_PLATFORM
select NOP_USB_XCEIV
help
The Designware USB2.0 PCI interface module for controllers
connected to a PCI bus. This is only used for host mode.
connected to a PCI bus.
config USB_DWC2_DEBUG
bool "Enable Debugging Messages"
......
......@@ -19,10 +19,8 @@ endif
# mode. The PCI bus interface module will called dwc2_pci.ko and the platform
# interface module will be called dwc2_platform.ko.
ifneq ($(CONFIG_USB_DWC2_PCI),)
obj-$(CONFIG_USB_DWC2) += dwc2_pci.o
dwc2_pci-y := pci.o
endif
obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o
dwc2_pci-y := pci.o
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
dwc2_platform-y := platform.o
......@@ -593,6 +593,8 @@ struct dwc2_hsotg {
struct dwc2_core_params *core_params;
enum usb_otg_state op_state;
enum usb_dr_mode dr_mode;
unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1;
struct phy *phy;
struct usb_phy *uphy;
......
......@@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
*/
channel->qh = NULL;
}
/* All channels have been freed, mark them available */
if (hsotg->core_params->uframe_sched > 0) {
hsotg->available_host_channels =
hsotg->core_params->host_channels;
} else {
hsotg->non_periodic_channels = 0;
hsotg->periodic_channels = 0;
}
}
/**
......
......@@ -50,113 +50,97 @@
#include <linux/usb/hcd.h>
#include <linux/usb/ch11.h>
#include <linux/platform_device.h>
#include <linux/usb/usb_phy_generic.h>
#include "core.h"
#include "hcd.h"
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0
static const char dwc2_driver_name[] = "dwc2";
static const struct dwc2_core_params dwc2_module_params = {
.otg_cap = -1,
.otg_ver = -1,
.dma_enable = -1,
.dma_desc_enable = 0,
.speed = -1,
.enable_dynamic_fifo = -1,
.en_multiple_tx_fifo = -1,
.host_rx_fifo_size = 1024,
.host_nperio_tx_fifo_size = 256,
.host_perio_tx_fifo_size = 1024,
.max_transfer_size = 65535,
.max_packet_count = 511,
.host_channels = -1,
.phy_type = -1,
.phy_utmi_width = -1,
.phy_ulpi_ddr = -1,
.phy_ulpi_ext_vbus = -1,
.i2c_enable = -1,
.ulpi_fs_ls = -1,
.host_support_fs_ls_low_power = -1,
.host_ls_low_power_phy_clk = -1,
.ts_dline = -1,
.reload_ctl = -1,
.ahbcfg = -1,
.uframe_sched = -1,
static const char dwc2_driver_name[] = "dwc2-pci";
struct dwc2_pci_glue {
struct platform_device *dwc2;
struct platform_device *phy;
};
/**
* dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
* DWC_otg driver
*
* @dev: Bus device
*
* This routine is called, for example, when the rmmod command is executed. The
* device may or may not be electrically present. If it is present, the driver
* stops device processing. Any resources used on behalf of this device are
* freed.
*/
static void dwc2_driver_remove(struct pci_dev *dev)
static void dwc2_pci_remove(struct pci_dev *pci)
{
struct dwc2_hsotg *hsotg = pci_get_drvdata(dev);
struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
dwc2_hcd_remove(hsotg);
pci_disable_device(dev);
platform_device_unregister(glue->dwc2);
usb_phy_generic_unregister(glue->phy);
kfree(glue);
pci_set_drvdata(pci, NULL);
}
/**
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
* driver
*
* @dev: Bus device
*
* This routine creates the driver components required to control the device
* (core, HCD, and PCD) and initializes the device. The driver components are
* stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved
* in the device private data. This allows the driver to access the dwc2_hsotg
* structure on subsequent calls to driver methods for this device.
*/
static int dwc2_driver_probe(struct pci_dev *dev,
static int dwc2_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
struct dwc2_hsotg *hsotg;
int retval;
struct resource res[2];
struct platform_device *dwc2;
struct platform_device *phy;
int ret;
struct device *dev = &pci->dev;
struct dwc2_pci_glue *glue;
ret = pcim_enable_device(pci);
if (ret) {
dev_err(dev, "failed to enable pci device\n");
return -ENODEV;
}
pci_set_master(pci);
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
if (!hsotg)
dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
if (!dwc2) {
dev_err(dev, "couldn't allocate dwc2 device\n");
return -ENOMEM;
}
hsotg->dev = &dev->dev;
hsotg->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]);
if (IS_ERR(hsotg->regs))
return PTR_ERR(hsotg->regs);
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
(unsigned long)pci_resource_start(dev, 0), hsotg->regs);
res[0].start = pci_resource_start(pci, 0);
res[0].end = pci_resource_end(pci, 0);
res[0].name = "dwc2";
res[0].flags = IORESOURCE_MEM;
if (pci_enable_device(dev) < 0)
return -ENODEV;
res[1].start = pci->irq;
res[1].name = "dwc2";
res[1].flags = IORESOURCE_IRQ;
pci_set_master(dev);
ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc2 device\n");
return ret;
}
retval = devm_request_irq(hsotg->dev, dev->irq,
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (retval)
return retval;
dwc2->dev.parent = dev;
spin_lock_init(&hsotg->lock);
retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params);
if (retval) {
pci_disable_device(dev);
return retval;
phy = usb_phy_generic_register();
if (IS_ERR(phy)) {
dev_err(dev, "error registering generic PHY (%ld)\n",
PTR_ERR(phy));
return PTR_ERR(phy);
}
pci_set_drvdata(dev, hsotg);
ret = platform_device_add(dwc2);
if (ret) {
dev_err(dev, "failed to register dwc2 device\n");
goto err;
}
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue)
return -ENOMEM;
glue->phy = phy;
glue->dwc2 = dwc2;
pci_set_drvdata(pci, glue);
return retval;
return 0;
err:
usb_phy_generic_unregister(phy);
platform_device_put(dwc2);
return ret;
}
static const struct pci_device_id dwc2_pci_ids[] = {
......@@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
static struct pci_driver dwc2_pci_driver = {
.name = dwc2_driver_name,
.id_table = dwc2_pci_ids,
.probe = dwc2_driver_probe,
.remove = dwc2_driver_remove,
.probe = dwc2_pci_probe,
.remove = dwc2_pci_remove,
};
module_pci_driver(dwc2_pci_driver);
......
......@@ -121,7 +121,9 @@ static int dwc2_driver_remove(struct platform_device *dev)
{
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
if (hsotg->hcd_enabled)
dwc2_hcd_remove(hsotg);
if (hsotg->gadget_enabled)
s3c_hsotg_remove(hsotg);
return 0;
......@@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock);
mutex_init(&hsotg->init_mutex);
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg, irq);
if (retval)
return retval;
hsotg->gadget_enabled = 1;
}
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
retval = dwc2_hcd_init(hsotg, irq, params);
if (retval)
if (retval) {
if (hsotg->gadget_enabled)
s3c_hsotg_remove(hsotg);
return retval;
}
hsotg->hcd_enabled = 1;
}
platform_set_drvdata(dev, hsotg);
......
......@@ -104,11 +104,4 @@ config USB_DWC3_DEBUG
help
Say Y here to enable debugging messages on DWC3 Driver.
config DWC3_HOST_USB3_LPM_ENABLE
bool "Enable USB3 LPM Capability"
depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
default n
help
Select this when you want to enable USB3 LPM with dwc3 xhci host.
endif
......@@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev)
* since it will be requested by the xhci-plat driver.
*/
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
goto err0;
}
dwc->regs = regs;
dwc->regs_size = resource_size(res);
/*
* restore res->start back to its original value so that,
* in case the probe is deferred, we don't end up getting error in
* request the memory region the next time probe is called.
*/
res->start -= DWC3_GLOBALS_REGS_START;
/* default to highest possible threshold */
lpm_nyet_threshold = 0xff;
......@@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,is-utmi-l1-suspend");
of_property_read_u8(node, "snps,hird-threshold",
&hird_threshold);
dwc->usb3_lpm_capable = of_property_read_bool(node,
"snps,usb3_lpm_capable");
dwc->needs_fifo_resize = of_property_read_bool(node,
"tx-fifo-resize");
......@@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev)
hird_threshold = pdata->hird_threshold;
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
dwc->dr_mode = pdata->dr_mode;
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
......@@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_get_phy(dwc);
if (ret)
return ret;
goto err0;
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
......@@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM;
goto err0;
goto err1;
}
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
......@@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize core\n");
goto err0;
goto err1;
}
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
if (ret < 0)
goto err1;
goto err2;
ret = phy_power_on(dwc->usb3_generic_phy);
if (ret < 0)
goto err_usb2phy_power;
goto err3;
ret = dwc3_event_buffers_setup(dwc);
if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n");
goto err_usb3phy_power;
goto err4;
}
ret = dwc3_core_init_mode(dwc);
if (ret)
goto err2;
goto err5;
ret = dwc3_debugfs_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize debugfs\n");
goto err3;
goto err6;
}
pm_runtime_allow(dev);
return 0;
err3:
err6:
dwc3_core_exit_mode(dwc);
err2:
err5:
dwc3_event_buffers_cleanup(dwc);
err_usb3phy_power:
err4:
phy_power_off(dwc->usb3_generic_phy);
err_usb2phy_power:
err3:
phy_power_off(dwc->usb2_generic_phy);
err1:
err2:
usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1);
dwc3_core_exit(dwc);
err0:
err1:
dwc3_free_event_buffers(dwc);
err0:
/*
* restore res->start back to its original value so that, in case the
* probe is deferred, we don't end up getting error in request the
* memory region the next time probe is called.
*/
res->start -= DWC3_GLOBALS_REGS_START;
return ret;
}
static int dwc3_remove(struct platform_device *pdev)
{
struct dwc3 *dwc = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/*
* restore res->start back to its original value so that, in case the
* probe is deferred, we don't end up getting error in request the
* memory region the next time probe is called.
*/
res->start -= DWC3_GLOBALS_REGS_START;
dwc3_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc);
......
......@@ -689,6 +689,7 @@ struct dwc3_scratchpad_array {
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup
* @usb3_lpm_capable: set if hadrware supports Link Power Management
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
......@@ -812,6 +813,7 @@ struct dwc3 {
unsigned setup_packet_pending:1;
unsigned start_config_issued:1;
unsigned three_stage_setup:1;
unsigned usb3_lpm_capable:1;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
......
......@@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
return IRQ_HANDLED;
}
static int dwc3_omap_remove_core(struct device *dev, void *c)
{
struct platform_device *pdev = to_platform_device(dev);
of_device_unregister(pdev);
return 0;
}
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
{
u32 reg;
......@@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev)
if (omap->extcon_id_dev.edev)
extcon_unregister_interest(&omap->extcon_id_dev);
dwc3_omap_disable_irqs(omap);
device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
of_platform_depopulate(omap->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
......
......@@ -24,8 +24,6 @@
#include "platform_data.h"
/* FIXME define these in <linux/pci_ids.h> */
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
#define PCI_DEVICE_ID_INTEL_BYT 0x0f37
#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
......
......@@ -1855,7 +1855,6 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int i;
int ret;
do {
req = next_request(&dep->req_queued);
if (!req) {
WARN_ON_ONCE(1);
......@@ -1874,14 +1873,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status);
if (ret)
break;
}while (++i < req->request.num_mapped_sgs);
} while (++i < req->request.num_mapped_sgs);
dwc3_gadget_giveback(dep, req, status);
if (ret)
break;
} while (1);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->req_queued)) {
if (list_empty(&dep->request_list)) {
......
......@@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(&pdata, 0, sizeof(pdata));
#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
pdata.usb3_lpm_capable = 1;
#endif
pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
if (ret) {
......
......@@ -24,6 +24,7 @@ struct dwc3_platform_data {
enum usb_device_speed maximum_speed;
enum usb_dr_mode dr_mode;
bool tx_fifo_resize;
bool usb3_lpm_capable;
unsigned is_utmi_l1_suspend:1;
u8 hird_threshold;
......
......@@ -196,6 +196,9 @@ config USB_F_MIDI
config USB_F_HID
tristate
config USB_F_PRINTER
tristate
choice
tristate "USB Gadget Drivers"
default USB_ETH
......@@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC
device. It provides a userspace API to process UVC control requests
and stream video data to the host.
config USB_CONFIGFS_F_PRINTER
bool "Printer function"
select USB_F_PRINTER
depends on USB_CONFIGFS
help
The Printer function channels data between the USB host and a
userspace program driving the print engine. The user space
program reads and writes the device file /dev/g_printer<X> to
receive or send printer data. It can use ioctl calls to
the device file to get or set printer status.
For more information, see Documentation/usb/gadget_printer.txt
which includes sample code for accessing the device file.
source "drivers/usb/gadget/legacy/Kconfig"
endchoice
......
......@@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings(
* This function will create a deep copy of usb_gadget_strings and usb_string
* and attach it to the cdev. The actual string (usb_string.s) will not be
* copied but only a referenced will be made. The struct usb_gadget_strings
* array may contain multiple languges and should be NULL terminated.
* array may contain multiple languages and should be NULL terminated.
* The ->language pointer of each struct usb_gadget_strings has to contain the
* same amount of entries.
* For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
* usb_string entry of es-ES containts the translation of the first usb_string
* usb_string entry of es-ES contains the translation of the first usb_string
* entry of en-US. Therefore both entries become the same id assign.
*/
struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
......@@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = 0;
gadget->ep0->driver_data = cdev;
/*
* Don't let non-standard requests match any of the cases below
* by accident.
*/
if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
goto unknown;
switch (ctrl->bRequest) {
/* we handle all standard USB descriptors */
......@@ -1751,6 +1758,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
* take such requests too, if that's ever needed: to work
* in config 0, etc.
*/
list_for_each_entry(f, &cdev->config->functions, list)
if (f->req_match && f->req_match(f, ctrl))
goto try_fun_setup;
f = NULL;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
......@@ -1768,7 +1779,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
f = NULL;
break;
}
try_fun_setup:
if (f && f->setup)
value = f->setup(f, ctrl);
else {
......
......@@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
usb_f_printer-y := f_printer.o
obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o
......@@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
/* disable/free request and end point */
usb_ep_disable(hidg->in_ep);
usb_ep_dequeue(hidg->in_ep, hidg->req);
kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req);
......
......@@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
if (!curlun) { /* Unsupported LUNs are okay */
common->bad_lun_okay = 1;
memset(buf, 0, 36);
buf[0] = 0x7f; /* Unsupported, no device-type */
buf[0] = TYPE_NO_LUN; /* Unsupported, no device-type */
buf[4] = 31; /* Additional length */
return 36;
}
......
/*
* f_printer.c - USB printer function driver
*
* Copied from drivers/usb/gadget/legacy/printer.c,
* which was:
*
* printer.c -- Printer gadget driver
*
* Copyright (C) 2003-2005 David Brownell
* Copyright (C) 2006 Craig W. Nadler
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/idr.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/cdev.h>
#include <asm/byteorder.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/usb/g_printer.h>
#include "u_printer.h"
#define PNP_STRING_LEN 1024
#define PRINTER_MINORS 4
#define GET_DEVICE_ID 0
#define GET_PORT_STATUS 1
#define SOFT_RESET 2
static int major, minors;
static struct class *usb_gadget_class;
static DEFINE_IDA(printer_ida);
static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */
/*-------------------------------------------------------------------------*/
struct printer_dev {
spinlock_t lock; /* lock this structure */
/* lock buffer lists during read/write calls */
struct mutex lock_printer_io;
struct usb_gadget *gadget;
s8 interface;
struct usb_ep *in_ep, *out_ep;
struct list_head rx_reqs; /* List of free RX structs */
struct list_head rx_reqs_active; /* List of Active RX xfers */
struct list_head rx_buffers; /* List of completed xfers */
/* wait until there is data to be read. */
wait_queue_head_t rx_wait;
struct list_head tx_reqs; /* List of free TX structs */
struct list_head tx_reqs_active; /* List of Active TX xfers */
/* Wait until there are write buffers available to use. */
wait_queue_head_t tx_wait;
/* Wait until all write buffers have been sent. */
wait_queue_head_t tx_flush_wait;
struct usb_request *current_rx_req;
size_t current_rx_bytes;
u8 *current_rx_buf;
u8 printer_status;
u8 reset_printer;
int minor;
struct cdev printer_cdev;
u8 printer_cdev_open;
wait_queue_head_t wait;
unsigned q_len;
char *pnp_string; /* We don't own memory! */
struct usb_function function;
};
static inline struct printer_dev *func_to_printer(struct usb_function *f)
{
return container_of(f, struct printer_dev, function);
}
/*-------------------------------------------------------------------------*/
/*
* DESCRIPTORS ... most are static, but strings and (full) configuration
* descriptors are built on demand.
*/
/* holds our biggest descriptor */
#define USB_DESC_BUFSIZE 256
#define USB_BUFSIZE 8192
static struct usb_interface_descriptor intf_desc = {
.bLength = sizeof(intf_desc),
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_PRINTER,
.bInterfaceSubClass = 1, /* Printer Sub-Class */
.bInterfaceProtocol = 2, /* Bi-Directional */
.iInterface = 0
};
static struct usb_endpoint_descriptor fs_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK
};
static struct usb_endpoint_descriptor fs_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK
};
static struct usb_descriptor_header *fs_printer_function[] = {
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &fs_ep_in_desc,
(struct usb_descriptor_header *) &fs_ep_out_desc,
NULL
};
/*
* usb 2.0 devices need to expose both high speed and full speed
* descriptors, unless they only run at full speed.
*/
static struct usb_endpoint_descriptor hs_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512)
};
static struct usb_endpoint_descriptor hs_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512)
};
static struct usb_qualifier_descriptor dev_qualifier = {
.bLength = sizeof(dev_qualifier),
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PRINTER,
.bNumConfigurations = 1
};
static struct usb_descriptor_header *hs_printer_function[] = {
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &hs_ep_in_desc,
(struct usb_descriptor_header *) &hs_ep_out_desc,
NULL
};
/*
* Added endpoint descriptors for 3.0 devices
*/
static struct usb_endpoint_descriptor ss_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
.bLength = sizeof(ss_ep_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_endpoint_descriptor ss_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
.bLength = sizeof(ss_ep_out_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_descriptor_header *ss_printer_function[] = {
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &ss_ep_in_desc,
(struct usb_descriptor_header *) &ss_ep_in_comp_desc,
(struct usb_descriptor_header *) &ss_ep_out_desc,
(struct usb_descriptor_header *) &ss_ep_out_comp_desc,
NULL
};
/* maxpacket and other transfer characteristics vary by speed. */
static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
struct usb_endpoint_descriptor *fs,
struct usb_endpoint_descriptor *hs,
struct usb_endpoint_descriptor *ss)
{
switch (gadget->speed) {
case USB_SPEED_SUPER:
return ss;
case USB_SPEED_HIGH:
return hs;
default:
return fs;
}
}
/*-------------------------------------------------------------------------*/
static struct usb_request *
printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
{
struct usb_request *req;
req = usb_ep_alloc_request(ep, gfp_flags);
if (req != NULL) {
req->length = len;
req->buf = kmalloc(len, gfp_flags);
if (req->buf == NULL) {
usb_ep_free_request(ep, req);
return NULL;
}
}
return req;
}
static void
printer_req_free(struct usb_ep *ep, struct usb_request *req)
{
if (ep != NULL && req != NULL) {
kfree(req->buf);
usb_ep_free_request(ep, req);
}
}
/*-------------------------------------------------------------------------*/
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct printer_dev *dev = ep->driver_data;
int status = req->status;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
list_del_init(&req->list); /* Remode from Active List */
switch (status) {
/* normal completion */
case 0:
if (req->actual > 0) {
list_add_tail(&req->list, &dev->rx_buffers);
DBG(dev, "G_Printer : rx length %d\n", req->actual);
} else {
list_add(&req->list, &dev->rx_reqs);
}
break;
/* software-driven interface shutdown */
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
VDBG(dev, "rx shutdown, code %d\n", status);
list_add(&req->list, &dev->rx_reqs);
break;
/* for hardware automagic (such as pxa) */
case -ECONNABORTED: /* endpoint reset */
DBG(dev, "rx %s reset\n", ep->name);
list_add(&req->list, &dev->rx_reqs);
break;
/* data overrun */
case -EOVERFLOW:
/* FALLTHROUGH */
default:
DBG(dev, "rx status %d\n", status);
list_add(&req->list, &dev->rx_reqs);
break;
}
wake_up_interruptible(&dev->rx_wait);
spin_unlock_irqrestore(&dev->lock, flags);
}
static void tx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct printer_dev *dev = ep->driver_data;
switch (req->status) {
default:
VDBG(dev, "tx err %d\n", req->status);
/* FALLTHROUGH */
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
break;
case 0:
break;
}
spin_lock(&dev->lock);
/* Take the request struct off the active list and put it on the
* free list.
*/
list_del_init(&req->list);
list_add(&req->list, &dev->tx_reqs);
wake_up_interruptible(&dev->tx_wait);
if (likely(list_empty(&dev->tx_reqs_active)))
wake_up_interruptible(&dev->tx_flush_wait);
spin_unlock(&dev->lock);
}
/*-------------------------------------------------------------------------*/
static int
printer_open(struct inode *inode, struct file *fd)
{
struct printer_dev *dev;
unsigned long flags;
int ret = -EBUSY;
dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
spin_lock_irqsave(&dev->lock, flags);
if (!dev->printer_cdev_open) {
dev->printer_cdev_open = 1;
fd->private_data = dev;
ret = 0;
/* Change the printer status to show that it's on-line. */
dev->printer_status |= PRINTER_SELECTED;
}
spin_unlock_irqrestore(&dev->lock, flags);
DBG(dev, "printer_open returned %x\n", ret);
return ret;
}
static int
printer_close(struct inode *inode, struct file *fd)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
dev->printer_cdev_open = 0;
fd->private_data = NULL;
/* Change printer status to show that the printer is off-line. */
dev->printer_status &= ~PRINTER_SELECTED;
spin_unlock_irqrestore(&dev->lock, flags);
DBG(dev, "printer_close\n");
return 0;
}
/* This function must be called with interrupts turned off. */
static void
setup_rx_reqs(struct printer_dev *dev)
{
struct usb_request *req;
while (likely(!list_empty(&dev->rx_reqs))) {
int error;
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_del_init(&req->list);
/* The USB Host sends us whatever amount of data it wants to
* so we always set the length field to the full USB_BUFSIZE.
* If the amount of data is more than the read() caller asked
* for it will be stored in the request buffer until it is
* asked for by read().
*/
req->length = USB_BUFSIZE;
req->complete = rx_complete;
/* here, we unlock, and only unlock, to avoid deadlock. */
spin_unlock(&dev->lock);
error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
spin_lock(&dev->lock);
if (error) {
DBG(dev, "rx submit --> %d\n", error);
list_add(&req->list, &dev->rx_reqs);
break;
}
/* if the req is empty, then add it into dev->rx_reqs_active. */
else if (list_empty(&req->list))
list_add(&req->list, &dev->rx_reqs_active);
}
}
static ssize_t
printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
size_t size;
size_t bytes_copied;
struct usb_request *req;
/* This is a pointer to the current USB rx request. */
struct usb_request *current_rx_req;
/* This is the number of bytes in the current rx buffer. */
size_t current_rx_bytes;
/* This is a pointer to the current rx buffer. */
u8 *current_rx_buf;
if (len == 0)
return -EINVAL;
DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
/* We will use this flag later to check if a printer reset happened
* after we turn interrupts back on.
*/
dev->reset_printer = 0;
setup_rx_reqs(dev);
bytes_copied = 0;
current_rx_req = dev->current_rx_req;
current_rx_bytes = dev->current_rx_bytes;
current_rx_buf = dev->current_rx_buf;
dev->current_rx_req = NULL;
dev->current_rx_bytes = 0;
dev->current_rx_buf = NULL;
/* Check if there is any data in the read buffers. Please note that
* current_rx_bytes is the number of bytes in the current rx buffer.
* If it is zero then check if there are any other rx_buffers that
* are on the completed list. We are only out of data if all rx
* buffers are empty.
*/
if ((current_rx_bytes == 0) &&
(likely(list_empty(&dev->rx_buffers)))) {
/* Turn interrupts back on before sleeping. */
spin_unlock_irqrestore(&dev->lock, flags);
/*
* If no data is available check if this is a NON-Blocking
* call or not.
*/
if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
/* Sleep until data is available */
wait_event_interruptible(dev->rx_wait,
(likely(!list_empty(&dev->rx_buffers))));
spin_lock_irqsave(&dev->lock, flags);
}
/* We have data to return then copy it to the caller's buffer.*/
while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
&& len) {
if (current_rx_bytes == 0) {
req = container_of(dev->rx_buffers.next,
struct usb_request, list);
list_del_init(&req->list);
if (req->actual && req->buf) {
current_rx_req = req;
current_rx_bytes = req->actual;
current_rx_buf = req->buf;
} else {
list_add(&req->list, &dev->rx_reqs);
continue;
}
}
/* Don't leave irqs off while doing memory copies */
spin_unlock_irqrestore(&dev->lock, flags);
if (len > current_rx_bytes)
size = current_rx_bytes;
else
size = len;
size -= copy_to_user(buf, current_rx_buf, size);
bytes_copied += size;
len -= size;
buf += size;
spin_lock_irqsave(&dev->lock, flags);
/* We've disconnected or reset so return. */
if (dev->reset_printer) {
list_add(&current_rx_req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
/* If we not returning all the data left in this RX request
* buffer then adjust the amount of data left in the buffer.
* Othewise if we are done with this RX request buffer then
* requeue it to get any incoming data from the USB host.
*/
if (size < current_rx_bytes) {
current_rx_bytes -= size;
current_rx_buf += size;
} else {
list_add(&current_rx_req->list, &dev->rx_reqs);
current_rx_bytes = 0;
current_rx_buf = NULL;
current_rx_req = NULL;
}
}
dev->current_rx_req = current_rx_req;
dev->current_rx_bytes = current_rx_bytes;
dev->current_rx_buf = current_rx_buf;
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
if (bytes_copied)
return bytes_copied;
else
return -EAGAIN;
}
static ssize_t
printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
size_t size; /* Amount of data in a TX request. */
size_t bytes_copied = 0;
struct usb_request *req;
DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
if (len == 0)
return -EINVAL;
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
/* Check if a printer reset happens while we have interrupts on */
dev->reset_printer = 0;
/* Check if there is any available write buffers */
if (likely(list_empty(&dev->tx_reqs))) {
/* Turn interrupts back on before sleeping. */
spin_unlock_irqrestore(&dev->lock, flags);
/*
* If write buffers are available check if this is
* a NON-Blocking call or not.
*/
if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
/* Sleep until a write buffer is available */
wait_event_interruptible(dev->tx_wait,
(likely(!list_empty(&dev->tx_reqs))));
spin_lock_irqsave(&dev->lock, flags);
}
while (likely(!list_empty(&dev->tx_reqs)) && len) {
if (len > USB_BUFSIZE)
size = USB_BUFSIZE;
else
size = len;
req = container_of(dev->tx_reqs.next, struct usb_request,
list);
list_del_init(&req->list);
req->complete = tx_complete;
req->length = size;
/* Check if we need to send a zero length packet. */
if (len > size)
/* They will be more TX requests so no yet. */
req->zero = 0;
else
/* If the data amount is not a multiple of the
* maxpacket size then send a zero length packet.
*/
req->zero = ((len % dev->in_ep->maxpacket) == 0);
/* Don't leave irqs off while doing memory copies */
spin_unlock_irqrestore(&dev->lock, flags);
if (copy_from_user(req->buf, buf, size)) {
list_add(&req->list, &dev->tx_reqs);
mutex_unlock(&dev->lock_printer_io);
return bytes_copied;
}
bytes_copied += size;
len -= size;
buf += size;
spin_lock_irqsave(&dev->lock, flags);
/* We've disconnected or reset so free the req and buffer */
if (dev->reset_printer) {
list_add(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
list_add(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
list_add(&req->list, &dev->tx_reqs_active);
}
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
if (bytes_copied)
return bytes_copied;
else
return -EAGAIN;
}
static int
printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
{
struct printer_dev *dev = fd->private_data;
struct inode *inode = file_inode(fd);
unsigned long flags;
int tx_list_empty;
mutex_lock(&inode->i_mutex);
spin_lock_irqsave(&dev->lock, flags);
tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
spin_unlock_irqrestore(&dev->lock, flags);
if (!tx_list_empty) {
/* Sleep until all data has been sent */
wait_event_interruptible(dev->tx_flush_wait,
(likely(list_empty(&dev->tx_reqs_active))));
}
mutex_unlock(&inode->i_mutex);
return 0;
}
static unsigned int
printer_poll(struct file *fd, poll_table *wait)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
int status = 0;
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
setup_rx_reqs(dev);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
poll_wait(fd, &dev->rx_wait, wait);
poll_wait(fd, &dev->tx_wait, wait);
spin_lock_irqsave(&dev->lock, flags);
if (likely(!list_empty(&dev->tx_reqs)))
status |= POLLOUT | POLLWRNORM;
if (likely(dev->current_rx_bytes) ||
likely(!list_empty(&dev->rx_buffers)))
status |= POLLIN | POLLRDNORM;
spin_unlock_irqrestore(&dev->lock, flags);
return status;
}
static long
printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
int status = 0;
DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
/* handle ioctls */
spin_lock_irqsave(&dev->lock, flags);
switch (code) {
case GADGET_GET_PRINTER_STATUS:
status = (int)dev->printer_status;
break;
case GADGET_SET_PRINTER_STATUS:
dev->printer_status = (u8)arg;
break;
default:
/* could not handle ioctl */
DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
code);
status = -ENOTTY;
}
spin_unlock_irqrestore(&dev->lock, flags);
return status;
}
/* used after endpoint configuration */
static const struct file_operations printer_io_operations = {
.owner = THIS_MODULE,
.open = printer_open,
.read = printer_read,
.write = printer_write,
.fsync = printer_fsync,
.poll = printer_poll,
.unlocked_ioctl = printer_ioctl,
.release = printer_close,
.llseek = noop_llseek,
};
/*-------------------------------------------------------------------------*/
static int
set_printer_interface(struct printer_dev *dev)
{
int result = 0;
dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
&ss_ep_in_desc);
dev->in_ep->driver_data = dev;
dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
&hs_ep_out_desc, &ss_ep_out_desc);
dev->out_ep->driver_data = dev;
result = usb_ep_enable(dev->in_ep);
if (result != 0) {
DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
goto done;
}
result = usb_ep_enable(dev->out_ep);
if (result != 0) {
DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
goto done;
}
done:
/* on error, disable any endpoints */
if (result != 0) {
(void) usb_ep_disable(dev->in_ep);
(void) usb_ep_disable(dev->out_ep);
dev->in_ep->desc = NULL;
dev->out_ep->desc = NULL;
}
/* caller is responsible for cleanup on error */
return result;
}
static void printer_reset_interface(struct printer_dev *dev)
{
if (dev->interface < 0)
return;
DBG(dev, "%s\n", __func__);
if (dev->in_ep->desc)
usb_ep_disable(dev->in_ep);
if (dev->out_ep->desc)
usb_ep_disable(dev->out_ep);
dev->in_ep->desc = NULL;
dev->out_ep->desc = NULL;
dev->interface = -1;
}
/* Change our operational Interface. */
static int set_interface(struct printer_dev *dev, unsigned number)
{
int result = 0;
/* Free the current interface */
printer_reset_interface(dev);
result = set_printer_interface(dev);
if (result)
printer_reset_interface(dev);
else
dev->interface = number;
if (!result)
INFO(dev, "Using interface %x\n", number);
return result;
}
static void printer_soft_reset(struct printer_dev *dev)
{
struct usb_request *req;
INFO(dev, "Received Printer Reset Request\n");
if (usb_ep_disable(dev->in_ep))
DBG(dev, "Failed to disable USB in_ep\n");
if (usb_ep_disable(dev->out_ep))
DBG(dev, "Failed to disable USB out_ep\n");
if (dev->current_rx_req != NULL) {
list_add(&dev->current_rx_req->list, &dev->rx_reqs);
dev->current_rx_req = NULL;
}
dev->current_rx_bytes = 0;
dev->current_rx_buf = NULL;
dev->reset_printer = 1;
while (likely(!(list_empty(&dev->rx_buffers)))) {
req = container_of(dev->rx_buffers.next, struct usb_request,
list);
list_del_init(&req->list);
list_add(&req->list, &dev->rx_reqs);
}
while (likely(!(list_empty(&dev->rx_reqs_active)))) {
req = container_of(dev->rx_buffers.next, struct usb_request,
list);
list_del_init(&req->list);
list_add(&req->list, &dev->rx_reqs);
}
while (likely(!(list_empty(&dev->tx_reqs_active)))) {
req = container_of(dev->tx_reqs_active.next,
struct usb_request, list);
list_del_init(&req->list);
list_add(&req->list, &dev->tx_reqs);
}
if (usb_ep_enable(dev->in_ep))
DBG(dev, "Failed to enable USB in_ep\n");
if (usb_ep_enable(dev->out_ep))
DBG(dev, "Failed to enable USB out_ep\n");
wake_up_interruptible(&dev->rx_wait);
wake_up_interruptible(&dev->tx_wait);
wake_up_interruptible(&dev->tx_flush_wait);
}
/*-------------------------------------------------------------------------*/
static bool gprinter_req_match(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct printer_dev *dev = func_to_printer(f);
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE ||
(ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
return false;
switch (ctrl->bRequest) {
case GET_DEVICE_ID:
w_index >>= 8;
if (w_length <= PNP_STRING_LEN &&
(USB_DIR_IN & ctrl->bRequestType))
break;
return false;
case GET_PORT_STATUS:
if (!w_value && w_length == 1 &&
(USB_DIR_IN & ctrl->bRequestType))
break;
return false;
case SOFT_RESET:
if (!w_value && !w_length &&
!(USB_DIR_IN & ctrl->bRequestType))
break;
/* fall through */
default:
return false;
}
return w_index == dev->interface;
}
/*
* The setup() callback implements all the ep0 functionality that's not
* handled lower down.
*/
static int printer_func_setup(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct printer_dev *dev = func_to_printer(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 wIndex = le16_to_cpu(ctrl->wIndex);
u16 wValue = le16_to_cpu(ctrl->wValue);
u16 wLength = le16_to_cpu(ctrl->wLength);
DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
switch (ctrl->bRequestType&USB_TYPE_MASK) {
case USB_TYPE_CLASS:
switch (ctrl->bRequest) {
case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */
/* Only one printer interface is supported. */
if ((wIndex>>8) != dev->interface)
break;
value = (dev->pnp_string[0] << 8) | dev->pnp_string[1];
memcpy(req->buf, dev->pnp_string, value);
DBG(dev, "1284 PNP String: %x %s\n", value,
&dev->pnp_string[2]);
break;
case GET_PORT_STATUS: /* Get Port Status */
/* Only one printer interface is supported. */
if (wIndex != dev->interface)
break;
*(u8 *)req->buf = dev->printer_status;
value = min_t(u16, wLength, 1);
break;
case SOFT_RESET: /* Soft Reset */
/* Only one printer interface is supported. */
if (wIndex != dev->interface)
break;
printer_soft_reset(dev);
value = 0;
break;
default:
goto unknown;
}
break;
default:
unknown:
VDBG(dev,
"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
wValue, wIndex, wLength);
break;
}
/* host either stalls (value < 0) or reports success */
if (value >= 0) {
req->length = value;
req->zero = value < wLength;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0) {
ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);
req->status = 0;
}
}
return value;
}
static int printer_func_bind(struct usb_configuration *c,
struct usb_function *f)
{
struct usb_gadget *gadget = c->cdev->gadget;
struct printer_dev *dev = func_to_printer(f);
struct device *pdev;
struct usb_composite_dev *cdev = c->cdev;
struct usb_ep *in_ep;
struct usb_ep *out_ep = NULL;
struct usb_request *req;
dev_t devt;
int id;
int ret;
u32 i;
id = usb_interface_id(c, f);
if (id < 0)
return id;
intf_desc.bInterfaceNumber = id;
/* finish hookup to lower layer ... */
dev->gadget = gadget;
/* all we really need is bulk IN/OUT */
in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
if (!in_ep) {
autoconf_fail:
dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
cdev->gadget->name);
return -ENODEV;
}
in_ep->driver_data = in_ep; /* claim */
out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
if (!out_ep)
goto autoconf_fail;
out_ep->driver_data = out_ep; /* claim */
/* assumes that all endpoints are dual-speed */
hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
ret = usb_assign_descriptors(f, fs_printer_function,
hs_printer_function, ss_printer_function);
if (ret)
return ret;
dev->in_ep = in_ep;
dev->out_ep = out_ep;
ret = -ENOMEM;
for (i = 0; i < dev->q_len; i++) {
req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
if (!req)
goto fail_tx_reqs;
list_add(&req->list, &dev->tx_reqs);
}
for (i = 0; i < dev->q_len; i++) {
req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
if (!req)
goto fail_rx_reqs;
list_add(&req->list, &dev->rx_reqs);
}
/* Setup the sysfs files for the printer gadget. */
devt = MKDEV(major, dev->minor);
pdev = device_create(usb_gadget_class, NULL, devt,
NULL, "g_printer%d", dev->minor);
if (IS_ERR(pdev)) {
ERROR(dev, "Failed to create device: g_printer\n");
ret = PTR_ERR(pdev);
goto fail_rx_reqs;
}
/*
* Register a character device as an interface to a user mode
* program that handles the printer specific functionality.
*/
cdev_init(&dev->printer_cdev, &printer_io_operations);
dev->printer_cdev.owner = THIS_MODULE;
ret = cdev_add(&dev->printer_cdev, devt, 1);
if (ret) {
ERROR(dev, "Failed to open char device\n");
goto fail_cdev_add;
}
return 0;
fail_cdev_add:
device_destroy(usb_gadget_class, devt);
fail_rx_reqs:
while (!list_empty(&dev->rx_reqs)) {
req = container_of(dev->rx_reqs.next, struct usb_request, list);
list_del(&req->list);
printer_req_free(dev->out_ep, req);
}
fail_tx_reqs:
while (!list_empty(&dev->tx_reqs)) {
req = container_of(dev->tx_reqs.next, struct usb_request, list);
list_del(&req->list);
printer_req_free(dev->in_ep, req);
}
return ret;
}
static int printer_func_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
struct printer_dev *dev = func_to_printer(f);
int ret = -ENOTSUPP;
if (!alt)
ret = set_interface(dev, intf);
return ret;
}
static void printer_func_disable(struct usb_function *f)
{
struct printer_dev *dev = func_to_printer(f);
unsigned long flags;
DBG(dev, "%s\n", __func__);
spin_lock_irqsave(&dev->lock, flags);
printer_reset_interface(dev);
spin_unlock_irqrestore(&dev->lock, flags);
}
static inline struct f_printer_opts
*to_f_printer_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_printer_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_printer_opts);
CONFIGFS_ATTR_OPS(f_printer_opts);
static void printer_attr_release(struct config_item *item)
{
struct f_printer_opts *opts = to_f_printer_opts(item);
usb_put_function_instance(&opts->func_inst);
}
static struct configfs_item_operations printer_item_ops = {
.release = printer_attr_release,
.show_attribute = f_printer_opts_attr_show,
.store_attribute = f_printer_opts_attr_store,
};
static ssize_t f_printer_opts_pnp_string_show(struct f_printer_opts *opts,
char *page)
{
int result;
mutex_lock(&opts->lock);
result = strlcpy(page, opts->pnp_string + 2, PNP_STRING_LEN - 2);
mutex_unlock(&opts->lock);
return result;
}
static ssize_t f_printer_opts_pnp_string_store(struct f_printer_opts *opts,
const char *page, size_t len)
{
int result, l;
mutex_lock(&opts->lock);
result = strlcpy(opts->pnp_string + 2, page, PNP_STRING_LEN - 2);
l = strlen(opts->pnp_string + 2) + 2;
opts->pnp_string[0] = (l >> 8) & 0xFF;
opts->pnp_string[1] = l & 0xFF;
mutex_unlock(&opts->lock);
return result;
}
static struct f_printer_opts_attribute f_printer_opts_pnp_string =
__CONFIGFS_ATTR(pnp_string, S_IRUGO | S_IWUSR,
f_printer_opts_pnp_string_show,
f_printer_opts_pnp_string_store);
static ssize_t f_printer_opts_q_len_show(struct f_printer_opts *opts,
char *page)
{
int result;
mutex_lock(&opts->lock);
result = sprintf(page, "%d\n", opts->q_len);
mutex_unlock(&opts->lock);
return result;
}
static ssize_t f_printer_opts_q_len_store(struct f_printer_opts *opts,
const char *page, size_t len)
{
int ret;
u16 num;
mutex_lock(&opts->lock);
if (opts->refcnt) {
ret = -EBUSY;
goto end;
}
ret = kstrtou16(page, 0, &num);
if (ret)
goto end;
opts->q_len = (unsigned)num;
ret = len;
end:
mutex_unlock(&opts->lock);
return ret;
}
static struct f_printer_opts_attribute f_printer_opts_q_len =
__CONFIGFS_ATTR(q_len, S_IRUGO | S_IWUSR, f_printer_opts_q_len_show,
f_printer_opts_q_len_store);
static struct configfs_attribute *printer_attrs[] = {
&f_printer_opts_pnp_string.attr,
&f_printer_opts_q_len.attr,
NULL,
};
static struct config_item_type printer_func_type = {
.ct_item_ops = &printer_item_ops,
.ct_attrs = printer_attrs,
.ct_owner = THIS_MODULE,
};
static inline int gprinter_get_minor(void)
{
return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
}
static inline void gprinter_put_minor(int minor)
{
ida_simple_remove(&printer_ida, minor);
}
static int gprinter_setup(int);
static void gprinter_cleanup(void);
static void gprinter_free_inst(struct usb_function_instance *f)
{
struct f_printer_opts *opts;
opts = container_of(f, struct f_printer_opts, func_inst);
mutex_lock(&printer_ida_lock);
gprinter_put_minor(opts->minor);
if (idr_is_empty(&printer_ida.idr))
gprinter_cleanup();
mutex_unlock(&printer_ida_lock);
kfree(opts);
}
static struct usb_function_instance *gprinter_alloc_inst(void)
{
struct f_printer_opts *opts;
struct usb_function_instance *ret;
int status = 0;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = gprinter_free_inst;
ret = &opts->func_inst;
mutex_lock(&printer_ida_lock);
if (idr_is_empty(&printer_ida.idr)) {
status = gprinter_setup(PRINTER_MINORS);
if (status) {
ret = ERR_PTR(status);
kfree(opts);
goto unlock;
}
}
opts->minor = gprinter_get_minor();
if (opts->minor < 0) {
ret = ERR_PTR(opts->minor);
kfree(opts);
if (idr_is_empty(&printer_ida.idr))
gprinter_cleanup();
goto unlock;
}
config_group_init_type_name(&opts->func_inst.group, "",
&printer_func_type);
unlock:
mutex_unlock(&printer_ida_lock);
return ret;
}
static void gprinter_free(struct usb_function *f)
{
struct printer_dev *dev = func_to_printer(f);
struct f_printer_opts *opts;
opts = container_of(f->fi, struct f_printer_opts, func_inst);
kfree(dev);
mutex_lock(&opts->lock);
--opts->refcnt;
mutex_unlock(&opts->lock);
}
static void printer_func_unbind(struct usb_configuration *c,
struct usb_function *f)
{
struct printer_dev *dev;
struct usb_request *req;
dev = func_to_printer(f);
device_destroy(usb_gadget_class, MKDEV(major, dev->minor));
/* Remove Character Device */
cdev_del(&dev->printer_cdev);
/* we must already have been disconnected ... no i/o may be active */
WARN_ON(!list_empty(&dev->tx_reqs_active));
WARN_ON(!list_empty(&dev->rx_reqs_active));
/* Free all memory for this driver. */
while (!list_empty(&dev->tx_reqs)) {
req = container_of(dev->tx_reqs.next, struct usb_request,
list);
list_del(&req->list);
printer_req_free(dev->in_ep, req);
}
if (dev->current_rx_req != NULL)
printer_req_free(dev->out_ep, dev->current_rx_req);
while (!list_empty(&dev->rx_reqs)) {
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_del(&req->list);
printer_req_free(dev->out_ep, req);
}
while (!list_empty(&dev->rx_buffers)) {
req = container_of(dev->rx_buffers.next,
struct usb_request, list);
list_del(&req->list);
printer_req_free(dev->out_ep, req);
}
usb_free_all_descriptors(f);
}
static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
{
struct printer_dev *dev;
struct f_printer_opts *opts;
opts = container_of(fi, struct f_printer_opts, func_inst);
mutex_lock(&opts->lock);
if (opts->minor >= minors) {
mutex_unlock(&opts->lock);
return ERR_PTR(-ENOENT);
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
mutex_unlock(&opts->lock);
return ERR_PTR(-ENOMEM);
}
++opts->refcnt;
dev->minor = opts->minor;
dev->pnp_string = opts->pnp_string;
dev->q_len = opts->q_len;
mutex_unlock(&opts->lock);
dev->function.name = "printer";
dev->function.bind = printer_func_bind;
dev->function.setup = printer_func_setup;
dev->function.unbind = printer_func_unbind;
dev->function.set_alt = printer_func_set_alt;
dev->function.disable = printer_func_disable;
dev->function.req_match = gprinter_req_match;
dev->function.free_func = gprinter_free;
INIT_LIST_HEAD(&dev->tx_reqs);
INIT_LIST_HEAD(&dev->rx_reqs);
INIT_LIST_HEAD(&dev->rx_buffers);
INIT_LIST_HEAD(&dev->tx_reqs_active);
INIT_LIST_HEAD(&dev->rx_reqs_active);
spin_lock_init(&dev->lock);
mutex_init(&dev->lock_printer_io);
init_waitqueue_head(&dev->rx_wait);
init_waitqueue_head(&dev->tx_wait);
init_waitqueue_head(&dev->tx_flush_wait);
dev->interface = -1;
dev->printer_cdev_open = 0;
dev->printer_status = PRINTER_NOT_ERROR;
dev->current_rx_req = NULL;
dev->current_rx_bytes = 0;
dev->current_rx_buf = NULL;
return &dev->function;
}
DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Craig Nadler");
static int gprinter_setup(int count)
{
int status;
dev_t devt;
usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
if (IS_ERR(usb_gadget_class)) {
status = PTR_ERR(usb_gadget_class);
usb_gadget_class = NULL;
pr_err("unable to create usb_gadget class %d\n", status);
return status;
}
status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget");
if (status) {
pr_err("alloc_chrdev_region %d\n", status);
class_destroy(usb_gadget_class);
usb_gadget_class = NULL;
return status;
}
major = MAJOR(devt);
minors = count;
return status;
}
static void gprinter_cleanup(void)
{
if (major) {
unregister_chrdev_region(MKDEV(major, 0), minors);
major = minors = 0;
}
class_destroy(usb_gadget_class);
usb_gadget_class = NULL;
}
/*
* u_printer.h
*
* Utility definitions for the printer function
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_PRINTER_H
#define U_PRINTER_H
#include <linux/usb/composite.h>
#define PNP_STRING_LEN 1024
struct f_printer_opts {
struct usb_function_instance func_inst;
int minor;
char pnp_string[PNP_STRING_LEN];
unsigned q_len;
/*
* Protect the data from concurrent access by read/write
* and create symlink/remove symlink
*/
struct mutex lock;
int refcnt;
};
#endif /* U_PRINTER_H */
......@@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
unsigned long flags;
int status;
pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n",
port->port_num, tty, ch, __builtin_return_address(0));
spin_lock_irqsave(&port->port_lock, flags);
......
......@@ -301,6 +301,7 @@ config USB_MIDI_GADGET
config USB_G_PRINTER
tristate "Printer Gadget"
select USB_LIBCOMPOSITE
select USB_F_PRINTER
help
The Printer Gadget channels data between the USB host and a
userspace program driving the print engine. The user space
......
......@@ -12,29 +12,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/cdev.h>
#include <asm/byteorder.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
......@@ -46,50 +24,12 @@
USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_DESC "Printer Gadget"
#define DRIVER_VERSION "2007 OCT 06"
#define DRIVER_VERSION "2015 FEB 17"
static DEFINE_MUTEX(printer_mutex);
static const char shortname [] = "printer";
static const char driver_desc [] = DRIVER_DESC;
static dev_t g_printer_devno;
static struct class *usb_gadget_class;
/*-------------------------------------------------------------------------*/
struct printer_dev {
spinlock_t lock; /* lock this structure */
/* lock buffer lists during read/write calls */
struct mutex lock_printer_io;
struct usb_gadget *gadget;
s8 interface;
struct usb_ep *in_ep, *out_ep;
struct list_head rx_reqs; /* List of free RX structs */
struct list_head rx_reqs_active; /* List of Active RX xfers */
struct list_head rx_buffers; /* List of completed xfers */
/* wait until there is data to be read. */
wait_queue_head_t rx_wait;
struct list_head tx_reqs; /* List of free TX structs */
struct list_head tx_reqs_active; /* List of Active TX xfers */
/* Wait until there are write buffers available to use. */
wait_queue_head_t tx_wait;
/* Wait until all write buffers have been sent. */
wait_queue_head_t tx_flush_wait;
struct usb_request *current_rx_req;
size_t current_rx_bytes;
u8 *current_rx_buf;
u8 printer_status;
u8 reset_printer;
struct cdev printer_cdev;
struct device *pdev;
u8 printer_cdev_open;
wait_queue_head_t wait;
struct usb_function function;
};
static struct printer_dev usb_printer_gadget;
#include "u_printer.h"
/*-------------------------------------------------------------------------*/
......@@ -120,6 +60,9 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
#define QLEN qlen
static struct usb_function_instance *fi_printer;
static struct usb_function *f_printer;
/*-------------------------------------------------------------------------*/
/*
......@@ -127,10 +70,6 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
* descriptors are built on demand.
*/
/* holds our biggest descriptor */
#define USB_DESC_BUFSIZE 256
#define USB_BUFSIZE 8192
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
......@@ -143,108 +82,6 @@ static struct usb_device_descriptor device_desc = {
.bNumConfigurations = 1
};
static struct usb_interface_descriptor intf_desc = {
.bLength = sizeof intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_PRINTER,
.bInterfaceSubClass = 1, /* Printer Sub-Class */
.bInterfaceProtocol = 2, /* Bi-Directional */
.iInterface = 0
};
static struct usb_endpoint_descriptor fs_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK
};
static struct usb_endpoint_descriptor fs_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK
};
static struct usb_descriptor_header *fs_printer_function[] = {
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &fs_ep_in_desc,
(struct usb_descriptor_header *) &fs_ep_out_desc,
NULL
};
/*
* usb 2.0 devices need to expose both high speed and full speed
* descriptors, unless they only run at full speed.
*/
static struct usb_endpoint_descriptor hs_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512)
};
static struct usb_endpoint_descriptor hs_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512)
};
static struct usb_qualifier_descriptor dev_qualifier = {
.bLength = sizeof dev_qualifier,
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PRINTER,
.bNumConfigurations = 1
};
static struct usb_descriptor_header *hs_printer_function[] = {
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &hs_ep_in_desc,
(struct usb_descriptor_header *) &hs_ep_out_desc,
NULL
};
/*
* Added endpoint descriptors for 3.0 devices
*/
static struct usb_endpoint_descriptor ss_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
.bLength = sizeof(ss_ep_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_endpoint_descriptor ss_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
.bLength = sizeof(ss_ep_out_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_descriptor_header *ss_printer_function[] = {
(struct usb_descriptor_header *) &intf_desc,
(struct usb_descriptor_header *) &ss_ep_in_desc,
(struct usb_descriptor_header *) &ss_ep_in_comp_desc,
(struct usb_descriptor_header *) &ss_ep_out_desc,
(struct usb_descriptor_header *) &ss_ep_out_comp_desc,
NULL
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
......@@ -256,29 +93,13 @@ static const struct usb_descriptor_header *otg_desc[] = {
NULL,
};
/* maxpacket and other transfer characteristics vary by speed. */
static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
struct usb_endpoint_descriptor *fs,
struct usb_endpoint_descriptor *hs,
struct usb_endpoint_descriptor *ss)
{
switch (gadget->speed) {
case USB_SPEED_SUPER:
return ss;
case USB_SPEED_HIGH:
return hs;
default:
return fs;
}
}
/*-------------------------------------------------------------------------*/
/* descriptors that are built on-demand */
static char product_desc [40] = DRIVER_DESC;
static char serial_num [40] = "1";
static char pnp_string [1024] =
static char pnp_string[PNP_STRING_LEN] =
"XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
/* static strings, in UTF-8 */
......@@ -299,921 +120,19 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
/*-------------------------------------------------------------------------*/
static struct usb_request *
printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
{
struct usb_request *req;
req = usb_ep_alloc_request(ep, gfp_flags);
if (req != NULL) {
req->length = len;
req->buf = kmalloc(len, gfp_flags);
if (req->buf == NULL) {
usb_ep_free_request(ep, req);
return NULL;
}
}
return req;
}
static void
printer_req_free(struct usb_ep *ep, struct usb_request *req)
{
if (ep != NULL && req != NULL) {
kfree(req->buf);
usb_ep_free_request(ep, req);
}
}
/*-------------------------------------------------------------------------*/
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct printer_dev *dev = ep->driver_data;
int status = req->status;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
list_del_init(&req->list); /* Remode from Active List */
switch (status) {
/* normal completion */
case 0:
if (req->actual > 0) {
list_add_tail(&req->list, &dev->rx_buffers);
DBG(dev, "G_Printer : rx length %d\n", req->actual);
} else {
list_add(&req->list, &dev->rx_reqs);
}
break;
/* software-driven interface shutdown */
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
VDBG(dev, "rx shutdown, code %d\n", status);
list_add(&req->list, &dev->rx_reqs);
break;
/* for hardware automagic (such as pxa) */
case -ECONNABORTED: /* endpoint reset */
DBG(dev, "rx %s reset\n", ep->name);
list_add(&req->list, &dev->rx_reqs);
break;
/* data overrun */
case -EOVERFLOW:
/* FALLTHROUGH */
default:
DBG(dev, "rx status %d\n", status);
list_add(&req->list, &dev->rx_reqs);
break;
}
wake_up_interruptible(&dev->rx_wait);
spin_unlock_irqrestore(&dev->lock, flags);
}
static void tx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct printer_dev *dev = ep->driver_data;
switch (req->status) {
default:
VDBG(dev, "tx err %d\n", req->status);
/* FALLTHROUGH */
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
break;
case 0:
break;
}
spin_lock(&dev->lock);
/* Take the request struct off the active list and put it on the
* free list.
*/
list_del_init(&req->list);
list_add(&req->list, &dev->tx_reqs);
wake_up_interruptible(&dev->tx_wait);
if (likely(list_empty(&dev->tx_reqs_active)))
wake_up_interruptible(&dev->tx_flush_wait);
spin_unlock(&dev->lock);
}
/*-------------------------------------------------------------------------*/
static int
printer_open(struct inode *inode, struct file *fd)
{
struct printer_dev *dev;
unsigned long flags;
int ret = -EBUSY;
mutex_lock(&printer_mutex);
dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
spin_lock_irqsave(&dev->lock, flags);
if (!dev->printer_cdev_open) {
dev->printer_cdev_open = 1;
fd->private_data = dev;
ret = 0;
/* Change the printer status to show that it's on-line. */
dev->printer_status |= PRINTER_SELECTED;
}
spin_unlock_irqrestore(&dev->lock, flags);
DBG(dev, "printer_open returned %x\n", ret);
mutex_unlock(&printer_mutex);
return ret;
}
static int
printer_close(struct inode *inode, struct file *fd)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
dev->printer_cdev_open = 0;
fd->private_data = NULL;
/* Change printer status to show that the printer is off-line. */
dev->printer_status &= ~PRINTER_SELECTED;
spin_unlock_irqrestore(&dev->lock, flags);
DBG(dev, "printer_close\n");
return 0;
}
/* This function must be called with interrupts turned off. */
static void
setup_rx_reqs(struct printer_dev *dev)
{
struct usb_request *req;
while (likely(!list_empty(&dev->rx_reqs))) {
int error;
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_del_init(&req->list);
/* The USB Host sends us whatever amount of data it wants to
* so we always set the length field to the full USB_BUFSIZE.
* If the amount of data is more than the read() caller asked
* for it will be stored in the request buffer until it is
* asked for by read().
*/
req->length = USB_BUFSIZE;
req->complete = rx_complete;
/* here, we unlock, and only unlock, to avoid deadlock. */
spin_unlock(&dev->lock);
error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
spin_lock(&dev->lock);
if (error) {
DBG(dev, "rx submit --> %d\n", error);
list_add(&req->list, &dev->rx_reqs);
break;
}
/* if the req is empty, then add it into dev->rx_reqs_active. */
else if (list_empty(&req->list)) {
list_add(&req->list, &dev->rx_reqs_active);
}
}
}
static ssize_t
printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
size_t size;
size_t bytes_copied;
struct usb_request *req;
/* This is a pointer to the current USB rx request. */
struct usb_request *current_rx_req;
/* This is the number of bytes in the current rx buffer. */
size_t current_rx_bytes;
/* This is a pointer to the current rx buffer. */
u8 *current_rx_buf;
if (len == 0)
return -EINVAL;
DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
/* We will use this flag later to check if a printer reset happened
* after we turn interrupts back on.
*/
dev->reset_printer = 0;
setup_rx_reqs(dev);
bytes_copied = 0;
current_rx_req = dev->current_rx_req;
current_rx_bytes = dev->current_rx_bytes;
current_rx_buf = dev->current_rx_buf;
dev->current_rx_req = NULL;
dev->current_rx_bytes = 0;
dev->current_rx_buf = NULL;
/* Check if there is any data in the read buffers. Please note that
* current_rx_bytes is the number of bytes in the current rx buffer.
* If it is zero then check if there are any other rx_buffers that
* are on the completed list. We are only out of data if all rx
* buffers are empty.
*/
if ((current_rx_bytes == 0) &&
(likely(list_empty(&dev->rx_buffers)))) {
/* Turn interrupts back on before sleeping. */
spin_unlock_irqrestore(&dev->lock, flags);
/*
* If no data is available check if this is a NON-Blocking
* call or not.
*/
if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
/* Sleep until data is available */
wait_event_interruptible(dev->rx_wait,
(likely(!list_empty(&dev->rx_buffers))));
spin_lock_irqsave(&dev->lock, flags);
}
/* We have data to return then copy it to the caller's buffer.*/
while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
&& len) {
if (current_rx_bytes == 0) {
req = container_of(dev->rx_buffers.next,
struct usb_request, list);
list_del_init(&req->list);
if (req->actual && req->buf) {
current_rx_req = req;
current_rx_bytes = req->actual;
current_rx_buf = req->buf;
} else {
list_add(&req->list, &dev->rx_reqs);
continue;
}
}
/* Don't leave irqs off while doing memory copies */
spin_unlock_irqrestore(&dev->lock, flags);
if (len > current_rx_bytes)
size = current_rx_bytes;
else
size = len;
size -= copy_to_user(buf, current_rx_buf, size);
bytes_copied += size;
len -= size;
buf += size;
spin_lock_irqsave(&dev->lock, flags);
/* We've disconnected or reset so return. */
if (dev->reset_printer) {
list_add(&current_rx_req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
/* If we not returning all the data left in this RX request
* buffer then adjust the amount of data left in the buffer.
* Othewise if we are done with this RX request buffer then
* requeue it to get any incoming data from the USB host.
*/
if (size < current_rx_bytes) {
current_rx_bytes -= size;
current_rx_buf += size;
} else {
list_add(&current_rx_req->list, &dev->rx_reqs);
current_rx_bytes = 0;
current_rx_buf = NULL;
current_rx_req = NULL;
}
}
dev->current_rx_req = current_rx_req;
dev->current_rx_bytes = current_rx_bytes;
dev->current_rx_buf = current_rx_buf;
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
if (bytes_copied)
return bytes_copied;
else
return -EAGAIN;
}
static ssize_t
printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
size_t size; /* Amount of data in a TX request. */
size_t bytes_copied = 0;
struct usb_request *req;
DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
if (len == 0)
return -EINVAL;
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
/* Check if a printer reset happens while we have interrupts on */
dev->reset_printer = 0;
/* Check if there is any available write buffers */
if (likely(list_empty(&dev->tx_reqs))) {
/* Turn interrupts back on before sleeping. */
spin_unlock_irqrestore(&dev->lock, flags);
/*
* If write buffers are available check if this is
* a NON-Blocking call or not.
*/
if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
/* Sleep until a write buffer is available */
wait_event_interruptible(dev->tx_wait,
(likely(!list_empty(&dev->tx_reqs))));
spin_lock_irqsave(&dev->lock, flags);
}
while (likely(!list_empty(&dev->tx_reqs)) && len) {
if (len > USB_BUFSIZE)
size = USB_BUFSIZE;
else
size = len;
req = container_of(dev->tx_reqs.next, struct usb_request,
list);
list_del_init(&req->list);
req->complete = tx_complete;
req->length = size;
/* Check if we need to send a zero length packet. */
if (len > size)
/* They will be more TX requests so no yet. */
req->zero = 0;
else
/* If the data amount is not a multple of the
* maxpacket size then send a zero length packet.
*/
req->zero = ((len % dev->in_ep->maxpacket) == 0);
/* Don't leave irqs off while doing memory copies */
spin_unlock_irqrestore(&dev->lock, flags);
if (copy_from_user(req->buf, buf, size)) {
list_add(&req->list, &dev->tx_reqs);
mutex_unlock(&dev->lock_printer_io);
return bytes_copied;
}
bytes_copied += size;
len -= size;
buf += size;
spin_lock_irqsave(&dev->lock, flags);
/* We've disconnected or reset so free the req and buffer */
if (dev->reset_printer) {
list_add(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
list_add(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -EAGAIN;
}
list_add(&req->list, &dev->tx_reqs_active);
}
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
if (bytes_copied) {
return bytes_copied;
} else {
return -EAGAIN;
}
}
static int
printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
{
struct printer_dev *dev = fd->private_data;
struct inode *inode = file_inode(fd);
unsigned long flags;
int tx_list_empty;
mutex_lock(&inode->i_mutex);
spin_lock_irqsave(&dev->lock, flags);
tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
spin_unlock_irqrestore(&dev->lock, flags);
if (!tx_list_empty) {
/* Sleep until all data has been sent */
wait_event_interruptible(dev->tx_flush_wait,
(likely(list_empty(&dev->tx_reqs_active))));
}
mutex_unlock(&inode->i_mutex);
return 0;
}
static unsigned int
printer_poll(struct file *fd, poll_table *wait)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
int status = 0;
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
setup_rx_reqs(dev);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
poll_wait(fd, &dev->rx_wait, wait);
poll_wait(fd, &dev->tx_wait, wait);
spin_lock_irqsave(&dev->lock, flags);
if (likely(!list_empty(&dev->tx_reqs)))
status |= POLLOUT | POLLWRNORM;
if (likely(dev->current_rx_bytes) ||
likely(!list_empty(&dev->rx_buffers)))
status |= POLLIN | POLLRDNORM;
spin_unlock_irqrestore(&dev->lock, flags);
return status;
}
static long
printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
int status = 0;
DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
/* handle ioctls */
spin_lock_irqsave(&dev->lock, flags);
switch (code) {
case GADGET_GET_PRINTER_STATUS:
status = (int)dev->printer_status;
break;
case GADGET_SET_PRINTER_STATUS:
dev->printer_status = (u8)arg;
break;
default:
/* could not handle ioctl */
DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
code);
status = -ENOTTY;
}
spin_unlock_irqrestore(&dev->lock, flags);
return status;
}
/* used after endpoint configuration */
static const struct file_operations printer_io_operations = {
.owner = THIS_MODULE,
.open = printer_open,
.read = printer_read,
.write = printer_write,
.fsync = printer_fsync,
.poll = printer_poll,
.unlocked_ioctl = printer_ioctl,
.release = printer_close,
.llseek = noop_llseek,
};
/*-------------------------------------------------------------------------*/
static int
set_printer_interface(struct printer_dev *dev)
{
int result = 0;
dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
&ss_ep_in_desc);
dev->in_ep->driver_data = dev;
dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
&hs_ep_out_desc, &ss_ep_out_desc);
dev->out_ep->driver_data = dev;
result = usb_ep_enable(dev->in_ep);
if (result != 0) {
DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
goto done;
}
result = usb_ep_enable(dev->out_ep);
if (result != 0) {
DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
goto done;
}
done:
/* on error, disable any endpoints */
if (result != 0) {
(void) usb_ep_disable(dev->in_ep);
(void) usb_ep_disable(dev->out_ep);
dev->in_ep->desc = NULL;
dev->out_ep->desc = NULL;
}
/* caller is responsible for cleanup on error */
return result;
}
static void printer_reset_interface(struct printer_dev *dev)
{
if (dev->interface < 0)
return;
DBG(dev, "%s\n", __func__);
if (dev->in_ep->desc)
usb_ep_disable(dev->in_ep);
if (dev->out_ep->desc)
usb_ep_disable(dev->out_ep);
dev->in_ep->desc = NULL;
dev->out_ep->desc = NULL;
dev->interface = -1;
}
/* Change our operational Interface. */
static int set_interface(struct printer_dev *dev, unsigned number)
{
int result = 0;
/* Free the current interface */
printer_reset_interface(dev);
result = set_printer_interface(dev);
if (result)
printer_reset_interface(dev);
else
dev->interface = number;
if (!result)
INFO(dev, "Using interface %x\n", number);
return result;
}
static void printer_soft_reset(struct printer_dev *dev)
{
struct usb_request *req;
INFO(dev, "Received Printer Reset Request\n");
if (usb_ep_disable(dev->in_ep))
DBG(dev, "Failed to disable USB in_ep\n");
if (usb_ep_disable(dev->out_ep))
DBG(dev, "Failed to disable USB out_ep\n");
if (dev->current_rx_req != NULL) {
list_add(&dev->current_rx_req->list, &dev->rx_reqs);
dev->current_rx_req = NULL;
}
dev->current_rx_bytes = 0;
dev->current_rx_buf = NULL;
dev->reset_printer = 1;
while (likely(!(list_empty(&dev->rx_buffers)))) {
req = container_of(dev->rx_buffers.next, struct usb_request,
list);
list_del_init(&req->list);
list_add(&req->list, &dev->rx_reqs);
}
while (likely(!(list_empty(&dev->rx_reqs_active)))) {
req = container_of(dev->rx_buffers.next, struct usb_request,
list);
list_del_init(&req->list);
list_add(&req->list, &dev->rx_reqs);
}
while (likely(!(list_empty(&dev->tx_reqs_active)))) {
req = container_of(dev->tx_reqs_active.next,
struct usb_request, list);
list_del_init(&req->list);
list_add(&req->list, &dev->tx_reqs);
}
if (usb_ep_enable(dev->in_ep))
DBG(dev, "Failed to enable USB in_ep\n");
if (usb_ep_enable(dev->out_ep))
DBG(dev, "Failed to enable USB out_ep\n");
wake_up_interruptible(&dev->rx_wait);
wake_up_interruptible(&dev->tx_wait);
wake_up_interruptible(&dev->tx_flush_wait);
}
/*-------------------------------------------------------------------------*/
/*
* The setup() callback implements all the ep0 functionality that's not
* handled lower down.
*/
static int printer_func_setup(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct printer_dev *dev = container_of(f, struct printer_dev, function);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 wIndex = le16_to_cpu(ctrl->wIndex);
u16 wValue = le16_to_cpu(ctrl->wValue);
u16 wLength = le16_to_cpu(ctrl->wLength);
DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
switch (ctrl->bRequestType&USB_TYPE_MASK) {
case USB_TYPE_CLASS:
switch (ctrl->bRequest) {
case 0: /* Get the IEEE-1284 PNP String */
/* Only one printer interface is supported. */
if ((wIndex>>8) != dev->interface)
break;
value = (pnp_string[0]<<8)|pnp_string[1];
memcpy(req->buf, pnp_string, value);
DBG(dev, "1284 PNP String: %x %s\n", value,
&pnp_string[2]);
break;
case 1: /* Get Port Status */
/* Only one printer interface is supported. */
if (wIndex != dev->interface)
break;
*(u8 *)req->buf = dev->printer_status;
value = min(wLength, (u16) 1);
break;
case 2: /* Soft Reset */
/* Only one printer interface is supported. */
if (wIndex != dev->interface)
break;
printer_soft_reset(dev);
value = 0;
break;
default:
goto unknown;
}
break;
default:
unknown:
VDBG(dev,
"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
wValue, wIndex, wLength);
break;
}
/* host either stalls (value < 0) or reports success */
return value;
}
static int __init printer_func_bind(struct usb_configuration *c,
struct usb_function *f)
{
struct printer_dev *dev = container_of(f, struct printer_dev, function);
struct usb_composite_dev *cdev = c->cdev;
struct usb_ep *in_ep;
struct usb_ep *out_ep = NULL;
int id;
int ret;
id = usb_interface_id(c, f);
if (id < 0)
return id;
intf_desc.bInterfaceNumber = id;
/* all we really need is bulk IN/OUT */
in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
if (!in_ep) {
autoconf_fail:
dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
cdev->gadget->name);
return -ENODEV;
}
in_ep->driver_data = in_ep; /* claim */
out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
if (!out_ep)
goto autoconf_fail;
out_ep->driver_data = out_ep; /* claim */
/* assumes that all endpoints are dual-speed */
hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
ret = usb_assign_descriptors(f, fs_printer_function,
hs_printer_function, ss_printer_function);
if (ret)
return ret;
dev->in_ep = in_ep;
dev->out_ep = out_ep;
return 0;
}
static void printer_func_unbind(struct usb_configuration *c,
struct usb_function *f)
{
usb_free_all_descriptors(f);
}
static int printer_func_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
struct printer_dev *dev = container_of(f, struct printer_dev, function);
int ret = -ENOTSUPP;
if (!alt)
ret = set_interface(dev, intf);
return ret;
}
static void printer_func_disable(struct usb_function *f)
{
struct printer_dev *dev = container_of(f, struct printer_dev, function);
unsigned long flags;
DBG(dev, "%s\n", __func__);
spin_lock_irqsave(&dev->lock, flags);
printer_reset_interface(dev);
spin_unlock_irqrestore(&dev->lock, flags);
}
static void printer_cfg_unbind(struct usb_configuration *c)
{
struct printer_dev *dev;
struct usb_request *req;
dev = &usb_printer_gadget;
DBG(dev, "%s\n", __func__);
/* Remove sysfs files */
device_destroy(usb_gadget_class, g_printer_devno);
/* Remove Character Device */
cdev_del(&dev->printer_cdev);
/* we must already have been disconnected ... no i/o may be active */
WARN_ON(!list_empty(&dev->tx_reqs_active));
WARN_ON(!list_empty(&dev->rx_reqs_active));
/* Free all memory for this driver. */
while (!list_empty(&dev->tx_reqs)) {
req = container_of(dev->tx_reqs.next, struct usb_request,
list);
list_del(&req->list);
printer_req_free(dev->in_ep, req);
}
if (dev->current_rx_req != NULL)
printer_req_free(dev->out_ep, dev->current_rx_req);
while (!list_empty(&dev->rx_reqs)) {
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_del(&req->list);
printer_req_free(dev->out_ep, req);
}
while (!list_empty(&dev->rx_buffers)) {
req = container_of(dev->rx_buffers.next,
struct usb_request, list);
list_del(&req->list);
printer_req_free(dev->out_ep, req);
}
}
static struct usb_configuration printer_cfg_driver = {
.label = "printer",
.unbind = printer_cfg_unbind,
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
};
static int __init printer_bind_config(struct usb_configuration *c)
static int __init printer_do_config(struct usb_configuration *c)
{
struct usb_gadget *gadget = c->cdev->gadget;
struct printer_dev *dev;
int status = -ENOMEM;
size_t len;
u32 i;
struct usb_request *req;
int status = 0;
usb_ep_autoconfig_reset(gadget);
dev = &usb_printer_gadget;
dev->function.name = shortname;
dev->function.bind = printer_func_bind;
dev->function.setup = printer_func_setup;
dev->function.unbind = printer_func_unbind;
dev->function.set_alt = printer_func_set_alt;
dev->function.disable = printer_func_disable;
status = usb_add_function(c, &dev->function);
if (status)
return status;
/* Setup the sysfs files for the printer gadget. */
dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
NULL, "g_printer");
if (IS_ERR(dev->pdev)) {
ERROR(dev, "Failed to create device: g_printer\n");
status = PTR_ERR(dev->pdev);
goto fail;
}
/*
* Register a character device as an interface to a user mode
* program that handles the printer specific functionality.
*/
cdev_init(&dev->printer_cdev, &printer_io_operations);
dev->printer_cdev.owner = THIS_MODULE;
status = cdev_add(&dev->printer_cdev, g_printer_devno, 1);
if (status) {
ERROR(dev, "Failed to open char device\n");
goto fail;
}
if (iPNPstring)
strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2);
len = strlen(pnp_string);
pnp_string[0] = (len >> 8) & 0xFF;
pnp_string[1] = len & 0xFF;
usb_gadget_set_selfpowered(gadget);
if (gadget_is_otg(gadget)) {
......@@ -1222,86 +141,64 @@ static int __init printer_bind_config(struct usb_configuration *c)
printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
spin_lock_init(&dev->lock);
mutex_init(&dev->lock_printer_io);
INIT_LIST_HEAD(&dev->tx_reqs);
INIT_LIST_HEAD(&dev->tx_reqs_active);
INIT_LIST_HEAD(&dev->rx_reqs);
INIT_LIST_HEAD(&dev->rx_reqs_active);
INIT_LIST_HEAD(&dev->rx_buffers);
init_waitqueue_head(&dev->rx_wait);
init_waitqueue_head(&dev->tx_wait);
init_waitqueue_head(&dev->tx_flush_wait);
dev->interface = -1;
dev->printer_cdev_open = 0;
dev->printer_status = PRINTER_NOT_ERROR;
dev->current_rx_req = NULL;
dev->current_rx_bytes = 0;
dev->current_rx_buf = NULL;
for (i = 0; i < QLEN; i++) {
req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
if (!req) {
while (!list_empty(&dev->tx_reqs)) {
req = container_of(dev->tx_reqs.next,
struct usb_request, list);
list_del(&req->list);
printer_req_free(dev->in_ep, req);
}
return -ENOMEM;
}
list_add(&req->list, &dev->tx_reqs);
}
for (i = 0; i < QLEN; i++) {
req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
if (!req) {
while (!list_empty(&dev->rx_reqs)) {
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_del(&req->list);
printer_req_free(dev->out_ep, req);
}
return -ENOMEM;
}
list_add(&req->list, &dev->rx_reqs);
}
/* finish hookup to lower layer ... */
dev->gadget = gadget;
f_printer = usb_get_function(fi_printer);
if (IS_ERR(f_printer))
return PTR_ERR(f_printer);
INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
return 0;
status = usb_add_function(c, f_printer);
if (status < 0)
usb_put_function(f_printer);
fail:
printer_cfg_unbind(c);
return status;
}
static int printer_unbind(struct usb_composite_dev *cdev)
{
return 0;
}
static int __init printer_bind(struct usb_composite_dev *cdev)
{
int ret;
struct f_printer_opts *opts;
int ret, len;
fi_printer = usb_get_function_instance("printer");
if (IS_ERR(fi_printer))
return PTR_ERR(fi_printer);
if (iPNPstring)
strlcpy(&pnp_string[2], iPNPstring, PNP_STRING_LEN - 2);
len = strlen(pnp_string);
pnp_string[0] = (len >> 8) & 0xFF;
pnp_string[1] = len & 0xFF;
opts = container_of(fi_printer, struct f_printer_opts, func_inst);
opts->minor = 0;
memcpy(opts->pnp_string, pnp_string, PNP_STRING_LEN);
opts->q_len = QLEN;
ret = usb_string_ids_tab(cdev, strings);
if (ret < 0)
if (ret < 0) {
usb_put_function_instance(fi_printer);
return ret;
}
device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
if (ret)
ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
if (ret) {
usb_put_function_instance(fi_printer);
return ret;
}
usb_composite_overwrite_options(cdev, &coverwrite);
return ret;
}
static int __exit printer_unbind(struct usb_composite_dev *cdev)
{
usb_put_function(f_printer);
usb_put_function_instance(fi_printer);
return 0;
}
static __refdata struct usb_composite_driver printer_driver = {
.name = shortname,
.dev = &device_desc,
......@@ -1311,47 +208,7 @@ static __refdata struct usb_composite_driver printer_driver = {
.unbind = printer_unbind,
};
static int __init
init(void)
{
int status;
usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
if (IS_ERR(usb_gadget_class)) {
status = PTR_ERR(usb_gadget_class);
pr_err("unable to create usb_gadget class %d\n", status);
return status;
}
status = alloc_chrdev_region(&g_printer_devno, 0, 1,
"USB printer gadget");
if (status) {
pr_err("alloc_chrdev_region %d\n", status);
class_destroy(usb_gadget_class);
return status;
}
status = usb_composite_probe(&printer_driver);
if (status) {
class_destroy(usb_gadget_class);
unregister_chrdev_region(g_printer_devno, 1);
pr_err("usb_gadget_probe_driver %x\n", status);
}
return status;
}
module_init(init);
static void __exit
cleanup(void)
{
mutex_lock(&usb_printer_gadget.lock_printer_io);
usb_composite_unregister(&printer_driver);
unregister_chrdev_region(g_printer_devno, 1);
class_destroy(usb_gadget_class);
mutex_unlock(&usb_printer_gadget.lock_printer_io);
}
module_exit(cleanup);
module_usb_composite_driver(printer_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Craig Nadler");
......
......@@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file)
spin_lock_irq(&udc->lock);
for (i = 0; i < inode->i_size / 4; i++)
data[i] = __raw_readl(udc->regs + i * 4);
data[i] = usba_io_readl(udc->regs + i * 4);
spin_unlock_irq(&udc->lock);
file->private_data = data;
......@@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
if (crq->wLength != cpu_to_le16(sizeof(status)))
goto stall;
ep->state = DATA_STAGE_IN;
__raw_writew(status, ep->fifo);
usba_io_writew(status, ep->fifo);
usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
break;
}
......@@ -1739,43 +1739,95 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
return IRQ_HANDLED;
}
static irqreturn_t usba_vbus_irq(int irq, void *devid)
static int start_clock(struct usba_udc *udc)
{
struct usba_udc *udc = devid;
int vbus;
int ret;
/* debounce */
udelay(10);
if (udc->clocked)
return 0;
spin_lock(&udc->lock);
ret = clk_prepare_enable(udc->pclk);
if (ret)
return ret;
ret = clk_prepare_enable(udc->hclk);
if (ret) {
clk_disable_unprepare(udc->pclk);
return ret;
}
/* May happen if Vbus pin toggles during probe() */
if (!udc->driver)
goto out;
udc->clocked = true;
return 0;
}
vbus = vbus_is_present(udc);
if (vbus != udc->vbus_prev) {
if (vbus) {
static void stop_clock(struct usba_udc *udc)
{
if (!udc->clocked)
return;
clk_disable_unprepare(udc->hclk);
clk_disable_unprepare(udc->pclk);
udc->clocked = false;
}
static int usba_start(struct usba_udc *udc)
{
unsigned long flags;
int ret;
ret = start_clock(udc);
if (ret)
return ret;
spin_lock_irqsave(&udc->lock, flags);
toggle_bias(udc, 1);
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
usba_int_enb_set(udc, USBA_END_OF_RESET);
} else {
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static void usba_stop(struct usba_udc *udc)
{
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
udc->gadget.speed = USB_SPEED_UNKNOWN;
reset_all_endpoints(udc);
/* This will also disable the DP pullup */
toggle_bias(udc, 0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
if (udc->driver->disconnect) {
spin_unlock(&udc->lock);
spin_unlock_irqrestore(&udc->lock, flags);
stop_clock(udc);
}
static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
{
struct usba_udc *udc = devid;
int vbus;
/* debounce */
udelay(10);
mutex_lock(&udc->vbus_mutex);
vbus = vbus_is_present(udc);
if (vbus != udc->vbus_prev) {
if (vbus) {
usba_start(udc);
} else {
usba_stop(udc);
if (udc->driver->disconnect)
udc->driver->disconnect(&udc->gadget);
spin_lock(&udc->lock);
}
}
udc->vbus_prev = vbus;
}
out:
spin_unlock(&udc->lock);
mutex_unlock(&udc->vbus_mutex);
return IRQ_HANDLED;
}
......@@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget,
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
udc->driver = driver;
spin_unlock_irqrestore(&udc->lock, flags);
ret = clk_prepare_enable(udc->pclk);
if (ret)
return ret;
ret = clk_prepare_enable(udc->hclk);
if (ret) {
clk_disable_unprepare(udc->pclk);
return ret;
}
mutex_lock(&udc->vbus_mutex);
udc->vbus_prev = 0;
if (gpio_is_valid(udc->vbus_pin))
enable_irq(gpio_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */
spin_lock_irqsave(&udc->lock, flags);
if (vbus_is_present(udc) && udc->vbus_prev == 0) {
toggle_bias(udc, 1);
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
usba_int_enb_set(udc, USBA_END_OF_RESET);
udc->vbus_prev = vbus_is_present(udc);
if (udc->vbus_prev) {
ret = usba_start(udc);
if (ret)
goto err;
}
spin_unlock_irqrestore(&udc->lock, flags);
mutex_unlock(&udc->vbus_mutex);
return 0;
err:
if (gpio_is_valid(udc->vbus_pin))
disable_irq(gpio_to_irq(udc->vbus_pin));
mutex_unlock(&udc->vbus_mutex);
spin_lock_irqsave(&udc->lock, flags);
udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
udc->driver = NULL;
spin_unlock_irqrestore(&udc->lock, flags);
return ret;
}
static int atmel_usba_stop(struct usb_gadget *gadget)
{
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
unsigned long flags;
if (gpio_is_valid(udc->vbus_pin))
disable_irq(gpio_to_irq(udc->vbus_pin));
spin_lock_irqsave(&udc->lock, flags);
udc->gadget.speed = USB_SPEED_UNKNOWN;
reset_all_endpoints(udc);
spin_unlock_irqrestore(&udc->lock, flags);
/* This will also disable the DP pullup */
toggle_bias(udc, 0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
clk_disable_unprepare(udc->hclk);
clk_disable_unprepare(udc->pclk);
usba_stop(udc);
udc->driver = NULL;
......@@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev)
return PTR_ERR(hclk);
spin_lock_init(&udc->lock);
mutex_init(&udc->vbus_mutex);
udc->pdev = pdev;
udc->pclk = pclk;
udc->hclk = hclk;
......@@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev)
if (gpio_is_valid(udc->vbus_pin)) {
if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
ret = devm_request_irq(&pdev->dev,
gpio_to_irq(udc->vbus_pin),
usba_vbus_irq, 0,
irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
IRQ_NOAUTOEN);
ret = devm_request_threaded_irq(&pdev->dev,
gpio_to_irq(udc->vbus_pin), NULL,
usba_vbus_irq_thread, IRQF_ONESHOT,
"atmel_usba_udc", udc);
if (ret) {
udc->vbus_pin = -ENODEV;
dev_warn(&udc->pdev->dev,
"failed to request vbus irq; "
"assuming always on\n");
} else {
disable_irq(gpio_to_irq(udc->vbus_pin));
}
} else {
/* gpio_request fail so use -EINVAL for gpio_is_valid */
......@@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev)
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (ret)
return ret;
device_init_wakeup(&pdev->dev, 1);
usba_init_debugfs(udc);
for (i = 1; i < udc->num_ep; i++)
......@@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
udc = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
usb_del_gadget_udc(&udc->gadget);
for (i = 1; i < udc->num_ep; i++)
......@@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int usba_udc_suspend(struct device *dev)
{
struct usba_udc *udc = dev_get_drvdata(dev);
/* Not started */
if (!udc->driver)
return 0;
mutex_lock(&udc->vbus_mutex);
if (!device_may_wakeup(dev)) {
usba_stop(udc);
goto out;
}
/*
* Device may wake up. We stay clocked if we failed
* to request vbus irq, assuming always on.
*/
if (gpio_is_valid(udc->vbus_pin)) {
usba_stop(udc);
enable_irq_wake(gpio_to_irq(udc->vbus_pin));
}
out:
mutex_unlock(&udc->vbus_mutex);
return 0;
}
static int usba_udc_resume(struct device *dev)
{
struct usba_udc *udc = dev_get_drvdata(dev);
/* Not started */
if (!udc->driver)
return 0;
if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin))
disable_irq_wake(gpio_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */
mutex_lock(&udc->vbus_mutex);
udc->vbus_prev = vbus_is_present(udc);
if (udc->vbus_prev)
usba_start(udc);
mutex_unlock(&udc->vbus_mutex);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume);
static struct platform_driver udc_driver = {
.remove = __exit_p(usba_udc_remove),
.driver = {
.name = "atmel_usba_udc",
.pm = &usba_udc_pm_ops,
.of_match_table = of_match_ptr(atmel_udc_dt_ids),
},
};
......
......@@ -191,18 +191,28 @@
| USBA_BF(name, value))
/* Register access macros */
#ifdef CONFIG_AVR32
#define usba_io_readl __raw_readl
#define usba_io_writel __raw_writel
#define usba_io_writew __raw_writew
#else
#define usba_io_readl readl_relaxed
#define usba_io_writel writel_relaxed
#define usba_io_writew writew_relaxed
#endif
#define usba_readl(udc, reg) \
__raw_readl((udc)->regs + USBA_##reg)
usba_io_readl((udc)->regs + USBA_##reg)
#define usba_writel(udc, reg, value) \
__raw_writel((value), (udc)->regs + USBA_##reg)
usba_io_writel((value), (udc)->regs + USBA_##reg)
#define usba_ep_readl(ep, reg) \
__raw_readl((ep)->ep_regs + USBA_EPT_##reg)
usba_io_readl((ep)->ep_regs + USBA_EPT_##reg)
#define usba_ep_writel(ep, reg, value) \
__raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
#define usba_dma_readl(ep, reg) \
__raw_readl((ep)->dma_regs + USBA_DMA_##reg)
usba_io_readl((ep)->dma_regs + USBA_DMA_##reg)
#define usba_dma_writel(ep, reg, value) \
__raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
/* Calculate base address for a given endpoint or DMA controller */
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
......@@ -313,6 +323,9 @@ struct usba_udc {
/* Protect hw registers from concurrent modifications */
spinlock_t lock;
/* Mutex to prevent concurrent start or stop */
struct mutex vbus_mutex;
void __iomem *regs;
void __iomem *fifo;
......@@ -328,6 +341,7 @@ struct usba_udc {
struct clk *hclk;
struct usba_ep *usba_ep;
bool bias_pulse_needed;
bool clocked;
u16 devstatus;
......
......@@ -2631,7 +2631,7 @@ static int __init init(void)
return -EINVAL;
if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) {
pr_err("Number of emulated UDC must be in range of 1%d\n",
pr_err("Number of emulated UDC must be in range of 1...%d\n",
MAX_NUM_UDC);
return -EINVAL;
}
......
......@@ -1024,8 +1024,7 @@ static const char proc_node_name [] = "driver/udc";
static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
{
/* int_status is the same format ... */
seq_printf(m,
"%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
label, mask,
(mask & INT_PWRDETECT) ? " power" : "",
(mask & INT_SYSERROR) ? " sys" : "",
......@@ -1053,6 +1052,51 @@ static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
(mask & INT_SUSPEND) ? " suspend" : "");
}
static const char *udc_ep_state(enum ep0state state)
{
switch (state) {
case EP0_DISCONNECT:
return "ep0_disconnect";
case EP0_IDLE:
return "ep0_idle";
case EP0_IN:
return "ep0_in";
case EP0_OUT:
return "ep0_out";
case EP0_STATUS:
return "ep0_status";
case EP0_STALL:
return "ep0_stall";
case EP0_SUSPEND:
return "ep0_suspend";
}
return "ep0_?";
}
static const char *udc_ep_status(u32 status)
{
switch (status & EPxSTATUS_EP_MASK) {
case EPxSTATUS_EP_READY:
return "ready";
case EPxSTATUS_EP_DATAIN:
return "packet";
case EPxSTATUS_EP_FULL:
return "full";
case EPxSTATUS_EP_TX_ERR: /* host will retry */
return "tx_err";
case EPxSTATUS_EP_RX_ERR:
return "rx_err";
case EPxSTATUS_EP_BUSY: /* ep0 only */
return "busy";
case EPxSTATUS_EP_STALL:
return "stall";
case EPxSTATUS_EP_INVALID: /* these "can't happen" */
return "invalid";
}
return "?";
}
static int udc_proc_read(struct seq_file *m, void *v)
{
......@@ -1079,18 +1123,7 @@ static int udc_proc_read(struct seq_file *m, void *v)
is_usb_connected
? ((tmp & PW_PULLUP) ? "full speed" : "powered")
: "disconnected",
({const char *state;
switch(dev->ep0state){
case EP0_DISCONNECT: state = "ep0_disconnect"; break;
case EP0_IDLE: state = "ep0_idle"; break;
case EP0_IN: state = "ep0_in"; break;
case EP0_OUT: state = "ep0_out"; break;
case EP0_STATUS: state = "ep0_status"; break;
case EP0_STALL: state = "ep0_stall"; break;
case EP0_SUSPEND: state = "ep0_suspend"; break;
default: state = "ep0_?"; break;
} state; })
);
udc_ep_state(dev->ep0state));
dump_intmask(m, "int_status", readl(&regs->int_status));
dump_intmask(m, "int_enable", readl(&regs->int_enable));
......@@ -1099,17 +1132,17 @@ static int udc_proc_read(struct seq_file *m, void *v)
goto done;
/* registers for (active) device and ep0 */
if (seq_printf(m, "\nirqs %lu\ndataset %02x "
"single.bcs %02x.%02x state %x addr %u\n",
seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n",
dev->irqs, readl(&regs->DataSet),
readl(&regs->EPxSingle), readl(&regs->EPxBCS),
readl(&regs->UsbState),
readl(&regs->address)) < 0)
readl(&regs->address));
if (seq_has_overflowed(m))
goto done;
tmp = readl(&regs->dma_master);
if (seq_printf(m,
"dma %03X =" EIGHTBITS "%s %s\n", tmp,
seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n",
tmp,
(tmp & MST_EOPB_DIS) ? " eopb-" : "",
(tmp & MST_EOPB_ENA) ? " eopb+" : "",
(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
......@@ -1121,9 +1154,8 @@ static int udc_proc_read(struct seq_file *m, void *v)
(tmp & MST_RD_ENA) ? " IN" : "",
(tmp & MST_WR_ENA) ? " OUT" : "",
(tmp & MST_CONNECTION)
? "ep1in/ep2out"
: "ep1out/ep2in") < 0)
(tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in");
if (seq_has_overflowed(m))
goto done;
/* dump endpoint queues */
......@@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v)
continue;
tmp = readl(ep->reg_status);
if (seq_printf(m,
"%s %s max %u %s, irqs %lu, "
"status %02x (%s) " FOURBITS "\n",
seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n",
ep->ep.name,
ep->is_in ? "in" : "out",
ep->ep.maxpacket,
ep->dma ? "dma" : "pio",
ep->irqs,
tmp, ({ char *s;
switch (tmp & EPxSTATUS_EP_MASK) {
case EPxSTATUS_EP_READY:
s = "ready"; break;
case EPxSTATUS_EP_DATAIN:
s = "packet"; break;
case EPxSTATUS_EP_FULL:
s = "full"; break;
case EPxSTATUS_EP_TX_ERR: // host will retry
s = "tx_err"; break;
case EPxSTATUS_EP_RX_ERR:
s = "rx_err"; break;
case EPxSTATUS_EP_BUSY: /* ep0 only */
s = "busy"; break;
case EPxSTATUS_EP_STALL:
s = "stall"; break;
case EPxSTATUS_EP_INVALID: // these "can't happen"
s = "invalid"; break;
default:
s = "?"; break;
} s; }),
tmp, udc_ep_status(tmp),
(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""
) < 0)
(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "");
if (seq_has_overflowed(m))
goto done;
if (list_empty(&ep->queue)) {
if (seq_puts(m, "\t(nothing queued)\n") < 0)
seq_puts(m, "\t(nothing queued)\n");
if (seq_has_overflowed(m))
goto done;
continue;
}
......@@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v)
} else
tmp = req->req.actual;
if (seq_printf(m,
"\treq %p len %u/%u buf %p\n",
seq_printf(m, "\treq %p len %u/%u buf %p\n",
&req->req, tmp, req->req.length,
req->req.buf) < 0)
req->req.buf);
if (seq_has_overflowed(m))
goto done;
}
}
......
......@@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
req = container_of(_req, struct lpc32xx_request, req);
ep = container_of(_ep, struct lpc32xx_ep, ep);
if (!_req || !_req->complete || !_req->buf ||
if (!_ep || !_req || !_req->complete || !_req->buf ||
!list_empty(&req->queue))
return -EINVAL;
udc = ep->udc;
if (!_ep) {
dev_dbg(udc->dev, "invalid ep\n");
return -EINVAL;
}
if ((!udc) || (!udc->driver) ||
(udc->gadget.speed == USB_SPEED_UNKNOWN)) {
dev_dbg(udc->dev, "invalid device\n");
return -EINVAL;
}
if (udc->gadget.speed == USB_SPEED_UNKNOWN)
return -EPIPE;
if (ep->lep) {
struct lpc32xx_usbd_dd_gad *dd;
......
......@@ -80,6 +80,13 @@ static const char *const ep_name[] = {
"ep-e", "ep-f", "ep-g", "ep-h",
};
/* Endpoint names for usb3380 advance mode */
static const char *const ep_name_adv[] = {
ep0name,
"ep1in", "ep2out", "ep3in", "ep4out",
"ep1out", "ep2in", "ep3out", "ep4in",
};
/* mode 0 == ep-{a,b,c,d} 1K fifo each
* mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
* mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
......@@ -138,31 +145,44 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
u32 max, tmp;
unsigned long flags;
static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
int ret = 0;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
desc->bDescriptorType != USB_DT_ENDPOINT)
desc->bDescriptorType != USB_DT_ENDPOINT) {
pr_err("%s: failed at line=%d\n", __func__, __LINE__);
return -EINVAL;
}
dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
ret = -ESHUTDOWN;
goto print_err;
}
/* erratum 0119 workaround ties up an endpoint number */
if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
return -EDOM;
if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) {
ret = -EDOM;
goto print_err;
}
if (dev->quirks & PLX_SUPERSPEED) {
if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
return -EDOM;
if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
ret = -EDOM;
goto print_err;
}
ep->is_in = !!usb_endpoint_dir_in(desc);
if (dev->enhanced_mode && ep->is_in && ep_key[ep->num])
return -EINVAL;
if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) {
ret = -EINVAL;
goto print_err;
}
}
/* sanity check ep-e/ep-f since their fifos are small */
max = usb_endpoint_maxp(desc) & 0x1fff;
if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY))
return -ERANGE;
if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) {
ret = -ERANGE;
goto print_err;
}
spin_lock_irqsave(&dev->lock, flags);
_ep->maxpacket = max & 0x7ff;
......@@ -192,7 +212,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
(dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
(dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
spin_unlock_irqrestore(&dev->lock, flags);
return -ERANGE;
ret = -ERANGE;
goto print_err;
}
}
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
......@@ -271,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
/* pci writes may still be posted */
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
return ret;
print_err:
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
return ret;
}
static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
......@@ -426,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep)
unsigned long flags;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || !ep->desc || _ep->name == ep0name)
if (!_ep || !ep->desc || _ep->name == ep0name) {
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return -EINVAL;
}
spin_lock_irqsave(&ep->dev->lock, flags);
nuke(ep);
......@@ -458,8 +484,10 @@ static struct usb_request
struct net2280_ep *ep;
struct net2280_request *req;
if (!_ep)
if (!_ep) {
pr_err("%s: Invalid ep\n", __func__);
return NULL;
}
ep = container_of(_ep, struct net2280_ep, ep);
req = kzalloc(sizeof(*req), gfp_flags);
......@@ -491,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
struct net2280_request *req;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || !_req)
if (!_ep || !_req) {
dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n",
__func__, _ep, _req);
return;
}
req = container_of(_req, struct net2280_request, req);
WARN_ON(!list_empty(&req->queue));
......@@ -896,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
struct net2280_ep *ep;
struct net2280 *dev;
unsigned long flags;
int ret = 0;
/* we always require a cpu-view buffer, so that we can
* always use pio (as fallback or whatever).
*/
req = container_of(_req, struct net2280_request, req);
if (!_req || !_req->complete || !_req->buf ||
!list_empty(&req->queue))
return -EINVAL;
if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
return -EDOM;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0))
if (!_ep || (!ep->desc && ep->num != 0)) {
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return -EINVAL;
}
req = container_of(_req, struct net2280_request, req);
if (!_req || !_req->complete || !_req->buf ||
!list_empty(&req->queue)) {
ret = -EINVAL;
goto print_err;
}
if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) {
ret = -EDOM;
goto print_err;
}
dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
ret = -ESHUTDOWN;
goto print_err;
}
/* FIXME implement PIO fallback for ZLPs with DMA */
if (ep->dma && _req->length == 0)
return -EOPNOTSUPP;
if (ep->dma && _req->length == 0) {
ret = -EOPNOTSUPP;
goto print_err;
}
/* set up dma mapping in case the caller didn't */
if (ep->dma) {
int ret;
ret = usb_gadget_map_request(&dev->gadget, _req,
ep->is_in);
if (ret)
return ret;
goto print_err;
}
ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
......@@ -1013,7 +1053,11 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
spin_unlock_irqrestore(&dev->lock, flags);
/* pci writes may still be posted */
return 0;
return ret;
print_err:
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
return ret;
}
static inline void
......@@ -1134,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
int stopped;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0) || !_req)
if (!_ep || (!ep->desc && ep->num != 0) || !_req) {
pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n",
__func__, _ep, _req);
return -EINVAL;
}
spin_lock_irqsave(&ep->dev->lock, flags);
stopped = ep->stopped;
......@@ -1157,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
}
if (&req->req != _req) {
spin_unlock_irqrestore(&ep->dev->lock, flags);
dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n",
__func__);
return -EINVAL;
}
......@@ -1214,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
int retval = 0;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0))
if (!_ep || (!ep->desc && ep->num != 0)) {
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return -EINVAL;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
}
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
retval = -ESHUTDOWN;
goto print_err;
}
if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
== USB_ENDPOINT_XFER_ISOC)
return -EINVAL;
== USB_ENDPOINT_XFER_ISOC) {
retval = -EINVAL;
goto print_err;
}
spin_lock_irqsave(&ep->dev->lock, flags);
if (!list_empty(&ep->queue))
if (!list_empty(&ep->queue)) {
retval = -EAGAIN;
else if (ep->is_in && value && net2280_fifo_status(_ep) != 0)
goto print_unlock;
} else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) {
retval = -EAGAIN;
else {
goto print_unlock;
} else {
ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
value ? "set" : "clear",
wedged ? "wedge" : "halt");
......@@ -1251,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
spin_unlock_irqrestore(&ep->dev->lock, flags);
return retval;
print_unlock:
spin_unlock_irqrestore(&ep->dev->lock, flags);
print_err:
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval);
return retval;
}
static int net2280_set_halt(struct usb_ep *_ep, int value)
......@@ -1260,8 +1323,10 @@ static int net2280_set_halt(struct usb_ep *_ep, int value)
static int net2280_set_wedge(struct usb_ep *_ep)
{
if (!_ep || _ep->name == ep0name)
if (!_ep || _ep->name == ep0name) {
pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep);
return -EINVAL;
}
return net2280_set_halt_and_wedge(_ep, 1, 1);
}
......@@ -1271,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep)
u32 avail;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0))
if (!_ep || (!ep->desc && ep->num != 0)) {
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return -ENODEV;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
}
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
dev_err(&ep->dev->pdev->dev,
"%s: Invalid driver=%p or speed=%d\n",
__func__, ep->dev->driver, ep->dev->gadget.speed);
return -ESHUTDOWN;
}
avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
if (avail > ep->fifo_size)
if (avail > ep->fifo_size) {
dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__);
return -EOVERFLOW;
}
if (ep->is_in)
avail = ep->fifo_size - avail;
return avail;
......@@ -1289,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep)
struct net2280_ep *ep;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0))
if (!_ep || (!ep->desc && ep->num != 0)) {
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
return;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
}
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
dev_err(&ep->dev->pdev->dev,
"%s: Invalid driver=%p or speed=%d\n",
__func__, ep->dev->driver, ep->dev->gadget.speed);
return;
}
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
(void) readl(&ep->regs->ep_rsp);
......@@ -1977,7 +2056,7 @@ static void usb_reinit_338x(struct net2280 *dev)
for (i = 0; i < dev->n_ep; i++) {
struct net2280_ep *ep = &dev->ep[i];
ep->ep.name = ep_name[i];
ep->ep.name = dev->enhanced_mode ? ep_name_adv[i] : ep_name[i];
ep->dev = dev;
ep->num = i;
......@@ -1989,11 +2068,9 @@ static void usb_reinit_338x(struct net2280 *dev)
ep->regs = (struct net2280_ep_regs __iomem *)
(((void __iomem *)&dev->epregs[ne[i]]) +
ep_reg_addr[i]);
ep->fiforegs = &dev->fiforegs[i];
} else {
ep->cfg = &dev->epregs[i];
ep->regs = &dev->epregs[i];
ep->fiforegs = &dev->fiforegs[i];
}
ep->fifo_size = (i != 0) ? 2048 : 512;
......@@ -2186,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget,
dev->ep[i].irqs = 0;
/* hook up the driver ... */
dev->softconnect = 1;
driver->driver.bus = NULL;
dev->driver = driver;
......@@ -3052,6 +3128,8 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
BIT(PCI_RETRY_ABORT_INTERRUPT))
static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
__releases(dev->lock)
__acquires(dev->lock)
{
struct net2280_ep *ep;
u32 tmp, num, mask, scratch;
......@@ -3373,8 +3451,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
u32 usbstat;
dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
(base + 0x00b4);
dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
(base + 0x0500);
dev->llregs = (struct usb338x_ll_regs __iomem *)
(base + 0x0700);
dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
......
......@@ -96,7 +96,6 @@ struct net2280_ep {
struct net2280_ep_regs __iomem *regs;
struct net2280_dma_regs __iomem *dma;
struct net2280_dma *dummy;
struct usb338x_fifo_regs __iomem *fiforegs;
dma_addr_t td_dma; /* of dummy */
struct net2280 *dev;
unsigned long irqs;
......@@ -181,7 +180,6 @@ struct net2280 {
struct net2280_dma_regs __iomem *dma;
struct net2280_dep_regs __iomem *dep;
struct net2280_ep_regs __iomem *epregs;
struct usb338x_fifo_regs __iomem *fiforegs;
struct usb338x_ll_regs __iomem *llregs;
struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
......
......@@ -93,23 +93,22 @@ static void handle_ep(struct pxa_ep *ep);
static int state_dbg_show(struct seq_file *s, void *p)
{
struct pxa_udc *udc = s->private;
int pos = 0, ret;
u32 tmp;
ret = -ENODEV;
if (!udc->driver)
goto out;
return -ENODEV;
/* basic device status */
pos += seq_printf(s, DRIVER_DESC "\n"
"%s version: %s\nGadget driver: %s\n",
seq_printf(s, DRIVER_DESC "\n"
"%s version: %s\n"
"Gadget driver: %s\n",
driver_name, DRIVER_VERSION,
udc->driver ? udc->driver->driver.name : "(none)");
tmp = udc_readl(udc, UDCCR);
pos += seq_printf(s,
"udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), "
"con=%d,inter=%d,altinter=%d\n", tmp,
seq_printf(s,
"udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n",
tmp,
(tmp & UDCCR_OEN) ? " oen":"",
(tmp & UDCCR_AALTHNP) ? " aalthnp":"",
(tmp & UDCCR_AHNP) ? " rem" : "",
......@@ -124,19 +123,16 @@ static int state_dbg_show(struct seq_file *s, void *p)
(tmp & UDCCR_AIN) >> UDCCR_AIN_S,
(tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
/* registers for device and ep0 */
pos += seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
pos += seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
pos += seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
pos += seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, "
"reconfig=%lu\n",
seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n",
udc->stats.irqs_reset, udc->stats.irqs_suspend,
udc->stats.irqs_resume, udc->stats.irqs_reconfig);
ret = 0;
out:
return ret;
return 0;
}
static int queues_dbg_show(struct seq_file *s, void *p)
......@@ -144,50 +140,47 @@ static int queues_dbg_show(struct seq_file *s, void *p)
struct pxa_udc *udc = s->private;
struct pxa_ep *ep;
struct pxa27x_request *req;
int pos = 0, i, maxpkt, ret;
int i, maxpkt;
ret = -ENODEV;
if (!udc->driver)
goto out;
return -ENODEV;
/* dump endpoint queues */
for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
ep = &udc->pxa_ep[i];
maxpkt = ep->fifo_size;
pos += seq_printf(s, "%-12s max_pkt=%d %s\n",
seq_printf(s, "%-12s max_pkt=%d %s\n",
EPNAME(ep), maxpkt, "pio");
if (list_empty(&ep->queue)) {
pos += seq_printf(s, "\t(nothing queued)\n");
seq_puts(s, "\t(nothing queued)\n");
continue;
}
list_for_each_entry(req, &ep->queue, queue) {
pos += seq_printf(s, "\treq %p len %d/%d buf %p\n",
seq_printf(s, "\treq %p len %d/%d buf %p\n",
&req->req, req->req.actual,
req->req.length, req->req.buf);
}
}
ret = 0;
out:
return ret;
return 0;
}
static int eps_dbg_show(struct seq_file *s, void *p)
{
struct pxa_udc *udc = s->private;
struct pxa_ep *ep;
int pos = 0, i, ret;
int i;
u32 tmp;
ret = -ENODEV;
if (!udc->driver)
goto out;
return -ENODEV;
ep = &udc->pxa_ep[0];
tmp = udc_ep_readl(ep, UDCCSR);
pos += seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", tmp,
seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n",
tmp,
(tmp & UDCCSR0_SA) ? " sa" : "",
(tmp & UDCCSR0_RNE) ? " rne" : "",
(tmp & UDCCSR0_FST) ? " fst" : "",
......@@ -198,10 +191,7 @@ static int eps_dbg_show(struct seq_file *s, void *p)
for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
ep = &udc->pxa_ep[i];
tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR);
pos += seq_printf(s, "%-12s: "
"IN %lu(%lu reqs), OUT %lu(%lu reqs), "
"irqs=%lu, udccr=0x%08x, udccsr=0x%03x, "
"udcbcr=%d\n",
seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n",
EPNAME(ep),
ep->stats.in_bytes, ep->stats.in_ops,
ep->stats.out_bytes, ep->stats.out_ops,
......@@ -210,9 +200,7 @@ static int eps_dbg_show(struct seq_file *s, void *p)
udc_ep_readl(ep, UDCBCR));
}
ret = 0;
out:
return ret;
return 0;
}
static int eps_dbg_open(struct inode *inode, struct file *file)
......
......@@ -35,6 +35,8 @@
* @dev - the child device to the actual controller
* @gadget - the gadget. For use by the class code
* @list - for use by the udc class driver
* @vbus - for udcs who care about vbus status, this value is real vbus status;
* for udcs who do not care about vbus status, this value is always true
*
* This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together.
......@@ -44,6 +46,7 @@ struct usb_udc {
struct usb_gadget *gadget;
struct device dev;
struct list_head list;
bool vbus;
};
static struct class *udc_class;
......@@ -129,19 +132,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
static void usb_gadget_state_work(struct work_struct *work)
{
struct usb_gadget *gadget = work_to_gadget(work);
struct usb_udc *udc = NULL;
mutex_lock(&udc_lock);
list_for_each_entry(udc, &udc_list, list)
if (udc->gadget == gadget)
goto found;
mutex_unlock(&udc_lock);
return;
found:
mutex_unlock(&udc_lock);
struct usb_udc *udc = gadget->udc;
if (udc)
sysfs_notify(&udc->dev.kobj, NULL, "state");
}
......@@ -155,6 +148,34 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
/* ------------------------------------------------------------------------- */
static void usb_udc_connect_control(struct usb_udc *udc)
{
if (udc->vbus)
usb_gadget_connect(udc->gadget);
else
usb_gadget_disconnect(udc->gadget);
}
/**
* usb_udc_vbus_handler - updates the udc core vbus status, and try to
* connect or disconnect gadget
* @gadget: The gadget which vbus change occurs
* @status: The vbus status
*
* The udc driver calls it when it wants to connect or disconnect gadget
* according to vbus status.
*/
void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
{
struct usb_udc *udc = gadget->udc;
if (udc) {
udc->vbus = status;
usb_udc_connect_control(udc);
}
}
EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
/**
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
* @gadget: The gadget which bus reset occurs
......@@ -278,6 +299,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
goto err3;
udc->gadget = gadget;
gadget->udc = udc;
mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list);
......@@ -287,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
goto err4;
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
udc->vbus = true;
mutex_unlock(&udc_lock);
......@@ -348,21 +371,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
*/
void usb_del_gadget_udc(struct usb_gadget *gadget)
{
struct usb_udc *udc = NULL;
mutex_lock(&udc_lock);
list_for_each_entry(udc, &udc_list, list)
if (udc->gadget == gadget)
goto found;
dev_err(gadget->dev.parent, "gadget not registered.\n");
mutex_unlock(&udc_lock);
struct usb_udc *udc = gadget->udc;
if (!udc)
return;
found:
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
mutex_lock(&udc_lock);
list_del(&udc->list);
mutex_unlock(&udc_lock);
......@@ -397,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
driver->unbind(udc->gadget);
goto err1;
}
usb_gadget_connect(udc->gadget);
usb_udc_connect_control(udc);
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
return 0;
......
......@@ -507,7 +507,8 @@ void musb_hnp_stop(struct musb *musb)
musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
}
static void musb_generic_disable(struct musb *musb);
static void musb_recover_from_babble(struct musb *musb);
/*
* Interrupt Service Routine to record USB "global" interrupts.
* Since these do not happen often and signify things of
......@@ -534,30 +535,16 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
*/
if (int_usb & MUSB_INTR_RESUME) {
handled = IRQ_HANDLED;
dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->otg->state));
dev_dbg(musb->controller, "RESUME (%s)\n",
usb_otg_state_string(musb->xceiv->otg->state));
if (devctl & MUSB_DEVCTL_HM) {
void __iomem *mbase = musb->mregs;
u8 power;
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND:
/* remote wakeup? later, GetPortStatus
* will stop RESUME signaling
*/
power = musb_readb(musb->mregs, MUSB_POWER);
if (power & MUSB_POWER_SUSPENDM) {
/* spurious */
musb->int_usb &= ~MUSB_INTR_SUSPEND;
dev_dbg(musb->controller, "Spurious SUSPENDM\n");
break;
}
power &= ~MUSB_POWER_SUSPENDM;
musb_writeb(mbase, MUSB_POWER,
power | MUSB_POWER_RESUME);
musb->port1_status |=
(USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME;
......@@ -775,10 +762,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->ep0_stage = MUSB_EP0_START;
/* flush endpoints when transitioning from Device Mode */
if (is_peripheral_active(musb)) {
/* REVISIT HNP; just force disconnect */
}
musb->intrtxe = musb->epmask;
musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
musb->intrrxe = musb->epmask & 0xfffe;
......@@ -879,20 +862,19 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
*/
if (int_usb & MUSB_INTR_RESET) {
handled = IRQ_HANDLED;
if ((devctl & MUSB_DEVCTL_HM) != 0) {
if (devctl & MUSB_DEVCTL_HM) {
/*
* Looks like non-HS BABBLE can be ignored, but
* HS BABBLE is an error condition. For HS the solution
* is to avoid babble in the first place and fix what
* caused BABBLE. When HS BABBLE happens we can only
* stop the session.
* When BABBLE happens what we can depends on which
* platform MUSB is running, because some platforms
* implemented proprietary means for 'recovering' from
* Babble conditions. One such platform is AM335x. In
* most cases, however, the only thing we can do is
* drop the session.
*/
if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV))
dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl);
else {
ERR("Stopping host session -- babble\n");
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
}
dev_err(musb->controller, "Babble\n");
if (is_host_active(musb))
musb_recover_from_babble(musb);
} else {
dev_dbg(musb->controller, "BUS RESET as %s\n",
usb_otg_state_string(musb->xceiv->otg->state));
......@@ -931,13 +913,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
}
}
/* handle babble condition */
if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) {
musb_generic_disable(musb);
schedule_delayed_work(&musb->recover_work,
msecs_to_jiffies(100));
}
#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
* supporting transfer phasing to prevent exceeding ISO bandwidth
......@@ -990,7 +965,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
/*-------------------------------------------------------------------------*/
static void musb_generic_disable(struct musb *musb)
static void musb_disable_interrupts(struct musb *musb)
{
void __iomem *mbase = musb->mregs;
u16 temp;
......@@ -1002,16 +977,35 @@ static void musb_generic_disable(struct musb *musb)
musb->intrrxe = 0;
musb_writew(mbase, MUSB_INTRRXE, 0);
/* off */
musb_writeb(mbase, MUSB_DEVCTL, 0);
/* flush pending interrupts */
temp = musb_readb(mbase, MUSB_INTRUSB);
temp = musb_readw(mbase, MUSB_INTRTX);
temp = musb_readw(mbase, MUSB_INTRRX);
}
static void musb_enable_interrupts(struct musb *musb)
{
void __iomem *regs = musb->mregs;
/* Set INT enable registers, enable interrupts */
musb->intrtxe = musb->epmask;
musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
musb->intrrxe = musb->epmask & 0xfffe;
musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
}
static void musb_generic_disable(struct musb *musb)
{
void __iomem *mbase = musb->mregs;
musb_disable_interrupts(musb);
/* off */
musb_writeb(mbase, MUSB_DEVCTL, 0);
}
/*
* Program the HDRC to start (enable interrupts, dma, etc.).
*/
......@@ -1022,13 +1016,7 @@ void musb_start(struct musb *musb)
dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
/* Set INT enable registers, enable interrupts */
musb->intrtxe = musb->epmask;
musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
musb->intrrxe = musb->epmask & 0xfffe;
musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
musb_enable_interrupts(musb);
musb_writeb(regs, MUSB_TESTMODE, 0);
/* put into basic highspeed mode and start session */
......@@ -1587,9 +1575,12 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
irqreturn_t musb_interrupt(struct musb *musb)
{
irqreturn_t retval = IRQ_NONE;
unsigned long status;
unsigned long epnum;
u8 devctl;
int ep_num;
u32 reg;
if (!musb->int_usb && !musb->int_tx && !musb->int_rx)
return IRQ_NONE;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
......@@ -1597,56 +1588,57 @@ irqreturn_t musb_interrupt(struct musb *musb)
is_host_active(musb) ? "host" : "peripheral",
musb->int_usb, musb->int_tx, musb->int_rx);
/* the core can interrupt us for multiple reasons; docs have
* a generic interrupt flowchart to follow
/**
* According to Mentor Graphics' documentation, flowchart on page 98,
* IRQ should be handled as follows:
*
* . Resume IRQ
* . Session Request IRQ
* . VBUS Error IRQ
* . Suspend IRQ
* . Connect IRQ
* . Disconnect IRQ
* . Reset/Babble IRQ
* . SOF IRQ (we're not using this one)
* . Endpoint 0 IRQ
* . TX Endpoints
* . RX Endpoints
*
* We will be following that flowchart in order to avoid any problems
* that might arise with internal Finite State Machine.
*/
if (musb->int_usb)
retval |= musb_stage0_irq(musb, musb->int_usb,
devctl);
/* "stage 1" is handling endpoint irqs */
if (musb->int_usb)
retval |= musb_stage0_irq(musb, musb->int_usb, devctl);
/* handle endpoint 0 first */
if (musb->int_tx & 1) {
if (is_host_active(musb))
retval |= musb_h_ep0_irq(musb);
else
retval |= musb_g_ep0_irq(musb);
/* we have just handled endpoint 0 IRQ, clear it */
musb->int_tx &= ~BIT(0);
}
/* RX on endpoints 1-15 */
reg = musb->int_rx >> 1;
ep_num = 1;
while (reg) {
if (reg & 1) {
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval = ep->rx_irq(...) */
status = musb->int_tx;
for_each_set_bit(epnum, &status, 16) {
retval = IRQ_HANDLED;
if (is_host_active(musb))
musb_host_rx(musb, ep_num);
musb_host_tx(musb, epnum);
else
musb_g_rx(musb, ep_num);
musb_g_tx(musb, epnum);
}
reg >>= 1;
ep_num++;
}
status = musb->int_rx;
/* TX on endpoints 1-15 */
reg = musb->int_tx >> 1;
ep_num = 1;
while (reg) {
if (reg & 1) {
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval |= ep->tx_irq(...) */
for_each_set_bit(epnum, &status, 16) {
retval = IRQ_HANDLED;
if (is_host_active(musb))
musb_host_tx(musb, ep_num);
musb_host_rx(musb, epnum);
else
musb_g_tx(musb, ep_num);
}
reg >>= 1;
ep_num++;
musb_g_rx(musb, epnum);
}
return retval;
......@@ -1825,33 +1817,44 @@ static void musb_irq_work(struct work_struct *data)
}
}
/* Recover from babble interrupt conditions */
static void musb_recover_work(struct work_struct *data)
static void musb_recover_from_babble(struct musb *musb)
{
struct musb *musb = container_of(data, struct musb, recover_work.work);
int status, ret;
int ret;
u8 devctl;
musb_disable_interrupts(musb);
ret = musb_platform_reset(musb);
if (ret)
/*
* wait at least 320 cycles of 60MHz clock. That's 5.3us, we will give
* it some slack and wait for 10us.
*/
udelay(10);
ret = musb_platform_recover(musb);
if (ret) {
musb_enable_interrupts(musb);
return;
}
usb_phy_vbus_off(musb->xceiv);
usleep_range(100, 200);
/* drop session bit */
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
usb_phy_vbus_on(musb->xceiv);
usleep_range(100, 200);
/* tell usbcore about it */
musb_root_disconnect(musb);
/*
* When a babble condition occurs, the musb controller
* removes the session bit and the endpoint config is lost.
*/
if (musb->dyn_fifo)
status = ep_config_from_table(musb);
ret = ep_config_from_table(musb);
else
status = ep_config_from_hw(musb);
ret = ep_config_from_hw(musb);
/* start the session again */
if (status == 0)
/* restart session */
if (ret == 0)
musb_start(musb);
}
......@@ -2087,7 +2090,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work);
INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work);
INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
......@@ -2183,7 +2185,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
fail3:
cancel_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->recover_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
......@@ -2249,7 +2250,6 @@ static int musb_remove(struct platform_device *pdev)
dma_controller_destroy(musb->dma_controller);
cancel_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->recover_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
musb_free(musb);
......
......@@ -160,7 +160,8 @@ struct musb_io;
* @init: turns on clocks, sets up platform-specific registers, etc
* @exit: undoes @init
* @set_mode: forcefully changes operating mode
* @try_ilde: tries to idle the IP
* @try_idle: tries to idle the IP
* @recover: platform-specific babble recovery
* @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status
* @adjust_channel_params: pre check for standard dma channel_program func
......@@ -196,7 +197,7 @@ struct musb_platform_ops {
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
int (*set_mode)(struct musb *musb, u8 mode);
void (*try_idle)(struct musb *musb, unsigned long timeout);
int (*reset)(struct musb *musb);
int (*recover)(struct musb *musb);
int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on);
......@@ -300,7 +301,6 @@ struct musb {
irqreturn_t (*isr)(int, void *);
struct work_struct irq_work;
struct delayed_work recover_work;
struct delayed_work deassert_reset_work;
struct delayed_work finish_resume_work;
u16 hwvers;
......@@ -558,12 +558,12 @@ static inline void musb_platform_try_idle(struct musb *musb,
musb->ops->try_idle(musb, timeout);
}
static inline int musb_platform_reset(struct musb *musb)
static inline int musb_platform_recover(struct musb *musb)
{
if (!musb->ops->reset)
return -EINVAL;
if (!musb->ops->recover)
return 0;
return musb->ops->reset(musb);
return musb->ops->recover(musb);
}
static inline int musb_platform_get_vbus_status(struct musb *musb)
......
......@@ -225,10 +225,12 @@ static void cppi41_dma_callback(void *private_data)
struct dma_channel *channel = private_data;
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
struct cppi41_dma_controller *controller;
struct musb *musb = hw_ep->musb;
unsigned long flags;
struct dma_tx_state txstate;
u32 transferred;
int is_hs = 0;
bool empty;
spin_lock_irqsave(&musb->lock, flags);
......@@ -248,12 +250,14 @@ static void cppi41_dma_callback(void *private_data)
transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0;
if (cppi41_channel->is_tx)
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
if (!cppi41_channel->is_tx || empty) {
cppi41_trans_done(cppi41_channel);
} else {
struct cppi41_dma_controller *controller;
int is_hs = 0;
goto out;
}
/*
* On AM335x it has been observed that the TX interrupt fires
* too early that means the TXFIFO is not yet empty but the DMA
......@@ -277,20 +281,16 @@ static void cppi41_dma_callback(void *private_data)
unsigned wait = 25;
do {
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty)
break;
wait--;
if (!wait)
break;
udelay(1);
} while (1);
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
cppi41_trans_done(cppi41_channel);
goto out;
}
wait--;
if (!wait)
break;
cpu_relax();
} while (1);
}
list_add_tail(&cppi41_channel->tx_check,
&controller->early_tx_list);
......@@ -302,7 +302,7 @@ static void cppi41_dma_callback(void *private_data)
20 * NSEC_PER_USEC,
HRTIMER_MODE_REL);
}
}
out:
spin_unlock_irqrestore(&musb->lock, flags);
}
......
......@@ -119,7 +119,7 @@ struct dsps_musb_wrapper {
unsigned iddig:5;
unsigned iddig_mux:5;
/* miscellaneous stuff */
u8 poll_seconds;
unsigned poll_timeout;
};
/*
......@@ -225,9 +225,8 @@ static void dsps_musb_enable(struct musb *musb)
dsps_writel(reg_base, wrp->epintr_set, epmask);
dsps_writel(reg_base, wrp->coreintr_set, coremask);
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
dsps_writel(reg_base, wrp->coreintr_set,
(1 << wrp->drvvbus) << wrp->usb_shift);
/* start polling for ID change. */
mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout));
dsps_musb_try_idle(musb, 0);
}
......@@ -285,7 +284,8 @@ static void otg_timer(unsigned long _musb)
}
if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
mod_timer(&glue->timer, jiffies +
msecs_to_jiffies(wrp->poll_timeout));
break;
case OTG_STATE_A_WAIT_VFALL:
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
......@@ -330,28 +330,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
usbintr, epintr);
/*
* DRVVBUS IRQs are the only proxy we have (a very poor one!) for
* DSPS IP's missing ID change IRQ. We need an ID change IRQ to
* switch appropriately between halves of the OTG state machine.
* Managing DEVCTL.SESSION per Mentor docs requires that we know its
* value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
* Also, DRVVBUS pulses for SRP (but not at 5V) ...
*/
if (is_host_active(musb) && usbintr & MUSB_INTR_BABBLE) {
pr_info("CAUTION: musb: Babble Interrupt Occurred\n");
/*
* When a babble condition occurs, the musb controller removes
* the session and is no longer in host mode. Hence, all
* devices connected to its root hub get disconnected.
*
* Hand this error down to the musb core isr, so it can
* recover.
*/
musb->int_usb = MUSB_INTR_BABBLE | MUSB_INTR_DISCONNECT;
musb->int_tx = musb->int_rx = 0;
}
if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
int drvvbus = dsps_readl(reg_base, wrp->status);
......@@ -374,8 +352,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&glue->timer,
jiffies + wrp->poll_seconds * HZ);
mod_timer(&glue->timer, jiffies +
msecs_to_jiffies(wrp->poll_timeout));
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
......@@ -404,7 +382,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
/* Poll for ID change in OTG port mode */
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
mod_timer(&glue->timer, jiffies +
msecs_to_jiffies(wrp->poll_timeout));
out:
spin_unlock_irqrestore(&musb->lock, flags);
......@@ -453,7 +432,7 @@ static int dsps_musb_init(struct musb *musb)
musb->ctrl_base = reg_base;
/* NOP driver needs change if supporting dual instance */
musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0);
musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "phys", 0);
if (IS_ERR(musb->xceiv))
return PTR_ERR(musb->xceiv);
......@@ -497,7 +476,7 @@ static int dsps_musb_init(struct musb *musb)
* logic enabled.
*/
val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
if (val == MUSB_BABBLE_RCV_DISABLE) {
if (val & MUSB_BABBLE_RCV_DISABLE) {
glue->sw_babble_enabled = true;
val |= MUSB_BABBLE_SW_SESSION_CTRL;
dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
......@@ -571,7 +550,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
return 0;
}
static bool sw_babble_control(struct musb *musb)
static bool dsps_sw_babble_control(struct musb *musb)
{
u8 babble_ctl;
bool session_restart = false;
......@@ -622,37 +601,36 @@ static bool sw_babble_control(struct musb *musb)
return session_restart;
}
static int dsps_musb_reset(struct musb *musb)
static int dsps_musb_recover(struct musb *musb)
{
struct device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
int session_restart = 0, error;
int session_restart = 0;
if (glue->sw_babble_enabled)
session_restart = sw_babble_control(musb);
/*
* In case of new silicon version babble condition can be recovered
* without resetting the MUSB. But for older silicon versions, MUSB
* reset is needed
*/
if (session_restart || !glue->sw_babble_enabled) {
dev_info(musb->controller, "Restarting MUSB to recover from Babble\n");
dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
usleep_range(100, 200);
usb_phy_shutdown(musb->xceiv);
error = phy_power_off(musb->phy);
if (error)
dev_err(dev, "phy shutdown failed: %i\n", error);
usleep_range(100, 200);
usb_phy_init(musb->xceiv);
error = phy_power_on(musb->phy);
if (error)
dev_err(dev, "phy powerup failed: %i\n", error);
session_restart = dsps_sw_babble_control(musb);
else
session_restart = 1;
return session_restart ? 0 : -EPIPE;
}
/* Similar to am35x, dm81xx support only 32-bit read operation */
static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
{
void __iomem *fifo = hw_ep->fifo;
if (len >= 4) {
readsl(fifo, dst, len >> 2);
dst += len & ~0x03;
len &= 0x03;
}
return !session_restart;
/* Read any remaining 1 to 3 bytes */
if (len > 0) {
u32 val = musb_readl(fifo, 0);
memcpy(dst, &val, len);
}
}
static struct musb_platform_ops dsps_ops = {
......@@ -665,7 +643,7 @@ static struct musb_platform_ops dsps_ops = {
.try_idle = dsps_musb_try_idle,
.set_mode = dsps_musb_set_mode,
.reset = dsps_musb_reset,
.recover = dsps_musb_recover,
};
static u64 musb_dmamask = DMA_BIT_MASK(32);
......@@ -737,7 +715,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
musb->dev.parent = dev;
musb->dev.dma_mask = &musb_dmamask;
musb->dev.coherent_dma_mask = musb_dmamask;
musb->dev.of_node = of_node_get(dn);
glue->musb = musb;
......@@ -802,6 +779,9 @@ static int dsps_probe(struct platform_device *pdev)
}
wrp = match->data;
if (of_device_is_compatible(pdev->dev.of_node, "ti,musb-dm816"))
dsps_ops.read_fifo = dsps_read_fifo32;
/* allocate glue */
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
......@@ -873,12 +853,14 @@ static const struct dsps_musb_wrapper am33xx_driver_data = {
.rxep_shift = 16,
.rxep_mask = 0xfffe,
.rxep_bitmap = (0xfffe << 16),
.poll_seconds = 2,
.poll_timeout = 2000, /* ms */
};
static const struct of_device_id musb_dsps_of_match[] = {
{ .compatible = "ti,musb-am33xx",
.data = (void *) &am33xx_driver_data, },
.data = &am33xx_driver_data, },
{ .compatible = "ti,musb-dm816",
.data = &am33xx_driver_data, },
{ },
};
MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
......@@ -929,7 +911,8 @@ static int dsps_resume(struct device *dev)
dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
mod_timer(&glue->timer, jiffies +
msecs_to_jiffies(wrp->poll_timeout));
return 0;
}
......
......@@ -1876,44 +1876,6 @@ static int musb_gadget_start(struct usb_gadget *g,
return retval;
}
static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
{
int i;
struct musb_hw_ep *hw_ep;
/* don't disconnect if it's not connected */
if (musb->g.speed == USB_SPEED_UNKNOWN)
driver = NULL;
else
musb->g.speed = USB_SPEED_UNKNOWN;
/* deactivate the hardware */
if (musb->softconnect) {
musb->softconnect = 0;
musb_pullup(musb, 0);
}
musb_stop(musb);
/* killing any outstanding requests will quiesce the driver;
* then report disconnect
*/
if (driver) {
for (i = 0, hw_ep = musb->endpoints;
i < musb->nr_endpoints;
i++, hw_ep++) {
musb_ep_select(musb->mregs, i);
if (hw_ep->is_shared_fifo /* || !epnum */) {
nuke(&hw_ep->ep_in, -ESHUTDOWN);
} else {
if (hw_ep->max_packet_sz_tx)
nuke(&hw_ep->ep_in, -ESHUTDOWN);
if (hw_ep->max_packet_sz_rx)
nuke(&hw_ep->ep_out, -ESHUTDOWN);
}
}
}
}
/*
* Unregister the gadget driver. Used by gadget drivers when
* unregistering themselves from the controller.
......@@ -1940,7 +1902,7 @@ static int musb_gadget_stop(struct usb_gadget *g)
(void) musb_gadget_vbus_draw(&musb->g, 0);
musb->xceiv->otg->state = OTG_STATE_UNDEFINED;
stop_activity(musb, NULL);
musb_stop(musb);
otg_set_peripheral(musb->xceiv->otg, NULL);
musb->is_active = 0;
......
......@@ -202,13 +202,13 @@ config USB_RCAR_GEN2_PHY
config USB_ULPI
bool "Generic ULPI Transceiver Driver"
depends on ARM || ARM64
select USB_ULPI_VIEWPORT
help
Enable this to support ULPI connected USB OTG transceivers which
are likely found on embedded boards.
config USB_ULPI_VIEWPORT
bool
depends on USB_ULPI
help
Provides read/write operations to the ULPI phy register set for
controllers with a viewport register (e.g. Chipidea/ARC controllers).
......
......@@ -27,7 +27,7 @@ static const char *const usbphy_modes[] = {
* @np: Pointer to the given device_node
*
* The function gets phy interface string from property 'phy_type',
* and returns the correspondig enum usb_phy_interface
* and returns the corresponding enum usb_phy_interface
*/
enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np)
{
......
......@@ -893,7 +893,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
/*
* Disconnection Sequence:
* 1. Disconect Interrupt
* 1. Disconnect Interrupt
* 2. Disable regulators
* 3. Disable AB clock
* 4. Disable the Phy
......
......@@ -62,14 +62,14 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
return 0;
}
static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
static void nop_reset(struct usb_phy_generic *nop)
{
if (!nop->gpiod_reset)
return;
gpiod_direction_output(nop->gpiod_reset, !asserted);
gpiod_set_value(nop->gpiod_reset, 1);
usleep_range(10000, 20000);
gpiod_set_value(nop->gpiod_reset, asserted);
gpiod_set_value(nop->gpiod_reset, 0);
}
/* interface to regulator framework */
......@@ -151,8 +151,7 @@ int usb_gen_phy_init(struct usb_phy *phy)
if (!IS_ERR(nop->clk))
clk_prepare_enable(nop->clk);
/* De-assert RESET */
nop_reset_set(nop, 0);
nop_reset(nop);
return 0;
}
......@@ -162,8 +161,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
{
struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
/* Assert RESET */
nop_reset_set(nop, 1);
gpiod_set_value(nop->gpiod_reset, 1);
if (!IS_ERR(nop->clk))
clk_disable_unprepare(nop->clk);
......
......@@ -81,7 +81,9 @@ static void devm_usb_phy_release(struct device *dev, void *res)
static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
{
return res == match_data;
struct usb_phy **phy = res;
return *phy == match_data;
}
/**
......
......@@ -275,6 +275,16 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum,
return 0;
}
/*
* interrupt functions
*/
void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit)
{
u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0);
usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask);
}
/*
* local functions
*/
......@@ -487,6 +497,15 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
if (gpio > 0)
dparam->enable_gpio = gpio;
switch (dparam->type) {
case USBHS_TYPE_R8A7790:
case USBHS_TYPE_R8A7791:
dparam->has_usb_dmac = 1;
break;
default:
break;
}
return info;
}
......
......@@ -193,6 +193,7 @@ struct usbhs_priv;
#define TYPE_BULK (1 << 14)
#define TYPE_INT (2 << 14)
#define TYPE_ISO (3 << 14)
#define BFRE (1 << 10) /* BRDY Interrupt Operation Spec. */
#define DBLB (1 << 9) /* Double Buffer Mode */
#define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */
#define DIR_OUT (1 << 4) /* Transfer Direction */
......@@ -216,6 +217,7 @@ struct usbhs_priv;
#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */
#define SQCLR (1 << 8) /* Toggle Bit Clear */
#define SQSET (1 << 7) /* Toggle Bit Set */
#define SQMON (1 << 6) /* Toggle Bit Check */
#define PBUSY (1 << 5) /* Pipe Busy */
#define PID_MASK (0x3) /* Response PID */
#define PID_NAK 0
......@@ -323,6 +325,11 @@ int usbhs_frame_get_num(struct usbhs_priv *priv);
int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub,
u16 hubport, u16 speed);
/*
* interrupt functions
*/
void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit);
/*
* data
*/
......
......@@ -813,7 +813,8 @@ static void xfer_work(struct work_struct *work)
desc->callback = usbhsf_dma_complete;
desc->callback_param = pipe;
if (dmaengine_submit(desc) < 0) {
pkt->cookie = dmaengine_submit(desc);
if (pkt->cookie < 0) {
dev_err(dev, "Failed to submit dma descriptor\n");
return;
}
......@@ -822,10 +823,10 @@ static void xfer_work(struct work_struct *work)
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
usbhs_pipe_running(pipe, 1);
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
usbhs_pipe_enable(pipe);
usbhsf_dma_start(pipe, fifo);
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
dma_async_issue_pending(chan);
usbhs_pipe_enable(pipe);
}
/*
......@@ -838,6 +839,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
struct usbhs_fifo *fifo;
int len = pkt->length - pkt->actual;
int ret;
uintptr_t align_mask;
if (usbhs_pipe_is_busy(pipe))
return 0;
......@@ -847,10 +849,14 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
usbhs_pipe_is_dcp(pipe))
goto usbhsf_pio_prepare_push;
if (len & 0x7) /* 8byte alignment */
/* check data length if this driver don't use USB-DMAC */
if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7)
goto usbhsf_pio_prepare_push;
if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
/* check buffer alignment */
align_mask = usbhs_get_dparam(priv, has_usb_dmac) ?
USBHS_USB_DMAC_XFER_SIZE - 1 : 0x7;
if ((uintptr_t)(pkt->buf + pkt->actual) & align_mask)
goto usbhsf_pio_prepare_push;
/* return at this time if the pipe is running */
......@@ -924,7 +930,85 @@ struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
/*
* DMA pop handler
*/
static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
static int usbhsf_dma_prepare_pop_with_rx_irq(struct usbhs_pkt *pkt,
int *is_done)
{
return usbhsf_prepare_pop(pkt, is_done);
}
static int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt,
int *is_done)
{
struct usbhs_pipe *pipe = pkt->pipe;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct usbhs_fifo *fifo;
int ret;
if (usbhs_pipe_is_busy(pipe))
return 0;
/* use PIO if packet is less than pio_dma_border or pipe is DCP */
if ((pkt->length < usbhs_get_dparam(priv, pio_dma_border)) ||
usbhs_pipe_is_dcp(pipe))
goto usbhsf_pio_prepare_pop;
fifo = usbhsf_get_dma_fifo(priv, pkt);
if (!fifo)
goto usbhsf_pio_prepare_pop;
if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1))
goto usbhsf_pio_prepare_pop;
usbhs_pipe_config_change_bfre(pipe, 1);
ret = usbhsf_fifo_select(pipe, fifo, 0);
if (ret < 0)
goto usbhsf_pio_prepare_pop;
if (usbhsf_dma_map(pkt) < 0)
goto usbhsf_pio_prepare_pop_unselect;
/* DMA */
/*
* usbhs_fifo_dma_pop_handler :: prepare
* enabled irq to come here.
* but it is no longer needed for DMA. disable it.
*/
usbhsf_rx_irq_ctrl(pipe, 0);
pkt->trans = pkt->length;
INIT_WORK(&pkt->work, xfer_work);
schedule_work(&pkt->work);
return 0;
usbhsf_pio_prepare_pop_unselect:
usbhsf_fifo_unselect(pipe, fifo);
usbhsf_pio_prepare_pop:
/*
* change handler to PIO
*/
pkt->handler = &usbhs_fifo_pio_pop_handler;
usbhs_pipe_config_change_bfre(pipe, 0);
return pkt->handler->prepare(pkt, is_done);
}
static int usbhsf_dma_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
if (usbhs_get_dparam(priv, has_usb_dmac))
return usbhsf_dma_prepare_pop_with_usb_dmac(pkt, is_done);
else
return usbhsf_dma_prepare_pop_with_rx_irq(pkt, is_done);
}
static int usbhsf_dma_try_pop_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
{
struct usbhs_pipe *pipe = pkt->pipe;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
......@@ -993,7 +1077,16 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
return pkt->handler->try_run(pkt, is_done);
}
static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
BUG_ON(usbhs_get_dparam(priv, has_usb_dmac));
return usbhsf_dma_try_pop_with_rx_irq(pkt, is_done);
}
static int usbhsf_dma_pop_done_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
{
struct usbhs_pipe *pipe = pkt->pipe;
int maxp = usbhs_pipe_get_maxpacket(pipe);
......@@ -1017,8 +1110,68 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
return 0;
}
static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt,
struct dma_chan *chan, int dtln)
{
struct usbhs_pipe *pipe = pkt->pipe;
struct dma_tx_state state;
size_t received_size;
int maxp = usbhs_pipe_get_maxpacket(pipe);
dmaengine_tx_status(chan, pkt->cookie, &state);
received_size = pkt->length - state.residue;
if (dtln) {
received_size -= USBHS_USB_DMAC_XFER_SIZE;
received_size &= ~(maxp - 1);
received_size += dtln;
}
return received_size;
}
static int usbhsf_dma_pop_done_with_usb_dmac(struct usbhs_pkt *pkt,
int *is_done)
{
struct usbhs_pipe *pipe = pkt->pipe;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
int rcv_len;
/*
* Since the driver disables rx_irq in DMA mode, the interrupt handler
* cannot the BRDYSTS. So, the function clears it here because the
* driver may use PIO mode next time.
*/
usbhs_xxxsts_clear(priv, BRDYSTS, usbhs_pipe_number(pipe));
rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
usbhsf_fifo_clear(pipe, fifo);
pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len);
usbhsf_dma_stop(pipe, fifo);
usbhsf_dma_unmap(pkt);
usbhsf_fifo_unselect(pipe, pipe->fifo);
/* The driver can assume the rx transaction is always "done" */
*is_done = 1;
return 0;
}
static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
if (usbhs_get_dparam(priv, has_usb_dmac))
return usbhsf_dma_pop_done_with_usb_dmac(pkt, is_done);
else
return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done);
}
struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
.prepare = usbhsf_prepare_pop,
.prepare = usbhsf_dma_prepare_pop,
.try_run = usbhsf_dma_try_pop,
.dma_done = usbhsf_dma_pop_done
};
......@@ -1069,23 +1222,29 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo)
&fifo->rx_slave);
}
static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo)
static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo,
int channel)
{
fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx");
char name[16];
snprintf(name, sizeof(name), "tx%d", channel);
fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
if (IS_ERR(fifo->tx_chan))
fifo->tx_chan = NULL;
fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx");
snprintf(name, sizeof(name), "rx%d", channel);
fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
if (IS_ERR(fifo->rx_chan))
fifo->rx_chan = NULL;
}
static void usbhsf_dma_init(struct usbhs_priv *priv,
struct usbhs_fifo *fifo)
static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,
int channel)
{
struct device *dev = usbhs_priv_to_dev(priv);
if (dev->of_node)
usbhsf_dma_init_dt(dev, fifo);
usbhsf_dma_init_dt(dev, fifo, channel);
else
usbhsf_dma_init_pdev(fifo);
......@@ -1231,7 +1390,7 @@ do { \
usbhs_get_dparam(priv, d##channel##_tx_id); \
fifo->rx_slave.shdma_slave.slave_id = \
usbhs_get_dparam(priv, d##channel##_rx_id); \
usbhsf_dma_init(priv, fifo); \
usbhsf_dma_init(priv, fifo, channel); \
} while (0)
#define USBHS_DFIFO_INIT(priv, fifo, channel) \
......
......@@ -58,6 +58,7 @@ struct usbhs_pkt {
struct usbhs_pkt *pkt);
struct work_struct work;
dma_addr_t dma;
dma_cookie_t cookie;
void *buf;
int length;
int trans;
......
......@@ -119,18 +119,34 @@ struct usbhsg_recip_handle {
/*
* queue push/pop
*/
static void usbhsg_queue_pop(struct usbhsg_uep *uep,
static void __usbhsg_queue_pop(struct usbhsg_uep *uep,
struct usbhsg_request *ureq,
int status)
{
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
ureq->req.status = status;
spin_unlock(usbhs_priv_to_lock(priv));
usb_gadget_giveback_request(&uep->ep, &ureq->req);
spin_lock(usbhs_priv_to_lock(priv));
}
static void usbhsg_queue_pop(struct usbhsg_uep *uep,
struct usbhsg_request *ureq,
int status)
{
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
unsigned long flags;
usbhs_lock(priv, flags);
__usbhsg_queue_pop(uep, ureq, status);
usbhs_unlock(priv, flags);
}
static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
......
......@@ -84,6 +84,17 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
usbhs_bset(priv, pipe_reg, mask, val);
}
static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
u16 dcp_reg, u16 pipe_reg)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
if (usbhs_pipe_is_dcp(pipe))
return usbhs_read(priv, dcp_reg);
else
return usbhs_read(priv, pipe_reg);
}
/*
* DCPCFG/PIPECFG functions
*/
......@@ -92,6 +103,11 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
}
static u16 usbhsp_pipe_cfg_get(struct usbhs_pipe *pipe)
{
return __usbhsp_pipe_xxx_get(pipe, DCPCFG, PIPECFG);
}
/*
* PIPEnTRN/PIPEnTRE functions
*/
......@@ -616,6 +632,11 @@ void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
usbhsp_pipectrl_set(pipe, mask, val);
}
static int usbhs_pipe_get_data_sequence(struct usbhs_pipe *pipe)
{
return !!(usbhsp_pipectrl_get(pipe) & SQMON);
}
void usbhs_pipe_clear(struct usbhs_pipe *pipe)
{
if (usbhs_pipe_is_dcp(pipe)) {
......@@ -626,6 +647,24 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe)
}
}
void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable)
{
int sequence;
if (usbhs_pipe_is_dcp(pipe))
return;
usbhsp_pipe_select(pipe);
/* check if the driver needs to change the BFRE value */
if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE)))
return;
sequence = usbhs_pipe_get_data_sequence(pipe);
usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0);
usbhs_pipe_clear(pipe);
usbhs_pipe_data_sequence(pipe, sequence);
}
static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
{
struct usbhs_pipe *pos, *pipe;
......
......@@ -97,6 +97,7 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len);
void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
u16 epnum, u16 maxp);
void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable);
#define usbhs_pipe_sequence_data0(pipe) usbhs_pipe_data_sequence(pipe, 0)
#define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1)
......
......@@ -2315,6 +2315,8 @@
#define PCI_VENDOR_ID_CENATEK 0x16CA
#define PCI_DEVICE_ID_CENATEK_IDE 0x0001
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_VENDOR_ID_VITESSE 0x1725
#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174
......
......@@ -148,6 +148,7 @@ struct usb_os_desc_table {
* @disable: (REQUIRED) Indicates the function should be disabled. Reasons
* include host resetting or reconfiguring the gadget, and disconnection.
* @setup: Used for interface-specific control requests.
* @req_match: Tests if a given class request can be handled by this function.
* @suspend: Notifies functions when the host stops sending USB traffic.
* @resume: Notifies functions when the host restarts USB traffic.
* @get_status: Returns function status as a reply to
......@@ -213,6 +214,8 @@ struct usb_function {
void (*disable)(struct usb_function *);
int (*setup)(struct usb_function *,
const struct usb_ctrlrequest *);
bool (*req_match)(struct usb_function *,
const struct usb_ctrlrequest *);
void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *);
......
......@@ -190,7 +190,7 @@ struct usb_ep {
* @ep:the endpoint being configured
* @maxpacket_limit:value of maximum packet size limit
*
* This function shoud be used only in UDC drivers to initialize endpoint
* This function should be used only in UDC drivers to initialize endpoint
* (usually in probe function).
*/
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
......@@ -474,6 +474,7 @@ struct usb_dcd_config_params {
struct usb_gadget;
struct usb_gadget_driver;
struct usb_udc;
/* the rest of the api to the controller hardware: device operations,
* which don't involve endpoints (or i/o).
......@@ -496,6 +497,7 @@ struct usb_gadget_ops {
/**
* struct usb_gadget - represents a usb slave device
* @work: (internal use) Workqueue to be used for sysfs_notify()
* @udc: struct usb_udc pointer for this gadget
* @ops: Function pointers used to access hardware-specific operations.
* @ep0: Endpoint zero, used when reading or writing responses to
* driver setup() requests
......@@ -545,6 +547,7 @@ struct usb_gadget_ops {
*/
struct usb_gadget {
struct work_struct work;
struct usb_udc *udc;
/* readonly to gadget driver */
const struct usb_gadget_ops *ops;
struct usb_ep *ep0;
......@@ -1029,6 +1032,10 @@ extern void usb_gadget_udc_reset(struct usb_gadget *gadget,
extern void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req);
/*-------------------------------------------------------------------------*/
/* utility to update vbus status for udc core, it may be scheduled */
extern void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status);
/*-------------------------------------------------------------------------*/
......
......@@ -165,6 +165,8 @@ struct renesas_usbhs_driver_param {
*/
u32 has_otg:1; /* for controlling PWEN/EXTLP */
u32 has_sudmac:1; /* for SUDMAC */
u32 has_usb_dmac:1; /* for USB-DMAC */
#define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */
};
#define USBHS_TYPE_R8A7790 1
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册