mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 11:41:38 -04:00
USB patches for 3.18-rc1
Here's the big USB patchset for 3.18-rc1. Also in here is the PHY tree, as it seems to fit well with the USB tree for various reasons... Anyway, lots of little changes in here, all over the place, full details in the changelog below. All have been in the linux-next tree for a while with no issues. Signed-off-by: Greg Kroah-Hartman <[email protected]> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlQ0aLYACgkQMUfUDdst+ylBvwCgs9fGRj0RQkLyGhQdEpzdZtTU ZcwAoMPBImnaA1ZeSl7ZnoO8vC/WE4bR =tfpj -----END PGP SIGNATURE----- Merge tag 'usb-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB updates from Greg KH: "Here's the big USB patchset for 3.18-rc1. Also in here is the PHY tree, as it seems to fit well with the USB tree for various reasons... Anyway, lots of little changes in here, all over the place, full details in the changelog All have been in the linux-next tree for a while with no issues" * tag 'usb-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (244 commits) USB: host: st: fix typo 'CONFIG_USB_EHCI_HCD_ST' uas: Reduce number of function arguments for uas_alloc_foo functions xhci: Allow xHCI drivers to be built as separate modules xhci: Export symbols used by host-controller drivers xhci: Check for XHCI_COMP_MODE_QUIRK when disabling D3cold xhci: Introduce xhci_init_driver() usb: hcd: add generic PHY support usb: rename phy to usb_phy in HCD usb: gadget: uvc: fix up uvcg_v4l2_get_unmapped_area typo USB: host: st: fix ehci/ohci driver selection usb: host: ehci-exynos: Remove unnecessary usb-phy support usb: core: return -ENOTSUPP for all targeted hosts USB: Remove .owner field for driver usb: core: log higher level message on malformed LANGID descriptor usb: Add LED triggers for USB activity usb: Rename usb-common.c usb: gadget: Refactor request completion usb: gadget: Introduce usb_gadget_giveback_request() usb: dwc2/gadget: move phy bus legth initialization phy: remove .owner field for drivers using module_platform_driver ...
This commit is contained in:
commit
463311960e
258 changed files with 9501 additions and 4546 deletions
|
|
@ -149,4 +149,14 @@ source "drivers/usb/phy/Kconfig"
|
|||
|
||||
source "drivers/usb/gadget/Kconfig"
|
||||
|
||||
config USB_LED_TRIG
|
||||
bool "USB LED Triggers"
|
||||
depends on LEDS_CLASS && USB_COMMON && LEDS_TRIGGERS
|
||||
help
|
||||
This option adds LED triggers for USB host and/or gadget activity.
|
||||
|
||||
Say Y here if you are working on a system with led-class supported
|
||||
LEDs and you want to use them as activity indicators for USB host or
|
||||
gadget.
|
||||
|
||||
endif # USB_SUPPORT
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Here is a list of what each subdirectory here is, and what is contained in
|
|||
them.
|
||||
|
||||
core/ - This is for the core USB host code, including the
|
||||
usbfs files and the hub class driver ("khubd").
|
||||
usbfs files and the hub class driver ("hub_wq").
|
||||
|
||||
host/ - This is for USB host controller drivers. This
|
||||
includes UHCI, OHCI, EHCI, and others that might
|
||||
|
|
|
|||
|
|
@ -99,10 +99,10 @@ enum ci_role {
|
|||
|
||||
/**
|
||||
* struct ci_role_driver - host/gadget role driver
|
||||
* start: start this role
|
||||
* stop: stop this role
|
||||
* irq: irq handler for this role
|
||||
* name: role name string (host/gadget)
|
||||
* @start: start this role
|
||||
* @stop: stop this role
|
||||
* @irq: irq handler for this role
|
||||
* @name: role name string (host/gadget)
|
||||
*/
|
||||
struct ci_role_driver {
|
||||
int (*start)(struct ci_hdrc *);
|
||||
|
|
@ -245,6 +245,7 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
|
|||
|
||||
/**
|
||||
* hw_read: reads from a hw register
|
||||
* @ci: the controller
|
||||
* @reg: register index
|
||||
* @mask: bitfield mask
|
||||
*
|
||||
|
|
@ -277,6 +278,7 @@ static inline void __hw_write(struct ci_hdrc *ci, u32 val,
|
|||
|
||||
/**
|
||||
* hw_write: writes to a hw register
|
||||
* @ci: the controller
|
||||
* @reg: register index
|
||||
* @mask: bitfield mask
|
||||
* @data: new value
|
||||
|
|
@ -293,6 +295,7 @@ static inline void hw_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
|
|||
|
||||
/**
|
||||
* hw_test_and_clear: tests & clears a hw register
|
||||
* @ci: the controller
|
||||
* @reg: register index
|
||||
* @mask: bitfield mask
|
||||
*
|
||||
|
|
@ -309,6 +312,7 @@ static inline u32 hw_test_and_clear(struct ci_hdrc *ci, enum ci_hw_regs reg,
|
|||
|
||||
/**
|
||||
* hw_test_and_write: tests & writes a hw register
|
||||
* @ci: the controller
|
||||
* @reg: register index
|
||||
* @mask: bitfield mask
|
||||
* @data: new value
|
||||
|
|
@ -327,6 +331,8 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
|
|||
/**
|
||||
* ci_otg_is_fsm_mode: runtime check if otg controller
|
||||
* is in otg fsm mode.
|
||||
*
|
||||
* @ci: chipidea device
|
||||
*/
|
||||
static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ struct ci_hdrc_imx_data {
|
|||
|
||||
static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
|
||||
{
|
||||
struct platform_device *misc_pdev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct of_phandle_args args;
|
||||
struct imx_usbmisc_data *data;
|
||||
|
|
@ -79,8 +80,15 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
|
|||
}
|
||||
|
||||
data->index = args.args[0];
|
||||
|
||||
misc_pdev = of_find_device_by_node(args.np);
|
||||
of_node_put(args.np);
|
||||
|
||||
if (!misc_pdev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
data->dev = &misc_pdev->dev;
|
||||
|
||||
if (of_find_property(np, "disable-over-current", NULL))
|
||||
data->disable_oc = 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#define __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H
|
||||
|
||||
struct imx_usbmisc_data {
|
||||
struct device *dev;
|
||||
int index;
|
||||
|
||||
unsigned int disable_oc:1; /* over current detect disabled */
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ static int hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
|
|||
/**
|
||||
* hw_read_intr_enable: returns interrupt enable register
|
||||
*
|
||||
* @ci: the controller
|
||||
*
|
||||
* This function returns register data
|
||||
*/
|
||||
u32 hw_read_intr_enable(struct ci_hdrc *ci)
|
||||
|
|
@ -149,6 +151,8 @@ u32 hw_read_intr_enable(struct ci_hdrc *ci)
|
|||
/**
|
||||
* hw_read_intr_status: returns interrupt status register
|
||||
*
|
||||
* @ci: the controller
|
||||
*
|
||||
* This function returns register data
|
||||
*/
|
||||
u32 hw_read_intr_status(struct ci_hdrc *ci)
|
||||
|
|
@ -176,6 +180,8 @@ int hw_port_test_set(struct ci_hdrc *ci, u8 mode)
|
|||
/**
|
||||
* hw_port_test_get: reads port test mode value
|
||||
*
|
||||
* @ci: the controller
|
||||
*
|
||||
* This function returns port test mode value
|
||||
*/
|
||||
u8 hw_port_test_get(struct ci_hdrc *ci)
|
||||
|
|
@ -295,7 +301,7 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
|
|||
/**
|
||||
* ci_usb_phy_init: initialize phy according to different phy type
|
||||
* @ci: the controller
|
||||
*
|
||||
*
|
||||
* This function returns an error code if usb_phy_init has failed
|
||||
*/
|
||||
static int ci_usb_phy_init(struct ci_hdrc *ci)
|
||||
|
|
@ -473,6 +479,10 @@ static int ci_get_platdata(struct device *dev,
|
|||
PTR_ERR(platdata->reg_vbus));
|
||||
return PTR_ERR(platdata->reg_vbus);
|
||||
}
|
||||
/* Get TPL support */
|
||||
if (!platdata->tpl_support)
|
||||
platdata->tpl_support =
|
||||
of_usb_host_tpl_support(dev->of_node);
|
||||
}
|
||||
|
||||
if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL)
|
||||
|
|
@ -658,7 +668,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
goto deinit_phy;
|
||||
}
|
||||
|
||||
if (ci->is_otg) {
|
||||
if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) {
|
||||
/* Disable and clear all OTG irq */
|
||||
hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
|
||||
OTGSC_INT_STATUS_BITS);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ static int host_start(struct ci_hdrc *ci)
|
|||
hcd->has_tt = 1;
|
||||
|
||||
hcd->power_budget = ci->platdata->power_budget;
|
||||
hcd->phy = ci->transceiver;
|
||||
hcd->usb_phy = ci->transceiver;
|
||||
hcd->tpl_support = ci->platdata->tpl_support;
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = ci->hw_bank.cap;
|
||||
|
|
|
|||
|
|
@ -627,7 +627,7 @@ __acquires(hwep->lock)
|
|||
|
||||
if (hwreq->req.complete != NULL) {
|
||||
spin_unlock(hwep->lock);
|
||||
hwreq->req.complete(&hwep->ep, &hwreq->req);
|
||||
usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
|
||||
spin_lock(hwep->lock);
|
||||
}
|
||||
}
|
||||
|
|
@ -922,7 +922,7 @@ __acquires(hwep->lock)
|
|||
if ((hwep->type == USB_ENDPOINT_XFER_CONTROL) &&
|
||||
hwreq->req.length)
|
||||
hweptemp = hwep->ci->ep0in;
|
||||
hwreq->req.complete(&hweptemp->ep, &hwreq->req);
|
||||
usb_gadget_giveback_request(&hweptemp->ep, &hwreq->req);
|
||||
spin_lock(hwep->lock);
|
||||
}
|
||||
}
|
||||
|
|
@ -1347,7 +1347,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
|||
|
||||
if (hwreq->req.complete != NULL) {
|
||||
spin_unlock(hwep->lock);
|
||||
hwreq->req.complete(&hwep->ep, &hwreq->req);
|
||||
usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
|
||||
spin_lock(hwep->lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@
|
|||
|
||||
#define MX6_BM_OVER_CUR_DIS BIT(7)
|
||||
|
||||
#define VF610_OVER_CUR_DIS BIT(7)
|
||||
|
||||
struct usbmisc_ops {
|
||||
/* It's called once when probe a usb device */
|
||||
int (*init)(struct imx_usbmisc_data *data);
|
||||
|
|
@ -71,10 +73,9 @@ struct imx_usbmisc {
|
|||
const struct usbmisc_ops *ops;
|
||||
};
|
||||
|
||||
static struct imx_usbmisc *usbmisc;
|
||||
|
||||
static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
unsigned long flags;
|
||||
u32 val = 0;
|
||||
|
||||
|
|
@ -108,6 +109,7 @@ static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
|
|||
|
||||
static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
void __iomem *reg;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
|
@ -130,6 +132,7 @@ static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
|
|||
|
||||
static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
|
|
@ -160,6 +163,7 @@ static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
|
|||
|
||||
static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
void __iomem *reg = NULL;
|
||||
unsigned long flags;
|
||||
u32 val = 0;
|
||||
|
|
@ -204,6 +208,7 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
|
|||
|
||||
static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
|
|
@ -221,6 +226,26 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_vf610_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* Vybrid only has one misc register set, but in two different
|
||||
* areas. These is reflected in two instances of this driver.
|
||||
*/
|
||||
if (data->index >= 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->disable_oc) {
|
||||
reg = readl(usbmisc->base);
|
||||
writel(reg | VF610_OVER_CUR_DIS, usbmisc->base);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct usbmisc_ops imx25_usbmisc_ops = {
|
||||
.init = usbmisc_imx25_init,
|
||||
.post = usbmisc_imx25_post,
|
||||
|
|
@ -238,10 +263,14 @@ static const struct usbmisc_ops imx6q_usbmisc_ops = {
|
|||
.init = usbmisc_imx6q_init,
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops vf610_usbmisc_ops = {
|
||||
.init = usbmisc_vf610_init,
|
||||
};
|
||||
|
||||
int imx_usbmisc_init(struct imx_usbmisc_data *data)
|
||||
{
|
||||
if (!usbmisc)
|
||||
return -EPROBE_DEFER;
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
|
||||
if (!usbmisc->ops->init)
|
||||
return 0;
|
||||
return usbmisc->ops->init(data);
|
||||
|
|
@ -250,8 +279,8 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init);
|
|||
|
||||
int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
|
||||
{
|
||||
if (!usbmisc)
|
||||
return -EPROBE_DEFER;
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
|
||||
|
||||
if (!usbmisc->ops->post)
|
||||
return 0;
|
||||
return usbmisc->ops->post(data);
|
||||
|
|
@ -283,6 +312,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
|||
.compatible = "fsl,imx6q-usbmisc",
|
||||
.data = &imx6q_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,vf610-usbmisc",
|
||||
.data = &vf610_usbmisc_ops,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
|
||||
|
|
@ -294,9 +327,6 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
struct of_device_id *tmp_dev;
|
||||
|
||||
if (usbmisc)
|
||||
return -EBUSY;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
|
@ -325,15 +355,15 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
|||
tmp_dev = (struct of_device_id *)
|
||||
of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
|
||||
data->ops = (const struct usbmisc_ops *)tmp_dev->data;
|
||||
usbmisc = data;
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_usbmisc *usbmisc = dev_get_drvdata(&pdev->dev);
|
||||
clk_disable_unprepare(usbmisc->clk);
|
||||
usbmisc = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,5 +2,8 @@
|
|||
# Makefile for the usb common parts.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_USB_COMMON) += usb-common.o
|
||||
obj-$(CONFIG_USB_COMMON) += usb-common.o
|
||||
usb-common-y += common.o
|
||||
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
|
||||
|
||||
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
|
||||
|
|
|
|||
|
|
@ -139,6 +139,21 @@ enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_usb_get_maximum_speed);
|
||||
|
||||
/**
|
||||
* of_usb_host_tpl_support - to get if Targeted Peripheral List is supported
|
||||
* for given targeted hosts (non-PC hosts)
|
||||
* @np: Pointer to the given device_node
|
||||
*
|
||||
* The function gets if the targeted hosts support TPL or not
|
||||
*/
|
||||
bool of_usb_host_tpl_support(struct device_node *np)
|
||||
{
|
||||
if (of_find_property(np, "tpl-support", NULL))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_usb_host_tpl_support);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
57
drivers/usb/common/led.c
Normal file
57
drivers/usb/common/led.c
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* LED Triggers for USB Activity
|
||||
*
|
||||
* Copyright 2014 Michal Sojka <sojka@merica.cz>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#define BLINK_DELAY 30
|
||||
|
||||
static unsigned long usb_blink_delay = BLINK_DELAY;
|
||||
|
||||
DEFINE_LED_TRIGGER(ledtrig_usb_gadget);
|
||||
DEFINE_LED_TRIGGER(ledtrig_usb_host);
|
||||
|
||||
void usb_led_activity(enum usb_led_event ev)
|
||||
{
|
||||
struct led_trigger *trig = NULL;
|
||||
|
||||
switch (ev) {
|
||||
case USB_LED_EVENT_GADGET:
|
||||
trig = ledtrig_usb_gadget;
|
||||
break;
|
||||
case USB_LED_EVENT_HOST:
|
||||
trig = ledtrig_usb_host;
|
||||
break;
|
||||
}
|
||||
/* led_trigger_blink_oneshot() handles trig == NULL gracefully */
|
||||
led_trigger_blink_oneshot(trig, &usb_blink_delay, &usb_blink_delay, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_led_activity);
|
||||
|
||||
|
||||
static int __init ledtrig_usb_init(void)
|
||||
{
|
||||
led_trigger_register_simple("usb-gadget", &ledtrig_usb_gadget);
|
||||
led_trigger_register_simple("usb-host", &ledtrig_usb_host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ledtrig_usb_exit(void)
|
||||
{
|
||||
led_trigger_unregister_simple(ledtrig_usb_gadget);
|
||||
led_trigger_unregister_simple(ledtrig_usb_host);
|
||||
}
|
||||
|
||||
module_init(ledtrig_usb_init);
|
||||
module_exit(ledtrig_usb_exit);
|
||||
|
|
@ -56,22 +56,16 @@ config USB_OTG
|
|||
connector.
|
||||
|
||||
config USB_OTG_WHITELIST
|
||||
bool "Rely on OTG Targeted Peripherals List"
|
||||
depends on USB_OTG || EXPERT
|
||||
default y if USB_OTG
|
||||
bool "Rely on OTG and EH Targeted Peripherals List"
|
||||
depends on USB
|
||||
help
|
||||
If you say Y here, the "otg_whitelist.h" file will be used as a
|
||||
product whitelist, so USB peripherals not listed there will be
|
||||
rejected during enumeration. This behavior is required by the
|
||||
USB OTG specification for all devices not on your product's
|
||||
USB OTG and EH specification for all devices not on your product's
|
||||
"Targeted Peripherals List". "Embedded Hosts" are likewise
|
||||
allowed to support only a limited number of peripherals.
|
||||
|
||||
Otherwise, peripherals not listed there will only generate a
|
||||
warning and enumeration will continue. That's more like what
|
||||
normal Linux-USB hosts do (other than the warning), and is
|
||||
convenient for many stages of product development.
|
||||
|
||||
config USB_OTG_BLACKLIST_HUB
|
||||
bool "Disable external hubs"
|
||||
depends on USB_OTG || EXPERT
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
|
@ -1272,7 +1273,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
|
|||
* The usb core itself is however optimized for host controllers that can dma
|
||||
* using regular system memory - like pci devices doing bus mastering.
|
||||
*
|
||||
* To support host controllers with limited dma capabilites we provide dma
|
||||
* To support host controllers with limited dma capabilities we provide dma
|
||||
* bounce buffers. This feature can be enabled using the HCD_LOCAL_MEM flag.
|
||||
* For this to work properly the host controller code must first use the
|
||||
* function dma_declare_coherent_memory() to point out which memory area
|
||||
|
|
@ -1664,6 +1665,8 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
|
|||
usbmon_urb_complete(&hcd->self, urb, status);
|
||||
usb_anchor_suspend_wakeups(anchor);
|
||||
usb_unanchor_urb(urb);
|
||||
if (likely(status == 0))
|
||||
usb_led_activity(USB_LED_EVENT_HOST);
|
||||
|
||||
/* pass ownership to the completion handler */
|
||||
urb->status = status;
|
||||
|
|
@ -2301,7 +2304,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
|
|||
* Context: in_interrupt()
|
||||
*
|
||||
* Starts enumeration, with an immediate reset followed later by
|
||||
* khubd identifying and possibly configuring the device.
|
||||
* hub_wq identifying and possibly configuring the device.
|
||||
* This is needed by OTG controller drivers, where it helps meet
|
||||
* HNP protocol timing requirements for starting a port reset.
|
||||
*
|
||||
|
|
@ -2320,7 +2323,7 @@ int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)
|
|||
if (port_num && hcd->driver->start_port_reset)
|
||||
status = hcd->driver->start_port_reset(hcd, port_num);
|
||||
|
||||
/* run khubd shortly after (first) root port reset finishes;
|
||||
/* allocate hub_wq shortly after (first) root port reset finishes;
|
||||
* it may issue others, until at least 50 msecs have passed.
|
||||
*/
|
||||
if (status == 0)
|
||||
|
|
@ -2383,20 +2386,20 @@ void usb_hc_died (struct usb_hcd *hcd)
|
|||
if (hcd->rh_registered) {
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
||||
/* make khubd clean up old urbs and devices */
|
||||
/* make hub_wq clean up old urbs and devices */
|
||||
usb_set_device_state (hcd->self.root_hub,
|
||||
USB_STATE_NOTATTACHED);
|
||||
usb_kick_khubd (hcd->self.root_hub);
|
||||
usb_kick_hub_wq(hcd->self.root_hub);
|
||||
}
|
||||
if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) {
|
||||
hcd = hcd->shared_hcd;
|
||||
if (hcd->rh_registered) {
|
||||
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
||||
|
||||
/* make khubd clean up old urbs and devices */
|
||||
/* make hub_wq clean up old urbs and devices */
|
||||
usb_set_device_state(hcd->self.root_hub,
|
||||
USB_STATE_NOTATTACHED);
|
||||
usb_kick_khubd(hcd->self.root_hub);
|
||||
usb_kick_hub_wq(hcd->self.root_hub);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
|
||||
|
|
@ -2627,7 +2630,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|||
int retval;
|
||||
struct usb_device *rhdev;
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->phy) {
|
||||
if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->usb_phy) {
|
||||
struct usb_phy *phy = usb_get_phy_dev(hcd->self.controller, 0);
|
||||
|
||||
if (IS_ERR(phy)) {
|
||||
|
|
@ -2640,11 +2643,34 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|||
usb_put_phy(phy);
|
||||
return retval;
|
||||
}
|
||||
hcd->phy = phy;
|
||||
hcd->usb_phy = phy;
|
||||
hcd->remove_phy = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY)) {
|
||||
struct phy *phy = phy_get(hcd->self.controller, "usb");
|
||||
|
||||
if (IS_ERR(phy)) {
|
||||
retval = PTR_ERR(phy);
|
||||
if (retval == -EPROBE_DEFER)
|
||||
goto err_phy;
|
||||
} else {
|
||||
retval = phy_init(phy);
|
||||
if (retval) {
|
||||
phy_put(phy);
|
||||
goto err_phy;
|
||||
}
|
||||
retval = phy_power_on(phy);
|
||||
if (retval) {
|
||||
phy_exit(phy);
|
||||
phy_put(phy);
|
||||
goto err_phy;
|
||||
}
|
||||
hcd->phy = phy;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
|
||||
|
||||
/* Keep old behaviour if authorized_default is not in [0, 1]. */
|
||||
|
|
@ -2655,12 +2681,12 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
|||
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||
|
||||
/* HC is in reset state, but accessible. Now do the one-time init,
|
||||
* bottom up so that hcds can customize the root hubs before khubd
|
||||
* bottom up so that hcds can customize the root hubs before hub_wq
|
||||
* starts talking to them. (Note, bus id is assigned early too.)
|
||||
*/
|
||||
if ((retval = hcd_buffer_create(hcd)) != 0) {
|
||||
dev_dbg(hcd->self.controller, "pool alloc failed\n");
|
||||
goto err_remove_phy;
|
||||
goto err_create_buf;
|
||||
}
|
||||
|
||||
if ((retval = usb_register_bus(&hcd->self)) < 0)
|
||||
|
|
@ -2787,12 +2813,19 @@ err_allocate_root_hub:
|
|||
usb_deregister_bus(&hcd->self);
|
||||
err_register_bus:
|
||||
hcd_buffer_destroy(hcd);
|
||||
err_remove_phy:
|
||||
if (hcd->remove_phy && hcd->phy) {
|
||||
usb_phy_shutdown(hcd->phy);
|
||||
usb_put_phy(hcd->phy);
|
||||
err_create_buf:
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) {
|
||||
phy_power_off(hcd->phy);
|
||||
phy_exit(hcd->phy);
|
||||
phy_put(hcd->phy);
|
||||
hcd->phy = NULL;
|
||||
}
|
||||
err_phy:
|
||||
if (hcd->remove_phy && hcd->usb_phy) {
|
||||
usb_phy_shutdown(hcd->usb_phy);
|
||||
usb_put_phy(hcd->usb_phy);
|
||||
hcd->usb_phy = NULL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_add_hcd);
|
||||
|
|
@ -2864,11 +2897,18 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
|||
|
||||
usb_deregister_bus(&hcd->self);
|
||||
hcd_buffer_destroy(hcd);
|
||||
if (hcd->remove_phy && hcd->phy) {
|
||||
usb_phy_shutdown(hcd->phy);
|
||||
usb_put_phy(hcd->phy);
|
||||
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) {
|
||||
phy_power_off(hcd->phy);
|
||||
phy_exit(hcd->phy);
|
||||
phy_put(hcd->phy);
|
||||
hcd->phy = NULL;
|
||||
}
|
||||
if (hcd->remove_phy && hcd->usb_phy) {
|
||||
usb_phy_shutdown(hcd->usb_phy);
|
||||
usb_put_phy(hcd->usb_phy);
|
||||
hcd->usb_phy = NULL;
|
||||
}
|
||||
|
||||
usb_put_invalidate_rhdev(hcd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,8 @@
|
|||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
|
|
@ -32,6 +31,7 @@
|
|||
#include <asm/byteorder.h>
|
||||
|
||||
#include "hub.h"
|
||||
#include "otg_whitelist.h"
|
||||
|
||||
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
|
||||
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
|
||||
|
|
@ -41,14 +41,9 @@
|
|||
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
|
||||
static DEFINE_SPINLOCK(device_state_lock);
|
||||
|
||||
/* khubd's worklist and its lock */
|
||||
static DEFINE_SPINLOCK(hub_event_lock);
|
||||
static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
|
||||
|
||||
/* Wakes up khubd */
|
||||
static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
|
||||
|
||||
static struct task_struct *khubd_task;
|
||||
/* workqueue to process hub events */
|
||||
static struct workqueue_struct *hub_wq;
|
||||
static void hub_event(struct work_struct *work);
|
||||
|
||||
/* synchronize hub-port add/remove and peering operations */
|
||||
DEFINE_MUTEX(usb_port_peer_mutex);
|
||||
|
|
@ -104,6 +99,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
|
|||
#define HUB_DEBOUNCE_STEP 25
|
||||
#define HUB_DEBOUNCE_STABLE 100
|
||||
|
||||
static void hub_release(struct kref *kref);
|
||||
static int usb_reset_and_verify_device(struct usb_device *udev);
|
||||
|
||||
static inline char *portspeed(struct usb_hub *hub, int portstatus)
|
||||
|
|
@ -575,28 +571,39 @@ static int hub_port_status(struct usb_hub *hub, int port1,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void kick_khubd(struct usb_hub *hub)
|
||||
static void kick_hub_wq(struct usb_hub *hub)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct usb_interface *intf;
|
||||
|
||||
spin_lock_irqsave(&hub_event_lock, flags);
|
||||
if (!hub->disconnected && list_empty(&hub->event_list)) {
|
||||
list_add_tail(&hub->event_list, &hub_event_list);
|
||||
if (hub->disconnected || work_pending(&hub->events))
|
||||
return;
|
||||
|
||||
/* Suppress autosuspend until khubd runs */
|
||||
usb_autopm_get_interface_no_resume(
|
||||
to_usb_interface(hub->intfdev));
|
||||
wake_up(&khubd_wait);
|
||||
}
|
||||
spin_unlock_irqrestore(&hub_event_lock, flags);
|
||||
/*
|
||||
* Suppress autosuspend until the event is proceed.
|
||||
*
|
||||
* Be careful and make sure that the symmetric operation is
|
||||
* always called. We are here only when there is no pending
|
||||
* work for this hub. Therefore put the interface either when
|
||||
* the new work is called or when it is canceled.
|
||||
*/
|
||||
intf = to_usb_interface(hub->intfdev);
|
||||
usb_autopm_get_interface_no_resume(intf);
|
||||
kref_get(&hub->kref);
|
||||
|
||||
if (queue_work(hub_wq, &hub->events))
|
||||
return;
|
||||
|
||||
/* the work has already been scheduled */
|
||||
usb_autopm_put_interface_async(intf);
|
||||
kref_put(&hub->kref, hub_release);
|
||||
}
|
||||
|
||||
void usb_kick_khubd(struct usb_device *hdev)
|
||||
void usb_kick_hub_wq(struct usb_device *hdev)
|
||||
{
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
|
||||
|
||||
if (hub)
|
||||
kick_khubd(hub);
|
||||
kick_hub_wq(hub);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -618,7 +625,7 @@ void usb_wakeup_notification(struct usb_device *hdev,
|
|||
hub = usb_hub_to_struct_hub(hdev);
|
||||
if (hub) {
|
||||
set_bit(portnum, hub->wakeup_bits);
|
||||
kick_khubd(hub);
|
||||
kick_hub_wq(hub);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_wakeup_notification);
|
||||
|
|
@ -645,7 +652,7 @@ static void hub_irq(struct urb *urb)
|
|||
hub->error = status;
|
||||
/* FALL THROUGH */
|
||||
|
||||
/* let khubd handle things */
|
||||
/* let hub_wq handle things */
|
||||
case 0: /* we got data: port status changed */
|
||||
bits = 0;
|
||||
for (i = 0; i < urb->actual_length; ++i)
|
||||
|
|
@ -657,8 +664,8 @@ static void hub_irq(struct urb *urb)
|
|||
|
||||
hub->nerrors = 0;
|
||||
|
||||
/* Something happened, let khubd figure it out */
|
||||
kick_khubd(hub);
|
||||
/* Something happened, let hub_wq figure it out */
|
||||
kick_hub_wq(hub);
|
||||
|
||||
resubmit:
|
||||
if (hub->quiescing)
|
||||
|
|
@ -688,7 +695,7 @@ hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt)
|
|||
}
|
||||
|
||||
/*
|
||||
* enumeration blocks khubd for a long time. we use keventd instead, since
|
||||
* enumeration blocks hub_wq for a long time. we use keventd instead, since
|
||||
* long blocking there is the exception, not the rule. accordingly, HCDs
|
||||
* talking to TTs must queue control transfers (not just bulk and iso), so
|
||||
* both can talk to the same hub concurrently.
|
||||
|
|
@ -954,7 +961,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
|
|||
|
||||
/*
|
||||
* Disable a port and mark a logical connect-change event, so that some
|
||||
* time later khubd will disconnect() any existing usb_device on the port
|
||||
* time later hub_wq will disconnect() any existing usb_device on the port
|
||||
* and will re-enumerate if there actually is a device attached.
|
||||
*/
|
||||
static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
|
||||
|
|
@ -967,12 +974,12 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
|
|||
* - SRP saves power that way
|
||||
* - ... new call, TBD ...
|
||||
* That's easy if this hub can switch power per-port, and
|
||||
* khubd reactivates the port later (timer, SRP, etc).
|
||||
* hub_wq reactivates the port later (timer, SRP, etc).
|
||||
* Powerdown must be optional, because of reset/DFU.
|
||||
*/
|
||||
|
||||
set_bit(port1, hub->change_bits);
|
||||
kick_khubd(hub);
|
||||
kick_hub_wq(hub);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -980,7 +987,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
|
|||
* @udev: device to be disabled and removed
|
||||
* Context: @udev locked, must be able to sleep.
|
||||
*
|
||||
* After @udev's port has been disabled, khubd is notified and it will
|
||||
* After @udev's port has been disabled, hub_wq is notified and it will
|
||||
* see that the device has been disconnected. When the device is
|
||||
* physically unplugged and something is plugged in, the events will
|
||||
* be received and processed normally.
|
||||
|
|
@ -1100,7 +1107,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
init2:
|
||||
|
||||
/*
|
||||
* Check each port and set hub->change_bits to let khubd know
|
||||
* Check each port and set hub->change_bits to let hub_wq know
|
||||
* which ports need attention.
|
||||
*/
|
||||
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
|
||||
|
|
@ -1167,7 +1174,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
clear_bit(port1, hub->removed_bits);
|
||||
|
||||
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
|
||||
/* Tell khubd to disconnect the device or
|
||||
/* Tell hub_wq to disconnect the device or
|
||||
* check for a new connection
|
||||
*/
|
||||
if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
|
||||
|
|
@ -1180,7 +1187,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
USB_SS_PORT_LS_U0;
|
||||
/* The power session apparently survived the resume.
|
||||
* If there was an overcurrent or suspend change
|
||||
* (i.e., remote wakeup request), have khubd
|
||||
* (i.e., remote wakeup request), have hub_wq
|
||||
* take care of it. Look at the port link state
|
||||
* for USB 3.0 hubs, since they don't have a suspend
|
||||
* change bit, and they don't set the port link change
|
||||
|
|
@ -1201,7 +1208,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
set_bit(port1, hub->change_bits);
|
||||
|
||||
} else {
|
||||
/* The power session is gone; tell khubd */
|
||||
/* The power session is gone; tell hub_wq */
|
||||
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
|
||||
set_bit(port1, hub->change_bits);
|
||||
}
|
||||
|
|
@ -1209,10 +1216,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
|
||||
/* If no port-status-change flags were set, we don't need any
|
||||
* debouncing. If flags were set we can try to debounce the
|
||||
* ports all at once right now, instead of letting khubd do them
|
||||
* ports all at once right now, instead of letting hub_wq do them
|
||||
* one at a time later on.
|
||||
*
|
||||
* If any port-status changes do occur during this delay, khubd
|
||||
* If any port-status changes do occur during this delay, hub_wq
|
||||
* will see them later and handle them normally.
|
||||
*/
|
||||
if (need_debounce_delay) {
|
||||
|
|
@ -1240,7 +1247,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||
&hub->leds, LED_CYCLE_PERIOD);
|
||||
|
||||
/* Scan all ports that need attention */
|
||||
kick_khubd(hub);
|
||||
kick_hub_wq(hub);
|
||||
|
||||
/* Allow autosuspend if it was suppressed */
|
||||
if (type <= HUB_INIT3)
|
||||
|
|
@ -1273,7 +1280,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
|
|||
|
||||
cancel_delayed_work_sync(&hub->init_work);
|
||||
|
||||
/* khubd and related activity won't re-trigger */
|
||||
/* hub_wq and related activity won't re-trigger */
|
||||
hub->quiescing = 1;
|
||||
|
||||
if (type != HUB_SUSPEND) {
|
||||
|
|
@ -1284,7 +1291,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
|
|||
}
|
||||
}
|
||||
|
||||
/* Stop khubd and related activity */
|
||||
/* Stop hub_wq and related activity */
|
||||
usb_kill_urb(hub->urb);
|
||||
if (hub->has_indicators)
|
||||
cancel_delayed_work_sync(&hub->leds);
|
||||
|
|
@ -1606,7 +1613,7 @@ static int hub_configure(struct usb_hub *hub,
|
|||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
/* Update the HCD's internal representation of this hub before khubd
|
||||
/* Update the HCD's internal representation of this hub before hub_wq
|
||||
* starts getting port status changes for devices under the hub.
|
||||
*/
|
||||
if (hcd->driver->update_hub_device) {
|
||||
|
|
@ -1634,6 +1641,7 @@ static void hub_release(struct kref *kref)
|
|||
{
|
||||
struct usb_hub *hub = container_of(kref, struct usb_hub, kref);
|
||||
|
||||
usb_put_dev(hub->hdev);
|
||||
usb_put_intf(to_usb_interface(hub->intfdev));
|
||||
kfree(hub);
|
||||
}
|
||||
|
|
@ -1646,14 +1654,11 @@ static void hub_disconnect(struct usb_interface *intf)
|
|||
struct usb_device *hdev = interface_to_usbdev(intf);
|
||||
int port1;
|
||||
|
||||
/* Take the hub off the event list and don't let it be added again */
|
||||
spin_lock_irq(&hub_event_lock);
|
||||
if (!list_empty(&hub->event_list)) {
|
||||
list_del_init(&hub->event_list);
|
||||
usb_autopm_put_interface_no_suspend(intf);
|
||||
}
|
||||
/*
|
||||
* Stop adding new hub events. We do not want to block here and thus
|
||||
* will not try to remove any pending work item.
|
||||
*/
|
||||
hub->disconnected = 1;
|
||||
spin_unlock_irq(&hub_event_lock);
|
||||
|
||||
/* Disconnect all children and quiesce the hub */
|
||||
hub->error = 0;
|
||||
|
|
@ -1793,12 +1798,13 @@ descriptor_error:
|
|||
}
|
||||
|
||||
kref_init(&hub->kref);
|
||||
INIT_LIST_HEAD(&hub->event_list);
|
||||
hub->intfdev = &intf->dev;
|
||||
hub->hdev = hdev;
|
||||
INIT_DELAYED_WORK(&hub->leds, led_work);
|
||||
INIT_DELAYED_WORK(&hub->init_work, NULL);
|
||||
INIT_WORK(&hub->events, hub_event);
|
||||
usb_get_intf(intf);
|
||||
usb_get_dev(hdev);
|
||||
|
||||
usb_set_intfdata (intf, hub);
|
||||
intf->needs_remote_wakeup = 1;
|
||||
|
|
@ -1983,8 +1989,10 @@ void usb_set_device_state(struct usb_device *udev,
|
|||
|| new_state == USB_STATE_SUSPENDED)
|
||||
; /* No change to wakeup settings */
|
||||
else if (new_state == USB_STATE_CONFIGURED)
|
||||
wakeup = udev->actconfig->desc.bmAttributes
|
||||
& USB_CONFIG_ATT_WAKEUP;
|
||||
wakeup = (udev->quirks &
|
||||
USB_QUIRK_IGNORE_REMOTE_WAKEUP) ? 0 :
|
||||
udev->actconfig->desc.bmAttributes &
|
||||
USB_CONFIG_ATT_WAKEUP;
|
||||
else
|
||||
wakeup = 0;
|
||||
}
|
||||
|
|
@ -2037,7 +2045,8 @@ static void choose_devnum(struct usb_device *udev)
|
|||
int devnum;
|
||||
struct usb_bus *bus = udev->bus;
|
||||
|
||||
/* If khubd ever becomes multithreaded, this will need a lock */
|
||||
/* be safe when more hub events are proceed in parallel */
|
||||
mutex_lock(&bus->usb_address0_mutex);
|
||||
if (udev->wusb) {
|
||||
devnum = udev->portnum + 1;
|
||||
BUG_ON(test_bit(devnum, bus->devmap.devicemap));
|
||||
|
|
@ -2055,6 +2064,7 @@ static void choose_devnum(struct usb_device *udev)
|
|||
set_bit(devnum, bus->devmap.devicemap);
|
||||
udev->devnum = devnum;
|
||||
}
|
||||
mutex_unlock(&bus->usb_address0_mutex);
|
||||
}
|
||||
|
||||
static void release_devnum(struct usb_device *udev)
|
||||
|
|
@ -2205,9 +2215,6 @@ static void announce_device(struct usb_device *udev)
|
|||
static inline void announce_device(struct usb_device *udev) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_OTG
|
||||
#include "otg_whitelist.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* usb_enumerate_device_otg - FIXME (usbcore-internal)
|
||||
|
|
@ -2267,21 +2274,6 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_targeted(udev)) {
|
||||
|
||||
/* Maybe it can talk to us, though we can't talk to it.
|
||||
* (Includes HNP test device.)
|
||||
*/
|
||||
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
|
||||
err = usb_port_suspend(udev, PMSG_SUSPEND);
|
||||
if (err < 0)
|
||||
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
|
||||
}
|
||||
err = -ENOTSUPP;
|
||||
goto fail;
|
||||
}
|
||||
fail:
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2304,6 +2296,7 @@ fail:
|
|||
static int usb_enumerate_device(struct usb_device *udev)
|
||||
{
|
||||
int err;
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
if (udev->config == NULL) {
|
||||
err = usb_get_configuration(udev);
|
||||
|
|
@ -2325,6 +2318,20 @@ static int usb_enumerate_device(struct usb_device *udev)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_OTG_WHITELIST) && hcd->tpl_support &&
|
||||
!is_targeted(udev)) {
|
||||
/* Maybe it can talk to us, though we can't talk to it.
|
||||
* (Includes HNP test device.)
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_USB_OTG) && (udev->bus->b_hnp_enable
|
||||
|| udev->bus->is_b_host)) {
|
||||
err = usb_port_suspend(udev, PMSG_AUTO_SUSPEND);
|
||||
if (err < 0)
|
||||
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
|
||||
}
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
usb_detect_interface_quirks(udev);
|
||||
|
||||
return 0;
|
||||
|
|
@ -3070,7 +3077,7 @@ static unsigned wakeup_enabled_descendants(struct usb_device *udev)
|
|||
* Once VBUS drop breaks the circuit, the port it's using has to go through
|
||||
* normal re-enumeration procedures, starting with enabling VBUS power.
|
||||
* Other than re-initializing the hub (plug/unplug, except for root hubs),
|
||||
* Linux (2.6) currently has NO mechanisms to initiate that: no khubd
|
||||
* Linux (2.6) currently has NO mechanisms to initiate that: no hub_wq
|
||||
* timer, no SRP, no requests through sysfs.
|
||||
*
|
||||
* If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get
|
||||
|
|
@ -3212,7 +3219,7 @@ static int finish_port_resume(struct usb_device *udev)
|
|||
/* usb ch9 identifies four variants of SUSPENDED, based on what
|
||||
* state the device resumes to. Linux currently won't see the
|
||||
* first two on the host side; they'd be inside hub_port_init()
|
||||
* during many timeouts, but khubd can't suspend until later.
|
||||
* during many timeouts, but hub_wq can't suspend until later.
|
||||
*/
|
||||
usb_set_device_state(udev, udev->actconfig
|
||||
? USB_STATE_CONFIGURED
|
||||
|
|
@ -3577,7 +3584,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
|||
|
||||
dev_dbg(&intf->dev, "%s\n", __func__);
|
||||
|
||||
/* stop khubd and related activity */
|
||||
/* stop hub_wq and related activity */
|
||||
hub_quiesce(hub, HUB_SUSPEND);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4461,8 +4468,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
if (retval)
|
||||
goto fail;
|
||||
|
||||
if (hcd->phy && !hdev->parent)
|
||||
usb_phy_notify_connect(hcd->phy, udev->speed);
|
||||
if (hcd->usb_phy && !hdev->parent)
|
||||
usb_phy_notify_connect(hcd->usb_phy, udev->speed);
|
||||
|
||||
/*
|
||||
* Some superspeed devices have finished the link training process
|
||||
|
|
@ -4538,6 +4545,9 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
|
|||
struct usb_qualifier_descriptor *qual;
|
||||
int status;
|
||||
|
||||
if (udev->quirks & USB_QUIRK_DEVICE_QUALIFIER)
|
||||
return;
|
||||
|
||||
qual = kmalloc (sizeof *qual, GFP_KERNEL);
|
||||
if (qual == NULL)
|
||||
return;
|
||||
|
|
@ -4617,9 +4627,9 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
|
|||
|
||||
/* Disconnect any existing devices under this port */
|
||||
if (udev) {
|
||||
if (hcd->phy && !hdev->parent &&
|
||||
if (hcd->usb_phy && !hdev->parent &&
|
||||
!(portstatus & USB_PORT_STAT_CONNECTION))
|
||||
usb_phy_notify_disconnect(hcd->phy, udev->speed);
|
||||
usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
|
||||
usb_disconnect(&port_dev->child);
|
||||
}
|
||||
|
||||
|
|
@ -4970,10 +4980,10 @@ static void port_event(struct usb_hub *hub, int port1)
|
|||
* On disconnect USB3 protocol ports transit from U0 to
|
||||
* SS.Inactive to Rx.Detect. If this happens a warm-
|
||||
* reset is not needed, but a (re)connect may happen
|
||||
* before khubd runs and sees the disconnect, and the
|
||||
* before hub_wq runs and sees the disconnect, and the
|
||||
* device may be an unknown state.
|
||||
*
|
||||
* If the port went through SS.Inactive without khubd
|
||||
* If the port went through SS.Inactive without hub_wq
|
||||
* seeing it the C_LINK_STATE change flag will be set,
|
||||
* and we reset the dev to put it in a known state.
|
||||
*/
|
||||
|
|
@ -4992,10 +5002,8 @@ static void port_event(struct usb_hub *hub, int port1)
|
|||
hub_port_connect_change(hub, port1, portstatus, portchange);
|
||||
}
|
||||
|
||||
|
||||
static void hub_events(void)
|
||||
static void hub_event(struct work_struct *work)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct usb_device *hdev;
|
||||
struct usb_interface *intf;
|
||||
struct usb_hub *hub;
|
||||
|
|
@ -5004,166 +5012,117 @@ static void hub_events(void)
|
|||
u16 hubchange;
|
||||
int i, ret;
|
||||
|
||||
/*
|
||||
* We restart the list every time to avoid a deadlock with
|
||||
* deleting hubs downstream from this one. This should be
|
||||
* safe since we delete the hub from the event list.
|
||||
* Not the most efficient, but avoids deadlocks.
|
||||
*/
|
||||
while (1) {
|
||||
hub = container_of(work, struct usb_hub, events);
|
||||
hdev = hub->hdev;
|
||||
hub_dev = hub->intfdev;
|
||||
intf = to_usb_interface(hub_dev);
|
||||
|
||||
/* Grab the first entry at the beginning of the list */
|
||||
spin_lock_irq(&hub_event_lock);
|
||||
if (list_empty(&hub_event_list)) {
|
||||
spin_unlock_irq(&hub_event_lock);
|
||||
break;
|
||||
}
|
||||
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
|
||||
hdev->state, hdev->maxchild,
|
||||
/* NOTE: expects max 15 ports... */
|
||||
(u16) hub->change_bits[0],
|
||||
(u16) hub->event_bits[0]);
|
||||
|
||||
tmp = hub_event_list.next;
|
||||
list_del_init(tmp);
|
||||
/* Lock the device, then check to see if we were
|
||||
* disconnected while waiting for the lock to succeed. */
|
||||
usb_lock_device(hdev);
|
||||
if (unlikely(hub->disconnected))
|
||||
goto out_hdev_lock;
|
||||
|
||||
hub = list_entry(tmp, struct usb_hub, event_list);
|
||||
kref_get(&hub->kref);
|
||||
hdev = hub->hdev;
|
||||
usb_get_dev(hdev);
|
||||
spin_unlock_irq(&hub_event_lock);
|
||||
/* If the hub has died, clean up after it */
|
||||
if (hdev->state == USB_STATE_NOTATTACHED) {
|
||||
hub->error = -ENODEV;
|
||||
hub_quiesce(hub, HUB_DISCONNECT);
|
||||
goto out_hdev_lock;
|
||||
}
|
||||
|
||||
hub_dev = hub->intfdev;
|
||||
intf = to_usb_interface(hub_dev);
|
||||
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
|
||||
hdev->state, hdev->maxchild,
|
||||
/* NOTE: expects max 15 ports... */
|
||||
(u16) hub->change_bits[0],
|
||||
(u16) hub->event_bits[0]);
|
||||
/* Autoresume */
|
||||
ret = usb_autopm_get_interface(intf);
|
||||
if (ret) {
|
||||
dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
|
||||
goto out_hdev_lock;
|
||||
}
|
||||
|
||||
/* Lock the device, then check to see if we were
|
||||
* disconnected while waiting for the lock to succeed. */
|
||||
usb_lock_device(hdev);
|
||||
if (unlikely(hub->disconnected))
|
||||
goto loop_disconnected;
|
||||
/* If this is an inactive hub, do nothing */
|
||||
if (hub->quiescing)
|
||||
goto out_autopm;
|
||||
|
||||
/* If the hub has died, clean up after it */
|
||||
if (hdev->state == USB_STATE_NOTATTACHED) {
|
||||
hub->error = -ENODEV;
|
||||
hub_quiesce(hub, HUB_DISCONNECT);
|
||||
goto loop;
|
||||
}
|
||||
if (hub->error) {
|
||||
dev_dbg(hub_dev, "resetting for error %d\n", hub->error);
|
||||
|
||||
/* Autoresume */
|
||||
ret = usb_autopm_get_interface(intf);
|
||||
ret = usb_reset_device(hdev);
|
||||
if (ret) {
|
||||
dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
|
||||
goto loop;
|
||||
dev_dbg(hub_dev, "error resetting hub: %d\n", ret);
|
||||
goto out_autopm;
|
||||
}
|
||||
|
||||
/* If this is an inactive hub, do nothing */
|
||||
if (hub->quiescing)
|
||||
goto loop_autopm;
|
||||
hub->nerrors = 0;
|
||||
hub->error = 0;
|
||||
}
|
||||
|
||||
if (hub->error) {
|
||||
dev_dbg (hub_dev, "resetting for error %d\n",
|
||||
hub->error);
|
||||
/* deal with port status changes */
|
||||
for (i = 1; i <= hdev->maxchild; i++) {
|
||||
struct usb_port *port_dev = hub->ports[i - 1];
|
||||
|
||||
ret = usb_reset_device(hdev);
|
||||
if (ret) {
|
||||
dev_dbg (hub_dev,
|
||||
"error resetting hub: %d\n", ret);
|
||||
goto loop_autopm;
|
||||
}
|
||||
|
||||
hub->nerrors = 0;
|
||||
hub->error = 0;
|
||||
if (test_bit(i, hub->event_bits)
|
||||
|| test_bit(i, hub->change_bits)
|
||||
|| test_bit(i, hub->wakeup_bits)) {
|
||||
/*
|
||||
* The get_noresume and barrier ensure that if
|
||||
* the port was in the process of resuming, we
|
||||
* flush that work and keep the port active for
|
||||
* the duration of the port_event(). However,
|
||||
* if the port is runtime pm suspended
|
||||
* (powered-off), we leave it in that state, run
|
||||
* an abbreviated port_event(), and move on.
|
||||
*/
|
||||
pm_runtime_get_noresume(&port_dev->dev);
|
||||
pm_runtime_barrier(&port_dev->dev);
|
||||
usb_lock_port(port_dev);
|
||||
port_event(hub, i);
|
||||
usb_unlock_port(port_dev);
|
||||
pm_runtime_put_sync(&port_dev->dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* deal with port status changes */
|
||||
for (i = 1; i <= hdev->maxchild; i++) {
|
||||
struct usb_port *port_dev = hub->ports[i - 1];
|
||||
|
||||
if (test_bit(i, hub->event_bits)
|
||||
|| test_bit(i, hub->change_bits)
|
||||
|| test_bit(i, hub->wakeup_bits)) {
|
||||
/*
|
||||
* The get_noresume and barrier ensure that if
|
||||
* the port was in the process of resuming, we
|
||||
* flush that work and keep the port active for
|
||||
* the duration of the port_event(). However,
|
||||
* if the port is runtime pm suspended
|
||||
* (powered-off), we leave it in that state, run
|
||||
* an abbreviated port_event(), and move on.
|
||||
*/
|
||||
pm_runtime_get_noresume(&port_dev->dev);
|
||||
pm_runtime_barrier(&port_dev->dev);
|
||||
usb_lock_port(port_dev);
|
||||
port_event(hub, i);
|
||||
usb_unlock_port(port_dev);
|
||||
pm_runtime_put_sync(&port_dev->dev);
|
||||
}
|
||||
/* deal with hub status changes */
|
||||
if (test_and_clear_bit(0, hub->event_bits) == 0)
|
||||
; /* do nothing */
|
||||
else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
|
||||
dev_err(hub_dev, "get_hub_status failed\n");
|
||||
else {
|
||||
if (hubchange & HUB_CHANGE_LOCAL_POWER) {
|
||||
dev_dbg(hub_dev, "power change\n");
|
||||
clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
|
||||
if (hubstatus & HUB_STATUS_LOCAL_POWER)
|
||||
/* FIXME: Is this always true? */
|
||||
hub->limited_power = 1;
|
||||
else
|
||||
hub->limited_power = 0;
|
||||
}
|
||||
if (hubchange & HUB_CHANGE_OVERCURRENT) {
|
||||
u16 status = 0;
|
||||
u16 unused;
|
||||
|
||||
/* deal with hub status changes */
|
||||
if (test_and_clear_bit(0, hub->event_bits) == 0)
|
||||
; /* do nothing */
|
||||
else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
|
||||
dev_err (hub_dev, "get_hub_status failed\n");
|
||||
else {
|
||||
if (hubchange & HUB_CHANGE_LOCAL_POWER) {
|
||||
dev_dbg (hub_dev, "power change\n");
|
||||
clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
|
||||
if (hubstatus & HUB_STATUS_LOCAL_POWER)
|
||||
/* FIXME: Is this always true? */
|
||||
hub->limited_power = 1;
|
||||
else
|
||||
hub->limited_power = 0;
|
||||
}
|
||||
if (hubchange & HUB_CHANGE_OVERCURRENT) {
|
||||
u16 status = 0;
|
||||
u16 unused;
|
||||
|
||||
dev_dbg(hub_dev, "over-current change\n");
|
||||
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
|
||||
msleep(500); /* Cool down */
|
||||
hub_power_on(hub, true);
|
||||
hub_hub_status(hub, &status, &unused);
|
||||
if (status & HUB_STATUS_OVERCURRENT)
|
||||
dev_err(hub_dev, "over-current "
|
||||
"condition\n");
|
||||
}
|
||||
dev_dbg(hub_dev, "over-current change\n");
|
||||
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
|
||||
msleep(500); /* Cool down */
|
||||
hub_power_on(hub, true);
|
||||
hub_hub_status(hub, &status, &unused);
|
||||
if (status & HUB_STATUS_OVERCURRENT)
|
||||
dev_err(hub_dev, "over-current condition\n");
|
||||
}
|
||||
}
|
||||
|
||||
loop_autopm:
|
||||
/* Balance the usb_autopm_get_interface() above */
|
||||
usb_autopm_put_interface_no_suspend(intf);
|
||||
loop:
|
||||
/* Balance the usb_autopm_get_interface_no_resume() in
|
||||
* kick_khubd() and allow autosuspend.
|
||||
*/
|
||||
usb_autopm_put_interface(intf);
|
||||
loop_disconnected:
|
||||
usb_unlock_device(hdev);
|
||||
usb_put_dev(hdev);
|
||||
kref_put(&hub->kref, hub_release);
|
||||
out_autopm:
|
||||
/* Balance the usb_autopm_get_interface() above */
|
||||
usb_autopm_put_interface_no_suspend(intf);
|
||||
out_hdev_lock:
|
||||
usb_unlock_device(hdev);
|
||||
|
||||
} /* end while (1) */
|
||||
}
|
||||
|
||||
static int hub_thread(void *__unused)
|
||||
{
|
||||
/* khubd needs to be freezable to avoid interfering with USB-PERSIST
|
||||
* port handover. Otherwise it might see that a full-speed device
|
||||
* was gone before the EHCI controller had handed its port over to
|
||||
* the companion full-speed controller.
|
||||
*/
|
||||
set_freezable();
|
||||
|
||||
do {
|
||||
hub_events();
|
||||
wait_event_freezable(khubd_wait,
|
||||
!list_empty(&hub_event_list) ||
|
||||
kthread_should_stop());
|
||||
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
|
||||
|
||||
pr_debug("%s: khubd exiting\n", usbcore_name);
|
||||
return 0;
|
||||
/* Balance the stuff in kick_hub_wq() and allow autosuspend */
|
||||
usb_autopm_put_interface(intf);
|
||||
kref_put(&hub->kref, hub_release);
|
||||
}
|
||||
|
||||
static const struct usb_device_id hub_id_table[] = {
|
||||
|
|
@ -5203,20 +5162,26 @@ int usb_hub_init(void)
|
|||
return -1;
|
||||
}
|
||||
|
||||
khubd_task = kthread_run(hub_thread, NULL, "khubd");
|
||||
if (!IS_ERR(khubd_task))
|
||||
/*
|
||||
* The workqueue needs to be freezable to avoid interfering with
|
||||
* USB-PERSIST port handover. Otherwise it might see that a full-speed
|
||||
* device was gone before the EHCI controller had handed its port
|
||||
* over to the companion full-speed controller.
|
||||
*/
|
||||
hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
|
||||
if (hub_wq)
|
||||
return 0;
|
||||
|
||||
/* Fall through if kernel_thread failed */
|
||||
usb_deregister(&hub_driver);
|
||||
printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
|
||||
pr_err("%s: can't allocate workqueue for usb hub\n", usbcore_name);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void usb_hub_cleanup(void)
|
||||
{
|
||||
kthread_stop(khubd_task);
|
||||
destroy_workqueue(hub_wq);
|
||||
|
||||
/*
|
||||
* Hub resources are freed for us by usb_deregister. It calls
|
||||
|
|
@ -5325,7 +5290,7 @@ static int descriptors_changed(struct usb_device *udev,
|
|||
* former operating configuration. If the reset fails, or the device's
|
||||
* descriptors change from their values before the reset, or the original
|
||||
* configuration and altsettings cannot be restored, a flag will be set
|
||||
* telling khubd to pretend the device has been disconnected and then
|
||||
* telling hub_wq to pretend the device has been disconnected and then
|
||||
* re-connected. All drivers will be unbound, and the device will be
|
||||
* re-enumerated and probed all over again.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ struct usb_hub {
|
|||
int error; /* last reported error */
|
||||
int nerrors; /* track consecutive errors */
|
||||
|
||||
struct list_head event_list; /* hubs w/data or errs ready */
|
||||
unsigned long event_bits[1]; /* status change bitmask */
|
||||
unsigned long change_bits[1]; /* ports with logical connect
|
||||
status change */
|
||||
|
|
@ -77,6 +76,7 @@ struct usb_hub {
|
|||
u8 indicator[USB_MAXCHILDREN];
|
||||
struct delayed_work leds;
|
||||
struct delayed_work init_work;
|
||||
struct work_struct events;
|
||||
struct usb_port **ports;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -770,9 +770,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf)
|
|||
dev->string_langid = 0x0409;
|
||||
dev->have_langid = 1;
|
||||
dev_err(&dev->dev,
|
||||
"string descriptor 0 malformed (err = %d), "
|
||||
"defaulting to 0x%04x\n",
|
||||
err, dev->string_langid);
|
||||
"language id specifier not provided by device, defaulting to English\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* This OTG Whitelist is the OTG "Targeted Peripheral List". It should
|
||||
* mostly use of USB_DEVICE() or USB_DEVICE_VER() entries..
|
||||
* This OTG and Embedded Host Whitelist is "Targeted Peripheral List".
|
||||
* It should mostly use of USB_DEVICE() or USB_DEVICE_VER() entries..
|
||||
*
|
||||
* YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING!
|
||||
*/
|
||||
|
|
@ -50,10 +50,6 @@ static int is_targeted(struct usb_device *dev)
|
|||
{
|
||||
struct usb_device_id *id = whitelist_table;
|
||||
|
||||
/* possible in developer configs only! */
|
||||
if (!dev->bus->otg_port)
|
||||
return 1;
|
||||
|
||||
/* HNP test device is _never_ targeted (see OTG spec 6.6.6) */
|
||||
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a &&
|
||||
le16_to_cpu(dev->descriptor.idProduct) == 0xbadd))
|
||||
|
|
@ -103,10 +99,7 @@ static int is_targeted(struct usb_device *dev)
|
|||
dev_err(&dev->dev, "device v%04x p%04x is not supported\n",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
#ifdef CONFIG_USB_OTG_WHITELIST
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,10 @@ static const struct usb_device_id usb_quirk_list[] = {
|
|||
{ USB_DEVICE(0x04e8, 0x6601), .driver_info =
|
||||
USB_QUIRK_CONFIG_INTF_STRINGS },
|
||||
|
||||
/* Elan Touchscreen */
|
||||
{ USB_DEVICE(0x04f3, 0x0089), .driver_info =
|
||||
USB_QUIRK_DEVICE_QUALIFIER },
|
||||
|
||||
/* Roland SC-8820 */
|
||||
{ USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
|
|
@ -159,6 +163,10 @@ static const struct usb_device_id usb_quirk_list[] = {
|
|||
/* USB3503 */
|
||||
{ USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* ASUS Base Station(T100) */
|
||||
{ USB_DEVICE(0x0b05, 0x17e0), .driver_info =
|
||||
USB_QUIRK_IGNORE_REMOTE_WAKEUP },
|
||||
|
||||
{ } /* terminating entry must be last */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ static inline unsigned usb_get_max_power(struct usb_device *udev,
|
|||
return c->desc.bMaxPower * mul;
|
||||
}
|
||||
|
||||
extern void usb_kick_khubd(struct usb_device *dev);
|
||||
extern void usb_kick_hub_wq(struct usb_device *dev);
|
||||
extern int usb_match_one_id_intf(struct usb_device *dev,
|
||||
struct usb_host_interface *intf,
|
||||
const struct usb_device_id *id);
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
|
|||
{
|
||||
u32 greset;
|
||||
int count = 0;
|
||||
u32 gusbcfg;
|
||||
|
||||
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
||||
|
||||
|
|
@ -148,6 +149,23 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
|
|||
}
|
||||
} while (greset & GRSTCTL_CSFTRST);
|
||||
|
||||
if (hsotg->dr_mode == USB_DR_MODE_HOST) {
|
||||
gusbcfg = readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
|
||||
gusbcfg |= GUSBCFG_FORCEHOSTMODE;
|
||||
writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
} else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
|
||||
gusbcfg = readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
gusbcfg |= GUSBCFG_FORCEDEVMODE;
|
||||
writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
} else if (hsotg->dr_mode == USB_DR_MODE_OTG) {
|
||||
gusbcfg = readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
|
||||
writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: This long sleep is _very_ important, otherwise the core will
|
||||
* not stay in host mode after a connector ID change!
|
||||
|
|
@ -2674,23 +2692,23 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
|||
hwcfg2 = readl(hsotg->regs + GHWCFG2);
|
||||
hwcfg3 = readl(hsotg->regs + GHWCFG3);
|
||||
hwcfg4 = readl(hsotg->regs + GHWCFG4);
|
||||
gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ);
|
||||
grxfsiz = readl(hsotg->regs + GRXFSIZ);
|
||||
|
||||
dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1);
|
||||
dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2);
|
||||
dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3);
|
||||
dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
|
||||
dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
|
||||
dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
|
||||
|
||||
/* Force host mode to get HPTXFSIZ exact power on value */
|
||||
/* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */
|
||||
gusbcfg = readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg |= GUSBCFG_FORCEHOSTMODE;
|
||||
writel(gusbcfg, hsotg->regs + GUSBCFG);
|
||||
usleep_range(100000, 150000);
|
||||
|
||||
gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ);
|
||||
hptxfsiz = readl(hsotg->regs + HPTXFSIZ);
|
||||
dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
|
||||
dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
|
||||
gusbcfg = readl(hsotg->regs + GUSBCFG);
|
||||
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
|
||||
|
|
@ -2725,6 +2743,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
|||
width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
|
||||
GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
|
||||
hw->max_transfer_size = (1 << (width + 11)) - 1;
|
||||
/*
|
||||
* Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates
|
||||
* coherent buffers with this size, and if it's too large we can
|
||||
* exhaust the coherent DMA pool.
|
||||
*/
|
||||
if (hw->max_transfer_size > 65535)
|
||||
hw->max_transfer_size = 65535;
|
||||
width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
|
||||
GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
|
||||
hw->max_packet_count = (1 << (width + 4)) - 1;
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ struct s3c_hsotg_ep {
|
|||
unsigned int last_load;
|
||||
unsigned int fifo_load;
|
||||
unsigned short fifo_size;
|
||||
unsigned short fifo_index;
|
||||
|
||||
unsigned char dir_in;
|
||||
unsigned char index;
|
||||
|
|
@ -194,8 +195,10 @@ struct s3c_hsotg {
|
|||
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
|
||||
|
||||
u32 phyif;
|
||||
int fifo_mem;
|
||||
unsigned int dedicated_fifos:1;
|
||||
unsigned char num_of_eps;
|
||||
u32 fifo_map;
|
||||
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug_file;
|
||||
|
|
@ -501,6 +504,10 @@ struct dwc2_hw_params {
|
|||
* a_peripheral and b_device=>b_host) this may not match
|
||||
* the core, but allows the software to determine
|
||||
* transitions
|
||||
* @dr_mode: Requested mode of operation, one of following:
|
||||
* - USB_DR_MODE_PERIPHERAL
|
||||
* - USB_DR_MODE_HOST
|
||||
* - USB_DR_MODE_OTG
|
||||
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
|
||||
* transfer are in process of being queued
|
||||
* @srp_success: Stores status of SRP request in the case of a FS PHY
|
||||
|
|
@ -592,6 +599,7 @@ struct dwc2_hsotg {
|
|||
/** Params to actually use */
|
||||
struct dwc2_core_params *core_params;
|
||||
enum usb_otg_state op_state;
|
||||
enum usb_dr_mode dr_mode;
|
||||
|
||||
unsigned int queuing_high_bandwidth:1;
|
||||
unsigned int srp_success:1;
|
||||
|
|
|
|||
|
|
@ -182,16 +182,33 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
|
|||
|
||||
/* start at the end of the GNPTXFSIZ, rounded up */
|
||||
addr = 2048 + 1024;
|
||||
size = 768;
|
||||
|
||||
/*
|
||||
* currently we allocate TX FIFOs for all possible endpoints,
|
||||
* and assume that they are all the same size.
|
||||
* Because we have not enough memory to have each TX FIFO of size at
|
||||
* least 3072 bytes (the maximum single packet size), we create four
|
||||
* FIFOs of lenght 1024, and four of length 3072 bytes, and assing
|
||||
* them to endpoints dynamically according to maxpacket size value of
|
||||
* given endpoint.
|
||||
*/
|
||||
|
||||
for (ep = 1; ep <= 15; ep++) {
|
||||
/* 256*4=1024 bytes FIFO length */
|
||||
size = 256;
|
||||
for (ep = 1; ep <= 4; ep++) {
|
||||
val = addr;
|
||||
val |= size << FIFOSIZE_DEPTH_SHIFT;
|
||||
WARN_ONCE(addr + size > hsotg->fifo_mem,
|
||||
"insufficient fifo memory");
|
||||
addr += size;
|
||||
|
||||
writel(val, hsotg->regs + DPTXFSIZN(ep));
|
||||
}
|
||||
/* 768*4=3072 bytes FIFO length */
|
||||
size = 768;
|
||||
for (ep = 5; ep <= 8; ep++) {
|
||||
val = addr;
|
||||
val |= size << FIFOSIZE_DEPTH_SHIFT;
|
||||
WARN_ONCE(addr + size > hsotg->fifo_mem,
|
||||
"insufficient fifo memory");
|
||||
addr += size;
|
||||
|
||||
writel(val, hsotg->regs + DPTXFSIZN(ep));
|
||||
|
|
@ -987,8 +1004,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
|
|||
hs_req = ep->req;
|
||||
ep->req = NULL;
|
||||
list_del_init(&hs_req->queue);
|
||||
hs_req->req.complete(&ep->ep,
|
||||
&hs_req->req);
|
||||
usb_gadget_giveback_request(&ep->ep,
|
||||
&hs_req->req);
|
||||
}
|
||||
|
||||
/* If we have pending request, then start it */
|
||||
|
|
@ -1245,7 +1262,7 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
|
|||
|
||||
if (hs_req->req.complete) {
|
||||
spin_unlock(&hsotg->lock);
|
||||
hs_req->req.complete(&hs_ep->ep, &hs_req->req);
|
||||
usb_gadget_giveback_request(&hs_ep->ep, &hs_req->req);
|
||||
spin_lock(&hsotg->lock);
|
||||
}
|
||||
|
||||
|
|
@ -1832,7 +1849,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
|
|||
if (dir_in) {
|
||||
int epctl = readl(hsotg->regs + epctl_reg);
|
||||
|
||||
s3c_hsotg_txfifo_flush(hsotg, idx);
|
||||
s3c_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
|
||||
|
||||
if ((epctl & DXEPCTL_STALL) &&
|
||||
(epctl & DXEPCTL_EPTYPE_BULK)) {
|
||||
|
|
@ -1981,6 +1998,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
|
|||
int result, bool force)
|
||||
{
|
||||
struct s3c_hsotg_req *req, *treq;
|
||||
unsigned size;
|
||||
|
||||
list_for_each_entry_safe(req, treq, &ep->queue, queue) {
|
||||
/*
|
||||
|
|
@ -1994,9 +2012,11 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
|
|||
s3c_hsotg_complete_request(hsotg, ep, req,
|
||||
result);
|
||||
}
|
||||
if (hsotg->dedicated_fifos)
|
||||
if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072)
|
||||
s3c_hsotg_txfifo_flush(hsotg, ep->index);
|
||||
if (!hsotg->dedicated_fifos)
|
||||
return;
|
||||
size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4;
|
||||
if (size < ep->fifo_size)
|
||||
s3c_hsotg_txfifo_flush(hsotg, ep->fifo_index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2437,6 +2457,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
|||
u32 epctrl;
|
||||
u32 mps;
|
||||
int dir_in;
|
||||
int i, val, size;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(hsotg->dev,
|
||||
|
|
@ -2509,17 +2530,8 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
|||
break;
|
||||
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
if (dir_in) {
|
||||
/*
|
||||
* Allocate our TxFNum by simply using the index
|
||||
* of the endpoint for the moment. We could do
|
||||
* something better if the host indicates how
|
||||
* many FIFOs we are expecting to use.
|
||||
*/
|
||||
|
||||
if (dir_in)
|
||||
hs_ep->periodic = 1;
|
||||
epctrl |= DXEPCTL_TXFNUM(index);
|
||||
}
|
||||
|
||||
epctrl |= DXEPCTL_EPTYPE_INTERRUPT;
|
||||
break;
|
||||
|
|
@ -2533,8 +2545,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
|||
* if the hardware has dedicated fifos, we must give each IN EP
|
||||
* a unique tx-fifo even if it is non-periodic.
|
||||
*/
|
||||
if (dir_in && hsotg->dedicated_fifos)
|
||||
epctrl |= DXEPCTL_TXFNUM(index);
|
||||
if (dir_in && hsotg->dedicated_fifos) {
|
||||
size = hs_ep->ep.maxpacket*hs_ep->mc;
|
||||
for (i = 1; i <= 8; ++i) {
|
||||
if (hsotg->fifo_map & (1<<i))
|
||||
continue;
|
||||
val = readl(hsotg->regs + DPTXFSIZN(i));
|
||||
val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
|
||||
if (val < size)
|
||||
continue;
|
||||
hsotg->fifo_map |= 1<<i;
|
||||
|
||||
epctrl |= DXEPCTL_TXFNUM(i);
|
||||
hs_ep->fifo_index = i;
|
||||
hs_ep->fifo_size = val;
|
||||
break;
|
||||
}
|
||||
if (i == 8)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* for non control endpoints, set PID to D0 */
|
||||
if (index)
|
||||
|
|
@ -2568,7 +2597,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
|
|||
u32 epctrl_reg;
|
||||
u32 ctrl;
|
||||
|
||||
dev_info(hsotg->dev, "%s(ep %p)\n", __func__, ep);
|
||||
dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
|
||||
|
||||
if (ep == &hsotg->eps[0].ep) {
|
||||
dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
|
||||
|
|
@ -2581,6 +2610,9 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep)
|
|||
/* terminate all requests with shutdown */
|
||||
kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false);
|
||||
|
||||
hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
|
||||
hs_ep->fifo_index = 0;
|
||||
hs_ep->fifo_size = 0;
|
||||
|
||||
ctrl = readl(hsotg->regs + epctrl_reg);
|
||||
ctrl &= ~DXEPCTL_EPENA;
|
||||
|
|
@ -2626,7 +2658,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
|||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
unsigned long flags;
|
||||
|
||||
dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
|
||||
dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
|
||||
|
||||
spin_lock_irqsave(&hs->lock, flags);
|
||||
|
||||
|
|
@ -2861,6 +2893,8 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
|||
hsotg->gadget.dev.of_node = hsotg->dev->of_node;
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
clk_enable(hsotg->clk);
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
if (ret) {
|
||||
|
|
@ -2909,6 +2943,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
|
|||
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||
|
||||
clk_disable(hsotg->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2935,13 +2971,15 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
|
|||
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||
unsigned long flags = 0;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on);
|
||||
dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
if (is_on) {
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
clk_enable(hsotg->clk);
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
} else {
|
||||
clk_disable(hsotg->clk);
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
}
|
||||
|
||||
|
|
@ -2972,7 +3010,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
|||
struct s3c_hsotg_ep *hs_ep,
|
||||
int epnum)
|
||||
{
|
||||
u32 ptxfifo;
|
||||
char *dir;
|
||||
|
||||
if (epnum == 0)
|
||||
|
|
@ -3000,15 +3037,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
|||
usb_ep_set_maxpacket_limit(&hs_ep->ep, epnum ? 1024 : EP0_MPS_LIMIT);
|
||||
hs_ep->ep.ops = &s3c_hsotg_ep_ops;
|
||||
|
||||
/*
|
||||
* Read the FIFO size for the Periodic TX FIFO, even if we're
|
||||
* an OUT endpoint, we may as well do this if in future the
|
||||
* code is changed to make each endpoint's direction changeable.
|
||||
*/
|
||||
|
||||
ptxfifo = readl(hsotg->regs + DPTXFSIZN(epnum));
|
||||
hs_ep->fifo_size = FIFOSIZE_DEPTH_GET(ptxfifo) * 4;
|
||||
|
||||
/*
|
||||
* if we're using dma, we need to set the next-endpoint pointer
|
||||
* to be something valid.
|
||||
|
|
@ -3029,19 +3057,22 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
|||
*/
|
||||
static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg)
|
||||
{
|
||||
u32 cfg2, cfg4;
|
||||
u32 cfg2, cfg3, cfg4;
|
||||
/* check hardware configuration */
|
||||
|
||||
cfg2 = readl(hsotg->regs + 0x48);
|
||||
hsotg->num_of_eps = (cfg2 >> 10) & 0xF;
|
||||
|
||||
dev_info(hsotg->dev, "EPs:%d\n", hsotg->num_of_eps);
|
||||
cfg3 = readl(hsotg->regs + 0x4C);
|
||||
hsotg->fifo_mem = (cfg3 >> 16);
|
||||
|
||||
cfg4 = readl(hsotg->regs + 0x50);
|
||||
hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
|
||||
|
||||
dev_info(hsotg->dev, "%s fifos\n",
|
||||
hsotg->dedicated_fifos ? "dedicated" : "shared");
|
||||
dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
|
||||
hsotg->num_of_eps,
|
||||
hsotg->dedicated_fifos ? "dedicated" : "shared",
|
||||
hsotg->fifo_mem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3392,6 +3423,9 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
|||
if (!hsotg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set default UTMI width */
|
||||
hsotg->phyif = GUSBCFG_PHYIF16;
|
||||
|
||||
/*
|
||||
* Attempt to find a generic PHY, then look for an old style
|
||||
* USB PHY, finally fall back to pdata
|
||||
|
|
@ -3410,8 +3444,15 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
|||
hsotg->plat = plat;
|
||||
} else
|
||||
hsotg->uphy = uphy;
|
||||
} else
|
||||
} else {
|
||||
hsotg->phy = phy;
|
||||
/*
|
||||
* If using the generic PHY framework, check if the PHY bus
|
||||
* width is 8-bit and set the phyif appropriately.
|
||||
*/
|
||||
if (phy_get_bus_width(phy) == 8)
|
||||
hsotg->phyif = GUSBCFG_PHYIF8;
|
||||
}
|
||||
|
||||
hsotg->dev = dev;
|
||||
|
||||
|
|
@ -3471,22 +3512,12 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
|||
goto err_supplies;
|
||||
}
|
||||
|
||||
/* Set default UTMI width */
|
||||
hsotg->phyif = GUSBCFG_PHYIF16;
|
||||
|
||||
/*
|
||||
* If using the generic PHY framework, check if the PHY bus
|
||||
* width is 8-bit and set the phyif appropriately.
|
||||
*/
|
||||
if (hsotg->phy && (phy_get_bus_width(phy) == 8))
|
||||
hsotg->phyif = GUSBCFG_PHYIF8;
|
||||
|
||||
/* usb phy enable */
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
|
||||
s3c_hsotg_corereset(hsotg);
|
||||
s3c_hsotg_init(hsotg);
|
||||
s3c_hsotg_hw_cfg(hsotg);
|
||||
s3c_hsotg_init(hsotg);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
|
||||
dev_name(dev), hsotg);
|
||||
|
|
@ -3611,6 +3642,7 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
|
|||
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
clk_disable(hsotg->clk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -3625,6 +3657,8 @@ static int s3c_hsotg_resume(struct platform_device *pdev)
|
|||
if (hsotg->driver) {
|
||||
dev_info(hsotg->dev, "resuming usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
clk_enable(hsotg->clk);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -697,29 +697,45 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
|
|||
}
|
||||
|
||||
static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
struct dwc2_host_chan *chan, void *bufptr)
|
||||
struct dwc2_host_chan *chan,
|
||||
struct dwc2_hcd_urb *urb, void *bufptr)
|
||||
{
|
||||
u32 buf_size;
|
||||
|
||||
if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
|
||||
buf_size = hsotg->core_params->max_transfer_size;
|
||||
else
|
||||
buf_size = 4096;
|
||||
struct urb *usb_urb;
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (!qh->dw_align_buf) {
|
||||
if (chan->ep_type != USB_ENDPOINT_XFER_ISOC)
|
||||
buf_size = hsotg->core_params->max_transfer_size;
|
||||
else
|
||||
/* 3072 = 3 max-size Isoc packets */
|
||||
buf_size = 3072;
|
||||
|
||||
qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size,
|
||||
&qh->dw_align_buf_dma,
|
||||
GFP_ATOMIC);
|
||||
if (!qh->dw_align_buf)
|
||||
return -ENOMEM;
|
||||
qh->dw_align_buf_size = buf_size;
|
||||
}
|
||||
|
||||
if (!chan->ep_is_in && chan->xfer_len) {
|
||||
dma_sync_single_for_cpu(hsotg->dev, chan->xfer_dma, buf_size,
|
||||
DMA_TO_DEVICE);
|
||||
memcpy(qh->dw_align_buf, bufptr, chan->xfer_len);
|
||||
dma_sync_single_for_device(hsotg->dev, chan->xfer_dma, buf_size,
|
||||
DMA_TO_DEVICE);
|
||||
if (chan->xfer_len) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
usb_urb = urb->priv;
|
||||
|
||||
if (usb_urb) {
|
||||
if (usb_urb->transfer_flags &
|
||||
(URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG |
|
||||
URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) {
|
||||
hcd = dwc2_hsotg_to_hcd(hsotg);
|
||||
usb_hcd_unmap_urb_for_dma(hcd, usb_urb);
|
||||
}
|
||||
if (!chan->ep_is_in)
|
||||
memcpy(qh->dw_align_buf, bufptr,
|
||||
chan->xfer_len);
|
||||
} else {
|
||||
dev_warn(hsotg->dev, "no URB in dwc2_urb\n");
|
||||
}
|
||||
}
|
||||
|
||||
chan->align_buf = qh->dw_align_buf_dma;
|
||||
|
|
@ -828,7 +844,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|||
/* Non DWORD-aligned buffer case */
|
||||
if (bufptr) {
|
||||
dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
|
||||
if (dwc2_hc_setup_align_buf(hsotg, qh, chan, bufptr)) {
|
||||
if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) {
|
||||
dev_err(hsotg->dev,
|
||||
"%s: Failed to allocate memory to handle non-dword aligned buffer\n",
|
||||
__func__);
|
||||
|
|
|
|||
|
|
@ -243,7 +243,8 @@ enum dwc2_transaction_type {
|
|||
* @ntd: Actual number of transfer descriptors in a list
|
||||
* @dw_align_buf: Used instead of original buffer if its physical address
|
||||
* is not dword-aligned
|
||||
* @dw_align_buf_dma: DMA address for align_buf
|
||||
* @dw_align_buf_size: Size of dw_align_buf
|
||||
* @dw_align_buf_dma: DMA address for dw_align_buf
|
||||
* @qtd_list: List of QTDs for this QH
|
||||
* @channel: Host channel currently processing transfers for this QH
|
||||
* @qh_list_entry: Entry for QH in either the periodic or non-periodic
|
||||
|
|
@ -276,6 +277,7 @@ struct dwc2_qh {
|
|||
u16 start_split_frame;
|
||||
u16 ntd;
|
||||
u8 *dw_align_buf;
|
||||
int dw_align_buf_size;
|
||||
dma_addr_t dw_align_buf_dma;
|
||||
struct list_head qtd_list;
|
||||
struct dwc2_host_chan *channel;
|
||||
|
|
|
|||
|
|
@ -465,12 +465,8 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
|
|||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && xfer_length && chan->ep_is_in) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length,
|
||||
DMA_FROM_DEVICE);
|
||||
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
|
||||
xfer_length);
|
||||
dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length,
|
||||
DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
|
||||
|
|
@ -560,14 +556,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
|||
chan->ep_is_in) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
|
||||
__func__);
|
||||
dma_sync_single_for_cpu(hsotg->dev, urb->dma,
|
||||
urb->length, DMA_FROM_DEVICE);
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset, chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
dma_sync_single_for_device(hsotg->dev, urb->dma,
|
||||
urb->length,
|
||||
DMA_FROM_DEVICE);
|
||||
}
|
||||
break;
|
||||
case DWC2_HC_XFER_FRAME_OVERRUN:
|
||||
|
|
@ -594,14 +585,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
|||
chan->ep_is_in) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
|
||||
__func__);
|
||||
dma_sync_single_for_cpu(hsotg->dev, urb->dma,
|
||||
urb->length, DMA_FROM_DEVICE);
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset, chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
dma_sync_single_for_device(hsotg->dev, urb->dma,
|
||||
urb->length,
|
||||
DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
/* Skip whole frame */
|
||||
|
|
@ -937,12 +923,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
|
|||
|
||||
if (chan->align_buf) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_sync_single_for_cpu(hsotg->dev, qtd->urb->dma,
|
||||
qtd->urb->length, DMA_FROM_DEVICE);
|
||||
memcpy(qtd->urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
|
||||
dma_sync_single_for_device(hsotg->dev, qtd->urb->dma,
|
||||
qtd->urb->length, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
qtd->isoc_split_offset += len;
|
||||
|
|
@ -1170,12 +1152,8 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
|
|||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && xfer_length && chan->ep_is_in) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length,
|
||||
DMA_FROM_DEVICE);
|
||||
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
|
||||
xfer_length);
|
||||
dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length,
|
||||
DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
urb->actual_length += xfer_length;
|
||||
|
|
@ -1890,12 +1868,20 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
|
|||
"hcint 0x%08x, intsts 0x%08x\n",
|
||||
chan->hcint,
|
||||
readl(hsotg->regs + GINTSTS));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dev_info(hsotg->dev,
|
||||
"NYET/NAK/ACK/other in non-error case, 0x%08x\n",
|
||||
chan->hcint);
|
||||
error:
|
||||
/* Failthrough: use 3-strikes rule */
|
||||
qtd->error_count++;
|
||||
dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
|
||||
qtd, DWC2_HC_XFER_XACT_ERR);
|
||||
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
||||
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -229,19 +229,11 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
|
|||
*/
|
||||
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
u32 buf_size;
|
||||
|
||||
if (hsotg->core_params->dma_desc_enable > 0) {
|
||||
if (hsotg->core_params->dma_desc_enable > 0)
|
||||
dwc2_hcd_qh_free_ddma(hsotg, qh);
|
||||
} else if (qh->dw_align_buf) {
|
||||
if (qh->ep_type == USB_ENDPOINT_XFER_ISOC)
|
||||
buf_size = 4096;
|
||||
else
|
||||
buf_size = hsotg->core_params->max_transfer_size;
|
||||
dma_free_coherent(hsotg->dev, buf_size, qh->dw_align_buf,
|
||||
qh->dw_align_buf_dma);
|
||||
}
|
||||
|
||||
else if (qh->dw_align_buf)
|
||||
dma_free_coherent(hsotg->dev, qh->dw_align_buf_size,
|
||||
qh->dw_align_buf, qh->dw_align_buf_dma);
|
||||
kfree(qh);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb/of.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "hcd.h"
|
||||
|
||||
|
|
@ -75,6 +77,34 @@ static const struct dwc2_core_params params_bcm2835 = {
|
|||
.uframe_sched = 0,
|
||||
};
|
||||
|
||||
static const struct dwc2_core_params params_rk3066 = {
|
||||
.otg_cap = 2, /* non-HNP/non-SRP */
|
||||
.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 = 520, /* 520 DWORDs */
|
||||
.host_nperio_tx_fifo_size = 128, /* 128 DWORDs */
|
||||
.host_perio_tx_fifo_size = 256, /* 256 DWORDs */
|
||||
.max_transfer_size = 65535,
|
||||
.max_packet_count = -1,
|
||||
.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 = 0x7, /* INCR16 */
|
||||
.uframe_sched = -1,
|
||||
};
|
||||
|
||||
/**
|
||||
* dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
|
||||
* DWC_otg driver
|
||||
|
|
@ -97,6 +127,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
|||
|
||||
static const struct of_device_id dwc2_of_match_table[] = {
|
||||
{ .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 },
|
||||
{ .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 },
|
||||
{ .compatible = "snps,dwc2", .data = NULL },
|
||||
{},
|
||||
};
|
||||
|
|
@ -171,6 +202,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
|||
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
|
||||
(unsigned long)res->start, hsotg->regs);
|
||||
|
||||
hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
|
||||
|
||||
retval = dwc2_hcd_init(hsotg, irq, params);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,23 @@ config USB_DWC3_KEYSTONE
|
|||
Support of USB2/3 functionality in TI Keystone2 platforms.
|
||||
Say 'Y' or 'M' here if you have one such device
|
||||
|
||||
config USB_DWC3_ST
|
||||
tristate "STMicroelectronics Platforms"
|
||||
depends on ARCH_STI && OF
|
||||
default USB_DWC3
|
||||
help
|
||||
STMicroelectronics SoCs with one DesignWare Core USB3 IP
|
||||
inside (i.e. STiH407).
|
||||
Say 'Y' or 'M' if you have one such device.
|
||||
|
||||
config USB_DWC3_QCOM
|
||||
tristate "Qualcomm Platforms"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
default USB_DWC3
|
||||
help
|
||||
Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside,
|
||||
say 'Y' or 'M' if you have one such device.
|
||||
|
||||
comment "Debugging features"
|
||||
|
||||
config USB_DWC3_DEBUG
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
# define_trace.h needs to know how to find our header
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG
|
||||
ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
|
||||
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3.o
|
||||
|
||||
dwc3-y := core.o
|
||||
dwc3-y := core.o debug.o trace.o
|
||||
|
||||
ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
|
||||
dwc3-y += host.o
|
||||
|
|
@ -33,3 +36,5 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
|
|||
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
|
||||
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
|
||||
obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
|
||||
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
|
||||
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
|
||||
|
|
|
|||
|
|
@ -186,10 +186,8 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
|
|||
|
||||
dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num,
|
||||
GFP_KERNEL);
|
||||
if (!dwc->ev_buffs) {
|
||||
dev_err(dwc->dev, "can't allocate event buffers array\n");
|
||||
if (!dwc->ev_buffs)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct dwc3_event_buffer *evt;
|
||||
|
|
@ -639,10 +637,9 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
void *mem;
|
||||
|
||||
mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
|
||||
if (!mem) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
if (!mem)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
|
||||
dwc->mem = mem;
|
||||
dwc->dev = dev;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
#define DWC3_MSG_MAX 500
|
||||
|
||||
/* Global constants */
|
||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||
#define DWC3_ENDPOINTS_NUM 32
|
||||
|
|
@ -938,7 +940,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc);
|
|||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
|
|
|
|||
32
drivers/usb/dwc3/debug.c
Normal file
32
drivers/usb/dwc3/debug.c
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* debug.c - DesignWare USB3 DRD Controller Debug/Trace Support
|
||||
*
|
||||
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Felipe Balbi <balbi@ti.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 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
trace(&vaf);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
|
@ -16,8 +16,206 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __DWC3_DEBUG_H
|
||||
#define __DWC3_DEBUG_H
|
||||
|
||||
#include "core.h"
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_cmd_string - returns endpoint command string
|
||||
* @cmd: command code
|
||||
*/
|
||||
static inline const char *
|
||||
dwc3_gadget_ep_cmd_string(u8 cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case DWC3_DEPCMD_DEPSTARTCFG:
|
||||
return "Start New Configuration";
|
||||
case DWC3_DEPCMD_ENDTRANSFER:
|
||||
return "End Transfer";
|
||||
case DWC3_DEPCMD_UPDATETRANSFER:
|
||||
return "Update Transfer";
|
||||
case DWC3_DEPCMD_STARTTRANSFER:
|
||||
return "Start Transfer";
|
||||
case DWC3_DEPCMD_CLEARSTALL:
|
||||
return "Clear Stall";
|
||||
case DWC3_DEPCMD_SETSTALL:
|
||||
return "Set Stall";
|
||||
case DWC3_DEPCMD_GETEPSTATE:
|
||||
return "Get Endpoint State";
|
||||
case DWC3_DEPCMD_SETTRANSFRESOURCE:
|
||||
return "Set Endpoint Transfer Resource";
|
||||
case DWC3_DEPCMD_SETEPCONFIG:
|
||||
return "Set Endpoint Configuration";
|
||||
default:
|
||||
return "UNKNOWN command";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_generic_cmd_string - returns generic command string
|
||||
* @cmd: command code
|
||||
*/
|
||||
static inline const char *
|
||||
dwc3_gadget_generic_cmd_string(u8 cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case DWC3_DGCMD_SET_LMP:
|
||||
return "Set LMP";
|
||||
case DWC3_DGCMD_SET_PERIODIC_PAR:
|
||||
return "Set Periodic Parameters";
|
||||
case DWC3_DGCMD_XMIT_FUNCTION:
|
||||
return "Transmit Function Wake Device Notification";
|
||||
case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO:
|
||||
return "Set Scratchpad Buffer Array Address Lo";
|
||||
case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI:
|
||||
return "Set Scratchpad Buffer Array Address Hi";
|
||||
case DWC3_DGCMD_SELECTED_FIFO_FLUSH:
|
||||
return "Selected FIFO Flush";
|
||||
case DWC3_DGCMD_ALL_FIFO_FLUSH:
|
||||
return "All FIFO Flush";
|
||||
case DWC3_DGCMD_SET_ENDPOINT_NRDY:
|
||||
return "Set Endpoint NRDY";
|
||||
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
|
||||
return "Run SoC Bus Loopback Test";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_link_string - returns link name
|
||||
* @link_state: link state code
|
||||
*/
|
||||
static inline const char *
|
||||
dwc3_gadget_link_string(enum dwc3_link_state link_state)
|
||||
{
|
||||
switch (link_state) {
|
||||
case DWC3_LINK_STATE_U0:
|
||||
return "U0";
|
||||
case DWC3_LINK_STATE_U1:
|
||||
return "U1";
|
||||
case DWC3_LINK_STATE_U2:
|
||||
return "U2";
|
||||
case DWC3_LINK_STATE_U3:
|
||||
return "U3";
|
||||
case DWC3_LINK_STATE_SS_DIS:
|
||||
return "SS.Disabled";
|
||||
case DWC3_LINK_STATE_RX_DET:
|
||||
return "RX.Detect";
|
||||
case DWC3_LINK_STATE_SS_INACT:
|
||||
return "SS.Inactive";
|
||||
case DWC3_LINK_STATE_POLL:
|
||||
return "Polling";
|
||||
case DWC3_LINK_STATE_RECOV:
|
||||
return "Recovery";
|
||||
case DWC3_LINK_STATE_HRESET:
|
||||
return "Hot Reset";
|
||||
case DWC3_LINK_STATE_CMPLY:
|
||||
return "Compliance";
|
||||
case DWC3_LINK_STATE_LPBK:
|
||||
return "Loopback";
|
||||
case DWC3_LINK_STATE_RESET:
|
||||
return "Reset";
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
return "Resume";
|
||||
default:
|
||||
return "UNKNOWN link state\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_event_string - returns event name
|
||||
* @event: the event code
|
||||
*/
|
||||
static inline const char *dwc3_gadget_event_string(u8 event)
|
||||
{
|
||||
switch (event) {
|
||||
case DWC3_DEVICE_EVENT_DISCONNECT:
|
||||
return "Disconnect";
|
||||
case DWC3_DEVICE_EVENT_RESET:
|
||||
return "Reset";
|
||||
case DWC3_DEVICE_EVENT_CONNECT_DONE:
|
||||
return "Connection Done";
|
||||
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
|
||||
return "Link Status Change";
|
||||
case DWC3_DEVICE_EVENT_WAKEUP:
|
||||
return "WakeUp";
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
return "End-Of-Frame";
|
||||
case DWC3_DEVICE_EVENT_SOF:
|
||||
return "Start-Of-Frame";
|
||||
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
||||
return "Erratic Error";
|
||||
case DWC3_DEVICE_EVENT_CMD_CMPL:
|
||||
return "Command Complete";
|
||||
case DWC3_DEVICE_EVENT_OVERFLOW:
|
||||
return "Overflow";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_ep_event_string - returns event name
|
||||
* @event: then event code
|
||||
*/
|
||||
static inline const char *dwc3_ep_event_string(u8 event)
|
||||
{
|
||||
switch (event) {
|
||||
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||
return "Transfer Complete";
|
||||
case DWC3_DEPEVT_XFERINPROGRESS:
|
||||
return "Transfer In-Progress";
|
||||
case DWC3_DEPEVT_XFERNOTREADY:
|
||||
return "Transfer Not Ready";
|
||||
case DWC3_DEPEVT_RXTXFIFOEVT:
|
||||
return "FIFO";
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
return "Stream";
|
||||
case DWC3_DEPEVT_EPCMDCMPLT:
|
||||
return "Endpoint Command Complete";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_event_type_string - return event name
|
||||
* @event: the event code
|
||||
*/
|
||||
static inline const char *dwc3_gadget_event_type_string(u8 event)
|
||||
{
|
||||
switch (event) {
|
||||
case DWC3_DEVICE_EVENT_DISCONNECT:
|
||||
return "Disconnect";
|
||||
case DWC3_DEVICE_EVENT_RESET:
|
||||
return "Reset";
|
||||
case DWC3_DEVICE_EVENT_CONNECT_DONE:
|
||||
return "Connect Done";
|
||||
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
|
||||
return "Link Status Change";
|
||||
case DWC3_DEVICE_EVENT_WAKEUP:
|
||||
return "Wake-Up";
|
||||
case DWC3_DEVICE_EVENT_HIBER_REQ:
|
||||
return "Hibernation";
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
return "End of Periodic Frame";
|
||||
case DWC3_DEVICE_EVENT_SOF:
|
||||
return "Start of Frame";
|
||||
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
||||
return "Erratic Error";
|
||||
case DWC3_DEVICE_EVENT_CMD_CMPL:
|
||||
return "Command Complete";
|
||||
case DWC3_DEVICE_EVENT_OVERFLOW:
|
||||
return "Overflow";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern int dwc3_debugfs_init(struct dwc3 *);
|
||||
extern void dwc3_debugfs_exit(struct dwc3 *);
|
||||
|
|
@ -27,4 +225,4 @@ static inline int dwc3_debugfs_init(struct dwc3 *d)
|
|||
static inline void dwc3_debugfs_exit(struct dwc3 *d)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
#endif /* __DWC3_DEBUG_H */
|
||||
|
|
|
|||
|
|
@ -113,10 +113,8 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
|
||||
exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
|
||||
if (!exynos) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
if (!exynos)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Right now device-tree probed devices don't get dma_mask set.
|
||||
|
|
|
|||
|
|
@ -189,7 +189,6 @@ static struct platform_driver kdwc3_driver = {
|
|||
.remove = kdwc3_remove,
|
||||
.driver = {
|
||||
.name = "keystone-dwc3",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = kdwc3_of_match,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -481,10 +481,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
|
||||
if (!omap) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
if (!omap)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, omap);
|
||||
|
||||
|
|
|
|||
|
|
@ -103,10 +103,8 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
|||
struct device *dev = &pci->dev;
|
||||
|
||||
glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
if (!glue)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
glue->dev = dev;
|
||||
|
||||
|
|
|
|||
130
drivers/usb/dwc3/dwc3-qcom.c
Normal file
130
drivers/usb/dwc3/dwc3-qcom.c
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct dwc3_qcom {
|
||||
struct device *dev;
|
||||
|
||||
struct clk *core_clk;
|
||||
struct clk *iface_clk;
|
||||
struct clk *sleep_clk;
|
||||
};
|
||||
|
||||
static int dwc3_qcom_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct dwc3_qcom *qdwc;
|
||||
int ret;
|
||||
|
||||
qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL);
|
||||
if (!qdwc)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, qdwc);
|
||||
|
||||
qdwc->dev = &pdev->dev;
|
||||
|
||||
qdwc->core_clk = devm_clk_get(qdwc->dev, "core");
|
||||
if (IS_ERR(qdwc->core_clk)) {
|
||||
dev_err(qdwc->dev, "failed to get core clock\n");
|
||||
return PTR_ERR(qdwc->core_clk);
|
||||
}
|
||||
|
||||
qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface");
|
||||
if (IS_ERR(qdwc->iface_clk)) {
|
||||
dev_dbg(qdwc->dev, "failed to get optional iface clock\n");
|
||||
qdwc->iface_clk = NULL;
|
||||
}
|
||||
|
||||
qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep");
|
||||
if (IS_ERR(qdwc->sleep_clk)) {
|
||||
dev_dbg(qdwc->dev, "failed to get optional sleep clock\n");
|
||||
qdwc->sleep_clk = NULL;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->core_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable core clock\n");
|
||||
goto err_core;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->iface_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable optional iface clock\n");
|
||||
goto err_iface;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qdwc->sleep_clk);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to enable optional sleep clock\n");
|
||||
goto err_sleep;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(node, NULL, NULL, qdwc->dev);
|
||||
if (ret) {
|
||||
dev_err(qdwc->dev, "failed to register core - %d\n", ret);
|
||||
goto err_clks;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clks:
|
||||
clk_disable_unprepare(qdwc->sleep_clk);
|
||||
err_sleep:
|
||||
clk_disable_unprepare(qdwc->iface_clk);
|
||||
err_iface:
|
||||
clk_disable_unprepare(qdwc->core_clk);
|
||||
err_core:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_qcom_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_qcom *qdwc = platform_get_drvdata(pdev);
|
||||
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(qdwc->sleep_clk);
|
||||
clk_disable_unprepare(qdwc->iface_clk);
|
||||
clk_disable_unprepare(qdwc->core_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_dwc3_match[] = {
|
||||
{ .compatible = "qcom,dwc3" },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_match);
|
||||
|
||||
static struct platform_driver dwc3_qcom_driver = {
|
||||
.probe = dwc3_qcom_probe,
|
||||
.remove = dwc3_qcom_remove,
|
||||
.driver = {
|
||||
.name = "qcom-dwc3",
|
||||
.of_match_table = of_dwc3_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dwc3_qcom_driver);
|
||||
|
||||
MODULE_ALIAS("platform:qcom-dwc3");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer");
|
||||
MODULE_AUTHOR("Ivan T. Ivanov <[email protected]>");
|
||||
367
drivers/usb/dwc3/dwc3-st.c
Normal file
367
drivers/usb/dwc3/dwc3-st.c
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
/**
|
||||
* dwc3-st.c Support for dwc3 platform devices on ST Microelectronics platforms
|
||||
*
|
||||
* This is a small driver for the dwc3 to provide the glue logic
|
||||
* to configure the controller. Tested on STi platforms.
|
||||
*
|
||||
* Copyright (C) 2014 Stmicroelectronics
|
||||
*
|
||||
* Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
|
||||
* Contributors: Aymen Bouattay <aymen.bouattay@st.com>
|
||||
* Peter Griffin <peter.griffin@linaro.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Inspired by dwc3-omap.c and dwc3-exynos.c.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/usb/of.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "io.h"
|
||||
|
||||
/* glue registers */
|
||||
#define CLKRST_CTRL 0x00
|
||||
#define AUX_CLK_EN BIT(0)
|
||||
#define SW_PIPEW_RESET_N BIT(4)
|
||||
#define EXT_CFG_RESET_N BIT(8)
|
||||
/*
|
||||
* 1'b0 : The host controller complies with the xHCI revision 0.96
|
||||
* 1'b1 : The host controller complies with the xHCI revision 1.0
|
||||
*/
|
||||
#define XHCI_REVISION BIT(12)
|
||||
|
||||
#define USB2_VBUS_MNGMNT_SEL1 0x2C
|
||||
/*
|
||||
* For all fields in USB2_VBUS_MNGMNT_SEL1
|
||||
* 2’b00 : Override value from Reg 0x30 is selected
|
||||
* 2’b01 : utmiotg_<signal_name> from usb3_top is selected
|
||||
* 2’b10 : pipew_<signal_name> from PIPEW instance is selected
|
||||
* 2’b11 : value is 1'b0
|
||||
*/
|
||||
#define USB2_VBUS_REG30 0x0
|
||||
#define USB2_VBUS_UTMIOTG 0x1
|
||||
#define USB2_VBUS_PIPEW 0x2
|
||||
#define USB2_VBUS_ZERO 0x3
|
||||
|
||||
#define SEL_OVERRIDE_VBUSVALID(n) (n << 0)
|
||||
#define SEL_OVERRIDE_POWERPRESENT(n) (n << 4)
|
||||
#define SEL_OVERRIDE_BVALID(n) (n << 8)
|
||||
|
||||
/* Static DRD configuration */
|
||||
#define USB3_CONTROL_MASK 0xf77
|
||||
|
||||
#define USB3_DEVICE_NOT_HOST BIT(0)
|
||||
#define USB3_FORCE_VBUSVALID BIT(1)
|
||||
#define USB3_DELAY_VBUSVALID BIT(2)
|
||||
#define USB3_SEL_FORCE_OPMODE BIT(4)
|
||||
#define USB3_FORCE_OPMODE(n) (n << 5)
|
||||
#define USB3_SEL_FORCE_DPPULLDOWN2 BIT(8)
|
||||
#define USB3_FORCE_DPPULLDOWN2 BIT(9)
|
||||
#define USB3_SEL_FORCE_DMPULLDOWN2 BIT(10)
|
||||
#define USB3_FORCE_DMPULLDOWN2 BIT(11)
|
||||
|
||||
/**
|
||||
* struct st_dwc3 - dwc3-st driver private structure
|
||||
* @dev: device pointer
|
||||
* @glue_base: ioaddr for the glue registers
|
||||
* @regmap: regmap pointer for getting syscfg
|
||||
* @syscfg_reg_off: usb syscfg control offset
|
||||
* @dr_mode: drd static host/device config
|
||||
* @rstc_pwrdn: rest controller for powerdown signal
|
||||
* @rstc_rst: reset controller for softreset signal
|
||||
*/
|
||||
|
||||
struct st_dwc3 {
|
||||
struct device *dev;
|
||||
void __iomem *glue_base;
|
||||
struct regmap *regmap;
|
||||
int syscfg_reg_off;
|
||||
enum usb_dr_mode dr_mode;
|
||||
struct reset_control *rstc_pwrdn;
|
||||
struct reset_control *rstc_rst;
|
||||
};
|
||||
|
||||
static inline u32 st_dwc3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl_relaxed(base + offset);
|
||||
}
|
||||
|
||||
static inline void st_dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel_relaxed(value, base + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_dwc3_drd_init: program the port
|
||||
* @dwc3_data: driver private structure
|
||||
* Description: this function is to program the port as either host or device
|
||||
* according to the static configuration passed from devicetree.
|
||||
* OTG and dual role are not yet supported!
|
||||
*/
|
||||
static int st_dwc3_drd_init(struct st_dwc3 *dwc3_data)
|
||||
{
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
err = regmap_read(dwc3_data->regmap, dwc3_data->syscfg_reg_off, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val &= USB3_CONTROL_MASK;
|
||||
|
||||
switch (dwc3_data->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
|
||||
val &= ~(USB3_FORCE_VBUSVALID | USB3_DELAY_VBUSVALID
|
||||
| USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
|
||||
| USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
|
||||
| USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
|
||||
|
||||
val |= USB3_DEVICE_NOT_HOST;
|
||||
|
||||
dev_dbg(dwc3_data->dev, "Configuring as Device\n");
|
||||
break;
|
||||
|
||||
case USB_DR_MODE_HOST:
|
||||
|
||||
val &= ~(USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID
|
||||
| USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
|
||||
| USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
|
||||
| USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
|
||||
|
||||
/*
|
||||
* USB3_DELAY_VBUSVALID is ANDed with USB_C_VBUSVALID. Thus,
|
||||
* when set to ‘0‘, it can delay the arrival of VBUSVALID
|
||||
* information to VBUSVLDEXT2 input of the pico PHY.
|
||||
* We don't want to do that so we set the bit to '1'.
|
||||
*/
|
||||
|
||||
val |= USB3_DELAY_VBUSVALID;
|
||||
|
||||
dev_dbg(dwc3_data->dev, "Configuring as Host\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dwc3_data->dev, "Unsupported mode of operation %d\n",
|
||||
dwc3_data->dr_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_write(dwc3_data->regmap, dwc3_data->syscfg_reg_off, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_dwc3_init: init the controller via glue logic
|
||||
* @dwc3_data: driver private structure
|
||||
*/
|
||||
static void st_dwc3_init(struct st_dwc3 *dwc3_data)
|
||||
{
|
||||
u32 reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL);
|
||||
|
||||
reg |= AUX_CLK_EN | EXT_CFG_RESET_N | XHCI_REVISION;
|
||||
reg &= ~SW_PIPEW_RESET_N;
|
||||
st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg);
|
||||
|
||||
/* configure mux for vbus, powerpresent and bvalid signals */
|
||||
reg = st_dwc3_readl(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1);
|
||||
|
||||
reg |= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG) |
|
||||
SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG) |
|
||||
SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG);
|
||||
|
||||
st_dwc3_writel(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1, reg);
|
||||
|
||||
reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL);
|
||||
reg |= SW_PIPEW_RESET_N;
|
||||
st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg);
|
||||
}
|
||||
|
||||
static int st_dwc3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct st_dwc3 *dwc3_data;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node, *child;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
dwc3_data = devm_kzalloc(dev, sizeof(*dwc3_data), GFP_KERNEL);
|
||||
if (!dwc3_data)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg-glue");
|
||||
dwc3_data->glue_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(dwc3_data->glue_base))
|
||||
return PTR_ERR(dwc3_data->glue_base);
|
||||
|
||||
regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
dma_set_coherent_mask(dev, dev->coherent_dma_mask);
|
||||
dwc3_data->dev = dev;
|
||||
dwc3_data->regmap = regmap;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "syscfg-reg");
|
||||
if (!res) {
|
||||
ret = -ENXIO;
|
||||
goto undo_platform_dev_alloc;
|
||||
}
|
||||
|
||||
dwc3_data->syscfg_reg_off = res->start;
|
||||
|
||||
dev_vdbg(&pdev->dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n",
|
||||
dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
|
||||
|
||||
dwc3_data->rstc_pwrdn = devm_reset_control_get(dev, "powerdown");
|
||||
if (IS_ERR(dwc3_data->rstc_pwrdn)) {
|
||||
dev_err(&pdev->dev, "could not get power controller\n");
|
||||
ret = PTR_ERR(dwc3_data->rstc_pwrdn);
|
||||
goto undo_platform_dev_alloc;
|
||||
}
|
||||
|
||||
/* Manage PowerDown */
|
||||
reset_control_deassert(dwc3_data->rstc_pwrdn);
|
||||
|
||||
dwc3_data->rstc_rst = devm_reset_control_get(dev, "softreset");
|
||||
if (IS_ERR(dwc3_data->rstc_rst)) {
|
||||
dev_err(&pdev->dev, "could not get reset controller\n");
|
||||
ret = PTR_ERR(dwc3_data->rstc_pwrdn);
|
||||
goto undo_powerdown;
|
||||
}
|
||||
|
||||
/* Manage SoftReset */
|
||||
reset_control_deassert(dwc3_data->rstc_rst);
|
||||
|
||||
child = of_get_child_by_name(node, "dwc3");
|
||||
if (!child) {
|
||||
dev_err(&pdev->dev, "failed to find dwc3 core node\n");
|
||||
ret = -ENODEV;
|
||||
goto undo_softreset;
|
||||
}
|
||||
|
||||
dwc3_data->dr_mode = of_usb_get_dr_mode(child);
|
||||
|
||||
/* Allocate and initialize the core */
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add dwc3 core\n");
|
||||
goto undo_softreset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the USB port as device or host according to the static
|
||||
* configuration passed from DT.
|
||||
* DRD is the only mode currently supported so this will be enhanced
|
||||
* as soon as OTG is available.
|
||||
*/
|
||||
ret = st_dwc3_drd_init(dwc3_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "drd initialisation failed\n");
|
||||
goto undo_softreset;
|
||||
}
|
||||
|
||||
/* ST glue logic init */
|
||||
st_dwc3_init(dwc3_data);
|
||||
|
||||
platform_set_drvdata(pdev, dwc3_data);
|
||||
return 0;
|
||||
|
||||
undo_softreset:
|
||||
reset_control_assert(dwc3_data->rstc_rst);
|
||||
undo_powerdown:
|
||||
reset_control_assert(dwc3_data->rstc_pwrdn);
|
||||
undo_platform_dev_alloc:
|
||||
platform_device_put(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st_dwc3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct st_dwc3 *dwc3_data = platform_get_drvdata(pdev);
|
||||
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
|
||||
reset_control_assert(dwc3_data->rstc_pwrdn);
|
||||
reset_control_assert(dwc3_data->rstc_rst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int st_dwc3_suspend(struct device *dev)
|
||||
{
|
||||
struct st_dwc3 *dwc3_data = dev_get_drvdata(dev);
|
||||
|
||||
reset_control_assert(dwc3_data->rstc_pwrdn);
|
||||
reset_control_assert(dwc3_data->rstc_rst);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_dwc3_resume(struct device *dev)
|
||||
{
|
||||
struct st_dwc3 *dwc3_data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
reset_control_deassert(dwc3_data->rstc_pwrdn);
|
||||
reset_control_deassert(dwc3_data->rstc_rst);
|
||||
|
||||
ret = st_dwc3_drd_init(dwc3_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "drd initialisation failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ST glue logic init */
|
||||
st_dwc3_init(dwc3_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume);
|
||||
|
||||
static const struct of_device_id st_dwc3_match[] = {
|
||||
{ .compatible = "st,stih407-dwc3" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, st_dwc3_match);
|
||||
|
||||
static struct platform_driver st_dwc3_driver = {
|
||||
.probe = st_dwc3_probe,
|
||||
.remove = st_dwc3_remove,
|
||||
.driver = {
|
||||
.name = "usb-st-dwc3",
|
||||
.of_match_table = st_dwc3_match,
|
||||
.pm = &st_dwc3_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(st_dwc3_driver);
|
||||
|
||||
MODULE_AUTHOR("Giuseppe Cavallaro <[email protected]>");
|
||||
MODULE_DESCRIPTION("DesignWare USB3 STi Glue Layer");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/usb/composite.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
|||
|
||||
dep = dwc->eps[epnum];
|
||||
if (dep->flags & DWC3_EP_BUSY) {
|
||||
dev_vdbg(dwc->dev, "%s: still busy\n", dep->name);
|
||||
dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +89,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
|||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||
DWC3_DEPCMD_STARTTRANSFER, ¶ms);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
|
||||
dep->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -153,7 +155,8 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
|
|||
if (dwc->ep0state == EP0_STATUS_PHASE)
|
||||
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
|
||||
else
|
||||
dev_dbg(dwc->dev, "too early for delayed status\n");
|
||||
dwc3_trace(trace_dwc3_ep0,
|
||||
"too early for delayed status");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -217,7 +220,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!dep->endpoint.desc) {
|
||||
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
|
||||
dwc3_trace(trace_dwc3_ep0,
|
||||
"trying to queue request %p to disabled %s",
|
||||
request, dep->name);
|
||||
ret = -ESHUTDOWN;
|
||||
goto out;
|
||||
|
|
@ -229,7 +233,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
goto out;
|
||||
}
|
||||
|
||||
dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n",
|
||||
dwc3_trace(trace_dwc3_ep0,
|
||||
"queueing request %p to %s length %d state '%s'",
|
||||
request, dep->name, request->length,
|
||||
dwc3_ep0_state_string(dwc->ep0state));
|
||||
|
||||
|
|
@ -485,12 +490,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
|
||||
addr = le16_to_cpu(ctrl->wValue);
|
||||
if (addr > 127) {
|
||||
dev_dbg(dwc->dev, "invalid device address %d\n", addr);
|
||||
dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state == USB_STATE_CONFIGURED) {
|
||||
dev_dbg(dwc->dev, "trying to set address when configured\n");
|
||||
dwc3_trace(trace_dwc3_ep0,
|
||||
"trying to set address when configured");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -556,7 +562,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
||||
dwc->resize_fifos = true;
|
||||
dev_dbg(dwc->dev, "resize fifos flag SET\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET");
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -680,35 +686,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
|
||||
switch (ctrl->bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS\n");
|
||||
ret = dwc3_ep0_handle_status(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE\n");
|
||||
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE\n");
|
||||
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS\n");
|
||||
ret = dwc3_ep0_set_address(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION\n");
|
||||
ret = dwc3_ep0_set_config(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL\n");
|
||||
ret = dwc3_ep0_set_sel(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY\n");
|
||||
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
|
||||
break;
|
||||
default:
|
||||
dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver\n");
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
break;
|
||||
}
|
||||
|
|
@ -726,6 +732,8 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
|
|||
if (!dwc->gadget_driver)
|
||||
goto out;
|
||||
|
||||
trace_dwc3_ctrl_req(ctrl);
|
||||
|
||||
len = le16_to_cpu(ctrl->wLength);
|
||||
if (!len) {
|
||||
dwc->three_stage_setup = false;
|
||||
|
|
@ -774,7 +782,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING) {
|
||||
dev_dbg(dwc->dev, "Setup Pending received\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
|
||||
|
||||
if (r)
|
||||
dwc3_gadget_giveback(ep0, r, -ECONNRESET);
|
||||
|
|
@ -834,7 +842,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
|||
|
||||
ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dwc->dev, "Invalid Test #%d\n",
|
||||
dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d",
|
||||
dwc->test_mode_nr);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
return;
|
||||
|
|
@ -843,7 +851,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
|||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING)
|
||||
dev_dbg(dwc->dev, "Setup Pending received\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Pending received\n");
|
||||
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
|
|
@ -860,17 +868,17 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
|
|||
|
||||
switch (dwc->ep0state) {
|
||||
case EP0_SETUP_PHASE:
|
||||
dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Phase");
|
||||
dwc3_ep0_inspect_setup(dwc, event);
|
||||
break;
|
||||
|
||||
case EP0_DATA_PHASE:
|
||||
dev_vdbg(dwc->dev, "Data Phase\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Data Phase");
|
||||
dwc3_ep0_complete_data(dwc, event);
|
||||
break;
|
||||
|
||||
case EP0_STATUS_PHASE:
|
||||
dev_vdbg(dwc->dev, "Status Phase\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Status Phase");
|
||||
dwc3_ep0_complete_status(dwc, event);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -946,7 +954,7 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
|
|||
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
if (dwc->resize_fifos) {
|
||||
dev_dbg(dwc->dev, "starting to resize fifos\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs");
|
||||
dwc3_gadget_resize_tx_fifos(dwc);
|
||||
dwc->resize_fifos = 0;
|
||||
}
|
||||
|
|
@ -987,7 +995,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
|||
|
||||
switch (event->status) {
|
||||
case DEPEVT_STATUS_CONTROL_DATA:
|
||||
dev_vdbg(dwc->dev, "Control Data\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Control Data");
|
||||
|
||||
/*
|
||||
* We already have a DATA transfer in the controller's cache,
|
||||
|
|
@ -1001,7 +1009,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
|||
if (dwc->ep0_expect_in != event->endpoint_number) {
|
||||
struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
|
||||
|
||||
dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
|
||||
dwc3_trace(trace_dwc3_ep0,
|
||||
"Wrong direction for Data phase");
|
||||
dwc3_ep0_end_control_data(dwc, dep);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
return;
|
||||
|
|
@ -1013,13 +1022,13 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
|||
if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
|
||||
return;
|
||||
|
||||
dev_vdbg(dwc->dev, "Control Status\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Control Status");
|
||||
|
||||
dwc->ep0state = EP0_STATUS_PHASE;
|
||||
|
||||
if (dwc->delayed_status) {
|
||||
WARN_ON_ONCE(event->endpoint_number != 1);
|
||||
dev_vdbg(dwc->dev, "Mass Storage delayed status\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Delayed Status");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1032,7 +1041,7 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
|||
{
|
||||
u8 epnum = event->endpoint_number;
|
||||
|
||||
dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n",
|
||||
dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'",
|
||||
dwc3_ep_event_string(event->endpoint_event),
|
||||
epnum >> 1, (epnum & 1) ? "in" : "out",
|
||||
dwc3_ep0_state_string(dwc->ep0state));
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
#include "io.h"
|
||||
|
|
@ -266,107 +267,19 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|||
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
|
||||
req, dep->name, req->request.actual,
|
||||
req->request.length, status);
|
||||
trace_dwc3_gadget_giveback(req);
|
||||
|
||||
spin_unlock(&dwc->lock);
|
||||
req->request.complete(&dep->endpoint, &req->request);
|
||||
usb_gadget_giveback_request(&dep->endpoint, &req->request);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
|
||||
static const char *dwc3_gadget_ep_cmd_string(u8 cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case DWC3_DEPCMD_DEPSTARTCFG:
|
||||
return "Start New Configuration";
|
||||
case DWC3_DEPCMD_ENDTRANSFER:
|
||||
return "End Transfer";
|
||||
case DWC3_DEPCMD_UPDATETRANSFER:
|
||||
return "Update Transfer";
|
||||
case DWC3_DEPCMD_STARTTRANSFER:
|
||||
return "Start Transfer";
|
||||
case DWC3_DEPCMD_CLEARSTALL:
|
||||
return "Clear Stall";
|
||||
case DWC3_DEPCMD_SETSTALL:
|
||||
return "Set Stall";
|
||||
case DWC3_DEPCMD_GETEPSTATE:
|
||||
return "Get Endpoint State";
|
||||
case DWC3_DEPCMD_SETTRANSFRESOURCE:
|
||||
return "Set Endpoint Transfer Resource";
|
||||
case DWC3_DEPCMD_SETEPCONFIG:
|
||||
return "Set Endpoint Configuration";
|
||||
default:
|
||||
return "UNKNOWN command";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *dwc3_gadget_generic_cmd_string(u8 cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case DWC3_DGCMD_SET_LMP:
|
||||
return "Set LMP";
|
||||
case DWC3_DGCMD_SET_PERIODIC_PAR:
|
||||
return "Set Periodic Parameters";
|
||||
case DWC3_DGCMD_XMIT_FUNCTION:
|
||||
return "Transmit Function Wake Device Notification";
|
||||
case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO:
|
||||
return "Set Scratchpad Buffer Array Address Lo";
|
||||
case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI:
|
||||
return "Set Scratchpad Buffer Array Address Hi";
|
||||
case DWC3_DGCMD_SELECTED_FIFO_FLUSH:
|
||||
return "Selected FIFO Flush";
|
||||
case DWC3_DGCMD_ALL_FIFO_FLUSH:
|
||||
return "All FIFO Flush";
|
||||
case DWC3_DGCMD_SET_ENDPOINT_NRDY:
|
||||
return "Set Endpoint NRDY";
|
||||
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
|
||||
return "Run SoC Bus Loopback Test";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *dwc3_gadget_link_string(enum dwc3_link_state link_state)
|
||||
{
|
||||
switch (link_state) {
|
||||
case DWC3_LINK_STATE_U0:
|
||||
return "U0";
|
||||
case DWC3_LINK_STATE_U1:
|
||||
return "U1";
|
||||
case DWC3_LINK_STATE_U2:
|
||||
return "U2";
|
||||
case DWC3_LINK_STATE_U3:
|
||||
return "U3";
|
||||
case DWC3_LINK_STATE_SS_DIS:
|
||||
return "SS.Disabled";
|
||||
case DWC3_LINK_STATE_RX_DET:
|
||||
return "RX.Detect";
|
||||
case DWC3_LINK_STATE_SS_INACT:
|
||||
return "SS.Inactive";
|
||||
case DWC3_LINK_STATE_POLL:
|
||||
return "Polling";
|
||||
case DWC3_LINK_STATE_RECOV:
|
||||
return "Recovery";
|
||||
case DWC3_LINK_STATE_HRESET:
|
||||
return "Hot Reset";
|
||||
case DWC3_LINK_STATE_CMPLY:
|
||||
return "Compliance";
|
||||
case DWC3_LINK_STATE_LPBK:
|
||||
return "Loopback";
|
||||
case DWC3_LINK_STATE_RESET:
|
||||
return "Reset";
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
return "Resume";
|
||||
default:
|
||||
return "UNKNOWN link state\n";
|
||||
}
|
||||
}
|
||||
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param)
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
|
||||
{
|
||||
u32 timeout = 500;
|
||||
u32 reg;
|
||||
|
||||
dev_vdbg(dwc->dev, "generic cmd '%s' [%d] param %08x\n",
|
||||
dwc3_gadget_generic_cmd_string(cmd), cmd, param);
|
||||
trace_dwc3_gadget_generic_cmd(cmd, param);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
|
||||
dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
|
||||
|
|
@ -397,10 +310,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
|||
u32 timeout = 500;
|
||||
u32 reg;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s: cmd '%s' [%d] params %08x %08x %08x\n",
|
||||
dep->name,
|
||||
dwc3_gadget_ep_cmd_string(cmd), cmd, params->param0,
|
||||
params->param1, params->param2);
|
||||
trace_dwc3_gadget_ep_cmd(dep, cmd, params);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0);
|
||||
dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1);
|
||||
|
|
@ -789,17 +699,16 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep,
|
|||
{
|
||||
struct dwc3_request *req;
|
||||
struct dwc3_ep *dep = to_dwc3_ep(ep);
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
req = kzalloc(sizeof(*req), gfp_flags);
|
||||
if (!req) {
|
||||
dev_err(dwc->dev, "not enough memory\n");
|
||||
if (!req)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
req->epnum = dep->number;
|
||||
req->dep = dep;
|
||||
|
||||
trace_dwc3_alloc_request(req);
|
||||
|
||||
return &req->request;
|
||||
}
|
||||
|
||||
|
|
@ -808,6 +717,7 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
|
|||
{
|
||||
struct dwc3_request *req = to_dwc3_request(request);
|
||||
|
||||
trace_dwc3_free_request(req);
|
||||
kfree(req);
|
||||
}
|
||||
|
||||
|
|
@ -889,6 +799,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
|||
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
|
||||
|
||||
trb->ctrl |= DWC3_TRB_CTRL_HWO;
|
||||
|
||||
trace_dwc3_prepare_trb(dep, trb);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1235,6 +1147,7 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
|
||||
dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
|
||||
request, ep->name, request->length);
|
||||
trace_dwc3_ep_queue(req);
|
||||
|
||||
ret = __dwc3_gadget_ep_queue(dep, req);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
|
@ -1254,6 +1167,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
trace_dwc3_ep_dequeue(req);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
list_for_each_entry(r, &dep->request_list, list) {
|
||||
|
|
@ -1744,11 +1659,8 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
|||
u8 epnum = (i << 1) | (!!direction);
|
||||
|
||||
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
||||
if (!dep) {
|
||||
dev_err(dwc->dev, "can't allocate endpoint %d\n",
|
||||
epnum);
|
||||
if (!dep)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dep->dwc = dwc;
|
||||
dep->number = epnum;
|
||||
|
|
@ -1847,6 +1759,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|||
unsigned int s_pkt = 0;
|
||||
unsigned int trb_status;
|
||||
|
||||
trace_dwc3_complete_trb(dep, trb);
|
||||
|
||||
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
|
||||
/*
|
||||
* We continue despite the error. There is not much we
|
||||
|
|
@ -2021,9 +1935,6 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
|
|||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
return;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s: %s\n", dep->name,
|
||||
dwc3_ep_event_string(event->endpoint_event));
|
||||
|
||||
if (epnum == 0 || epnum == 1) {
|
||||
dwc3_ep0_interrupt(dwc, event);
|
||||
return;
|
||||
|
|
@ -2210,8 +2121,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
|||
{
|
||||
int reg;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s\n", __func__);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_INITU1ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
|
@ -2230,8 +2139,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s\n", __func__);
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.88a have an issue which
|
||||
* would cause a missing Disconnect Event if there's a
|
||||
|
|
@ -2316,8 +2223,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
u32 reg;
|
||||
u8 speed;
|
||||
|
||||
dev_vdbg(dwc->dev, "%s\n", __func__);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
dwc->speed = speed;
|
||||
|
|
@ -2415,8 +2320,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
|
||||
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
|
||||
{
|
||||
dev_vdbg(dwc->dev, "%s\n", __func__);
|
||||
|
||||
/*
|
||||
* TODO take core out of low power mode when that's
|
||||
* implemented.
|
||||
|
|
@ -2521,10 +2424,6 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
break;
|
||||
}
|
||||
|
||||
dev_vdbg(dwc->dev, "link change: %s [%d] -> %s [%d]\n",
|
||||
dwc3_gadget_link_string(dwc->link_state),
|
||||
dwc->link_state, dwc3_gadget_link_string(next), next);
|
||||
|
||||
dwc->link_state = next;
|
||||
}
|
||||
|
||||
|
|
@ -2601,6 +2500,8 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
|||
static void dwc3_process_event_entry(struct dwc3 *dwc,
|
||||
const union dwc3_event *event)
|
||||
{
|
||||
trace_dwc3_event(event->raw);
|
||||
|
||||
/* Endpoint IRQ, handle it and return early */
|
||||
if (event->type.is_devspec == 0) {
|
||||
/* depevt */
|
||||
|
|
@ -2754,7 +2655,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
|
||||
dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
|
||||
if (!dwc->setup_buf) {
|
||||
dev_err(dwc->dev, "failed to allocate setup buffer\n");
|
||||
ret = -ENOMEM;
|
||||
goto err2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,60 +103,4 @@ static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number)
|
|||
return DWC3_DEPCMD_GET_RSC_IDX(res_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_event_string - returns event name
|
||||
* @event: the event code
|
||||
*/
|
||||
static inline const char *dwc3_gadget_event_string(u8 event)
|
||||
{
|
||||
switch (event) {
|
||||
case DWC3_DEVICE_EVENT_DISCONNECT:
|
||||
return "Disconnect";
|
||||
case DWC3_DEVICE_EVENT_RESET:
|
||||
return "Reset";
|
||||
case DWC3_DEVICE_EVENT_CONNECT_DONE:
|
||||
return "Connection Done";
|
||||
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
|
||||
return "Link Status Change";
|
||||
case DWC3_DEVICE_EVENT_WAKEUP:
|
||||
return "WakeUp";
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
return "End-Of-Frame";
|
||||
case DWC3_DEVICE_EVENT_SOF:
|
||||
return "Start-Of-Frame";
|
||||
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
||||
return "Erratic Error";
|
||||
case DWC3_DEVICE_EVENT_CMD_CMPL:
|
||||
return "Command Complete";
|
||||
case DWC3_DEVICE_EVENT_OVERFLOW:
|
||||
return "Overflow";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_ep_event_string - returns event name
|
||||
* @event: then event code
|
||||
*/
|
||||
static inline const char *dwc3_ep_event_string(u8 event)
|
||||
{
|
||||
switch (event) {
|
||||
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||
return "Transfer Complete";
|
||||
case DWC3_DEPEVT_XFERINPROGRESS:
|
||||
return "Transfer In-Progress";
|
||||
case DWC3_DEPEVT_XFERNOTREADY:
|
||||
return "Transfer Not Ready";
|
||||
case DWC3_DEPEVT_RXTXFIFOEVT:
|
||||
return "FIFO";
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
return "Stream";
|
||||
case DWC3_DEPEVT_EPCMDCMPLT:
|
||||
return "Endpoint Command Complete";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
|
||||
|
|
|
|||
|
|
@ -20,27 +20,51 @@
|
|||
#define __DRIVERS_USB_DWC3_IO_H
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "debug.h"
|
||||
#include "core.h"
|
||||
|
||||
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
u32 offs = offset - DWC3_GLOBALS_REGS_START;
|
||||
u32 value;
|
||||
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
return readl(base + (offset - DWC3_GLOBALS_REGS_START));
|
||||
value = readl(base + offs);
|
||||
|
||||
/*
|
||||
* When tracing we want to make it easy to find the correct address on
|
||||
* documentation, so we revert it back to the proper addresses, the
|
||||
* same way they are described on SNPS documentation
|
||||
*/
|
||||
dwc3_trace(trace_dwc3_readl, "addr %p value %08x",
|
||||
base - DWC3_GLOBALS_REGS_START + offset, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
u32 offs = offset - DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
writel(value, base + (offset - DWC3_GLOBALS_REGS_START));
|
||||
writel(value, base + offs);
|
||||
|
||||
/*
|
||||
* When tracing we want to make it easy to find the correct address on
|
||||
* documentation, so we revert it back to the proper addresses, the
|
||||
* same way they are described on SNPS documentation
|
||||
*/
|
||||
dwc3_trace(trace_dwc3_writel, "addr %p value %08x",
|
||||
base - DWC3_GLOBALS_REGS_START + offset, value);
|
||||
}
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_IO_H */
|
||||
|
|
|
|||
19
drivers/usb/dwc3/trace.c
Normal file
19
drivers/usb/dwc3/trace.c
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* trace.c - DesignWare USB3 DRD Controller Trace Support
|
||||
*
|
||||
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Felipe Balbi <balbi@ti.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 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
220
drivers/usb/dwc3/trace.h
Normal file
220
drivers/usb/dwc3/trace.h
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/**
|
||||
* trace.h - DesignWare USB3 DRD Controller Trace Support
|
||||
*
|
||||
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
|
||||
*
|
||||
* Author: Felipe Balbi <balbi@ti.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 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM dwc3
|
||||
|
||||
#if !defined(__DWC3_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define __DWC3_TRACE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_msg,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf),
|
||||
TP_STRUCT__entry(__dynamic_array(char, msg, DWC3_MSG_MAX)),
|
||||
TP_fast_assign(
|
||||
vsnprintf(__get_str(msg), DWC3_MSG_MAX, vaf->fmt, *vaf->va);
|
||||
),
|
||||
TP_printk("%s", __get_str(msg))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_msg, dwc3_readl,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_msg, dwc3_writel,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
|
||||
TP_PROTO(struct va_format *vaf),
|
||||
TP_ARGS(vaf)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_event,
|
||||
TP_PROTO(u32 event),
|
||||
TP_ARGS(event),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, event)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->event = event;
|
||||
),
|
||||
TP_printk("event %08x\n", __entry->event)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_event, dwc3_event,
|
||||
TP_PROTO(u32 event),
|
||||
TP_ARGS(event)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_ctrl,
|
||||
TP_PROTO(struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(ctrl),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct usb_ctrlrequest *, ctrl)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ctrl = ctrl;
|
||||
),
|
||||
TP_printk("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %d",
|
||||
__entry->ctrl->bRequestType, __entry->ctrl->bRequest,
|
||||
le16_to_cpu(__entry->ctrl->wValue), le16_to_cpu(__entry->ctrl->wIndex),
|
||||
le16_to_cpu(__entry->ctrl->wLength)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req,
|
||||
TP_PROTO(struct usb_ctrlrequest *ctrl),
|
||||
TP_ARGS(ctrl)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_request,
|
||||
TP_PROTO(struct dwc3_request *req),
|
||||
TP_ARGS(req),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct dwc3_request *, req)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->req = req;
|
||||
),
|
||||
TP_printk("%s: req %p length %u/%u ==> %d",
|
||||
__entry->req->dep->name, __entry->req,
|
||||
__entry->req->request.actual, __entry->req->request.length,
|
||||
__entry->req->request.status
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_request, dwc3_alloc_request,
|
||||
TP_PROTO(struct dwc3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_request, dwc3_free_request,
|
||||
TP_PROTO(struct dwc3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_request, dwc3_ep_queue,
|
||||
TP_PROTO(struct dwc3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_request, dwc3_ep_dequeue,
|
||||
TP_PROTO(struct dwc3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback,
|
||||
TP_PROTO(struct dwc3_request *req),
|
||||
TP_ARGS(req)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
|
||||
TP_PROTO(unsigned int cmd, u32 param),
|
||||
TP_ARGS(cmd, param),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, cmd)
|
||||
__field(u32, param)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->cmd = cmd;
|
||||
__entry->param = param;
|
||||
),
|
||||
TP_printk("cmd '%s' [%d] param %08x\n",
|
||||
dwc3_gadget_generic_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd,
|
||||
TP_PROTO(unsigned int cmd, u32 param),
|
||||
TP_ARGS(cmd, param)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
||||
TP_PROTO(struct dwc3_ep *dep, unsigned int cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params),
|
||||
TP_ARGS(dep, cmd, params),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct dwc3_ep *, dep)
|
||||
__field(unsigned int, cmd)
|
||||
__field(struct dwc3_gadget_ep_cmd_params *, params)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dep = dep;
|
||||
__entry->cmd = cmd;
|
||||
__entry->params = params;
|
||||
),
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x\n",
|
||||
__entry->dep->name, dwc3_gadget_ep_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->params->param0,
|
||||
__entry->params->param1, __entry->params->param2
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_gadget_ep_cmd, dwc3_gadget_ep_cmd,
|
||||
TP_PROTO(struct dwc3_ep *dep, unsigned int cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params),
|
||||
TP_ARGS(dep, cmd, params)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
|
||||
TP_ARGS(dep, trb),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct dwc3_ep *, dep)
|
||||
__field(struct dwc3_trb *, trb)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dep = dep;
|
||||
__entry->trb = trb;
|
||||
),
|
||||
TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x\n",
|
||||
__entry->dep->name, __entry->trb, __entry->trb->bph,
|
||||
__entry->trb->bpl, __entry->trb->size, __entry->trb->ctrl
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_trb, dwc3_prepare_trb,
|
||||
TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
|
||||
TP_ARGS(dep, trb)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_trb, dwc3_complete_trb,
|
||||
TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
|
||||
TP_ARGS(dep, trb)
|
||||
);
|
||||
|
||||
#endif /* __DWC3_TRACE_H */
|
||||
|
||||
/* this part has to be here */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
#include <trace/define_trace.h>
|
||||
|
|
@ -181,6 +181,15 @@ config USB_F_MASS_STORAGE
|
|||
config USB_F_FS
|
||||
tristate
|
||||
|
||||
config USB_F_UAC1
|
||||
tristate
|
||||
|
||||
config USB_F_UAC2
|
||||
tristate
|
||||
|
||||
config USB_F_UVC
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
|
||||
subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
|
||||
ccflags-y += -Idrivers/usb/gadget/udc
|
||||
ccflags-y += -I$(srctree)/drivers/usb/gadget/udc
|
||||
|
||||
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
|
||||
libcomposite-y := usbstring.o config.o epautoconf.o
|
||||
|
|
|
|||
|
|
@ -1956,7 +1956,6 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
|
|||
}
|
||||
if (cdev->req) {
|
||||
kfree(cdev->req->buf);
|
||||
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
|
||||
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
|
||||
}
|
||||
cdev->next_string_id = 0;
|
||||
|
|
@ -2073,6 +2072,7 @@ static const struct usb_gadget_driver composite_driver_template = {
|
|||
.unbind = composite_unbind,
|
||||
|
||||
.setup = composite_setup,
|
||||
.reset = composite_disconnect,
|
||||
.disconnect = composite_disconnect,
|
||||
|
||||
.suspend = composite_suspend,
|
||||
|
|
|
|||
|
|
@ -1450,6 +1450,7 @@ static const struct usb_gadget_driver configfs_driver_template = {
|
|||
.unbind = configfs_composite_unbind,
|
||||
|
||||
.setup = composite_setup,
|
||||
.reset = composite_disconnect,
|
||||
.disconnect = composite_disconnect,
|
||||
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
# USB peripheral controller drivers
|
||||
#
|
||||
|
||||
ccflags-y := -Idrivers/usb/gadget/
|
||||
ccflags-y += -Idrivers/usb/gadget/udc/
|
||||
ccflags-y := -I$(srctree)/drivers/usb/gadget/
|
||||
ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/
|
||||
|
||||
# USB Functions
|
||||
usb_f_acm-y := f_acm.o
|
||||
|
|
@ -32,3 +32,9 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o
|
|||
obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
|
||||
usb_f_fs-y := f_fs.o
|
||||
obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
|
||||
usb_f_uac1-y := f_uac1.o u_uac1.o
|
||||
obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o
|
||||
usb_f_uac2-y := f_uac2.o
|
||||
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
|
||||
usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
|
||||
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
|
||||
|
|
|
|||
|
|
@ -313,15 +313,15 @@ static void acm_complete_set_line_coding(struct usb_ep *ep,
|
|||
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
|
||||
|
||||
if (req->status != 0) {
|
||||
DBG(cdev, "acm ttyGS%d completion, err %d\n",
|
||||
acm->port_num, req->status);
|
||||
dev_dbg(&cdev->gadget->dev, "acm ttyGS%d completion, err %d\n",
|
||||
acm->port_num, req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* normal completion */
|
||||
if (req->actual != sizeof(acm->port_line_coding)) {
|
||||
DBG(cdev, "acm ttyGS%d short resp, len %d\n",
|
||||
acm->port_num, req->actual);
|
||||
dev_dbg(&cdev->gadget->dev, "acm ttyGS%d short resp, len %d\n",
|
||||
acm->port_num, req->actual);
|
||||
usb_ep_set_halt(ep);
|
||||
} else {
|
||||
struct usb_cdc_line_coding *value = req->buf;
|
||||
|
|
@ -397,14 +397,16 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
|||
|
||||
default:
|
||||
invalid:
|
||||
VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
dev_vdbg(&cdev->gadget->dev,
|
||||
"invalid control req%02x.%02x v%04x i%04x l%d\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
}
|
||||
|
||||
/* respond with data transfer or status phase? */
|
||||
if (value >= 0) {
|
||||
DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
|
||||
acm->port_num, ctrl->bRequestType, ctrl->bRequest,
|
||||
w_value, w_index, w_length);
|
||||
req->zero = 0;
|
||||
|
|
@ -428,10 +430,12 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
|
||||
if (intf == acm->ctrl_id) {
|
||||
if (acm->notify->driver_data) {
|
||||
VDBG(cdev, "reset acm control interface %d\n", intf);
|
||||
dev_vdbg(&cdev->gadget->dev,
|
||||
"reset acm control interface %d\n", intf);
|
||||
usb_ep_disable(acm->notify);
|
||||
} else {
|
||||
VDBG(cdev, "init acm ctrl interface %d\n", intf);
|
||||
dev_vdbg(&cdev->gadget->dev,
|
||||
"init acm ctrl interface %d\n", intf);
|
||||
if (config_ep_by_speed(cdev->gadget, f, acm->notify))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -440,11 +444,13 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
|
||||
} else if (intf == acm->data_id) {
|
||||
if (acm->port.in->driver_data) {
|
||||
DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"reset acm ttyGS%d\n", acm->port_num);
|
||||
gserial_disconnect(&acm->port);
|
||||
}
|
||||
if (!acm->port.in->desc || !acm->port.out->desc) {
|
||||
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"activate acm ttyGS%d\n", acm->port_num);
|
||||
if (config_ep_by_speed(cdev->gadget, f,
|
||||
acm->port.in) ||
|
||||
config_ep_by_speed(cdev->gadget, f,
|
||||
|
|
@ -467,7 +473,7 @@ static void acm_disable(struct usb_function *f)
|
|||
struct f_acm *acm = func_to_acm(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
|
||||
dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num);
|
||||
gserial_disconnect(&acm->port);
|
||||
usb_ep_disable(acm->notify);
|
||||
acm->notify->driver_data = NULL;
|
||||
|
|
@ -537,8 +543,8 @@ static int acm_notify_serial_state(struct f_acm *acm)
|
|||
|
||||
spin_lock(&acm->lock);
|
||||
if (acm->notify_req) {
|
||||
DBG(cdev, "acm ttyGS%d serial state %04x\n",
|
||||
acm->port_num, acm->serial_state);
|
||||
dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n",
|
||||
acm->port_num, acm->serial_state);
|
||||
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
|
||||
0, &acm->serial_state, sizeof(acm->serial_state));
|
||||
} else {
|
||||
|
|
@ -691,12 +697,13 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
if (status)
|
||||
goto fail;
|
||||
|
||||
DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
|
||||
acm->port_num,
|
||||
gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
acm->port.in->name, acm->port.out->name,
|
||||
acm->notify->name);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
|
||||
acm->port_num,
|
||||
gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
acm->port.in->name, acm->port.out->name,
|
||||
acm->notify->name);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
|
|
|||
|
|
@ -1032,6 +1032,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
|
|||
case FUNCTIONFS_ENDPOINT_REVMAP:
|
||||
ret = epfile->ep->num;
|
||||
break;
|
||||
case FUNCTIONFS_ENDPOINT_DESC:
|
||||
{
|
||||
int desc_idx;
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
|
||||
switch (epfile->ffs->gadget->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
desc_idx = 2;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
desc_idx = 1;
|
||||
break;
|
||||
default:
|
||||
desc_idx = 0;
|
||||
}
|
||||
desc = epfile->ep->descs[desc_idx];
|
||||
|
||||
spin_unlock_irq(&epfile->ffs->eps_lock);
|
||||
ret = copy_to_user((void *)value, desc, sizeof(*desc));
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
|
|
@ -1534,7 +1557,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
|
|||
epfile->ffs = ffs;
|
||||
mutex_init(&epfile->mutex);
|
||||
init_waitqueue_head(&epfile->wait);
|
||||
sprintf(epfiles->name, "ep%u", i);
|
||||
if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
|
||||
sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]);
|
||||
else
|
||||
sprintf(epfiles->name, "ep%u", i);
|
||||
if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile,
|
||||
&ffs_epfile_operations,
|
||||
&epfile->dentry))) {
|
||||
|
|
@ -2083,10 +2109,12 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
|
|||
break;
|
||||
case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
|
||||
flags = get_unaligned_le32(data + 8);
|
||||
ffs->user_flags = flags;
|
||||
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
|
||||
FUNCTIONFS_HAS_HS_DESC |
|
||||
FUNCTIONFS_HAS_SS_DESC |
|
||||
FUNCTIONFS_HAS_MS_OS_DESC)) {
|
||||
FUNCTIONFS_HAS_MS_OS_DESC |
|
||||
FUNCTIONFS_VIRTUAL_ADDR)) {
|
||||
ret = -ENOSYS;
|
||||
goto error;
|
||||
}
|
||||
|
|
@ -2346,7 +2374,8 @@ static void __ffs_event_add(struct ffs_data *ffs,
|
|||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
WARN(1, "%d: unknown event, this should not happen\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -2393,7 +2422,8 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
|||
struct usb_endpoint_descriptor *ds = (void *)desc;
|
||||
struct ffs_function *func = priv;
|
||||
struct ffs_ep *ffs_ep;
|
||||
unsigned ep_desc_id, idx;
|
||||
unsigned ep_desc_id;
|
||||
int idx;
|
||||
static const char *speed_names[] = { "full", "high", "super" };
|
||||
|
||||
if (type != FFS_DESCRIPTOR)
|
||||
|
|
@ -2441,7 +2471,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
|||
} else {
|
||||
struct usb_request *req;
|
||||
struct usb_ep *ep;
|
||||
u8 bEndpointAddress;
|
||||
|
||||
/*
|
||||
* We back up bEndpointAddress because autoconfig overwrites
|
||||
* it with physical endpoint address.
|
||||
*/
|
||||
bEndpointAddress = ds->bEndpointAddress;
|
||||
pr_vdebug("autoconfig\n");
|
||||
ep = usb_ep_autoconfig(func->gadget, ds);
|
||||
if (unlikely(!ep))
|
||||
|
|
@ -2456,6 +2492,12 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
|
|||
ffs_ep->req = req;
|
||||
func->eps_revmap[ds->bEndpointAddress &
|
||||
USB_ENDPOINT_NUMBER_MASK] = idx + 1;
|
||||
/*
|
||||
* If we use virtual address mapping, we restore
|
||||
* original bEndpointAddress value.
|
||||
*/
|
||||
if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
|
||||
ds->bEndpointAddress = bEndpointAddress;
|
||||
}
|
||||
ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
|
||||
|
||||
|
|
@ -2900,6 +2942,8 @@ static int ffs_func_setup(struct usb_function *f,
|
|||
ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex));
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
|
||||
ret = func->ffs->eps_addrmap[ret];
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -298,7 +298,8 @@ static void disable_loopback(struct f_loopback *loop)
|
|||
struct usb_composite_dev *cdev;
|
||||
|
||||
cdev = loop->function.config->cdev;
|
||||
disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
|
||||
disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL,
|
||||
NULL);
|
||||
VDBG(cdev, "%s disabled\n", loop->function.name);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -566,22 +566,22 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
|
|||
*pbusy = 1;
|
||||
*state = BUF_STATE_BUSY;
|
||||
spin_unlock_irq(&fsg->common->lock);
|
||||
|
||||
rc = usb_ep_queue(ep, req, GFP_KERNEL);
|
||||
if (rc != 0) {
|
||||
*pbusy = 0;
|
||||
*state = BUF_STATE_EMPTY;
|
||||
if (rc == 0)
|
||||
return; /* All good, we're done */
|
||||
|
||||
/* We can't do much more than wait for a reset */
|
||||
*pbusy = 0;
|
||||
*state = BUF_STATE_EMPTY;
|
||||
|
||||
/*
|
||||
* Note: currently the net2280 driver fails zero-length
|
||||
* submissions if DMA is enabled.
|
||||
*/
|
||||
if (rc != -ESHUTDOWN &&
|
||||
!(rc == -EOPNOTSUPP && req->length == 0))
|
||||
WARNING(fsg, "error in submission: %s --> %d\n",
|
||||
ep->name, rc);
|
||||
}
|
||||
/* We can't do much more than wait for a reset */
|
||||
|
||||
/*
|
||||
* Note: currently the net2280 driver fails zero-length
|
||||
* submissions if DMA is enabled.
|
||||
*/
|
||||
if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0))
|
||||
WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc);
|
||||
}
|
||||
|
||||
static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||
|
|
@ -3665,4 +3665,3 @@ void fsg_config_from_params(struct fsg_config *cfg,
|
|||
cfg->fsg_num_buffers = fsg_num_buffers;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsg_config_from_params);
|
||||
|
||||
|
|
|
|||
|
|
@ -200,19 +200,22 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
if (alt != 0)
|
||||
goto fail;
|
||||
/* NOP */
|
||||
DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"reset obex ttyGS%d control\n", obex->port_num);
|
||||
|
||||
} else if (intf == obex->data_id) {
|
||||
if (alt > 1)
|
||||
goto fail;
|
||||
|
||||
if (obex->port.in->driver_data) {
|
||||
DBG(cdev, "reset obex ttyGS%d\n", obex->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"reset obex ttyGS%d\n", obex->port_num);
|
||||
gserial_disconnect(&obex->port);
|
||||
}
|
||||
|
||||
if (!obex->port.in->desc || !obex->port.out->desc) {
|
||||
DBG(cdev, "init obex ttyGS%d\n", obex->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"init obex ttyGS%d\n", obex->port_num);
|
||||
if (config_ep_by_speed(cdev->gadget, f,
|
||||
obex->port.in) ||
|
||||
config_ep_by_speed(cdev->gadget, f,
|
||||
|
|
@ -224,7 +227,8 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
}
|
||||
|
||||
if (alt == 1) {
|
||||
DBG(cdev, "activate obex ttyGS%d\n", obex->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"activate obex ttyGS%d\n", obex->port_num);
|
||||
gserial_connect(&obex->port, obex->port_num);
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +256,7 @@ static void obex_disable(struct usb_function *f)
|
|||
struct f_obex *obex = func_to_obex(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
DBG(cdev, "obex ttyGS%d disable\n", obex->port_num);
|
||||
dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num);
|
||||
gserial_disconnect(&obex->port);
|
||||
}
|
||||
|
||||
|
|
@ -269,7 +273,8 @@ static void obex_connect(struct gserial *g)
|
|||
|
||||
status = usb_function_activate(&g->func);
|
||||
if (status)
|
||||
DBG(cdev, "obex ttyGS%d function activate --> %d\n",
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"obex ttyGS%d function activate --> %d\n",
|
||||
obex->port_num, status);
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +289,8 @@ static void obex_disconnect(struct gserial *g)
|
|||
|
||||
status = usb_function_deactivate(&g->func);
|
||||
if (status)
|
||||
DBG(cdev, "obex ttyGS%d function deactivate --> %d\n",
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"obex ttyGS%d function deactivate --> %d\n",
|
||||
obex->port_num, status);
|
||||
}
|
||||
|
||||
|
|
@ -383,10 +389,10 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
obex->can_activate = true;
|
||||
|
||||
|
||||
DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
|
||||
obex->port_num,
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
obex->port.in->name, obex->port.out->name);
|
||||
dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
|
||||
obex->port_num,
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
obex->port.in->name, obex->port.out->name);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -155,11 +155,13 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
/* we know alt == 0, so this is an activation or a reset */
|
||||
|
||||
if (gser->port.in->driver_data) {
|
||||
DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"reset generic ttyGS%d\n", gser->port_num);
|
||||
gserial_disconnect(&gser->port);
|
||||
}
|
||||
if (!gser->port.in->desc || !gser->port.out->desc) {
|
||||
DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"activate generic ttyGS%d\n", gser->port_num);
|
||||
if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
|
||||
config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
|
||||
gser->port.in->desc = NULL;
|
||||
|
|
@ -176,7 +178,8 @@ static void gser_disable(struct usb_function *f)
|
|||
struct f_gser *gser = func_to_gser(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
|
||||
dev_dbg(&cdev->gadget->dev,
|
||||
"generic ttyGS%d deactivated\n", gser->port_num);
|
||||
gserial_disconnect(&gser->port);
|
||||
}
|
||||
|
||||
|
|
@ -239,11 +242,11 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
gser_ss_function);
|
||||
if (status)
|
||||
goto fail;
|
||||
DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
|
||||
gser->port_num,
|
||||
gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
gser->port.in->name, gser->port.out->name);
|
||||
dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
|
||||
gser->port_num,
|
||||
gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
||||
gser->port.in->name, gser->port.out->name);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
|
|
|||
|
|
@ -23,6 +23,15 @@
|
|||
#include "gadget_chips.h"
|
||||
#include "u_f.h"
|
||||
|
||||
#define USB_MS_TO_SS_INTERVAL(x) USB_MS_TO_HS_INTERVAL(x)
|
||||
|
||||
enum eptype {
|
||||
EP_CONTROL = 0,
|
||||
EP_BULK,
|
||||
EP_ISOC,
|
||||
EP_INTERRUPT,
|
||||
};
|
||||
|
||||
/*
|
||||
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
|
||||
* controller drivers.
|
||||
|
|
@ -55,6 +64,8 @@ struct f_sourcesink {
|
|||
struct usb_ep *out_ep;
|
||||
struct usb_ep *iso_in_ep;
|
||||
struct usb_ep *iso_out_ep;
|
||||
struct usb_ep *int_in_ep;
|
||||
struct usb_ep *int_out_ep;
|
||||
int cur_alt;
|
||||
};
|
||||
|
||||
|
|
@ -68,6 +79,10 @@ static unsigned isoc_interval;
|
|||
static unsigned isoc_maxpacket;
|
||||
static unsigned isoc_mult;
|
||||
static unsigned isoc_maxburst;
|
||||
static unsigned int_interval; /* In ms */
|
||||
static unsigned int_maxpacket;
|
||||
static unsigned int_mult;
|
||||
static unsigned int_maxburst;
|
||||
static unsigned buflen;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -92,6 +107,16 @@ static struct usb_interface_descriptor source_sink_intf_alt1 = {
|
|||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor source_sink_intf_alt2 = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
.bAlternateSetting = 2,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor fs_source_desc = {
|
||||
|
|
@ -130,6 +155,26 @@ static struct usb_endpoint_descriptor fs_iso_sink_desc = {
|
|||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_int_source_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(64),
|
||||
.bInterval = GZERO_INT_INTERVAL,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_int_sink_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(64),
|
||||
.bInterval = GZERO_INT_INTERVAL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *fs_source_sink_descs[] = {
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt0,
|
||||
(struct usb_descriptor_header *) &fs_sink_desc,
|
||||
|
|
@ -140,6 +185,10 @@ static struct usb_descriptor_header *fs_source_sink_descs[] = {
|
|||
(struct usb_descriptor_header *) &fs_source_desc,
|
||||
(struct usb_descriptor_header *) &fs_iso_sink_desc,
|
||||
(struct usb_descriptor_header *) &fs_iso_source_desc,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt2,
|
||||
#define FS_ALT_IFC_2_OFFSET 8
|
||||
(struct usb_descriptor_header *) &fs_int_sink_desc,
|
||||
(struct usb_descriptor_header *) &fs_int_source_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -179,6 +228,24 @@ static struct usb_endpoint_descriptor hs_iso_sink_desc = {
|
|||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_int_source_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
.bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_int_sink_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
.bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hs_source_sink_descs[] = {
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt0,
|
||||
(struct usb_descriptor_header *) &hs_source_desc,
|
||||
|
|
@ -189,6 +256,10 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = {
|
|||
(struct usb_descriptor_header *) &hs_sink_desc,
|
||||
(struct usb_descriptor_header *) &hs_iso_source_desc,
|
||||
(struct usb_descriptor_header *) &hs_iso_sink_desc,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt2,
|
||||
#define HS_ALT_IFC_2_OFFSET 8
|
||||
(struct usb_descriptor_header *) &hs_int_source_desc,
|
||||
(struct usb_descriptor_header *) &hs_int_sink_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -264,6 +335,42 @@ static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
|
|||
.wBytesPerInterval = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_int_source_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
.bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL),
|
||||
};
|
||||
|
||||
struct usb_ss_ep_comp_descriptor ss_int_source_comp_desc = {
|
||||
.bLength = USB_DT_SS_EP_COMP_SIZE,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
.bMaxBurst = 0,
|
||||
.bmAttributes = 0,
|
||||
.wBytesPerInterval = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ss_int_sink_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
.bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL),
|
||||
};
|
||||
|
||||
struct usb_ss_ep_comp_descriptor ss_int_sink_comp_desc = {
|
||||
.bLength = USB_DT_SS_EP_COMP_SIZE,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
.bMaxBurst = 0,
|
||||
.bmAttributes = 0,
|
||||
.wBytesPerInterval = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *ss_source_sink_descs[] = {
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt0,
|
||||
(struct usb_descriptor_header *) &ss_source_desc,
|
||||
|
|
@ -280,6 +387,12 @@ static struct usb_descriptor_header *ss_source_sink_descs[] = {
|
|||
(struct usb_descriptor_header *) &ss_iso_source_comp_desc,
|
||||
(struct usb_descriptor_header *) &ss_iso_sink_desc,
|
||||
(struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
|
||||
(struct usb_descriptor_header *) &source_sink_intf_alt2,
|
||||
#define SS_ALT_IFC_2_OFFSET 14
|
||||
(struct usb_descriptor_header *) &ss_int_source_desc,
|
||||
(struct usb_descriptor_header *) &ss_int_source_comp_desc,
|
||||
(struct usb_descriptor_header *) &ss_int_sink_desc,
|
||||
(struct usb_descriptor_header *) &ss_int_sink_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -301,6 +414,21 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
|
|||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static const char *get_ep_string(enum eptype ep_type)
|
||||
{
|
||||
switch (ep_type) {
|
||||
case EP_ISOC:
|
||||
return "ISOC-";
|
||||
case EP_INTERRUPT:
|
||||
return "INTERRUPT-";
|
||||
case EP_CONTROL:
|
||||
return "CTRL-";
|
||||
case EP_BULK:
|
||||
return "BULK-";
|
||||
default:
|
||||
return "UNKNOWN-";
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
|
||||
{
|
||||
|
|
@ -328,7 +456,8 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
|
|||
|
||||
void disable_endpoints(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *in, struct usb_ep *out,
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out)
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out,
|
||||
struct usb_ep *int_in, struct usb_ep *int_out)
|
||||
{
|
||||
disable_ep(cdev, in);
|
||||
disable_ep(cdev, out);
|
||||
|
|
@ -336,6 +465,10 @@ void disable_endpoints(struct usb_composite_dev *cdev,
|
|||
disable_ep(cdev, iso_in);
|
||||
if (iso_out)
|
||||
disable_ep(cdev, iso_out);
|
||||
if (int_in)
|
||||
disable_ep(cdev, int_in);
|
||||
if (int_out)
|
||||
disable_ep(cdev, int_out);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -352,6 +485,7 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
return id;
|
||||
source_sink_intf_alt0.bInterfaceNumber = id;
|
||||
source_sink_intf_alt1.bInterfaceNumber = id;
|
||||
source_sink_intf_alt2.bInterfaceNumber = id;
|
||||
|
||||
/* allocate bulk endpoints */
|
||||
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
|
||||
|
|
@ -412,14 +546,55 @@ no_iso:
|
|||
if (isoc_maxpacket > 1024)
|
||||
isoc_maxpacket = 1024;
|
||||
|
||||
/* sanity check the interrupt module parameters */
|
||||
if (int_interval < 1)
|
||||
int_interval = 1;
|
||||
if (int_interval > 4096)
|
||||
int_interval = 4096;
|
||||
if (int_mult > 2)
|
||||
int_mult = 2;
|
||||
if (int_maxburst > 15)
|
||||
int_maxburst = 15;
|
||||
|
||||
/* fill in the FS interrupt descriptors from the module parameters */
|
||||
fs_int_source_desc.wMaxPacketSize = int_maxpacket > 64 ?
|
||||
64 : int_maxpacket;
|
||||
fs_int_source_desc.bInterval = int_interval > 255 ?
|
||||
255 : int_interval;
|
||||
fs_int_sink_desc.wMaxPacketSize = int_maxpacket > 64 ?
|
||||
64 : int_maxpacket;
|
||||
fs_int_sink_desc.bInterval = int_interval > 255 ?
|
||||
255 : int_interval;
|
||||
|
||||
/* allocate int endpoints */
|
||||
ss->int_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_source_desc);
|
||||
if (!ss->int_in_ep)
|
||||
goto no_int;
|
||||
ss->int_in_ep->driver_data = cdev; /* claim */
|
||||
|
||||
ss->int_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_sink_desc);
|
||||
if (ss->int_out_ep) {
|
||||
ss->int_out_ep->driver_data = cdev; /* claim */
|
||||
} else {
|
||||
ss->int_in_ep->driver_data = NULL;
|
||||
ss->int_in_ep = NULL;
|
||||
no_int:
|
||||
fs_source_sink_descs[FS_ALT_IFC_2_OFFSET] = NULL;
|
||||
hs_source_sink_descs[HS_ALT_IFC_2_OFFSET] = NULL;
|
||||
ss_source_sink_descs[SS_ALT_IFC_2_OFFSET] = NULL;
|
||||
}
|
||||
|
||||
if (int_maxpacket > 1024)
|
||||
int_maxpacket = 1024;
|
||||
|
||||
/* support high speed hardware */
|
||||
hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
|
||||
hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
|
||||
|
||||
/*
|
||||
* Fill in the HS isoc descriptors from the module parameters.
|
||||
* We assume that the user knows what they are doing and won't
|
||||
* give parameters that their UDC doesn't support.
|
||||
* Fill in the HS isoc and interrupt descriptors from the module
|
||||
* parameters. We assume that the user knows what they are doing and
|
||||
* won't give parameters that their UDC doesn't support.
|
||||
*/
|
||||
hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
|
||||
hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
|
||||
|
|
@ -432,6 +607,17 @@ no_iso:
|
|||
hs_iso_sink_desc.bInterval = isoc_interval;
|
||||
hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
|
||||
|
||||
hs_int_source_desc.wMaxPacketSize = int_maxpacket;
|
||||
hs_int_source_desc.wMaxPacketSize |= int_mult << 11;
|
||||
hs_int_source_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval);
|
||||
hs_int_source_desc.bEndpointAddress =
|
||||
fs_int_source_desc.bEndpointAddress;
|
||||
|
||||
hs_int_sink_desc.wMaxPacketSize = int_maxpacket;
|
||||
hs_int_sink_desc.wMaxPacketSize |= int_mult << 11;
|
||||
hs_int_sink_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval);
|
||||
hs_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress;
|
||||
|
||||
/* support super speed hardware */
|
||||
ss_source_desc.bEndpointAddress =
|
||||
fs_source_desc.bEndpointAddress;
|
||||
|
|
@ -439,9 +625,9 @@ no_iso:
|
|||
fs_sink_desc.bEndpointAddress;
|
||||
|
||||
/*
|
||||
* Fill in the SS isoc descriptors from the module parameters.
|
||||
* We assume that the user knows what they are doing and won't
|
||||
* give parameters that their UDC doesn't support.
|
||||
* Fill in the SS isoc and interrupt descriptors from the module
|
||||
* parameters. We assume that the user knows what they are doing and
|
||||
* won't give parameters that their UDC doesn't support.
|
||||
*/
|
||||
ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
|
||||
ss_iso_source_desc.bInterval = isoc_interval;
|
||||
|
|
@ -460,17 +646,37 @@ no_iso:
|
|||
isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
|
||||
ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
|
||||
|
||||
ss_int_source_desc.wMaxPacketSize = int_maxpacket;
|
||||
ss_int_source_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval);
|
||||
ss_int_source_comp_desc.bmAttributes = int_mult;
|
||||
ss_int_source_comp_desc.bMaxBurst = int_maxburst;
|
||||
ss_int_source_comp_desc.wBytesPerInterval =
|
||||
int_maxpacket * (int_mult + 1) * (int_maxburst + 1);
|
||||
ss_int_source_desc.bEndpointAddress =
|
||||
fs_int_source_desc.bEndpointAddress;
|
||||
|
||||
ss_int_sink_desc.wMaxPacketSize = int_maxpacket;
|
||||
ss_int_sink_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval);
|
||||
ss_int_sink_comp_desc.bmAttributes = int_mult;
|
||||
ss_int_sink_comp_desc.bMaxBurst = int_maxburst;
|
||||
ss_int_sink_comp_desc.wBytesPerInterval =
|
||||
int_maxpacket * (int_mult + 1) * (int_maxburst + 1);
|
||||
ss_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress;
|
||||
|
||||
ret = usb_assign_descriptors(f, fs_source_sink_descs,
|
||||
hs_source_sink_descs, ss_source_sink_descs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
|
||||
DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, "
|
||||
"INT-IN/%s, INT-OUT/%s\n",
|
||||
(gadget_is_superspeed(c->cdev->gadget) ? "super" :
|
||||
(gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
|
||||
f->name, ss->in_ep->name, ss->out_ep->name,
|
||||
ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
|
||||
ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
|
||||
ss->iso_out_ep ? ss->iso_out_ep->name : "<none>",
|
||||
ss->int_in_ep ? ss->int_in_ep->name : "<none>",
|
||||
ss->int_out_ep ? ss->int_out_ep->name : "<none>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -601,14 +807,15 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
}
|
||||
|
||||
static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
||||
bool is_iso, int speed)
|
||||
enum eptype ep_type, int speed)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
int i, size, status;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (is_iso) {
|
||||
switch (ep_type) {
|
||||
case EP_ISOC:
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
size = isoc_maxpacket * (isoc_mult + 1) *
|
||||
|
|
@ -624,9 +831,28 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
|||
}
|
||||
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
|
||||
req = ss_alloc_ep_req(ep, size);
|
||||
} else {
|
||||
break;
|
||||
case EP_INTERRUPT:
|
||||
switch (speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
size = int_maxpacket * (int_mult + 1) *
|
||||
(int_maxburst + 1);
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
size = int_maxpacket * (int_mult + 1);
|
||||
break;
|
||||
default:
|
||||
size = int_maxpacket > 1023 ?
|
||||
1023 : int_maxpacket;
|
||||
break;
|
||||
}
|
||||
ep = is_in ? ss->int_in_ep : ss->int_out_ep;
|
||||
req = ss_alloc_ep_req(ep, size);
|
||||
break;
|
||||
default:
|
||||
ep = is_in ? ss->in_ep : ss->out_ep;
|
||||
req = ss_alloc_ep_req(ep, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!req)
|
||||
|
|
@ -644,12 +870,12 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
|||
|
||||
cdev = ss->function.config->cdev;
|
||||
ERROR(cdev, "start %s%s %s --> %d\n",
|
||||
is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
|
||||
ep->name, status);
|
||||
get_ep_string(ep_type), is_in ? "IN" : "OUT",
|
||||
ep->name, status);
|
||||
free_ep_req(ep, req);
|
||||
}
|
||||
|
||||
if (!is_iso)
|
||||
if (!(ep_type == EP_ISOC))
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -662,7 +888,7 @@ static void disable_source_sink(struct f_sourcesink *ss)
|
|||
|
||||
cdev = ss->function.config->cdev;
|
||||
disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
|
||||
ss->iso_out_ep);
|
||||
ss->iso_out_ep, ss->int_in_ep, ss->int_out_ep);
|
||||
VDBG(cdev, "%s disabled\n", ss->function.name);
|
||||
}
|
||||
|
||||
|
|
@ -674,6 +900,62 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
|
|||
int speed = cdev->gadget->speed;
|
||||
struct usb_ep *ep;
|
||||
|
||||
if (alt == 2) {
|
||||
/* Configure for periodic interrupt endpoint */
|
||||
ep = ss->int_in_ep;
|
||||
if (ep) {
|
||||
result = config_ep_by_speed(cdev->gadget,
|
||||
&(ss->function), ep);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = usb_ep_enable(ep);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
ep->driver_data = ss;
|
||||
result = source_sink_start_ep(ss, true, EP_INTERRUPT,
|
||||
speed);
|
||||
if (result < 0) {
|
||||
fail1:
|
||||
ep = ss->int_in_ep;
|
||||
if (ep) {
|
||||
usb_ep_disable(ep);
|
||||
ep->driver_data = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* one interrupt endpoint reads (sinks) anything OUT (from the
|
||||
* host)
|
||||
*/
|
||||
ep = ss->int_out_ep;
|
||||
if (ep) {
|
||||
result = config_ep_by_speed(cdev->gadget,
|
||||
&(ss->function), ep);
|
||||
if (result)
|
||||
goto fail1;
|
||||
|
||||
result = usb_ep_enable(ep);
|
||||
if (result < 0)
|
||||
goto fail1;
|
||||
|
||||
ep->driver_data = ss;
|
||||
result = source_sink_start_ep(ss, false, EP_INTERRUPT,
|
||||
speed);
|
||||
if (result < 0) {
|
||||
ep = ss->int_out_ep;
|
||||
usb_ep_disable(ep);
|
||||
ep->driver_data = NULL;
|
||||
goto fail1;
|
||||
}
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* one bulk endpoint writes (sources) zeroes IN (to the host) */
|
||||
ep = ss->in_ep;
|
||||
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
|
||||
|
|
@ -684,7 +966,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
|
|||
return result;
|
||||
ep->driver_data = ss;
|
||||
|
||||
result = source_sink_start_ep(ss, true, false, speed);
|
||||
result = source_sink_start_ep(ss, true, EP_BULK, speed);
|
||||
if (result < 0) {
|
||||
fail:
|
||||
ep = ss->in_ep;
|
||||
|
|
@ -703,7 +985,7 @@ fail:
|
|||
goto fail;
|
||||
ep->driver_data = ss;
|
||||
|
||||
result = source_sink_start_ep(ss, false, false, speed);
|
||||
result = source_sink_start_ep(ss, false, EP_BULK, speed);
|
||||
if (result < 0) {
|
||||
fail2:
|
||||
ep = ss->out_ep;
|
||||
|
|
@ -726,7 +1008,7 @@ fail2:
|
|||
goto fail2;
|
||||
ep->driver_data = ss;
|
||||
|
||||
result = source_sink_start_ep(ss, true, true, speed);
|
||||
result = source_sink_start_ep(ss, true, EP_ISOC, speed);
|
||||
if (result < 0) {
|
||||
fail3:
|
||||
ep = ss->iso_in_ep;
|
||||
|
|
@ -749,13 +1031,14 @@ fail3:
|
|||
goto fail3;
|
||||
ep->driver_data = ss;
|
||||
|
||||
result = source_sink_start_ep(ss, false, true, speed);
|
||||
result = source_sink_start_ep(ss, false, EP_ISOC, speed);
|
||||
if (result < 0) {
|
||||
usb_ep_disable(ep);
|
||||
ep->driver_data = NULL;
|
||||
goto fail3;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
ss->cur_alt = alt;
|
||||
|
||||
|
|
@ -771,6 +1054,8 @@ static int sourcesink_set_alt(struct usb_function *f,
|
|||
|
||||
if (ss->in_ep->driver_data)
|
||||
disable_source_sink(ss);
|
||||
else if (alt == 2 && ss->int_in_ep->driver_data)
|
||||
disable_source_sink(ss);
|
||||
return enable_source_sink(cdev, ss, alt);
|
||||
}
|
||||
|
||||
|
|
@ -883,6 +1168,10 @@ static struct usb_function *source_sink_alloc_func(
|
|||
isoc_maxpacket = ss_opts->isoc_maxpacket;
|
||||
isoc_mult = ss_opts->isoc_mult;
|
||||
isoc_maxburst = ss_opts->isoc_maxburst;
|
||||
int_interval = ss_opts->int_interval;
|
||||
int_maxpacket = ss_opts->int_maxpacket;
|
||||
int_mult = ss_opts->int_mult;
|
||||
int_maxburst = ss_opts->int_maxburst;
|
||||
buflen = ss_opts->bulk_buflen;
|
||||
|
||||
ss->function.name = "source/sink";
|
||||
|
|
@ -1179,6 +1468,182 @@ static struct f_ss_opts_attribute f_ss_opts_bulk_buflen =
|
|||
f_ss_opts_bulk_buflen_show,
|
||||
f_ss_opts_bulk_buflen_store);
|
||||
|
||||
static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page)
|
||||
{
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%d", opts->int_interval);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
int ret;
|
||||
u32 num;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = kstrtou32(page, 0, &num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
if (num > 4096) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
opts->int_interval = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct f_ss_opts_attribute f_ss_opts_int_interval =
|
||||
__CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR,
|
||||
f_ss_opts_int_interval_show,
|
||||
f_ss_opts_int_interval_store);
|
||||
|
||||
static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page)
|
||||
{
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%d", opts->int_maxpacket);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_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;
|
||||
|
||||
if (num > 1024) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
opts->int_maxpacket = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct f_ss_opts_attribute f_ss_opts_int_maxpacket =
|
||||
__CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR,
|
||||
f_ss_opts_int_maxpacket_show,
|
||||
f_ss_opts_int_maxpacket_store);
|
||||
|
||||
static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page)
|
||||
{
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%d", opts->int_mult);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
int ret;
|
||||
u8 num;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = kstrtou8(page, 0, &num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
if (num > 2) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
opts->int_mult = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct f_ss_opts_attribute f_ss_opts_int_mult =
|
||||
__CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR,
|
||||
f_ss_opts_int_mult_show,
|
||||
f_ss_opts_int_mult_store);
|
||||
|
||||
static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page)
|
||||
{
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = sprintf(page, "%d", opts->int_maxburst);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
int ret;
|
||||
u8 num;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = kstrtou8(page, 0, &num);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
if (num > 15) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
opts->int_maxburst = num;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct f_ss_opts_attribute f_ss_opts_int_maxburst =
|
||||
__CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR,
|
||||
f_ss_opts_int_maxburst_show,
|
||||
f_ss_opts_int_maxburst_store);
|
||||
|
||||
static struct configfs_attribute *ss_attrs[] = {
|
||||
&f_ss_opts_pattern.attr,
|
||||
&f_ss_opts_isoc_interval.attr,
|
||||
|
|
@ -1186,6 +1651,10 @@ static struct configfs_attribute *ss_attrs[] = {
|
|||
&f_ss_opts_isoc_mult.attr,
|
||||
&f_ss_opts_isoc_maxburst.attr,
|
||||
&f_ss_opts_bulk_buflen.attr,
|
||||
&f_ss_opts_int_interval.attr,
|
||||
&f_ss_opts_int_maxpacket.attr,
|
||||
&f_ss_opts_int_mult.attr,
|
||||
&f_ss_opts_int_maxburst.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -1215,6 +1684,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
|
|||
ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
|
||||
ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
|
||||
ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
|
||||
ss_opts->int_interval = GZERO_INT_INTERVAL;
|
||||
ss_opts->int_maxpacket = GZERO_INT_MAXPACKET;
|
||||
|
||||
config_group_init_type_name(&ss_opts->func_inst.group, "",
|
||||
&ss_func_type);
|
||||
|
|
|
|||
|
|
@ -11,24 +11,12 @@
|
|||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include "u_uac1.h"
|
||||
|
||||
#define OUT_EP_MAX_PACKET_SIZE 200
|
||||
static int req_buf_size = OUT_EP_MAX_PACKET_SIZE;
|
||||
module_param(req_buf_size, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
|
||||
|
||||
static int req_count = 256;
|
||||
module_param(req_count, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
|
||||
|
||||
static int audio_buf_size = 48000;
|
||||
module_param(audio_buf_size, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
|
||||
|
||||
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
|
||||
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
|
||||
|
||||
|
|
@ -46,7 +34,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
|
|||
#define F_AUDIO_NUM_INTERFACES 2
|
||||
|
||||
/* B.3.1 Standard AC Interface Descriptor */
|
||||
static struct usb_interface_descriptor ac_interface_desc __initdata = {
|
||||
static struct usb_interface_descriptor ac_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bNumEndpoints = 0,
|
||||
|
|
@ -183,12 +171,12 @@ static struct usb_endpoint_descriptor as_out_ep_desc = {
|
|||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE
|
||||
| USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
|
||||
.wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
/* Class-specific AS ISO OUT Endpoint Descriptor */
|
||||
static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = {
|
||||
static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
|
||||
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_ENDPOINT,
|
||||
.bDescriptorSubtype = UAC_EP_GENERAL,
|
||||
|
|
@ -197,7 +185,7 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = {
|
|||
.wLockDelay = __constant_cpu_to_le16(1),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *f_audio_desc[] __initdata = {
|
||||
static struct usb_descriptor_header *f_audio_desc[] = {
|
||||
(struct usb_descriptor_header *)&ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&ac_header_desc,
|
||||
|
||||
|
|
@ -216,6 +204,37 @@ static struct usb_descriptor_header *f_audio_desc[] __initdata = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
enum {
|
||||
STR_AC_IF,
|
||||
STR_INPUT_TERMINAL,
|
||||
STR_INPUT_TERMINAL_CH_NAMES,
|
||||
STR_FEAT_DESC_0,
|
||||
STR_OUTPUT_TERMINAL,
|
||||
STR_AS_IF_ALT0,
|
||||
STR_AS_IF_ALT1,
|
||||
};
|
||||
|
||||
static struct usb_string strings_uac1[] = {
|
||||
[STR_AC_IF].s = "AC Interface",
|
||||
[STR_INPUT_TERMINAL].s = "Input terminal",
|
||||
[STR_INPUT_TERMINAL_CH_NAMES].s = "Channels",
|
||||
[STR_FEAT_DESC_0].s = "Volume control & mute",
|
||||
[STR_OUTPUT_TERMINAL].s = "Output terminal",
|
||||
[STR_AS_IF_ALT0].s = "AS Interface",
|
||||
[STR_AS_IF_ALT1].s = "AS Interface",
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings str_uac1 = {
|
||||
.language = 0x0409, /* en-us */
|
||||
.strings = strings_uac1,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *uac1_strings[] = {
|
||||
&str_uac1,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
|
||||
*/
|
||||
|
|
@ -300,8 +319,14 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
struct f_audio *audio = req->context;
|
||||
struct usb_composite_dev *cdev = audio->card.func.config->cdev;
|
||||
struct f_audio_buf *copy_buf = audio->copy_buf;
|
||||
struct f_uac1_opts *opts;
|
||||
int audio_buf_size;
|
||||
int err;
|
||||
|
||||
opts = container_of(audio->card.func.fi, struct f_uac1_opts,
|
||||
func_inst);
|
||||
audio_buf_size = opts->audio_buf_size;
|
||||
|
||||
if (!copy_buf)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -546,10 +571,17 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct usb_ep *out_ep = audio->out_ep;
|
||||
struct usb_request *req;
|
||||
struct f_uac1_opts *opts;
|
||||
int req_buf_size, req_count, audio_buf_size;
|
||||
int i = 0, err = 0;
|
||||
|
||||
DBG(cdev, "intf %d, alt %d\n", intf, alt);
|
||||
|
||||
opts = container_of(f->fi, struct f_uac1_opts, func_inst);
|
||||
req_buf_size = opts->req_buf_size;
|
||||
req_count = opts->req_count;
|
||||
audio_buf_size = opts->audio_buf_size;
|
||||
|
||||
if (intf == 1) {
|
||||
if (alt == 1) {
|
||||
usb_ep_enable(out_ep);
|
||||
|
|
@ -625,13 +657,37 @@ static void f_audio_build_desc(struct f_audio *audio)
|
|||
}
|
||||
|
||||
/* audio function driver setup/binding */
|
||||
static int __init
|
||||
static int
|
||||
f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_audio *audio = func_to_audio(f);
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep = NULL;
|
||||
struct f_uac1_opts *audio_opts;
|
||||
|
||||
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
|
||||
audio->card.gadget = c->cdev->gadget;
|
||||
audio_opts->card = &audio->card;
|
||||
/* set up ASLA audio devices */
|
||||
if (!audio_opts->bound) {
|
||||
status = gaudio_setup(&audio->card);
|
||||
if (status < 0)
|
||||
return status;
|
||||
audio_opts->bound = true;
|
||||
}
|
||||
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
ac_interface_desc.iInterface = us[STR_AC_IF].id;
|
||||
input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id;
|
||||
input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id;
|
||||
feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id;
|
||||
output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id;
|
||||
as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id;
|
||||
as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id;
|
||||
|
||||
|
||||
f_audio_build_desc(audio);
|
||||
|
||||
|
|
@ -666,20 +722,12 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
return 0;
|
||||
|
||||
fail:
|
||||
gaudio_cleanup(&audio->card);
|
||||
if (ep)
|
||||
ep->driver_data = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_audio *audio = func_to_audio(f);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(audio);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
|
||||
|
|
@ -695,7 +743,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
|
|||
}
|
||||
|
||||
/* Todo: add more control selecotor dynamically */
|
||||
static int __init control_selector_init(struct f_audio *audio)
|
||||
static int control_selector_init(struct f_audio *audio)
|
||||
{
|
||||
INIT_LIST_HEAD(&audio->cs);
|
||||
list_add(&feature_unit.list, &audio->cs);
|
||||
|
|
@ -712,57 +760,226 @@ static int __init control_selector_init(struct f_audio *audio)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* audio_bind_config - add USB audio function to a configuration
|
||||
* @c: the configuration to supcard the USB audio function
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
static int __init audio_bind_config(struct usb_configuration *c)
|
||||
static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_uac1_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(f_uac1_opts);
|
||||
CONFIGFS_ATTR_OPS(f_uac1_opts);
|
||||
|
||||
static void f_uac1_attr_release(struct config_item *item)
|
||||
{
|
||||
struct f_uac1_opts *opts = to_f_uac1_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations f_uac1_item_ops = {
|
||||
.release = f_uac1_attr_release,
|
||||
.show_attribute = f_uac1_opts_attr_show,
|
||||
.store_attribute = f_uac1_opts_attr_store,
|
||||
};
|
||||
|
||||
#define UAC1_INT_ATTRIBUTE(name) \
|
||||
static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \
|
||||
char *page) \
|
||||
{ \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = sprintf(page, "%u\n", opts->name); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
int ret; \
|
||||
u32 num; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
ret = kstrtou32(page, 0, &num); \
|
||||
if (ret) \
|
||||
goto end; \
|
||||
\
|
||||
opts->name = num; \
|
||||
ret = len; \
|
||||
\
|
||||
end: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_uac1_opts_attribute f_uac1_opts_##name = \
|
||||
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
|
||||
f_uac1_opts_##name##_show, \
|
||||
f_uac1_opts_##name##_store)
|
||||
|
||||
UAC1_INT_ATTRIBUTE(req_buf_size);
|
||||
UAC1_INT_ATTRIBUTE(req_count);
|
||||
UAC1_INT_ATTRIBUTE(audio_buf_size);
|
||||
|
||||
#define UAC1_STR_ATTRIBUTE(name) \
|
||||
static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \
|
||||
char *page) \
|
||||
{ \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = sprintf(page, "%s\n", opts->name); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
int ret = -EBUSY; \
|
||||
char *tmp; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) \
|
||||
goto end; \
|
||||
\
|
||||
tmp = kstrndup(page, len, GFP_KERNEL); \
|
||||
if (tmp) { \
|
||||
ret = -ENOMEM; \
|
||||
goto end; \
|
||||
} \
|
||||
if (opts->name##_alloc) \
|
||||
kfree(opts->name); \
|
||||
opts->name##_alloc = true; \
|
||||
opts->name = tmp; \
|
||||
ret = len; \
|
||||
\
|
||||
end: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_uac1_opts_attribute f_uac1_opts_##name = \
|
||||
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
|
||||
f_uac1_opts_##name##_show, \
|
||||
f_uac1_opts_##name##_store)
|
||||
|
||||
UAC1_STR_ATTRIBUTE(fn_play);
|
||||
UAC1_STR_ATTRIBUTE(fn_cap);
|
||||
UAC1_STR_ATTRIBUTE(fn_cntl);
|
||||
|
||||
static struct configfs_attribute *f_uac1_attrs[] = {
|
||||
&f_uac1_opts_req_buf_size.attr,
|
||||
&f_uac1_opts_req_count.attr,
|
||||
&f_uac1_opts_audio_buf_size.attr,
|
||||
&f_uac1_opts_fn_play.attr,
|
||||
&f_uac1_opts_fn_cap.attr,
|
||||
&f_uac1_opts_fn_cntl.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type f_uac1_func_type = {
|
||||
.ct_item_ops = &f_uac1_item_ops,
|
||||
.ct_attrs = f_uac1_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void f_audio_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_uac1_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_uac1_opts, func_inst);
|
||||
gaudio_cleanup(opts->card);
|
||||
if (opts->fn_play_alloc)
|
||||
kfree(opts->fn_play);
|
||||
if (opts->fn_cap_alloc)
|
||||
kfree(opts->fn_cap);
|
||||
if (opts->fn_cntl_alloc)
|
||||
kfree(opts->fn_cntl);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *f_audio_alloc_inst(void)
|
||||
{
|
||||
struct f_uac1_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = f_audio_free_inst;
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&f_uac1_func_type);
|
||||
|
||||
opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
|
||||
opts->req_count = UAC1_REQ_COUNT;
|
||||
opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE;
|
||||
opts->fn_play = FILE_PCM_PLAYBACK;
|
||||
opts->fn_cap = FILE_PCM_CAPTURE;
|
||||
opts->fn_cntl = FILE_CONTROL;
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void f_audio_free(struct usb_function *f)
|
||||
{
|
||||
struct f_audio *audio = func_to_audio(f);
|
||||
struct f_uac1_opts *opts;
|
||||
|
||||
opts = container_of(f->fi, struct f_uac1_opts, func_inst);
|
||||
kfree(audio);
|
||||
mutex_lock(&opts->lock);
|
||||
--opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_audio *audio;
|
||||
int status;
|
||||
struct f_uac1_opts *opts;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
audio = kzalloc(sizeof *audio, GFP_KERNEL);
|
||||
audio = kzalloc(sizeof(*audio), GFP_KERNEL);
|
||||
if (!audio)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
audio->card.func.name = "g_audio";
|
||||
audio->card.gadget = c->cdev->gadget;
|
||||
|
||||
opts = container_of(fi, struct f_uac1_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
++opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
INIT_LIST_HEAD(&audio->play_queue);
|
||||
spin_lock_init(&audio->lock);
|
||||
|
||||
/* set up ASLA audio devices */
|
||||
status = gaudio_setup(&audio->card);
|
||||
if (status < 0)
|
||||
goto setup_fail;
|
||||
|
||||
audio->card.func.strings = audio_strings;
|
||||
audio->card.func.bind = f_audio_bind;
|
||||
audio->card.func.unbind = f_audio_unbind;
|
||||
audio->card.func.set_alt = f_audio_set_alt;
|
||||
audio->card.func.setup = f_audio_setup;
|
||||
audio->card.func.disable = f_audio_disable;
|
||||
audio->card.func.free_func = f_audio_free;
|
||||
|
||||
control_selector_init(audio);
|
||||
|
||||
INIT_WORK(&audio->playback_work, f_audio_playback_work);
|
||||
|
||||
status = usb_add_function(c, &audio->card.func);
|
||||
if (status)
|
||||
goto add_fail;
|
||||
|
||||
INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n",
|
||||
audio_buf_size, req_buf_size, req_count);
|
||||
|
||||
return status;
|
||||
|
||||
add_fail:
|
||||
gaudio_cleanup();
|
||||
setup_fail:
|
||||
kfree(audio);
|
||||
return status;
|
||||
return &audio->card.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bryan Wu");
|
||||
|
|
|
|||
|
|
@ -20,35 +20,7 @@
|
|||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
/* Playback(USB-IN) Default Stereo - Fl/Fr */
|
||||
static int p_chmask = 0x3;
|
||||
module_param(p_chmask, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
|
||||
|
||||
/* Playback Default 48 KHz */
|
||||
static int p_srate = 48000;
|
||||
module_param(p_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
|
||||
|
||||
/* Playback Default 16bits/sample */
|
||||
static int p_ssize = 2;
|
||||
module_param(p_ssize, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
|
||||
|
||||
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
|
||||
static int c_chmask = 0x3;
|
||||
module_param(c_chmask, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
|
||||
|
||||
/* Capture Default 64 KHz */
|
||||
static int c_srate = 64000;
|
||||
module_param(c_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
|
||||
|
||||
/* Capture Default 16bits/sample */
|
||||
static int c_ssize = 2;
|
||||
module_param(c_ssize, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
|
||||
#include "u_uac2.h"
|
||||
|
||||
/* Keep everyone on toes */
|
||||
#define USB_XFERS 2
|
||||
|
|
@ -120,6 +92,15 @@ struct snd_uac2_chip {
|
|||
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
|
||||
/* timekeeping for the playback endpoint */
|
||||
unsigned int p_interval;
|
||||
unsigned int p_residue;
|
||||
|
||||
/* pre-calculated values for playback iso completion */
|
||||
unsigned int p_pktsize;
|
||||
unsigned int p_pktsize_residue;
|
||||
unsigned int p_framesize;
|
||||
};
|
||||
|
||||
#define BUFF_SIZE_MAX (PAGE_SIZE * 16)
|
||||
|
|
@ -149,8 +130,6 @@ struct audio_dev {
|
|||
struct snd_uac2_chip uac2;
|
||||
};
|
||||
|
||||
static struct audio_dev *agdev_g;
|
||||
|
||||
static inline
|
||||
struct audio_dev *func_to_agdev(struct usb_function *f)
|
||||
{
|
||||
|
|
@ -169,6 +148,12 @@ struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p)
|
|||
return container_of(p, struct snd_uac2_chip, pdev);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct f_uac2_opts *agdev_to_uac2_opts(struct audio_dev *agdev)
|
||||
{
|
||||
return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
|
||||
}
|
||||
|
||||
static inline
|
||||
uint num_channels(uint chanmask)
|
||||
{
|
||||
|
|
@ -187,8 +172,8 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
{
|
||||
unsigned pending;
|
||||
unsigned long flags;
|
||||
unsigned int hw_ptr;
|
||||
bool update_alsa = false;
|
||||
unsigned char *src, *dst;
|
||||
int status = req->status;
|
||||
struct uac2_req *ur = req->context;
|
||||
struct snd_pcm_substream *substream;
|
||||
|
|
@ -216,12 +201,27 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
spin_lock_irqsave(&prm->lock, flags);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
src = prm->dma_area + prm->hw_ptr;
|
||||
/*
|
||||
* For each IN packet, take the quotient of the current data
|
||||
* rate and the endpoint's interval as the base packet size.
|
||||
* If there is a residue from this division, add it to the
|
||||
* residue accumulator.
|
||||
*/
|
||||
req->length = uac2->p_pktsize;
|
||||
uac2->p_residue += uac2->p_pktsize_residue;
|
||||
|
||||
/*
|
||||
* Whenever there are more bytes in the accumulator than we
|
||||
* need to add one more sample frame, increase this packet's
|
||||
* size and decrease the accumulator.
|
||||
*/
|
||||
if (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) {
|
||||
req->length += uac2->p_framesize;
|
||||
uac2->p_residue -= uac2->p_framesize *
|
||||
uac2->p_interval;
|
||||
}
|
||||
|
||||
req->actual = req->length;
|
||||
dst = req->buf;
|
||||
} else {
|
||||
dst = prm->dma_area + prm->hw_ptr;
|
||||
src = req->buf;
|
||||
}
|
||||
|
||||
pending = prm->hw_ptr % prm->period_size;
|
||||
|
|
@ -229,12 +229,32 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
if (pending >= prm->period_size)
|
||||
update_alsa = true;
|
||||
|
||||
hw_ptr = prm->hw_ptr;
|
||||
prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes;
|
||||
|
||||
spin_unlock_irqrestore(&prm->lock, flags);
|
||||
|
||||
/* Pack USB load in ALSA ring buffer */
|
||||
memcpy(dst, src, req->actual);
|
||||
pending = prm->dma_bytes - hw_ptr;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (unlikely(pending < req->actual)) {
|
||||
memcpy(req->buf, prm->dma_area + hw_ptr, pending);
|
||||
memcpy(req->buf + pending, prm->dma_area,
|
||||
req->actual - pending);
|
||||
} else {
|
||||
memcpy(req->buf, prm->dma_area + hw_ptr, req->actual);
|
||||
}
|
||||
} else {
|
||||
if (unlikely(pending < req->actual)) {
|
||||
memcpy(prm->dma_area + hw_ptr, req->buf, pending);
|
||||
memcpy(prm->dma_area, req->buf + pending,
|
||||
req->actual - pending);
|
||||
} else {
|
||||
memcpy(prm->dma_area + hw_ptr, req->buf, req->actual);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (usb_ep_queue(ep, req, GFP_ATOMIC))
|
||||
dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
|
||||
|
|
@ -342,6 +362,21 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct audio_dev *audio_dev;
|
||||
struct f_uac2_opts *opts;
|
||||
int p_ssize, c_ssize;
|
||||
int p_srate, c_srate;
|
||||
int p_chmask, c_chmask;
|
||||
|
||||
audio_dev = uac2_to_agdev(uac2);
|
||||
opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
|
||||
p_ssize = opts->p_ssize;
|
||||
c_ssize = opts->c_ssize;
|
||||
p_srate = opts->p_srate;
|
||||
c_srate = opts->c_srate;
|
||||
p_chmask = opts->p_chmask;
|
||||
c_chmask = opts->c_chmask;
|
||||
uac2->p_residue = 0;
|
||||
|
||||
runtime->hw = uac2_pcm_hardware;
|
||||
|
||||
|
|
@ -411,7 +446,15 @@ static int snd_uac2_probe(struct platform_device *pdev)
|
|||
struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev);
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
struct audio_dev *audio_dev;
|
||||
struct f_uac2_opts *opts;
|
||||
int err;
|
||||
int p_chmask, c_chmask;
|
||||
|
||||
audio_dev = uac2_to_agdev(uac2);
|
||||
opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst);
|
||||
p_chmask = opts->p_chmask;
|
||||
c_chmask = opts->c_chmask;
|
||||
|
||||
/* Choose any slot, with no id */
|
||||
err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card);
|
||||
|
|
@ -919,20 +962,58 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
|
|||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
static int __init
|
||||
static int
|
||||
afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
{
|
||||
struct audio_dev *agdev = func_to_agdev(fn);
|
||||
struct snd_uac2_chip *uac2 = &agdev->uac2;
|
||||
struct usb_composite_dev *cdev = cfg->cdev;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct device *dev = &uac2->pdev.dev;
|
||||
struct uac2_rtd_params *prm;
|
||||
struct f_uac2_opts *uac2_opts;
|
||||
struct usb_string *us;
|
||||
int ret;
|
||||
|
||||
uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst);
|
||||
|
||||
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
iad_desc.iFunction = us[STR_ASSOC].id;
|
||||
std_ac_if_desc.iInterface = us[STR_IF_CTRL].id;
|
||||
in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
|
||||
out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id;
|
||||
usb_out_it_desc.iTerminal = us[STR_USB_IT].id;
|
||||
io_in_it_desc.iTerminal = us[STR_IO_IT].id;
|
||||
usb_in_ot_desc.iTerminal = us[STR_USB_OT].id;
|
||||
io_out_ot_desc.iTerminal = us[STR_IO_OT].id;
|
||||
std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id;
|
||||
std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id;
|
||||
std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id;
|
||||
std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id;
|
||||
|
||||
|
||||
/* Initialize the configurable parameters */
|
||||
usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
|
||||
usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
|
||||
io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
|
||||
io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
|
||||
as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
|
||||
as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask);
|
||||
as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask);
|
||||
as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask);
|
||||
as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize;
|
||||
as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8;
|
||||
as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize;
|
||||
as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8;
|
||||
|
||||
snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
|
||||
snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
|
||||
|
||||
ret = usb_interface_id(cfg, fn);
|
||||
if (ret < 0) {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
std_ac_if_desc.bInterfaceNumber = ret;
|
||||
|
|
@ -941,8 +1022,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
|
||||
ret = usb_interface_id(cfg, fn);
|
||||
if (ret < 0) {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
std_as_out_if0_desc.bInterfaceNumber = ret;
|
||||
|
|
@ -952,8 +1032,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
|
||||
ret = usb_interface_id(cfg, fn);
|
||||
if (ret < 0) {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
std_as_in_if0_desc.bInterfaceNumber = ret;
|
||||
|
|
@ -963,16 +1042,14 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
|||
|
||||
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
|
||||
if (!agdev->out_ep) {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
goto err;
|
||||
}
|
||||
agdev->out_ep->driver_data = agdev;
|
||||
|
||||
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
|
||||
if (!agdev->in_ep) {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
goto err;
|
||||
}
|
||||
agdev->in_ep->driver_data = agdev;
|
||||
|
|
@ -1020,27 +1097,6 @@ err:
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
{
|
||||
struct audio_dev *agdev = func_to_agdev(fn);
|
||||
struct uac2_rtd_params *prm;
|
||||
|
||||
alsa_uac2_exit(agdev);
|
||||
|
||||
prm = &agdev->uac2.p_prm;
|
||||
kfree(prm->rbuf);
|
||||
|
||||
prm = &agdev->uac2.c_prm;
|
||||
kfree(prm->rbuf);
|
||||
usb_free_all_descriptors(fn);
|
||||
|
||||
if (agdev->in_ep)
|
||||
agdev->in_ep->driver_data = NULL;
|
||||
if (agdev->out_ep)
|
||||
agdev->out_ep->driver_data = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
|
||||
{
|
||||
|
|
@ -1048,23 +1104,22 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
|
|||
struct audio_dev *agdev = func_to_agdev(fn);
|
||||
struct snd_uac2_chip *uac2 = &agdev->uac2;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct device *dev = &uac2->pdev.dev;
|
||||
struct usb_request *req;
|
||||
struct usb_ep *ep;
|
||||
struct uac2_rtd_params *prm;
|
||||
int i;
|
||||
int req_len, i;
|
||||
|
||||
/* No i/f has more than 2 alt settings */
|
||||
if (alt > 1) {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (intf == agdev->ac_intf) {
|
||||
/* Control I/f has only 1 AltSetting - 0 */
|
||||
if (alt) {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -1075,14 +1130,43 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
|
|||
prm = &uac2->c_prm;
|
||||
config_ep_by_speed(gadget, fn, ep);
|
||||
agdev->as_out_alt = alt;
|
||||
req_len = prm->max_psize;
|
||||
} else if (intf == agdev->as_in_intf) {
|
||||
struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
|
||||
unsigned int factor, rate;
|
||||
struct usb_endpoint_descriptor *ep_desc;
|
||||
|
||||
ep = agdev->in_ep;
|
||||
prm = &uac2->p_prm;
|
||||
config_ep_by_speed(gadget, fn, ep);
|
||||
agdev->as_in_alt = alt;
|
||||
|
||||
/* pre-calculate the playback endpoint's interval */
|
||||
if (gadget->speed == USB_SPEED_FULL) {
|
||||
ep_desc = &fs_epin_desc;
|
||||
factor = 1000;
|
||||
} else {
|
||||
ep_desc = &hs_epin_desc;
|
||||
factor = 125;
|
||||
}
|
||||
|
||||
/* pre-compute some values for iso_complete() */
|
||||
uac2->p_framesize = opts->p_ssize *
|
||||
num_channels(opts->p_chmask);
|
||||
rate = opts->p_srate * uac2->p_framesize;
|
||||
uac2->p_interval = (1 << (ep_desc->bInterval - 1)) * factor;
|
||||
uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval,
|
||||
prm->max_psize);
|
||||
|
||||
if (uac2->p_pktsize < prm->max_psize)
|
||||
uac2->p_pktsize_residue = rate % uac2->p_interval;
|
||||
else
|
||||
uac2->p_pktsize_residue = 0;
|
||||
|
||||
req_len = uac2->p_pktsize;
|
||||
uac2->p_residue = 0;
|
||||
} else {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -1095,31 +1179,23 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
|
|||
usb_ep_enable(ep);
|
||||
|
||||
for (i = 0; i < USB_XFERS; i++) {
|
||||
if (prm->ureq[i].req) {
|
||||
if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
|
||||
dev_err(&uac2->pdev.dev, "%d Error!\n",
|
||||
__LINE__);
|
||||
continue;
|
||||
if (!prm->ureq[i].req) {
|
||||
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (req == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
prm->ureq[i].req = req;
|
||||
prm->ureq[i].pp = prm;
|
||||
|
||||
req->zero = 0;
|
||||
req->context = &prm->ureq[i];
|
||||
req->length = req_len;
|
||||
req->complete = agdev_iso_complete;
|
||||
req->buf = prm->rbuf + i * prm->max_psize;
|
||||
}
|
||||
|
||||
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
|
||||
if (req == NULL) {
|
||||
dev_err(&uac2->pdev.dev,
|
||||
"%s:%d Error!\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prm->ureq[i].req = req;
|
||||
prm->ureq[i].pp = prm;
|
||||
|
||||
req->zero = 0;
|
||||
req->context = &prm->ureq[i];
|
||||
req->length = prm->max_psize;
|
||||
req->complete = agdev_iso_complete;
|
||||
req->buf = prm->rbuf + i * req->length;
|
||||
|
||||
if (usb_ep_queue(ep, req, GFP_ATOMIC))
|
||||
dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__);
|
||||
if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
|
||||
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1164,12 +1240,18 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
|||
struct usb_request *req = fn->config->cdev->req;
|
||||
struct audio_dev *agdev = func_to_agdev(fn);
|
||||
struct snd_uac2_chip *uac2 = &agdev->uac2;
|
||||
struct f_uac2_opts *opts;
|
||||
u16 w_length = le16_to_cpu(cr->wLength);
|
||||
u16 w_index = le16_to_cpu(cr->wIndex);
|
||||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
u8 entity_id = (w_index >> 8) & 0xff;
|
||||
u8 control_selector = w_value >> 8;
|
||||
int value = -EOPNOTSUPP;
|
||||
int p_srate, c_srate;
|
||||
|
||||
opts = agdev_to_uac2_opts(agdev);
|
||||
p_srate = opts->p_srate;
|
||||
c_srate = opts->c_srate;
|
||||
|
||||
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
|
||||
struct cntrl_cur_lay3 c;
|
||||
|
|
@ -1199,6 +1281,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
|||
struct usb_request *req = fn->config->cdev->req;
|
||||
struct audio_dev *agdev = func_to_agdev(fn);
|
||||
struct snd_uac2_chip *uac2 = &agdev->uac2;
|
||||
struct f_uac2_opts *opts;
|
||||
u16 w_length = le16_to_cpu(cr->wLength);
|
||||
u16 w_index = le16_to_cpu(cr->wIndex);
|
||||
u16 w_value = le16_to_cpu(cr->wValue);
|
||||
|
|
@ -1206,6 +1289,11 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
|||
u8 control_selector = w_value >> 8;
|
||||
struct cntrl_range_lay3 r;
|
||||
int value = -EOPNOTSUPP;
|
||||
int p_srate, c_srate;
|
||||
|
||||
opts = agdev_to_uac2_opts(agdev);
|
||||
p_srate = opts->p_srate;
|
||||
c_srate = opts->c_srate;
|
||||
|
||||
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
|
||||
if (entity_id == USB_IN_CLK_ID)
|
||||
|
|
@ -1309,66 +1397,184 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr)
|
|||
return value;
|
||||
}
|
||||
|
||||
static int audio_bind_config(struct usb_configuration *cfg)
|
||||
static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item)
|
||||
{
|
||||
int res;
|
||||
|
||||
agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL);
|
||||
if (agdev_g == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
res = usb_string_ids_tab(cfg->cdev, strings_fn);
|
||||
if (res)
|
||||
return res;
|
||||
iad_desc.iFunction = strings_fn[STR_ASSOC].id;
|
||||
std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id;
|
||||
in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id;
|
||||
out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id;
|
||||
usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id;
|
||||
io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id;
|
||||
usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id;
|
||||
io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id;
|
||||
std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id;
|
||||
std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id;
|
||||
std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id;
|
||||
std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id;
|
||||
|
||||
agdev_g->func.name = "uac2_func";
|
||||
agdev_g->func.strings = fn_strings;
|
||||
agdev_g->func.bind = afunc_bind;
|
||||
agdev_g->func.unbind = afunc_unbind;
|
||||
agdev_g->func.set_alt = afunc_set_alt;
|
||||
agdev_g->func.get_alt = afunc_get_alt;
|
||||
agdev_g->func.disable = afunc_disable;
|
||||
agdev_g->func.setup = afunc_setup;
|
||||
|
||||
/* Initialize the configurable parameters */
|
||||
usb_out_it_desc.bNrChannels = num_channels(c_chmask);
|
||||
usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask);
|
||||
io_in_it_desc.bNrChannels = num_channels(p_chmask);
|
||||
io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask);
|
||||
as_out_hdr_desc.bNrChannels = num_channels(c_chmask);
|
||||
as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask);
|
||||
as_in_hdr_desc.bNrChannels = num_channels(p_chmask);
|
||||
as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask);
|
||||
as_out_fmt1_desc.bSubslotSize = c_ssize;
|
||||
as_out_fmt1_desc.bBitResolution = c_ssize * 8;
|
||||
as_in_fmt1_desc.bSubslotSize = p_ssize;
|
||||
as_in_fmt1_desc.bBitResolution = p_ssize * 8;
|
||||
|
||||
snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate);
|
||||
snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate);
|
||||
|
||||
res = usb_add_function(cfg, &agdev_g->func);
|
||||
if (res < 0)
|
||||
kfree(agdev_g);
|
||||
|
||||
return res;
|
||||
return container_of(to_config_group(item), struct f_uac2_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
static void
|
||||
uac2_unbind_config(struct usb_configuration *cfg)
|
||||
CONFIGFS_ATTR_STRUCT(f_uac2_opts);
|
||||
CONFIGFS_ATTR_OPS(f_uac2_opts);
|
||||
|
||||
static void f_uac2_attr_release(struct config_item *item)
|
||||
{
|
||||
kfree(agdev_g);
|
||||
agdev_g = NULL;
|
||||
struct f_uac2_opts *opts = to_f_uac2_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations f_uac2_item_ops = {
|
||||
.release = f_uac2_attr_release,
|
||||
.show_attribute = f_uac2_opts_attr_show,
|
||||
.store_attribute = f_uac2_opts_attr_store,
|
||||
};
|
||||
|
||||
#define UAC2_ATTRIBUTE(name) \
|
||||
static ssize_t f_uac2_opts_##name##_show(struct f_uac2_opts *opts, \
|
||||
char *page) \
|
||||
{ \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = sprintf(page, "%u\n", opts->name); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_uac2_opts_##name##_store(struct f_uac2_opts *opts, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
int ret; \
|
||||
u32 num; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
ret = kstrtou32(page, 0, &num); \
|
||||
if (ret) \
|
||||
goto end; \
|
||||
\
|
||||
opts->name = num; \
|
||||
ret = len; \
|
||||
\
|
||||
end: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_uac2_opts_attribute f_uac2_opts_##name = \
|
||||
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
|
||||
f_uac2_opts_##name##_show, \
|
||||
f_uac2_opts_##name##_store)
|
||||
|
||||
UAC2_ATTRIBUTE(p_chmask);
|
||||
UAC2_ATTRIBUTE(p_srate);
|
||||
UAC2_ATTRIBUTE(p_ssize);
|
||||
UAC2_ATTRIBUTE(c_chmask);
|
||||
UAC2_ATTRIBUTE(c_srate);
|
||||
UAC2_ATTRIBUTE(c_ssize);
|
||||
|
||||
static struct configfs_attribute *f_uac2_attrs[] = {
|
||||
&f_uac2_opts_p_chmask.attr,
|
||||
&f_uac2_opts_p_srate.attr,
|
||||
&f_uac2_opts_p_ssize.attr,
|
||||
&f_uac2_opts_c_chmask.attr,
|
||||
&f_uac2_opts_c_srate.attr,
|
||||
&f_uac2_opts_c_ssize.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type f_uac2_func_type = {
|
||||
.ct_item_ops = &f_uac2_item_ops,
|
||||
.ct_attrs = f_uac2_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void afunc_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_uac2_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_uac2_opts, func_inst);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *afunc_alloc_inst(void)
|
||||
{
|
||||
struct f_uac2_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = afunc_free_inst;
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&f_uac2_func_type);
|
||||
|
||||
opts->p_chmask = UAC2_DEF_PCHMASK;
|
||||
opts->p_srate = UAC2_DEF_PSRATE;
|
||||
opts->p_ssize = UAC2_DEF_PSSIZE;
|
||||
opts->c_chmask = UAC2_DEF_CCHMASK;
|
||||
opts->c_srate = UAC2_DEF_CSRATE;
|
||||
opts->c_ssize = UAC2_DEF_CSSIZE;
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void afunc_free(struct usb_function *f)
|
||||
{
|
||||
struct audio_dev *agdev;
|
||||
struct f_uac2_opts *opts;
|
||||
|
||||
agdev = func_to_agdev(f);
|
||||
opts = container_of(f->fi, struct f_uac2_opts, func_inst);
|
||||
kfree(agdev);
|
||||
mutex_lock(&opts->lock);
|
||||
--opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct audio_dev *agdev = func_to_agdev(f);
|
||||
struct uac2_rtd_params *prm;
|
||||
|
||||
alsa_uac2_exit(agdev);
|
||||
|
||||
prm = &agdev->uac2.p_prm;
|
||||
kfree(prm->rbuf);
|
||||
|
||||
prm = &agdev->uac2.c_prm;
|
||||
kfree(prm->rbuf);
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
if (agdev->in_ep)
|
||||
agdev->in_ep->driver_data = NULL;
|
||||
if (agdev->out_ep)
|
||||
agdev->out_ep->driver_data = NULL;
|
||||
}
|
||||
|
||||
struct usb_function *afunc_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct audio_dev *agdev;
|
||||
struct f_uac2_opts *opts;
|
||||
|
||||
agdev = kzalloc(sizeof(*agdev), GFP_KERNEL);
|
||||
if (agdev == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts = container_of(fi, struct f_uac2_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
++opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
agdev->func.name = "uac2_func";
|
||||
agdev->func.bind = afunc_bind;
|
||||
agdev->func.unbind = afunc_unbind;
|
||||
agdev->func.set_alt = afunc_set_alt;
|
||||
agdev->func.get_alt = afunc_get_alt;
|
||||
agdev->func.disable = afunc_disable;
|
||||
agdev->func.setup = afunc_setup;
|
||||
agdev->func.free_func = afunc_free;
|
||||
|
||||
return &agdev->func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Yadwinder Singh");
|
||||
MODULE_AUTHOR("Jaswinder Singh");
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
|
|
@ -27,24 +28,12 @@
|
|||
#include <media/v4l2-event.h>
|
||||
|
||||
#include "uvc.h"
|
||||
#include "uvc_v4l2.h"
|
||||
#include "uvc_video.h"
|
||||
#include "u_uvc.h"
|
||||
|
||||
unsigned int uvc_gadget_trace_param;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* module parameters specific to the Video streaming endpoint */
|
||||
static unsigned int streaming_interval = 1;
|
||||
module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(streaming_interval, "1 - 16");
|
||||
|
||||
static unsigned int streaming_maxpacket = 1024;
|
||||
module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
|
||||
|
||||
static unsigned int streaming_maxburst;
|
||||
module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Function descriptors
|
||||
*/
|
||||
|
|
@ -75,7 +64,7 @@ static struct usb_gadget_strings *uvc_function_strings[] = {
|
|||
|
||||
#define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */
|
||||
|
||||
static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
|
||||
static struct usb_interface_assoc_descriptor uvc_iad = {
|
||||
.bLength = sizeof(uvc_iad),
|
||||
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
|
||||
.bFirstInterface = 0,
|
||||
|
|
@ -86,7 +75,7 @@ static struct usb_interface_assoc_descriptor uvc_iad __initdata = {
|
|||
.iFunction = 0,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor uvc_control_intf __initdata = {
|
||||
static struct usb_interface_descriptor uvc_control_intf = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = UVC_INTF_VIDEO_CONTROL,
|
||||
|
|
@ -98,7 +87,7 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = {
|
|||
.iInterface = 0,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
|
||||
static struct usb_endpoint_descriptor uvc_control_ep = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
|
|
@ -107,7 +96,7 @@ static struct usb_endpoint_descriptor uvc_control_ep __initdata = {
|
|||
.bInterval = 8,
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
|
||||
static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp = {
|
||||
.bLength = sizeof(uvc_ss_control_comp),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
/* The following 3 values can be tweaked if necessary. */
|
||||
|
|
@ -116,14 +105,14 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = {
|
|||
.wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
|
||||
};
|
||||
|
||||
static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = {
|
||||
static struct uvc_control_endpoint_descriptor uvc_control_cs_ep = {
|
||||
.bLength = UVC_DT_CONTROL_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_ENDPOINT,
|
||||
.bDescriptorSubType = UVC_EP_INTERRUPT,
|
||||
.wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
|
||||
static struct usb_interface_descriptor uvc_streaming_intf_alt0 = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
|
||||
|
|
@ -135,7 +124,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = {
|
|||
.iInterface = 0,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
|
||||
static struct usb_interface_descriptor uvc_streaming_intf_alt1 = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
|
||||
|
|
@ -147,7 +136,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
|
|||
.iInterface = 0,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
|
||||
static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
|
|
@ -158,7 +147,7 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
|
|||
*/
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
|
||||
static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
|
|
@ -169,7 +158,7 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
|
|||
*/
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
|
||||
static struct usb_endpoint_descriptor uvc_ss_streaming_ep = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
|
@ -181,7 +170,7 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
|
|||
*/
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = {
|
||||
static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
|
||||
.bLength = sizeof(uvc_ss_streaming_comp),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
|
||||
|
|
@ -208,6 +197,12 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
void uvc_set_trace_param(unsigned int trace)
|
||||
{
|
||||
uvc_gadget_trace_param = trace;
|
||||
}
|
||||
EXPORT_SYMBOL(uvc_set_trace_param);
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Control requests
|
||||
*/
|
||||
|
|
@ -251,6 +246,12 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
|||
if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Tell the complete callback to generate an event for the next request
|
||||
* that will be enqueued by UVCIOC_SEND_RESPONSE.
|
||||
*/
|
||||
uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN);
|
||||
uvc->event_length = le16_to_cpu(ctrl->wLength);
|
||||
|
||||
memset(&v4l2_event, 0, sizeof(v4l2_event));
|
||||
v4l2_event.type = UVC_EVENT_SETUP;
|
||||
memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
|
||||
|
|
@ -408,7 +409,9 @@ uvc_register_video(struct uvc_device *uvc)
|
|||
|
||||
video->v4l2_dev = &uvc->v4l2_dev;
|
||||
video->fops = &uvc_v4l2_fops;
|
||||
video->ioctl_ops = &uvc_v4l2_ioctl_ops;
|
||||
video->release = video_device_release;
|
||||
video->vfl_dir = VFL_DIR_TX;
|
||||
strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
|
||||
|
||||
uvc->vdev = video;
|
||||
|
|
@ -434,7 +437,7 @@ uvc_register_video(struct uvc_device *uvc)
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
static struct usb_descriptor_header ** __init
|
||||
static struct usb_descriptor_header **
|
||||
uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
|
||||
{
|
||||
struct uvc_input_header_descriptor *uvc_streaming_header;
|
||||
|
|
@ -554,45 +557,26 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
|
|||
return hdr;
|
||||
}
|
||||
|
||||
static void
|
||||
uvc_function_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
|
||||
INFO(cdev, "uvc_function_unbind\n");
|
||||
|
||||
video_unregister_device(uvc->vdev);
|
||||
v4l2_device_unregister(&uvc->v4l2_dev);
|
||||
uvc->control_ep->driver_data = NULL;
|
||||
uvc->video.ep->driver_data = NULL;
|
||||
|
||||
uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0;
|
||||
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
|
||||
kfree(uvc->control_buf);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(uvc);
|
||||
}
|
||||
|
||||
static int __init
|
||||
static int
|
||||
uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
struct usb_string *us;
|
||||
unsigned int max_packet_mult;
|
||||
unsigned int max_packet_size;
|
||||
struct usb_ep *ep;
|
||||
struct f_uvc_opts *opts;
|
||||
int ret = -EINVAL;
|
||||
|
||||
INFO(cdev, "uvc_function_bind\n");
|
||||
|
||||
opts = to_f_uvc_opts(f->fi);
|
||||
/* Sanity check the streaming endpoint module parameters.
|
||||
*/
|
||||
streaming_interval = clamp(streaming_interval, 1U, 16U);
|
||||
streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U);
|
||||
streaming_maxburst = min(streaming_maxburst, 15U);
|
||||
opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
|
||||
opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
|
||||
opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
|
||||
|
||||
/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
|
||||
* module parameters.
|
||||
|
|
@ -600,30 +584,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
* NOTE: We assume that the user knows what they are doing and won't
|
||||
* give parameters that their UDC doesn't support.
|
||||
*/
|
||||
if (streaming_maxpacket <= 1024) {
|
||||
if (opts->streaming_maxpacket <= 1024) {
|
||||
max_packet_mult = 1;
|
||||
max_packet_size = streaming_maxpacket;
|
||||
} else if (streaming_maxpacket <= 2048) {
|
||||
max_packet_size = opts->streaming_maxpacket;
|
||||
} else if (opts->streaming_maxpacket <= 2048) {
|
||||
max_packet_mult = 2;
|
||||
max_packet_size = streaming_maxpacket / 2;
|
||||
max_packet_size = opts->streaming_maxpacket / 2;
|
||||
} else {
|
||||
max_packet_mult = 3;
|
||||
max_packet_size = streaming_maxpacket / 3;
|
||||
max_packet_size = opts->streaming_maxpacket / 3;
|
||||
}
|
||||
|
||||
uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U);
|
||||
uvc_fs_streaming_ep.bInterval = streaming_interval;
|
||||
uvc_fs_streaming_ep.wMaxPacketSize =
|
||||
cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
|
||||
uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
|
||||
|
||||
uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size;
|
||||
uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11);
|
||||
uvc_hs_streaming_ep.bInterval = streaming_interval;
|
||||
uvc_hs_streaming_ep.wMaxPacketSize =
|
||||
cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11));
|
||||
uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
|
||||
|
||||
uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size;
|
||||
uvc_ss_streaming_ep.bInterval = streaming_interval;
|
||||
uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);
|
||||
uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
|
||||
uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
|
||||
uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
|
||||
uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
|
||||
uvc_ss_streaming_comp.wBytesPerInterval =
|
||||
max_packet_size * max_packet_mult * streaming_maxburst;
|
||||
cpu_to_le16(max_packet_size * max_packet_mult *
|
||||
opts->streaming_maxburst);
|
||||
|
||||
/* Allocate endpoints. */
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
|
||||
|
|
@ -653,6 +639,18 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
|
||||
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
|
||||
|
||||
us = usb_gstrings_attach(cdev, uvc_function_strings,
|
||||
ARRAY_SIZE(uvc_en_us_strings));
|
||||
if (IS_ERR(us)) {
|
||||
ret = PTR_ERR(us);
|
||||
goto error;
|
||||
}
|
||||
uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id;
|
||||
uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id;
|
||||
ret = us[UVC_STRING_STREAMING_IDX].id;
|
||||
uvc_streaming_intf_alt0.iInterface = ret;
|
||||
uvc_streaming_intf_alt1.iInterface = ret;
|
||||
|
||||
/* Allocate interface IDs. */
|
||||
if ((ret = usb_interface_id(c, f)) < 0)
|
||||
goto error;
|
||||
|
|
@ -697,7 +695,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
}
|
||||
|
||||
/* Initialise video. */
|
||||
ret = uvc_video_init(&uvc->video);
|
||||
ret = uvcg_video_init(&uvc->video);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
|
|
@ -720,10 +718,9 @@ error:
|
|||
if (uvc->video.ep)
|
||||
uvc->video.ep->driver_data = NULL;
|
||||
|
||||
if (uvc->control_req) {
|
||||
if (uvc->control_req)
|
||||
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
|
||||
kfree(uvc->control_buf);
|
||||
}
|
||||
kfree(uvc->control_buf);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
return ret;
|
||||
|
|
@ -733,104 +730,81 @@ error:
|
|||
* USB gadget function
|
||||
*/
|
||||
|
||||
/**
|
||||
* uvc_bind_config - add a UVC function to a configuration
|
||||
* @c: the configuration to support the UVC instance
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @uvc_setup(). Caller is also responsible for
|
||||
* calling @uvc_cleanup() before module unload.
|
||||
*/
|
||||
int __init
|
||||
uvc_bind_config(struct usb_configuration *c,
|
||||
const struct uvc_descriptor_header * const *fs_control,
|
||||
const struct uvc_descriptor_header * const *ss_control,
|
||||
const struct uvc_descriptor_header * const *fs_streaming,
|
||||
const struct uvc_descriptor_header * const *hs_streaming,
|
||||
const struct uvc_descriptor_header * const *ss_streaming)
|
||||
static void uvc_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_uvc_opts *opts = to_f_uvc_opts(f);
|
||||
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *uvc_alloc_inst(void)
|
||||
{
|
||||
struct f_uvc_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
opts->func_inst.free_func_inst = uvc_free_inst;
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void uvc_free(struct usb_function *f)
|
||||
{
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
|
||||
kfree(uvc);
|
||||
}
|
||||
|
||||
static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct uvc_device *uvc = to_uvc(f);
|
||||
|
||||
INFO(cdev, "%s\n", __func__);
|
||||
|
||||
video_unregister_device(uvc->vdev);
|
||||
v4l2_device_unregister(&uvc->v4l2_dev);
|
||||
uvc->control_ep->driver_data = NULL;
|
||||
uvc->video.ep->driver_data = NULL;
|
||||
|
||||
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
|
||||
kfree(uvc->control_buf);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct uvc_device *uvc;
|
||||
int ret = 0;
|
||||
|
||||
/* TODO Check if the USB device controller supports the required
|
||||
* features.
|
||||
*/
|
||||
if (!gadget_is_dualspeed(c->cdev->gadget))
|
||||
return -EINVAL;
|
||||
struct f_uvc_opts *opts;
|
||||
|
||||
uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
|
||||
if (uvc == NULL)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
uvc->state = UVC_STATE_DISCONNECTED;
|
||||
opts = to_f_uvc_opts(fi);
|
||||
|
||||
/* Validate the descriptors. */
|
||||
if (fs_control == NULL || fs_control[0] == NULL ||
|
||||
fs_control[0]->bDescriptorSubType != UVC_VC_HEADER)
|
||||
goto error;
|
||||
|
||||
if (ss_control == NULL || ss_control[0] == NULL ||
|
||||
ss_control[0]->bDescriptorSubType != UVC_VC_HEADER)
|
||||
goto error;
|
||||
|
||||
if (fs_streaming == NULL || fs_streaming[0] == NULL ||
|
||||
fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
|
||||
goto error;
|
||||
|
||||
if (hs_streaming == NULL || hs_streaming[0] == NULL ||
|
||||
hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
|
||||
goto error;
|
||||
|
||||
if (ss_streaming == NULL || ss_streaming[0] == NULL ||
|
||||
ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER)
|
||||
goto error;
|
||||
|
||||
uvc->desc.fs_control = fs_control;
|
||||
uvc->desc.ss_control = ss_control;
|
||||
uvc->desc.fs_streaming = fs_streaming;
|
||||
uvc->desc.hs_streaming = hs_streaming;
|
||||
uvc->desc.ss_streaming = ss_streaming;
|
||||
|
||||
/* String descriptors are global, we only need to allocate string IDs
|
||||
* for the first UVC function. UVC functions beyond the first (if any)
|
||||
* will reuse the same IDs.
|
||||
*/
|
||||
if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) {
|
||||
ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings);
|
||||
if (ret)
|
||||
goto error;
|
||||
uvc_iad.iFunction =
|
||||
uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
|
||||
uvc_control_intf.iInterface =
|
||||
uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
|
||||
ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id;
|
||||
uvc_streaming_intf_alt0.iInterface = ret;
|
||||
uvc_streaming_intf_alt1.iInterface = ret;
|
||||
}
|
||||
uvc->desc.fs_control = opts->fs_control;
|
||||
uvc->desc.ss_control = opts->ss_control;
|
||||
uvc->desc.fs_streaming = opts->fs_streaming;
|
||||
uvc->desc.hs_streaming = opts->hs_streaming;
|
||||
uvc->desc.ss_streaming = opts->ss_streaming;
|
||||
|
||||
/* Register the function. */
|
||||
uvc->func.name = "uvc";
|
||||
uvc->func.strings = uvc_function_strings;
|
||||
uvc->func.bind = uvc_function_bind;
|
||||
uvc->func.unbind = uvc_function_unbind;
|
||||
uvc->func.unbind = uvc_unbind;
|
||||
uvc->func.get_alt = uvc_function_get_alt;
|
||||
uvc->func.set_alt = uvc_function_set_alt;
|
||||
uvc->func.disable = uvc_function_disable;
|
||||
uvc->func.setup = uvc_function_setup;
|
||||
uvc->func.free_func = uvc_free;
|
||||
|
||||
ret = usb_add_function(c, &uvc->func);
|
||||
if (ret)
|
||||
kfree(uvc);
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
kfree(uvc);
|
||||
return ret;
|
||||
return &uvc->func;
|
||||
}
|
||||
|
||||
module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(trace, "Trace level bitmask");
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Laurent Pinchart");
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@
|
|||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/video.h>
|
||||
|
||||
int uvc_bind_config(struct usb_configuration *c,
|
||||
const struct uvc_descriptor_header * const *fs_control,
|
||||
const struct uvc_descriptor_header * const *hs_control,
|
||||
const struct uvc_descriptor_header * const *fs_streaming,
|
||||
const struct uvc_descriptor_header * const *hs_streaming,
|
||||
const struct uvc_descriptor_header * const *ss_streaming);
|
||||
#include "uvc.h"
|
||||
|
||||
void uvc_function_setup_continue(struct uvc_device *uvc);
|
||||
|
||||
void uvc_function_connect(struct uvc_device *uvc);
|
||||
|
||||
void uvc_function_disconnect(struct uvc_device *uvc);
|
||||
|
||||
#endif /* _F_UVC_H_ */
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#define GZERO_QLEN 32
|
||||
#define GZERO_ISOC_INTERVAL 4
|
||||
#define GZERO_ISOC_MAXPACKET 1024
|
||||
#define GZERO_INT_INTERVAL 1 /* Default interrupt interval = 1 ms */
|
||||
#define GZERO_INT_MAXPACKET 1024
|
||||
|
||||
struct usb_zero_options {
|
||||
unsigned pattern;
|
||||
|
|
@ -17,6 +19,10 @@ struct usb_zero_options {
|
|||
unsigned isoc_maxpacket;
|
||||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned int_interval; /* In ms */
|
||||
unsigned int_maxpacket;
|
||||
unsigned int_mult;
|
||||
unsigned int_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
unsigned qlen;
|
||||
};
|
||||
|
|
@ -28,6 +34,10 @@ struct f_ss_opts {
|
|||
unsigned isoc_maxpacket;
|
||||
unsigned isoc_mult;
|
||||
unsigned isoc_maxburst;
|
||||
unsigned int_interval; /* In ms */
|
||||
unsigned int_maxpacket;
|
||||
unsigned int_mult;
|
||||
unsigned int_maxburst;
|
||||
unsigned bulk_buflen;
|
||||
|
||||
/*
|
||||
|
|
@ -62,6 +72,7 @@ int lb_modinit(void);
|
|||
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
|
||||
void disable_endpoints(struct usb_composite_dev *cdev,
|
||||
struct usb_ep *in, struct usb_ep *out,
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out);
|
||||
struct usb_ep *iso_in, struct usb_ep *iso_out,
|
||||
struct usb_ep *int_in, struct usb_ep *int_out);
|
||||
|
||||
#endif /* __G_ZERO_H */
|
||||
|
|
|
|||
|
|
@ -224,6 +224,8 @@ struct ffs_data {
|
|||
void *ms_os_descs_ext_prop_name_avail;
|
||||
void *ms_os_descs_ext_prop_data_avail;
|
||||
|
||||
unsigned user_flags;
|
||||
|
||||
u8 eps_addrmap[15];
|
||||
|
||||
unsigned short strings_count;
|
||||
|
|
|
|||
|
|
@ -55,11 +55,8 @@
|
|||
* for a telephone or fax link. And ttyGS2 might be something that just
|
||||
* needs a simple byte stream interface for some messaging protocol that
|
||||
* is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
|
||||
*/
|
||||
|
||||
#define PREFIX "ttyGS"
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* gserial is the lifecycle interface, used by USB functions
|
||||
* gs_port is the I/O nexus, used by the tty driver
|
||||
* tty_struct links to the tty/filesystem framework
|
||||
|
|
@ -385,9 +382,9 @@ __acquires(&port->port_lock)
|
|||
list_del(&req->list);
|
||||
req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
|
||||
|
||||
pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
|
||||
port->port_num, len, *((u8 *)req->buf),
|
||||
*((u8 *)req->buf+1), *((u8 *)req->buf+2));
|
||||
pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
|
||||
port->port_num, len, *((u8 *)req->buf),
|
||||
*((u8 *)req->buf+1), *((u8 *)req->buf+2));
|
||||
|
||||
/* Drop lock while we call out of driver; completions
|
||||
* could be issued while we do so. Disconnection may
|
||||
|
|
@ -503,13 +500,13 @@ static void gs_rx_push(unsigned long _port)
|
|||
switch (req->status) {
|
||||
case -ESHUTDOWN:
|
||||
disconnect = true;
|
||||
pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
|
||||
pr_vdebug("ttyGS%d: shutdown\n", port->port_num);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* presumably a transient fault */
|
||||
pr_warning(PREFIX "%d: unexpected RX status %d\n",
|
||||
port->port_num, req->status);
|
||||
pr_warn("ttyGS%d: unexpected RX status %d\n",
|
||||
port->port_num, req->status);
|
||||
/* FALLTHROUGH */
|
||||
case 0:
|
||||
/* normal completion */
|
||||
|
|
@ -537,9 +534,8 @@ static void gs_rx_push(unsigned long _port)
|
|||
if (count != size) {
|
||||
/* stop pushing; TTY layer can't handle more */
|
||||
port->n_read += count;
|
||||
pr_vdebug(PREFIX "%d: rx block %d/%d\n",
|
||||
port->port_num,
|
||||
count, req->actual);
|
||||
pr_vdebug("ttyGS%d: rx block %d/%d\n",
|
||||
port->port_num, count, req->actual);
|
||||
break;
|
||||
}
|
||||
port->n_read = 0;
|
||||
|
|
@ -569,7 +565,7 @@ static void gs_rx_push(unsigned long _port)
|
|||
if (do_push)
|
||||
tasklet_schedule(&port->push);
|
||||
else
|
||||
pr_warning(PREFIX "%d: RX not scheduled?\n",
|
||||
pr_warn("ttyGS%d: RX not scheduled?\n",
|
||||
port->port_num);
|
||||
}
|
||||
}
|
||||
|
|
@ -985,7 +981,7 @@ static void gs_unthrottle(struct tty_struct *tty)
|
|||
* read queue backs up enough we'll be NAKing OUT packets.
|
||||
*/
|
||||
tasklet_schedule(&port->push);
|
||||
pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
|
||||
pr_vdebug("ttyGS%d: unthrottle\n", port->port_num);
|
||||
}
|
||||
spin_unlock_irqrestore(&port->port_lock, flags);
|
||||
}
|
||||
|
|
@ -1295,7 +1291,7 @@ static int userial_init(void)
|
|||
return -ENOMEM;
|
||||
|
||||
gs_tty_driver->driver_name = "g_serial";
|
||||
gs_tty_driver->name = PREFIX;
|
||||
gs_tty_driver->name = "ttyGS";
|
||||
/* uses dynamically assigned dev_t values */
|
||||
|
||||
gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
@ -23,22 +24,6 @@
|
|||
* This component encapsulates the ALSA devices for USB audio gadget
|
||||
*/
|
||||
|
||||
#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
|
||||
#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
|
||||
#define FILE_CONTROL "/dev/snd/controlC0"
|
||||
|
||||
static char *fn_play = FILE_PCM_PLAYBACK;
|
||||
module_param(fn_play, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
|
||||
|
||||
static char *fn_cap = FILE_PCM_CAPTURE;
|
||||
module_param(fn_cap, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
|
||||
|
||||
static char *fn_cntl = FILE_CONTROL;
|
||||
module_param(fn_cntl, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_cntl, "Control device file name");
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
|
|
@ -167,7 +152,7 @@ static int playback_default_hw_params(struct gaudio_snd_dev *snd)
|
|||
/**
|
||||
* Playback audio buffer data by ALSA PCM device
|
||||
*/
|
||||
static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
|
||||
size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
|
||||
{
|
||||
struct gaudio_snd_dev *snd = &card->playback;
|
||||
struct snd_pcm_substream *substream = snd->substream;
|
||||
|
|
@ -202,12 +187,12 @@ try_again:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int u_audio_get_playback_channels(struct gaudio *card)
|
||||
int u_audio_get_playback_channels(struct gaudio *card)
|
||||
{
|
||||
return card->playback.channels;
|
||||
}
|
||||
|
||||
static int u_audio_get_playback_rate(struct gaudio *card)
|
||||
int u_audio_get_playback_rate(struct gaudio *card)
|
||||
{
|
||||
return card->playback.rate;
|
||||
}
|
||||
|
|
@ -220,6 +205,13 @@ static int gaudio_open_snd_dev(struct gaudio *card)
|
|||
{
|
||||
struct snd_pcm_file *pcm_file;
|
||||
struct gaudio_snd_dev *snd;
|
||||
struct f_uac1_opts *opts;
|
||||
char *fn_play, *fn_cap, *fn_cntl;
|
||||
|
||||
opts = container_of(card->func.fi, struct f_uac1_opts, func_inst);
|
||||
fn_play = opts->fn_play;
|
||||
fn_cap = opts->fn_cap;
|
||||
fn_cntl = opts->fn_cntl;
|
||||
|
||||
if (!card)
|
||||
return -ENODEV;
|
||||
|
|
@ -293,7 +285,6 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct gaudio *the_card;
|
||||
/**
|
||||
* gaudio_setup - setup ALSA interface and preparing for USB transfer
|
||||
*
|
||||
|
|
@ -301,15 +292,13 @@ static struct gaudio *the_card;
|
|||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
int __init gaudio_setup(struct gaudio *card)
|
||||
int gaudio_setup(struct gaudio *card)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gaudio_open_snd_dev(card);
|
||||
if (ret)
|
||||
ERROR(card, "we need at least one control device\n");
|
||||
else if (!the_card)
|
||||
the_card = card;
|
||||
|
||||
return ret;
|
||||
|
||||
|
|
@ -320,11 +309,10 @@ int __init gaudio_setup(struct gaudio *card)
|
|||
*
|
||||
* This is called to free all resources allocated by @gaudio_setup().
|
||||
*/
|
||||
void gaudio_cleanup(void)
|
||||
void gaudio_cleanup(struct gaudio *the_card)
|
||||
{
|
||||
if (the_card) {
|
||||
gaudio_close_snd_dev(the_card);
|
||||
the_card = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,14 @@
|
|||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
|
||||
#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
|
||||
#define FILE_CONTROL "/dev/snd/controlC0"
|
||||
|
||||
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
|
||||
#define UAC1_REQ_COUNT 256
|
||||
#define UAC1_AUDIO_BUF_SIZE 48000
|
||||
|
||||
/*
|
||||
* This represents the USB side of an audio card device, managed by a USB
|
||||
* function which provides control and stream interfaces.
|
||||
|
|
@ -50,7 +58,28 @@ struct gaudio {
|
|||
/* TODO */
|
||||
};
|
||||
|
||||
struct f_uac1_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int req_buf_size;
|
||||
int req_count;
|
||||
int audio_buf_size;
|
||||
char *fn_play;
|
||||
char *fn_cap;
|
||||
char *fn_cntl;
|
||||
unsigned bound:1;
|
||||
unsigned fn_play_alloc:1;
|
||||
unsigned fn_cap_alloc:1;
|
||||
unsigned fn_cntl_alloc:1;
|
||||
struct gaudio *card;
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
int gaudio_setup(struct gaudio *card);
|
||||
void gaudio_cleanup(void);
|
||||
void gaudio_cleanup(struct gaudio *the_card);
|
||||
|
||||
size_t u_audio_playback(struct gaudio *card, void *buf, size_t count);
|
||||
int u_audio_get_playback_channels(struct gaudio *card);
|
||||
int u_audio_get_playback_rate(struct gaudio *card);
|
||||
|
||||
#endif /* __U_AUDIO_H */
|
||||
|
|
|
|||
42
drivers/usb/gadget/function/u_uac2.h
Normal file
42
drivers/usb/gadget/function/u_uac2.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* u_uac2.h
|
||||
*
|
||||
* Utility definitions for UAC2 function
|
||||
*
|
||||
* Copyright (c) 2014 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_UAC2_H
|
||||
#define U_UAC2_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#define UAC2_DEF_PCHMASK 0x3
|
||||
#define UAC2_DEF_PSRATE 48000
|
||||
#define UAC2_DEF_PSSIZE 2
|
||||
#define UAC2_DEF_CCHMASK 0x3
|
||||
#define UAC2_DEF_CSRATE 64000
|
||||
#define UAC2_DEF_CSSIZE 2
|
||||
|
||||
struct f_uac2_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int p_chmask;
|
||||
int p_srate;
|
||||
int p_ssize;
|
||||
int c_chmask;
|
||||
int c_srate;
|
||||
int c_ssize;
|
||||
bool bound;
|
||||
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
#endif
|
||||
39
drivers/usb/gadget/function/u_uvc.h
Normal file
39
drivers/usb/gadget/function/u_uvc.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* u_uvc.h
|
||||
*
|
||||
* Utility definitions for the uvc function
|
||||
*
|
||||
* Copyright (c) 2013-2014 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_UVC_H
|
||||
#define U_UVC_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst)
|
||||
|
||||
struct f_uvc_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
unsigned int uvc_gadget_trace_param;
|
||||
unsigned int streaming_interval;
|
||||
unsigned int streaming_maxpacket;
|
||||
unsigned int streaming_maxburst;
|
||||
const struct uvc_descriptor_header * const *fs_control;
|
||||
const struct uvc_descriptor_header * const *ss_control;
|
||||
const struct uvc_descriptor_header * const *fs_streaming;
|
||||
const struct uvc_descriptor_header * const *hs_streaming;
|
||||
const struct uvc_descriptor_header * const *ss_streaming;
|
||||
};
|
||||
|
||||
void uvc_set_trace_param(unsigned int trace);
|
||||
|
||||
#endif /* U_UVC_H */
|
||||
|
||||
|
|
@ -53,6 +53,7 @@ struct uvc_event
|
|||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/usb.h> /* For usb_endpoint_* */
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/version.h>
|
||||
|
|
@ -96,9 +97,6 @@ extern unsigned int uvc_gadget_trace_param;
|
|||
* Driver specific constants
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "0.1.0"
|
||||
#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0)
|
||||
|
||||
#define UVC_NUM_REQUESTS 4
|
||||
#define UVC_MAX_REQUEST_SIZE 64
|
||||
#define UVC_MAX_EVENTS 4
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
/* ------------------------------------------------------------------------
|
||||
* Video buffers queue management.
|
||||
*
|
||||
* Video queues is initialized by uvc_queue_init(). The function performs
|
||||
* Video queues is initialized by uvcg_queue_init(). The function performs
|
||||
* basic initialization of the uvc_video_queue struct and never fails.
|
||||
*
|
||||
* Video buffers are managed by videobuf2. The driver uses a mutex to protect
|
||||
|
|
@ -126,13 +126,12 @@ static struct vb2_ops uvc_queue_qops = {
|
|||
.wait_finish = uvc_wait_finish,
|
||||
};
|
||||
|
||||
static int uvc_queue_init(struct uvc_video_queue *queue,
|
||||
enum v4l2_buf_type type)
|
||||
int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
queue->queue.type = type;
|
||||
queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
|
||||
queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
|
||||
queue->queue.drv_priv = queue;
|
||||
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
|
||||
queue->queue.ops = &uvc_queue_qops;
|
||||
|
|
@ -154,7 +153,7 @@ static int uvc_queue_init(struct uvc_video_queue *queue,
|
|||
/*
|
||||
* Free the video buffers.
|
||||
*/
|
||||
static void uvc_free_buffers(struct uvc_video_queue *queue)
|
||||
void uvcg_free_buffers(struct uvc_video_queue *queue)
|
||||
{
|
||||
mutex_lock(&queue->mutex);
|
||||
vb2_queue_release(&queue->queue);
|
||||
|
|
@ -164,8 +163,8 @@ static void uvc_free_buffers(struct uvc_video_queue *queue)
|
|||
/*
|
||||
* Allocate the video buffers.
|
||||
*/
|
||||
static int uvc_alloc_buffers(struct uvc_video_queue *queue,
|
||||
struct v4l2_requestbuffers *rb)
|
||||
int uvcg_alloc_buffers(struct uvc_video_queue *queue,
|
||||
struct v4l2_requestbuffers *rb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -176,8 +175,7 @@ static int uvc_alloc_buffers(struct uvc_video_queue *queue,
|
|||
return ret ? ret : rb->count;
|
||||
}
|
||||
|
||||
static int uvc_query_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *buf)
|
||||
int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -188,8 +186,7 @@ static int uvc_query_buffer(struct uvc_video_queue *queue,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int uvc_queue_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *buf)
|
||||
int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
|
@ -213,8 +210,8 @@ done:
|
|||
* Dequeue a video buffer. If nonblocking is false, block until a buffer is
|
||||
* available.
|
||||
*/
|
||||
static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *buf, int nonblocking)
|
||||
int uvcg_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
|
||||
int nonblocking)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -231,8 +228,8 @@ static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
|
|||
* This function implements video queue polling and is intended to be used by
|
||||
* the device poll handler.
|
||||
*/
|
||||
static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
|
||||
struct file *file, poll_table *wait)
|
||||
unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, struct file *file,
|
||||
poll_table *wait)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
|
|
@ -243,8 +240,7 @@ static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int uvc_queue_mmap(struct uvc_video_queue *queue,
|
||||
struct vm_area_struct *vma)
|
||||
int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -261,8 +257,8 @@ static int uvc_queue_mmap(struct uvc_video_queue *queue,
|
|||
*
|
||||
* NO-MMU arch need this function to make mmap() work correctly.
|
||||
*/
|
||||
static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
|
||||
unsigned long pgoff)
|
||||
unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
|
||||
unsigned long pgoff)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
|
|
@ -285,7 +281,7 @@ static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
|
|||
* This function acquires the irq spinlock and can be called from interrupt
|
||||
* context.
|
||||
*/
|
||||
static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
|
||||
void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect)
|
||||
{
|
||||
struct uvc_buffer *buf;
|
||||
unsigned long flags;
|
||||
|
|
@ -324,9 +320,9 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
|
|||
* the main queue.
|
||||
*
|
||||
* This function can't be called from interrupt context. Use
|
||||
* uvc_queue_cancel() instead.
|
||||
* uvcg_queue_cancel() instead.
|
||||
*/
|
||||
static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
|
||||
int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
|
@ -363,8 +359,8 @@ done:
|
|||
}
|
||||
|
||||
/* called with &queue_irqlock held.. */
|
||||
static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
|
||||
struct uvc_buffer *buf)
|
||||
struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
|
||||
struct uvc_buffer *buf)
|
||||
{
|
||||
struct uvc_buffer *nextbuf;
|
||||
|
||||
|
|
@ -392,7 +388,7 @@ static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
|
|||
return nextbuf;
|
||||
}
|
||||
|
||||
static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue)
|
||||
struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
|
||||
{
|
||||
struct uvc_buffer *buf = NULL;
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,39 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
|
|||
return vb2_is_streaming(&queue->queue);
|
||||
}
|
||||
|
||||
int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type);
|
||||
|
||||
void uvcg_free_buffers(struct uvc_video_queue *queue);
|
||||
|
||||
int uvcg_alloc_buffers(struct uvc_video_queue *queue,
|
||||
struct v4l2_requestbuffers *rb);
|
||||
|
||||
int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
|
||||
|
||||
int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
|
||||
|
||||
int uvcg_dequeue_buffer(struct uvc_video_queue *queue,
|
||||
struct v4l2_buffer *buf, int nonblocking);
|
||||
|
||||
unsigned int uvcg_queue_poll(struct uvc_video_queue *queue,
|
||||
struct file *file, poll_table *wait);
|
||||
|
||||
int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma);
|
||||
|
||||
#ifndef CONFIG_MMU
|
||||
unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
|
||||
unsigned long pgoff);
|
||||
#endif /* CONFIG_MMU */
|
||||
|
||||
void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect);
|
||||
|
||||
int uvcg_queue_enable(struct uvc_video_queue *queue, int enable);
|
||||
|
||||
struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
|
||||
struct uvc_buffer *buf);
|
||||
|
||||
struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* _UVC_QUEUE_H_ */
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@
|
|||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
|
||||
#include "f_uvc.h"
|
||||
#include "uvc.h"
|
||||
#include "uvc_queue.h"
|
||||
#include "uvc_video.h"
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Requests handling
|
||||
|
|
@ -48,7 +50,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
|
|||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* V4L2
|
||||
* V4L2 ioctls
|
||||
*/
|
||||
|
||||
struct uvc_format
|
||||
|
|
@ -63,8 +65,29 @@ static struct uvc_format uvc_formats[] = {
|
|||
};
|
||||
|
||||
static int
|
||||
uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
|
||||
uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
|
||||
strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
|
||||
strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
|
||||
strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
|
||||
sizeof(cap->bus_info));
|
||||
|
||||
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
|
||||
fmt->fmt.pix.pixelformat = video->fcc;
|
||||
fmt->fmt.pix.width = video->width;
|
||||
fmt->fmt.pix.height = video->height;
|
||||
|
|
@ -78,8 +101,11 @@ uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt)
|
|||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
|
||||
uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
struct uvc_format *format;
|
||||
unsigned int imagesize;
|
||||
unsigned int bpl;
|
||||
|
|
@ -115,6 +141,145 @@ uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
|
||||
if (b->type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvcg_alloc_buffers(&video->queue, b);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
|
||||
return uvcg_query_buffer(&video->queue, b);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
int ret;
|
||||
|
||||
ret = uvcg_queue_buffer(&video->queue, b);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return uvcg_video_pump(video);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
|
||||
return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
int ret;
|
||||
|
||||
if (type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
/* Enable UVC video. */
|
||||
ret = uvcg_video_enable(video, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Complete the alternate setting selection setup phase now that
|
||||
* userspace is ready to provide video frames.
|
||||
*/
|
||||
uvc_function_setup_continue(uvc);
|
||||
uvc->state = UVC_STATE_STREAMING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_video *video = &uvc->video;
|
||||
|
||||
if (type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvcg_video_enable(video, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
|
||||
const struct v4l2_event_subscription *sub)
|
||||
{
|
||||
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_event_subscribe(fh, sub, 2, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
|
||||
const struct v4l2_event_subscription *sub)
|
||||
{
|
||||
return v4l2_event_unsubscribe(fh, sub);
|
||||
}
|
||||
|
||||
static long
|
||||
uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio,
|
||||
unsigned int cmd, void *arg)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
|
||||
switch (cmd) {
|
||||
case UVCIOC_SEND_RESPONSE:
|
||||
return uvc_send_response(uvc, arg);
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
|
||||
.vidioc_querycap = uvc_v4l2_querycap,
|
||||
.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
|
||||
.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
|
||||
.vidioc_reqbufs = uvc_v4l2_reqbufs,
|
||||
.vidioc_querybuf = uvc_v4l2_querybuf,
|
||||
.vidioc_qbuf = uvc_v4l2_qbuf,
|
||||
.vidioc_dqbuf = uvc_v4l2_dqbuf,
|
||||
.vidioc_streamon = uvc_v4l2_streamon,
|
||||
.vidioc_streamoff = uvc_v4l2_streamoff,
|
||||
.vidioc_subscribe_event = uvc_v4l2_subscribe_event,
|
||||
.vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,
|
||||
.vidioc_default = uvc_v4l2_ioctl_default,
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* V4L2
|
||||
*/
|
||||
|
||||
static int
|
||||
uvc_v4l2_open(struct file *file)
|
||||
{
|
||||
|
|
@ -146,8 +311,8 @@ uvc_v4l2_release(struct file *file)
|
|||
|
||||
uvc_function_disconnect(uvc);
|
||||
|
||||
uvc_video_enable(video, 0);
|
||||
uvc_free_buffers(&video->queue);
|
||||
uvcg_video_enable(video, 0);
|
||||
uvcg_free_buffers(&video->queue);
|
||||
|
||||
file->private_data = NULL;
|
||||
v4l2_fh_del(&handle->vfh);
|
||||
|
|
@ -157,177 +322,13 @@ uvc_v4l2_release(struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long
|
||||
uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
|
||||
struct usb_composite_dev *cdev = uvc->func.config->cdev;
|
||||
struct uvc_video *video = &uvc->video;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
/* Query capabilities */
|
||||
case VIDIOC_QUERYCAP:
|
||||
{
|
||||
struct v4l2_capability *cap = arg;
|
||||
|
||||
memset(cap, 0, sizeof *cap);
|
||||
strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
|
||||
strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
|
||||
strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
|
||||
sizeof cap->bus_info);
|
||||
cap->version = DRIVER_VERSION_NUMBER;
|
||||
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get & Set format */
|
||||
case VIDIOC_G_FMT:
|
||||
{
|
||||
struct v4l2_format *fmt = arg;
|
||||
|
||||
if (fmt->type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvc_v4l2_get_format(video, fmt);
|
||||
}
|
||||
|
||||
case VIDIOC_S_FMT:
|
||||
{
|
||||
struct v4l2_format *fmt = arg;
|
||||
|
||||
if (fmt->type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvc_v4l2_set_format(video, fmt);
|
||||
}
|
||||
|
||||
/* Buffers & streaming */
|
||||
case VIDIOC_REQBUFS:
|
||||
{
|
||||
struct v4l2_requestbuffers *rb = arg;
|
||||
|
||||
if (rb->type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
ret = uvc_alloc_buffers(&video->queue, rb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_QUERYBUF:
|
||||
{
|
||||
struct v4l2_buffer *buf = arg;
|
||||
|
||||
return uvc_query_buffer(&video->queue, buf);
|
||||
}
|
||||
|
||||
case VIDIOC_QBUF:
|
||||
if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0)
|
||||
return ret;
|
||||
|
||||
return uvc_video_pump(video);
|
||||
|
||||
case VIDIOC_DQBUF:
|
||||
return uvc_dequeue_buffer(&video->queue, arg,
|
||||
file->f_flags & O_NONBLOCK);
|
||||
|
||||
case VIDIOC_STREAMON:
|
||||
{
|
||||
int *type = arg;
|
||||
|
||||
if (*type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
/* Enable UVC video. */
|
||||
ret = uvc_video_enable(video, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Complete the alternate setting selection setup phase now that
|
||||
* userspace is ready to provide video frames.
|
||||
*/
|
||||
uvc_function_setup_continue(uvc);
|
||||
uvc->state = UVC_STATE_STREAMING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case VIDIOC_STREAMOFF:
|
||||
{
|
||||
int *type = arg;
|
||||
|
||||
if (*type != video->queue.queue.type)
|
||||
return -EINVAL;
|
||||
|
||||
return uvc_video_enable(video, 0);
|
||||
}
|
||||
|
||||
/* Events */
|
||||
case VIDIOC_DQEVENT:
|
||||
{
|
||||
struct v4l2_event *event = arg;
|
||||
|
||||
ret = v4l2_event_dequeue(&handle->vfh, event,
|
||||
file->f_flags & O_NONBLOCK);
|
||||
if (ret == 0 && event->type == UVC_EVENT_SETUP) {
|
||||
struct uvc_event *uvc_event = (void *)&event->u.data;
|
||||
|
||||
/* Tell the complete callback to generate an event for
|
||||
* the next request that will be enqueued by
|
||||
* uvc_event_write.
|
||||
*/
|
||||
uvc->event_setup_out =
|
||||
!(uvc_event->req.bRequestType & USB_DIR_IN);
|
||||
uvc->event_length = uvc_event->req.wLength;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
case VIDIOC_SUBSCRIBE_EVENT:
|
||||
{
|
||||
struct v4l2_event_subscription *sub = arg;
|
||||
|
||||
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL);
|
||||
}
|
||||
|
||||
case VIDIOC_UNSUBSCRIBE_EVENT:
|
||||
return v4l2_event_unsubscribe(&handle->vfh, arg);
|
||||
|
||||
case UVCIOC_SEND_RESPONSE:
|
||||
ret = uvc_send_response(uvc, arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long
|
||||
uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
|
||||
}
|
||||
|
||||
static int
|
||||
uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
|
||||
return uvc_queue_mmap(&uvc->video.queue, vma);
|
||||
return uvcg_queue_mmap(&uvc->video.queue, vma);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
|
|
@ -336,30 +337,30 @@ uvc_v4l2_poll(struct file *file, poll_table *wait)
|
|||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
|
||||
return uvc_queue_poll(&uvc->video.queue, file, wait);
|
||||
return uvcg_queue_poll(&uvc->video.queue, file, wait);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_MMU
|
||||
static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
|
||||
static unsigned long uvcg_v4l2_get_unmapped_area(struct file *file,
|
||||
unsigned long addr, unsigned long len, unsigned long pgoff,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct video_device *vdev = video_devdata(file);
|
||||
struct uvc_device *uvc = video_get_drvdata(vdev);
|
||||
|
||||
return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff);
|
||||
return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct v4l2_file_operations uvc_v4l2_fops = {
|
||||
struct v4l2_file_operations uvc_v4l2_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = uvc_v4l2_open,
|
||||
.release = uvc_v4l2_release,
|
||||
.ioctl = uvc_v4l2_ioctl,
|
||||
.ioctl = video_ioctl2,
|
||||
.mmap = uvc_v4l2_mmap,
|
||||
.poll = uvc_v4l2_poll,
|
||||
#ifndef CONFIG_MMU
|
||||
.get_unmapped_area = uvc_v4l2_get_unmapped_area,
|
||||
.get_unmapped_area = uvcg_v4l2_get_unmapped_area,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
|||
22
drivers/usb/gadget/function/uvc_v4l2.h
Normal file
22
drivers/usb/gadget/function/uvc_v4l2.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* uvc_v4l2.h -- USB Video Class Gadget driver
|
||||
*
|
||||
* Copyright (C) 2009-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* Copyright (c) 2013 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 __UVC_V4L2_H__
|
||||
#define __UVC_V4L2_H__
|
||||
|
||||
extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops;
|
||||
extern struct v4l2_file_operations uvc_v4l2_fops;
|
||||
|
||||
#endif /* __UVC_V4L2_H__ */
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/video.h>
|
||||
|
||||
#include <media/v4l2-dev.h>
|
||||
|
||||
|
|
@ -85,7 +86,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
|
|||
if (buf->bytesused == video->queue.buf_used) {
|
||||
video->queue.buf_used = 0;
|
||||
buf->state = UVC_BUF_STATE_DONE;
|
||||
uvc_queue_next_buffer(&video->queue, buf);
|
||||
uvcg_queue_next_buffer(&video->queue, buf);
|
||||
video->fid ^= UVC_STREAM_FID;
|
||||
|
||||
video->payload_size = 0;
|
||||
|
|
@ -118,7 +119,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
|
|||
if (buf->bytesused == video->queue.buf_used) {
|
||||
video->queue.buf_used = 0;
|
||||
buf->state = UVC_BUF_STATE_DONE;
|
||||
uvc_queue_next_buffer(&video->queue, buf);
|
||||
uvcg_queue_next_buffer(&video->queue, buf);
|
||||
video->fid ^= UVC_STREAM_FID;
|
||||
}
|
||||
}
|
||||
|
|
@ -171,19 +172,19 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
break;
|
||||
|
||||
case -ESHUTDOWN: /* disconnect from host. */
|
||||
printk(KERN_INFO "VS request cancelled.\n");
|
||||
uvc_queue_cancel(queue, 1);
|
||||
printk(KERN_DEBUG "VS request cancelled.\n");
|
||||
uvcg_queue_cancel(queue, 1);
|
||||
goto requeue;
|
||||
|
||||
default:
|
||||
printk(KERN_INFO "VS request completed with status %d.\n",
|
||||
req->status);
|
||||
uvc_queue_cancel(queue, 0);
|
||||
uvcg_queue_cancel(queue, 0);
|
||||
goto requeue;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&video->queue.irqlock, flags);
|
||||
buf = uvc_queue_head(&video->queue);
|
||||
buf = uvcg_queue_head(&video->queue);
|
||||
if (buf == NULL) {
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
goto requeue;
|
||||
|
|
@ -195,7 +196,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
printk(KERN_INFO "Failed to queue request (%d).\n", ret);
|
||||
usb_ep_set_halt(ep);
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
uvc_queue_cancel(queue, 0);
|
||||
uvcg_queue_cancel(queue, 0);
|
||||
goto requeue;
|
||||
}
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
|
|
@ -274,13 +275,12 @@ error:
|
|||
*/
|
||||
|
||||
/*
|
||||
* uvc_video_pump - Pump video data into the USB requests
|
||||
* uvcg_video_pump - Pump video data into the USB requests
|
||||
*
|
||||
* This function fills the available USB requests (listed in req_free) with
|
||||
* video data from the queued buffers.
|
||||
*/
|
||||
static int
|
||||
uvc_video_pump(struct uvc_video *video)
|
||||
int uvcg_video_pump(struct uvc_video *video)
|
||||
{
|
||||
struct uvc_video_queue *queue = &video->queue;
|
||||
struct usb_request *req;
|
||||
|
|
@ -288,7 +288,7 @@ uvc_video_pump(struct uvc_video *video)
|
|||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/* FIXME TODO Race between uvc_video_pump and requests completion
|
||||
/* FIXME TODO Race between uvcg_video_pump and requests completion
|
||||
* handler ???
|
||||
*/
|
||||
|
||||
|
|
@ -309,10 +309,10 @@ uvc_video_pump(struct uvc_video *video)
|
|||
/* Retrieve the first available video buffer and fill the
|
||||
* request, protected by the video queue irqlock.
|
||||
*/
|
||||
spin_lock_irqsave(&video->queue.irqlock, flags);
|
||||
buf = uvc_queue_head(&video->queue);
|
||||
spin_lock_irqsave(&queue->irqlock, flags);
|
||||
buf = uvcg_queue_head(queue);
|
||||
if (buf == NULL) {
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -323,11 +323,11 @@ uvc_video_pump(struct uvc_video *video)
|
|||
if (ret < 0) {
|
||||
printk(KERN_INFO "Failed to queue request (%d)\n", ret);
|
||||
usb_ep_set_halt(video->ep);
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
uvc_queue_cancel(queue, 0);
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
uvcg_queue_cancel(queue, 0);
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&video->queue.irqlock, flags);
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&video->req_lock, flags);
|
||||
|
|
@ -339,8 +339,7 @@ uvc_video_pump(struct uvc_video *video)
|
|||
/*
|
||||
* Enable or disable the video stream.
|
||||
*/
|
||||
static int
|
||||
uvc_video_enable(struct uvc_video *video, int enable)
|
||||
int uvcg_video_enable(struct uvc_video *video, int enable)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
|
@ -356,11 +355,11 @@ uvc_video_enable(struct uvc_video *video, int enable)
|
|||
usb_ep_dequeue(video->ep, video->req[i]);
|
||||
|
||||
uvc_video_free_requests(video);
|
||||
uvc_queue_enable(&video->queue, 0);
|
||||
uvcg_queue_enable(&video->queue, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
|
||||
if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret = uvc_video_alloc_requests(video)) < 0)
|
||||
|
|
@ -372,14 +371,13 @@ uvc_video_enable(struct uvc_video *video, int enable)
|
|||
} else
|
||||
video->encode = uvc_video_encode_isoc;
|
||||
|
||||
return uvc_video_pump(video);
|
||||
return uvcg_video_pump(video);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the UVC video stream.
|
||||
*/
|
||||
static int
|
||||
uvc_video_init(struct uvc_video *video)
|
||||
int uvcg_video_init(struct uvc_video *video)
|
||||
{
|
||||
INIT_LIST_HEAD(&video->req_free);
|
||||
spin_lock_init(&video->req_lock);
|
||||
|
|
@ -391,7 +389,7 @@ uvc_video_init(struct uvc_video *video)
|
|||
video->imagesize = 320 * 240 * 2;
|
||||
|
||||
/* Initialize the video buffers queue. */
|
||||
uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
|
||||
uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
24
drivers/usb/gadget/function/uvc_video.h
Normal file
24
drivers/usb/gadget/function/uvc_video.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* uvc_video.h -- USB Video Class Gadget driver
|
||||
*
|
||||
* Copyright (C) 2009-2010
|
||||
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* Copyright (c) 2013 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 __UVC_VIDEO_H__
|
||||
#define __UVC_VIDEO_H__
|
||||
|
||||
int uvcg_video_pump(struct uvc_video *video);
|
||||
|
||||
int uvcg_video_enable(struct uvc_video *video, int enable);
|
||||
|
||||
int uvcg_video_init(struct uvc_video *video);
|
||||
|
||||
#endif /* __UVC_VIDEO_H__ */
|
||||
|
|
@ -54,6 +54,8 @@ config USB_AUDIO
|
|||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
select USB_F_UAC1 if GADGET_UAC1
|
||||
select USB_F_UAC2 if !GADGET_UAC1
|
||||
help
|
||||
This Gadget Audio driver is compatible with USB Audio Class
|
||||
specification 2.0. It implements 1 AudioControl interface,
|
||||
|
|
@ -466,6 +468,7 @@ config USB_G_WEBCAM
|
|||
depends on VIDEO_DEV
|
||||
select USB_LIBCOMPOSITE
|
||||
select VIDEOBUF2_VMALLOC
|
||||
select USB_F_UVC
|
||||
help
|
||||
The Webcam Gadget acts as a composite USB Audio and Video Class
|
||||
device. It provides a userspace API to process UVC control requests
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
# USB gadget drivers
|
||||
#
|
||||
|
||||
ccflags-y := -Idrivers/usb/gadget/
|
||||
ccflags-y += -Idrivers/usb/gadget/udc/
|
||||
ccflags-y += -Idrivers/usb/gadget/function/
|
||||
ccflags-y := -I$(srctree)/drivers/usb/gadget/
|
||||
ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/
|
||||
ccflags-y += -I$(srctree)/drivers/usb/gadget/function/
|
||||
|
||||
g_zero-y := zero.o
|
||||
g_audio-y := audio.o
|
||||
|
|
|
|||
|
|
@ -21,6 +21,66 @@
|
|||
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
#include "u_uac2.h"
|
||||
|
||||
/* Playback(USB-IN) Default Stereo - Fl/Fr */
|
||||
static int p_chmask = UAC2_DEF_PCHMASK;
|
||||
module_param(p_chmask, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
|
||||
|
||||
/* Playback Default 48 KHz */
|
||||
static int p_srate = UAC2_DEF_PSRATE;
|
||||
module_param(p_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
|
||||
|
||||
/* Playback Default 16bits/sample */
|
||||
static int p_ssize = UAC2_DEF_PSSIZE;
|
||||
module_param(p_ssize, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
|
||||
|
||||
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
|
||||
static int c_chmask = UAC2_DEF_CCHMASK;
|
||||
module_param(c_chmask, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
|
||||
|
||||
/* Capture Default 64 KHz */
|
||||
static int c_srate = UAC2_DEF_CSRATE;
|
||||
module_param(c_srate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
|
||||
|
||||
/* Capture Default 16bits/sample */
|
||||
static int c_ssize = UAC2_DEF_CSSIZE;
|
||||
module_param(c_ssize, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
|
||||
#else
|
||||
#include "u_uac1.h"
|
||||
|
||||
static char *fn_play = FILE_PCM_PLAYBACK;
|
||||
module_param(fn_play, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
|
||||
|
||||
static char *fn_cap = FILE_PCM_CAPTURE;
|
||||
module_param(fn_cap, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
|
||||
|
||||
static char *fn_cntl = FILE_CONTROL;
|
||||
module_param(fn_cntl, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(fn_cntl, "Control device file name");
|
||||
|
||||
static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
|
||||
module_param(req_buf_size, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
|
||||
|
||||
static int req_count = UAC1_REQ_COUNT;
|
||||
module_param(req_count, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
|
||||
|
||||
static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
|
||||
module_param(audio_buf_size, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
|
||||
#endif
|
||||
|
||||
/* string IDs are assigned dynamically */
|
||||
|
||||
static struct usb_string strings_dev[] = {
|
||||
|
|
@ -40,12 +100,12 @@ static struct usb_gadget_strings *audio_strings[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
#include "u_uac1.h"
|
||||
#include "u_uac1.c"
|
||||
#include "f_uac1.c"
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
static struct usb_function_instance *fi_uac2;
|
||||
static struct usb_function *f_uac2;
|
||||
#else
|
||||
#include "f_uac2.c"
|
||||
static struct usb_function_instance *fi_uac1;
|
||||
static struct usb_function *f_uac1;
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -109,6 +169,8 @@ static const struct usb_descriptor_header *otg_desc[] = {
|
|||
|
||||
static int __init audio_do_config(struct usb_configuration *c)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
|
|
@ -116,7 +178,31 @@ static int __init audio_do_config(struct usb_configuration *c)
|
|||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
audio_bind_config(c);
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
f_uac1 = usb_get_function(fi_uac1);
|
||||
if (IS_ERR(f_uac1)) {
|
||||
status = PTR_ERR(f_uac1);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = usb_add_function(c, f_uac1);
|
||||
if (status < 0) {
|
||||
usb_put_function(f_uac1);
|
||||
return status;
|
||||
}
|
||||
#else
|
||||
f_uac2 = usb_get_function(fi_uac2);
|
||||
if (IS_ERR(f_uac2)) {
|
||||
status = PTR_ERR(f_uac2);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = usb_add_function(c, f_uac2);
|
||||
if (status < 0) {
|
||||
usb_put_function(f_uac2);
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -126,17 +212,47 @@ static struct usb_configuration audio_config_driver = {
|
|||
.bConfigurationValue = 1,
|
||||
/* .iConfiguration = DYNAMIC */
|
||||
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
.unbind = uac2_unbind_config,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init audio_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
struct f_uac2_opts *uac2_opts;
|
||||
#else
|
||||
struct f_uac1_opts *uac1_opts;
|
||||
#endif
|
||||
int status;
|
||||
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
fi_uac2 = usb_get_function_instance("uac2");
|
||||
if (IS_ERR(fi_uac2))
|
||||
return PTR_ERR(fi_uac2);
|
||||
#else
|
||||
fi_uac1 = usb_get_function_instance("uac1");
|
||||
if (IS_ERR(fi_uac1))
|
||||
return PTR_ERR(fi_uac1);
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
|
||||
uac2_opts->p_chmask = p_chmask;
|
||||
uac2_opts->p_srate = p_srate;
|
||||
uac2_opts->p_ssize = p_ssize;
|
||||
uac2_opts->c_chmask = c_chmask;
|
||||
uac2_opts->c_srate = c_srate;
|
||||
uac2_opts->c_ssize = c_ssize;
|
||||
#else
|
||||
uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
|
||||
uac1_opts->fn_play = fn_play;
|
||||
uac1_opts->fn_cap = fn_cap;
|
||||
uac1_opts->fn_cntl = fn_cntl;
|
||||
uac1_opts->req_buf_size = req_buf_size;
|
||||
uac1_opts->req_count = req_count;
|
||||
uac1_opts->audio_buf_size = audio_buf_size;
|
||||
#endif
|
||||
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
|
|
@ -152,13 +268,26 @@ static int __init audio_bind(struct usb_composite_dev *cdev)
|
|||
return 0;
|
||||
|
||||
fail:
|
||||
#ifndef CONFIG_GADGET_UAC1
|
||||
usb_put_function_instance(fi_uac2);
|
||||
#else
|
||||
usb_put_function_instance(fi_uac1);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit audio_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
#ifdef CONFIG_GADGET_UAC1
|
||||
gaudio_cleanup();
|
||||
if (!IS_ERR_OR_NULL(f_uac1))
|
||||
usb_put_function(f_uac1);
|
||||
if (!IS_ERR_OR_NULL(fi_uac1))
|
||||
usb_put_function_instance(fi_uac1);
|
||||
#else
|
||||
if (!IS_ERR_OR_NULL(f_uac2))
|
||||
usb_put_function(f_uac2);
|
||||
if (!IS_ERR_OR_NULL(fi_uac2))
|
||||
usb_put_function_instance(fi_uac2);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@ static __refdata struct usb_gadget_driver dbgp_driver = {
|
|||
.bind = dbgp_bind,
|
||||
.unbind = dbgp_unbind,
|
||||
.setup = dbgp_setup,
|
||||
.reset = dbgp_disconnect,
|
||||
.disconnect = dbgp_disconnect,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
|||
|
|
@ -1775,6 +1775,7 @@ static struct usb_gadget_driver gadgetfs_driver = {
|
|||
.bind = gadgetfs_bind,
|
||||
.unbind = gadgetfs_unbind,
|
||||
.setup = gadgetfs_setup,
|
||||
.reset = gadgetfs_disconnect,
|
||||
.disconnect = gadgetfs_disconnect,
|
||||
.suspend = gadgetfs_suspend,
|
||||
|
||||
|
|
|
|||
|
|
@ -12,23 +12,31 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb/video.h>
|
||||
|
||||
#include "f_uvc.h"
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "uvc_queue.c"
|
||||
#include "uvc_video.c"
|
||||
#include "uvc_v4l2.c"
|
||||
#include "f_uvc.c"
|
||||
#include "u_uvc.h"
|
||||
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* module parameters specific to the Video streaming endpoint */
|
||||
static unsigned int streaming_interval = 1;
|
||||
module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(streaming_interval, "1 - 16");
|
||||
|
||||
static unsigned int streaming_maxpacket = 1024;
|
||||
module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
|
||||
|
||||
static unsigned int streaming_maxburst;
|
||||
module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
|
||||
|
||||
static unsigned int trace;
|
||||
module_param(trace, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(trace, "Trace level bitmask");
|
||||
/* --------------------------------------------------------------------------
|
||||
* Device descriptor
|
||||
*/
|
||||
|
|
@ -63,6 +71,9 @@ static struct usb_gadget_strings *webcam_device_strings[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_function_instance *fi_uvc;
|
||||
static struct usb_function *f_uvc;
|
||||
|
||||
static struct usb_device_descriptor webcam_device_descriptor = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
|
|
@ -326,9 +337,17 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
|
|||
static int __init
|
||||
webcam_config_bind(struct usb_configuration *c)
|
||||
{
|
||||
return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls,
|
||||
uvc_fs_streaming_cls, uvc_hs_streaming_cls,
|
||||
uvc_ss_streaming_cls);
|
||||
int status = 0;
|
||||
|
||||
f_uvc = usb_get_function(fi_uvc);
|
||||
if (IS_ERR(f_uvc))
|
||||
return PTR_ERR(f_uvc);
|
||||
|
||||
status = usb_add_function(c, f_uvc);
|
||||
if (status < 0)
|
||||
usb_put_function(f_uvc);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct usb_configuration webcam_config_driver = {
|
||||
|
|
@ -342,14 +361,36 @@ static struct usb_configuration webcam_config_driver = {
|
|||
static int /* __init_or_exit */
|
||||
webcam_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(f_uvc))
|
||||
usb_put_function(f_uvc);
|
||||
if (!IS_ERR_OR_NULL(fi_uvc))
|
||||
usb_put_function_instance(fi_uvc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init
|
||||
webcam_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct f_uvc_opts *uvc_opts;
|
||||
int ret;
|
||||
|
||||
fi_uvc = usb_get_function_instance("uvc");
|
||||
if (IS_ERR(fi_uvc))
|
||||
return PTR_ERR(fi_uvc);
|
||||
|
||||
uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);
|
||||
|
||||
uvc_opts->streaming_interval = streaming_interval;
|
||||
uvc_opts->streaming_maxpacket = streaming_maxpacket;
|
||||
uvc_opts->streaming_maxburst = streaming_maxburst;
|
||||
uvc_set_trace_param(trace);
|
||||
|
||||
uvc_opts->fs_control = uvc_fs_control_cls;
|
||||
uvc_opts->ss_control = uvc_ss_control_cls;
|
||||
uvc_opts->fs_streaming = uvc_fs_streaming_cls;
|
||||
uvc_opts->hs_streaming = uvc_hs_streaming_cls;
|
||||
uvc_opts->ss_streaming = uvc_ss_streaming_cls;
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string contents
|
||||
* can be overridden by the composite_dev glue.
|
||||
*/
|
||||
|
|
@ -373,7 +414,7 @@ webcam_bind(struct usb_composite_dev *cdev)
|
|||
return 0;
|
||||
|
||||
error:
|
||||
webcam_unbind(cdev);
|
||||
usb_put_function_instance(fi_uvc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ static struct usb_zero_options gzero_options = {
|
|||
.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
|
||||
.bulk_buflen = GZERO_BULK_BUFLEN,
|
||||
.qlen = GZERO_QLEN,
|
||||
.int_interval = GZERO_INT_INTERVAL,
|
||||
.int_maxpacket = GZERO_INT_MAXPACKET,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
@ -266,6 +268,21 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
|
|||
S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
|
||||
|
||||
module_param_named(int_interval, gzero_options.int_interval, uint,
|
||||
S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(int_interval, "1 - 16");
|
||||
|
||||
module_param_named(int_maxpacket, gzero_options.int_maxpacket, uint,
|
||||
S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(int_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
|
||||
|
||||
module_param_named(int_mult, gzero_options.int_mult, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(int_mult, "0 - 2 (hs/ss only)");
|
||||
|
||||
module_param_named(int_maxburst, gzero_options.int_maxburst, uint,
|
||||
S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(int_maxburst, "0 - 15 (ss only)");
|
||||
|
||||
static struct usb_function *func_lb;
|
||||
static struct usb_function_instance *func_inst_lb;
|
||||
|
||||
|
|
@ -301,6 +318,10 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
|
|||
ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
|
||||
ss_opts->isoc_mult = gzero_options.isoc_mult;
|
||||
ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
|
||||
ss_opts->int_interval = gzero_options.int_interval;
|
||||
ss_opts->int_maxpacket = gzero_options.int_maxpacket;
|
||||
ss_opts->int_mult = gzero_options.int_mult;
|
||||
ss_opts->int_maxburst = gzero_options.int_maxburst;
|
||||
ss_opts->bulk_buflen = gzero_options.bulk_buflen;
|
||||
|
||||
func_ss = usb_get_function(func_inst_ss);
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ config USB_R8A66597
|
|||
|
||||
config USB_RENESAS_USBHS_UDC
|
||||
tristate 'Renesas USBHS controller'
|
||||
depends on USB_RENESAS_USBHS
|
||||
depends on USB_RENESAS_USBHS && HAS_DMA
|
||||
help
|
||||
Renesas USBHS is a discrete USB host and peripheral controller chip
|
||||
that supports both full and high speed USB 2.0 data transfers.
|
||||
|
|
@ -354,6 +354,21 @@ config USB_EG20T
|
|||
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
|
||||
|
||||
This driver can be used with Intel's Quark X1000 SOC platform
|
||||
|
||||
config USB_GADGET_XILINX
|
||||
tristate "Xilinx USB Driver"
|
||||
depends on OF || COMPILE_TEST
|
||||
help
|
||||
USB peripheral controller driver for Xilinx USB2 device.
|
||||
Xilinx USB2 device is a soft IP which supports both full
|
||||
and high speed USB 2.0 data transfers. It has seven configurable
|
||||
endpoints(bulk or interrupt or isochronous), as well as
|
||||
endpoint zero(for control transfers).
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "udc-xilinx" and force all
|
||||
gadget drivers to also be dynamically linked.
|
||||
|
||||
#
|
||||
# LAST -- dummy/emulated controller
|
||||
#
|
||||
|
|
|
|||
|
|
@ -29,3 +29,4 @@ obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
|||
obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
|
||||
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
|
||||
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
|
||||
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
|
||||
|
|
|
|||
|
|
@ -841,7 +841,7 @@ __acquires(ep->dev->lock)
|
|||
&req->req, req->req.length, ep->ep.name, sts);
|
||||
|
||||
spin_unlock(&dev->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&dev->lock);
|
||||
ep->halted = halted;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ static void done(struct at91_ep *ep, struct at91_request *req, int status)
|
|||
|
||||
ep->stopped = 1;
|
||||
spin_unlock(&udc->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&udc->lock);
|
||||
ep->stopped = stopped;
|
||||
|
||||
|
|
|
|||
|
|
@ -463,7 +463,7 @@ static void receive_data(struct usba_ep *ep)
|
|||
list_del_init(&req->queue);
|
||||
usba_ep_writel(ep, CTL_DIS, USBA_RX_BK_RDY);
|
||||
spin_unlock(&udc->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
|
||||
|
|
@ -495,7 +495,7 @@ request_complete(struct usba_ep *ep, struct usba_request *req, int status)
|
|||
ep->ep.name, req, req->req.status, req->req.actual);
|
||||
|
||||
spin_unlock(&udc->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1088,7 +1088,7 @@ static int bcm63xx_ep_disable(struct usb_ep *ep)
|
|||
breq->req.status = -ESHUTDOWN;
|
||||
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
breq->req.complete(&iudma->bep->ep, &breq->req);
|
||||
usb_gadget_giveback_request(&iudma->bep->ep, &breq->req);
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep)
|
|||
req->req.status = -ESHUTDOWN;
|
||||
|
||||
spin_unlock(&dum->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&dum->lock);
|
||||
}
|
||||
}
|
||||
|
|
@ -658,7 +658,7 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
spin_unlock(&dum->lock);
|
||||
_req->actual = _req->length;
|
||||
_req->status = 0;
|
||||
_req->complete(_ep, _req);
|
||||
usb_gadget_giveback_request(_ep, _req);
|
||||
spin_lock(&dum->lock);
|
||||
} else
|
||||
list_add_tail(&req->queue, &ep->queue);
|
||||
|
|
@ -702,7 +702,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
dev_dbg(udc_dev(dum),
|
||||
"dequeued req %p from %s, len %d buf %p\n",
|
||||
req, _ep->name, _req->length, _req->buf);
|
||||
_req->complete(_ep, _req);
|
||||
usb_gadget_giveback_request(_ep, _req);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
return retval;
|
||||
|
|
@ -1385,7 +1385,7 @@ top:
|
|||
list_del_init(&req->queue);
|
||||
|
||||
spin_unlock(&dum->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&dum->lock);
|
||||
|
||||
/* requests might have been unlinked... */
|
||||
|
|
@ -1761,7 +1761,7 @@ restart:
|
|||
req);
|
||||
|
||||
spin_unlock(&dum->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&dum->lock);
|
||||
ep->already_seen = 0;
|
||||
goto restart;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req,
|
|||
req->req.status = status;
|
||||
|
||||
spin_unlock(&ep->fotg210->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&ep->fotg210->lock);
|
||||
|
||||
if (ep->epnum) {
|
||||
|
|
|
|||
|
|
@ -118,10 +118,7 @@ static void done(struct qe_ep *ep, struct qe_req *req, int status)
|
|||
ep->stopped = 1;
|
||||
spin_unlock(&udc->lock);
|
||||
|
||||
/* this complete() should a func implemented by gadget layer,
|
||||
* eg fsg->bulk_in_complete() */
|
||||
if (req->req.complete)
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
|
|
@ -2728,4 +2725,3 @@ module_platform_driver(udc_driver);
|
|||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
|
|
|||
|
|
@ -197,10 +197,8 @@ __acquires(ep->udc->lock)
|
|||
ep->stopped = 1;
|
||||
|
||||
spin_unlock(&ep->udc->lock);
|
||||
/* complete() is from gadget layer,
|
||||
* eg fsg->bulk_in_complete() */
|
||||
if (req->req.complete)
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
|
||||
spin_lock(&ep->udc->lock);
|
||||
ep->stopped = stopped;
|
||||
|
|
|
|||
|
|
@ -876,7 +876,7 @@ static void done(struct fusb300_ep *ep, struct fusb300_request *req,
|
|||
req->req.status = status;
|
||||
|
||||
spin_unlock(&ep->fusb300->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&ep->fusb300->lock);
|
||||
|
||||
if (ep->epnum) {
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ done(struct goku_ep *ep, struct goku_request *req, int status)
|
|||
/* don't modify queue heads during completion callback */
|
||||
ep->stopped = 1;
|
||||
spin_unlock(&dev->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&dev->lock);
|
||||
ep->stopped = stopped;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -318,8 +318,26 @@ static void gr_finish_request(struct gr_ep *ep, struct gr_request *req,
|
|||
usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
|
||||
gr_free_dma_desc_chain(dev, req);
|
||||
|
||||
if (ep->is_in) /* For OUT, actual gets updated bit by bit */
|
||||
if (ep->is_in) { /* For OUT, req->req.actual gets updated bit by bit */
|
||||
req->req.actual = req->req.length;
|
||||
} else if (req->oddlen && req->req.actual > req->evenlen) {
|
||||
/*
|
||||
* Copy to user buffer in this case where length was not evenly
|
||||
* divisible by ep->ep.maxpacket and the last descriptor was
|
||||
* actually used.
|
||||
*/
|
||||
char *buftail = ((char *)req->req.buf + req->evenlen);
|
||||
|
||||
memcpy(buftail, ep->tailbuf, req->oddlen);
|
||||
|
||||
if (req->req.actual > req->req.length) {
|
||||
/* We got more data than was requested */
|
||||
dev_dbg(ep->dev->dev, "Overflow for ep %s\n",
|
||||
ep->ep.name);
|
||||
gr_dbgprint_request("OVFL", ep, req);
|
||||
req->req.status = -EOVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
if (ep->is_in)
|
||||
|
|
@ -339,7 +357,7 @@ static void gr_finish_request(struct gr_ep *ep, struct gr_request *req,
|
|||
} else if (req->req.complete) {
|
||||
spin_unlock(&dev->lock);
|
||||
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
|
||||
spin_lock(&dev->lock);
|
||||
}
|
||||
|
|
@ -379,6 +397,15 @@ static void gr_start_dma(struct gr_ep *ep)
|
|||
/* A descriptor should already have been allocated */
|
||||
BUG_ON(!req->curr_desc);
|
||||
|
||||
/*
|
||||
* The DMA controller can not handle smaller OUT buffers than
|
||||
* ep->ep.maxpacket. It could lead to buffer overruns if an unexpectedly
|
||||
* long packet are received. Therefore an internal bounce buffer gets
|
||||
* used when such a request gets enabled.
|
||||
*/
|
||||
if (!ep->is_in && req->oddlen)
|
||||
req->last_desc->data = ep->tailbuf_paddr;
|
||||
|
||||
wmb(); /* Make sure all is settled before handing it over to DMA */
|
||||
|
||||
/* Set the descriptor pointer in the hardware */
|
||||
|
|
@ -480,11 +507,11 @@ static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req,
|
|||
dma_addr_t start = req->req.dma + bytes_used;
|
||||
u16 size = min(bytes_left, ep->bytes_per_buffer);
|
||||
|
||||
/* Should not happen however - gr_queue stops such lengths */
|
||||
if (size < ep->bytes_per_buffer)
|
||||
dev_warn(ep->dev->dev,
|
||||
"Buffer overrun risk: %u < %u bytes/buffer\n",
|
||||
size, ep->bytes_per_buffer);
|
||||
if (size < ep->bytes_per_buffer) {
|
||||
/* Prepare using bounce buffer */
|
||||
req->evenlen = req->req.length - bytes_left;
|
||||
req->oddlen = size;
|
||||
}
|
||||
|
||||
ret = gr_add_dma_desc(ep, req, start, size, gfp_flags);
|
||||
if (ret)
|
||||
|
|
@ -584,18 +611,6 @@ static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The DMA controller can not handle smaller OUT buffers than
|
||||
* maxpacket. It could lead to buffer overruns if unexpectedly long
|
||||
* packet are received.
|
||||
*/
|
||||
if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) {
|
||||
dev_err(dev->dev,
|
||||
"OUT request length %d is not multiple of maxpacket\n",
|
||||
req->req.length);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
|
||||
dev_err(dev->dev, "-ESHUTDOWN");
|
||||
return -ESHUTDOWN;
|
||||
|
|
@ -1286,8 +1301,8 @@ static int gr_handle_out_ep(struct gr_ep *ep)
|
|||
if (ctrl & GR_DESC_OUT_CTRL_SE)
|
||||
req->setup = 1;
|
||||
|
||||
if (len < ep->ep.maxpacket || req->req.actual == req->req.length) {
|
||||
/* Short packet or the expected size - we are done */
|
||||
if (len < ep->ep.maxpacket || req->req.actual >= req->req.length) {
|
||||
/* Short packet or >= expected size - we are done */
|
||||
|
||||
if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) {
|
||||
/*
|
||||
|
|
@ -2015,6 +2030,11 @@ static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit)
|
|||
}
|
||||
list_add_tail(&ep->ep_list, &dev->ep_list);
|
||||
|
||||
ep->tailbuf = dma_alloc_coherent(dev->dev, ep->ep.maxpacket_limit,
|
||||
&ep->tailbuf_paddr, GFP_ATOMIC);
|
||||
if (!ep->tailbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2067,9 +2087,24 @@ static int gr_udc_init(struct gr_udc *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void gr_ep_remove(struct gr_udc *dev, int num, int is_in)
|
||||
{
|
||||
struct gr_ep *ep;
|
||||
|
||||
if (is_in)
|
||||
ep = &dev->epi[num];
|
||||
else
|
||||
ep = &dev->epo[num];
|
||||
|
||||
if (ep->tailbuf)
|
||||
dma_free_coherent(dev->dev, ep->ep.maxpacket_limit,
|
||||
ep->tailbuf, ep->tailbuf_paddr);
|
||||
}
|
||||
|
||||
static int gr_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gr_udc *dev = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
if (dev->added)
|
||||
usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */
|
||||
|
|
@ -2084,6 +2119,11 @@ static int gr_remove(struct platform_device *pdev)
|
|||
gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req);
|
||||
gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req);
|
||||
|
||||
for (i = 0; i < dev->nepo; i++)
|
||||
gr_ep_remove(dev, i, 0);
|
||||
for (i = 0; i < dev->nepi; i++)
|
||||
gr_ep_remove(dev, i, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int gr_request_irq(struct gr_udc *dev, int irq)
|
||||
|
|
@ -2131,7 +2171,6 @@ static int gr_probe(struct platform_device *pdev)
|
|||
dev->gadget.name = driver_name;
|
||||
dev->gadget.max_speed = USB_SPEED_HIGH;
|
||||
dev->gadget.ops = &gr_ops;
|
||||
dev->gadget.quirk_ep_out_aligned_size = true;
|
||||
|
||||
spin_lock_init(&dev->lock);
|
||||
dev->regs = regs;
|
||||
|
|
|
|||
|
|
@ -156,6 +156,10 @@ struct gr_ep {
|
|||
struct list_head queue;
|
||||
|
||||
struct list_head ep_list;
|
||||
|
||||
/* Bounce buffer for end of "odd" sized OUT requests */
|
||||
void *tailbuf;
|
||||
dma_addr_t tailbuf_paddr;
|
||||
};
|
||||
|
||||
struct gr_request {
|
||||
|
|
@ -167,6 +171,9 @@ struct gr_request {
|
|||
struct gr_dma_desc *curr_desc; /* Current descriptor */
|
||||
struct gr_dma_desc *last_desc; /* Last in the chain */
|
||||
|
||||
u16 evenlen; /* Size of even length head (if oddlen != 0) */
|
||||
u16 oddlen; /* Size of odd length tail if buffer length is "odd" */
|
||||
|
||||
u8 setup; /* Setup packet */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1479,7 +1479,7 @@ static void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status)
|
|||
|
||||
ep->req_pending = 0;
|
||||
spin_unlock(&udc->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -729,7 +729,7 @@ __acquires(m66592->lock)
|
|||
restart = 1;
|
||||
|
||||
spin_unlock(&ep->m66592->lock);
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&ep->m66592->lock);
|
||||
|
||||
if (restart) {
|
||||
|
|
|
|||
|
|
@ -222,12 +222,8 @@ void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status)
|
|||
}
|
||||
|
||||
spin_unlock(&ep->u3d->lock);
|
||||
/*
|
||||
* complete() is from gadget layer,
|
||||
* eg fsg->bulk_in_complete()
|
||||
*/
|
||||
if (req->req.complete)
|
||||
req->req.complete(&ep->ep, &req->req);
|
||||
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
|
||||
spin_lock(&ep->u3d->lock);
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue