USB fixes for 4.4-rc2
Here are a number of USB fixes and new device ids for 4.4-rc2. All have been in linux-next and the details are in the shortlog. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlZSDW4ACgkQMUfUDdst+ymrlwCgha5PobWYrhVnhC/w5ODZxRaF oAQAn2tOK94L9sADvjbQlFUy+/Zaxxbj =x9f4 -----END PGP SIGNATURE----- Merge tag 'usb-4.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB fixes from Greg KH: "Here are a number of USB fixes and new device ids for 4.4-rc2. All have been in linux-next and the details are in the shortlog" * tag 'usb-4.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (28 commits) usblp: do not set TASK_INTERRUPTIBLE before lock USB: MAINTAINERS: cxacru usb: kconfig: fix warning of select USB_OTG USB: option: add XS Stick W100-2 from 4G Systems xhci: Fix a race in usb2 LPM resume, blocking U3 for usb2 devices usb: xhci: fix checking ep busy for CFC xhci: Workaround to get Intel xHCI reset working more reliably usb: chipidea: imx: fix a possible NULL dereference usb: chipidea: usbmisc_imx: fix a possible NULL dereference usb: chipidea: otg: gadget module load and unload support usb: chipidea: debug: disable usb irq while role switch ARM: dts: imx27.dtsi: change the clock information for usb usb: chipidea: imx: refine clock operations to adapt for all platforms usb: gadget: atmel_usba_udc: Expose correct device speed usb: musb: enable usb_dma parameter usb: phy: phy-mxs-usb: fix a possible NULL dereference usb: dwc3: gadget: let us set lower max_speed usb: musb: fix tx fifo flush handling usb: gadget: f_loopback: fix the warning during the enumeration usb: dwc2: host: Fix remote wakeup when not in DWC2_L2 ...
This commit is contained in:
Коммит
6d2d91b3e4
|
@ -2931,10 +2931,9 @@ S: Maintained
|
|||
F: drivers/platform/x86/compal-laptop.c
|
||||
|
||||
CONEXANT ACCESSRUNNER USB DRIVER
|
||||
M: Simon Arlott <cxacru@fire.lp0.eu>
|
||||
L: accessrunner-general@lists.sourceforge.net
|
||||
W: http://accessrunner.sourceforge.net/
|
||||
S: Maintained
|
||||
S: Orphan
|
||||
F: drivers/usb/atm/cxacru.c
|
||||
|
||||
CONFIGFS
|
||||
|
|
|
@ -486,7 +486,10 @@
|
|||
compatible = "fsl,imx27-usb";
|
||||
reg = <0x10024000 0x200>;
|
||||
interrupts = <56>;
|
||||
clocks = <&clks IMX27_CLK_USB_IPG_GATE>;
|
||||
clocks = <&clks IMX27_CLK_USB_IPG_GATE>,
|
||||
<&clks IMX27_CLK_USB_AHB_GATE>,
|
||||
<&clks IMX27_CLK_USB_DIV>;
|
||||
clock-names = "ipg", "ahb", "per";
|
||||
fsl,usbmisc = <&usbmisc 0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
@ -495,7 +498,10 @@
|
|||
compatible = "fsl,imx27-usb";
|
||||
reg = <0x10024200 0x200>;
|
||||
interrupts = <54>;
|
||||
clocks = <&clks IMX27_CLK_USB_IPG_GATE>;
|
||||
clocks = <&clks IMX27_CLK_USB_IPG_GATE>,
|
||||
<&clks IMX27_CLK_USB_AHB_GATE>,
|
||||
<&clks IMX27_CLK_USB_DIV>;
|
||||
clock-names = "ipg", "ahb", "per";
|
||||
fsl,usbmisc = <&usbmisc 1>;
|
||||
dr_mode = "host";
|
||||
status = "disabled";
|
||||
|
@ -505,7 +511,10 @@
|
|||
compatible = "fsl,imx27-usb";
|
||||
reg = <0x10024400 0x200>;
|
||||
interrupts = <55>;
|
||||
clocks = <&clks IMX27_CLK_USB_IPG_GATE>;
|
||||
clocks = <&clks IMX27_CLK_USB_IPG_GATE>,
|
||||
<&clks IMX27_CLK_USB_AHB_GATE>,
|
||||
<&clks IMX27_CLK_USB_DIV>;
|
||||
clock-names = "ipg", "ahb", "per";
|
||||
fsl,usbmisc = <&usbmisc 2>;
|
||||
dr_mode = "host";
|
||||
status = "disabled";
|
||||
|
@ -515,7 +524,6 @@
|
|||
#index-cells = <1>;
|
||||
compatible = "fsl,imx27-usbmisc";
|
||||
reg = <0x10024600 0x200>;
|
||||
clocks = <&clks IMX27_CLK_USB_AHB_GATE>;
|
||||
};
|
||||
|
||||
sahara2: sahara@10025000 {
|
||||
|
|
|
@ -84,6 +84,12 @@ struct ci_hdrc_imx_data {
|
|||
struct imx_usbmisc_data *usbmisc_data;
|
||||
bool supports_runtime_pm;
|
||||
bool in_lpm;
|
||||
/* SoC before i.mx6 (except imx23/imx28) needs three clks */
|
||||
bool need_three_clks;
|
||||
struct clk *clk_ipg;
|
||||
struct clk *clk_ahb;
|
||||
struct clk *clk_per;
|
||||
/* --------------------------------- */
|
||||
};
|
||||
|
||||
/* Common functions shared by usbmisc drivers */
|
||||
|
@ -135,6 +141,102 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
|
|||
}
|
||||
|
||||
/* End of common functions shared by usbmisc drivers*/
|
||||
static int imx_get_clks(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
data->clk_ipg = devm_clk_get(dev, "ipg");
|
||||
if (IS_ERR(data->clk_ipg)) {
|
||||
/* If the platform only needs one clocks */
|
||||
data->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(data->clk)) {
|
||||
ret = PTR_ERR(data->clk);
|
||||
dev_err(dev,
|
||||
"Failed to get clks, err=%ld,%ld\n",
|
||||
PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->clk_ahb = devm_clk_get(dev, "ahb");
|
||||
if (IS_ERR(data->clk_ahb)) {
|
||||
ret = PTR_ERR(data->clk_ahb);
|
||||
dev_err(dev,
|
||||
"Failed to get ahb clock, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->clk_per = devm_clk_get(dev, "per");
|
||||
if (IS_ERR(data->clk_per)) {
|
||||
ret = PTR_ERR(data->clk_per);
|
||||
dev_err(dev,
|
||||
"Failed to get per clock, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->need_three_clks = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_prepare_enable_clks(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (data->need_three_clks) {
|
||||
ret = clk_prepare_enable(data->clk_ipg);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to prepare/enable ipg clk, err=%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk_ahb);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to prepare/enable ahb clk, err=%d\n",
|
||||
ret);
|
||||
clk_disable_unprepare(data->clk_ipg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk_per);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to prepare/enable per clk, err=%d\n",
|
||||
ret);
|
||||
clk_disable_unprepare(data->clk_ahb);
|
||||
clk_disable_unprepare(data->clk_ipg);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to prepare/enable clk, err=%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void imx_disable_unprepare_clks(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (data->need_three_clks) {
|
||||
clk_disable_unprepare(data->clk_per);
|
||||
clk_disable_unprepare(data->clk_ahb);
|
||||
clk_disable_unprepare(data->clk_ipg);
|
||||
} else {
|
||||
clk_disable_unprepare(data->clk);
|
||||
}
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -145,31 +247,31 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
.flags = CI_HDRC_SET_NON_ZERO_TTHA,
|
||||
};
|
||||
int ret;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
|
||||
const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
|
||||
const struct of_device_id *of_id;
|
||||
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
|
||||
|
||||
of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
|
||||
if (!of_id)
|
||||
return -ENODEV;
|
||||
|
||||
imx_platform_flag = of_id->data;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
|
||||
if (IS_ERR(data->usbmisc_data))
|
||||
return PTR_ERR(data->usbmisc_data);
|
||||
|
||||
data->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(data->clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
|
||||
return PTR_ERR(data->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to prepare or enable clock, err=%d\n", ret);
|
||||
ret = imx_get_clks(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = imx_prepare_enable_clks(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
|
||||
if (IS_ERR(data->phy)) {
|
||||
|
@ -212,8 +314,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
goto disable_device;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
if (data->supports_runtime_pm) {
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
@ -226,7 +326,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
|||
disable_device:
|
||||
ci_hdrc_remove_device(data->ci_pdev);
|
||||
err_clk:
|
||||
clk_disable_unprepare(data->clk);
|
||||
imx_disable_unprepare_clks(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -240,7 +340,7 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
|
|||
pm_runtime_put_noidle(&pdev->dev);
|
||||
}
|
||||
ci_hdrc_remove_device(data->ci_pdev);
|
||||
clk_disable_unprepare(data->clk);
|
||||
imx_disable_unprepare_clks(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -252,7 +352,7 @@ static int imx_controller_suspend(struct device *dev)
|
|||
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
clk_disable_unprepare(data->clk);
|
||||
imx_disable_unprepare_clks(dev);
|
||||
data->in_lpm = true;
|
||||
|
||||
return 0;
|
||||
|
@ -270,7 +370,7 @@ static int imx_controller_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
ret = imx_prepare_enable_clks(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -285,7 +385,7 @@ static int imx_controller_resume(struct device *dev)
|
|||
return 0;
|
||||
|
||||
clk_disable:
|
||||
clk_disable_unprepare(data->clk);
|
||||
imx_disable_unprepare_clks(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -322,8 +322,10 @@ static ssize_t ci_role_write(struct file *file, const char __user *ubuf,
|
|||
return -EINVAL;
|
||||
|
||||
pm_runtime_get_sync(ci->dev);
|
||||
disable_irq(ci->irq);
|
||||
ci_role_stop(ci);
|
||||
ret = ci_role_start(ci, role);
|
||||
enable_irq(ci->irq);
|
||||
pm_runtime_put_sync(ci->dev);
|
||||
|
||||
return ret ? ret : count;
|
||||
|
|
|
@ -1751,6 +1751,22 @@ static int ci_udc_start(struct usb_gadget *gadget,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci)
|
||||
{
|
||||
if (!ci_otg_is_fsm_mode(ci))
|
||||
return;
|
||||
|
||||
mutex_lock(&ci->fsm.lock);
|
||||
if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
|
||||
ci->fsm.a_bidl_adis_tmout = 1;
|
||||
ci_hdrc_otg_fsm_start(ci);
|
||||
} else if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
|
||||
ci->fsm.protocol = PROTO_UNDEF;
|
||||
ci->fsm.otg->state = OTG_STATE_UNDEFINED;
|
||||
}
|
||||
mutex_unlock(&ci->fsm.lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* ci_udc_stop: unregister a gadget driver
|
||||
*/
|
||||
|
@ -1775,6 +1791,7 @@ static int ci_udc_stop(struct usb_gadget *gadget)
|
|||
ci->driver = NULL;
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
ci_udc_stop_for_otg_fsm(ci);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -500,7 +500,11 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct resource *res;
|
||||
struct imx_usbmisc *data;
|
||||
struct of_device_id *tmp_dev;
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
of_id = of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
|
||||
if (!of_id)
|
||||
return -ENODEV;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
|
@ -513,9 +517,7 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
tmp_dev = (struct of_device_id *)
|
||||
of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
|
||||
data->ops = (const struct usbmisc_ops *)tmp_dev->data;
|
||||
data->ops = (const struct usbmisc_ops *)of_id->data;
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -884,11 +884,11 @@ static int usblp_wwait(struct usblp *usblp, int nonblock)
|
|||
|
||||
add_wait_queue(&usblp->wwait, &waita);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (mutex_lock_interruptible(&usblp->mut)) {
|
||||
rc = -EINTR;
|
||||
break;
|
||||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
rc = usblp_wtest(usblp, nonblock);
|
||||
mutex_unlock(&usblp->mut);
|
||||
if (rc <= 0)
|
||||
|
|
|
@ -77,8 +77,7 @@ config USB_OTG_BLACKLIST_HUB
|
|||
|
||||
config USB_OTG_FSM
|
||||
tristate "USB 2.0 OTG FSM implementation"
|
||||
depends on USB
|
||||
select USB_OTG
|
||||
depends on USB && USB_OTG
|
||||
select USB_PHY
|
||||
help
|
||||
Implements OTG Finite State Machine as specified in On-The-Go
|
||||
|
|
|
@ -324,12 +324,13 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
|
|||
*/
|
||||
static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
if (hsotg->lx_state == DWC2_L2) {
|
||||
if (hsotg->bus_suspended) {
|
||||
hsotg->flags.b.port_suspend_change = 1;
|
||||
usb_hcd_resume_root_hub(hsotg->priv);
|
||||
} else {
|
||||
hsotg->flags.b.port_l1_change = 1;
|
||||
}
|
||||
|
||||
if (hsotg->lx_state == DWC2_L1)
|
||||
hsotg->flags.b.port_l1_change = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1428,8 +1429,8 @@ static void dwc2_wakeup_detected(unsigned long data)
|
|||
dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n",
|
||||
dwc2_readl(hsotg->regs + HPRT0));
|
||||
|
||||
hsotg->bus_suspended = 0;
|
||||
dwc2_hcd_rem_wakeup(hsotg);
|
||||
hsotg->bus_suspended = 0;
|
||||
|
||||
/* Change to L0 state */
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
|
|
|
@ -108,7 +108,8 @@ static const struct dwc2_core_params params_rk3066 = {
|
|||
.host_ls_low_power_phy_clk = -1,
|
||||
.ts_dline = -1,
|
||||
.reload_ctl = -1,
|
||||
.ahbcfg = 0x7, /* INCR16 */
|
||||
.ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
|
||||
GAHBCFG_HBSTLEN_SHIFT,
|
||||
.uframe_sched = -1,
|
||||
.external_id_pin_ctl = -1,
|
||||
.hibernation = -1,
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
#define PCI_DEVICE_ID_INTEL_BSW 0x22b7
|
||||
#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30
|
||||
#define PCI_DEVICE_ID_INTEL_SPTH 0xa130
|
||||
#define PCI_DEVICE_ID_INTEL_BXT 0x0aaa
|
||||
#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
|
||||
|
||||
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
|
||||
static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
|
||||
|
@ -210,6 +212,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTLP), },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTH), },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT), },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
|
||||
{ } /* Terminating Entry */
|
||||
};
|
||||
|
|
|
@ -2744,11 +2744,33 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
}
|
||||
|
||||
dwc->gadget.ops = &dwc3_gadget_ops;
|
||||
dwc->gadget.max_speed = USB_SPEED_SUPER;
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->gadget.sg_supported = true;
|
||||
dwc->gadget.name = "dwc3-gadget";
|
||||
|
||||
/*
|
||||
* FIXME We might be setting max_speed to <SUPER, however versions
|
||||
* <2.20a of dwc3 have an issue with metastability (documented
|
||||
* elsewhere in this driver) which tells us we can't set max speed to
|
||||
* anything lower than SUPER.
|
||||
*
|
||||
* Because gadget.max_speed is only used by composite.c and function
|
||||
* drivers (i.e. it won't go into dwc3's registers) we are allowing this
|
||||
* to happen so we avoid sending SuperSpeed Capability descriptor
|
||||
* together with our BOS descriptor as that could confuse host into
|
||||
* thinking we can handle super speed.
|
||||
*
|
||||
* Note that, in fact, we won't even support GetBOS requests when speed
|
||||
* is less than super speed because we don't have means, yet, to tell
|
||||
* composite.c that we are USB 2.0 + LPM ECN.
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_220A)
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"Changing max_speed on rev %08x\n",
|
||||
dwc->revision);
|
||||
|
||||
dwc->gadget.max_speed = dwc->maximum_speed;
|
||||
|
||||
/*
|
||||
* Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
|
||||
* on ep out.
|
||||
|
|
|
@ -329,7 +329,7 @@ static int alloc_requests(struct usb_composite_dev *cdev,
|
|||
for (i = 0; i < loop->qlen && result == 0; i++) {
|
||||
result = -ENOMEM;
|
||||
|
||||
in_req = usb_ep_alloc_request(loop->in_ep, GFP_KERNEL);
|
||||
in_req = usb_ep_alloc_request(loop->in_ep, GFP_ATOMIC);
|
||||
if (!in_req)
|
||||
goto fail;
|
||||
|
||||
|
|
|
@ -1633,7 +1633,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
|||
spin_lock(&udc->lock);
|
||||
|
||||
int_enb = usba_int_enb_get(udc);
|
||||
status = usba_readl(udc, INT_STA) & int_enb;
|
||||
status = usba_readl(udc, INT_STA) & (int_enb | USBA_HIGH_SPEED);
|
||||
DBG(DBG_INT, "irq, status=%#08x\n", status);
|
||||
|
||||
if (status & USBA_DET_SUSPEND) {
|
||||
|
|
|
@ -782,12 +782,15 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
|||
status |= USB_PORT_STAT_SUSPEND;
|
||||
}
|
||||
}
|
||||
if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0
|
||||
&& (raw_port_status & PORT_POWER)
|
||||
&& (bus_state->suspended_ports & (1 << wIndex))) {
|
||||
bus_state->suspended_ports &= ~(1 << wIndex);
|
||||
if (hcd->speed < HCD_USB3)
|
||||
bus_state->port_c_suspend |= 1 << wIndex;
|
||||
if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 &&
|
||||
(raw_port_status & PORT_POWER)) {
|
||||
if (bus_state->suspended_ports & (1 << wIndex)) {
|
||||
bus_state->suspended_ports &= ~(1 << wIndex);
|
||||
if (hcd->speed < HCD_USB3)
|
||||
bus_state->port_c_suspend |= 1 << wIndex;
|
||||
}
|
||||
bus_state->resume_done[wIndex] = 0;
|
||||
clear_bit(wIndex, &bus_state->resuming_ports);
|
||||
}
|
||||
if (raw_port_status & PORT_CONNECT) {
|
||||
status |= USB_PORT_STAT_CONNECTION;
|
||||
|
|
|
@ -3896,28 +3896,6 @@ cleanup:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ep_ring_is_processing(struct xhci_hcd *xhci,
|
||||
int slot_id, unsigned int ep_index)
|
||||
{
|
||||
struct xhci_virt_device *xdev;
|
||||
struct xhci_ring *ep_ring;
|
||||
struct xhci_ep_ctx *ep_ctx;
|
||||
struct xhci_virt_ep *xep;
|
||||
dma_addr_t hw_deq;
|
||||
|
||||
xdev = xhci->devs[slot_id];
|
||||
xep = &xhci->devs[slot_id]->eps[ep_index];
|
||||
ep_ring = xep->ring;
|
||||
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
|
||||
|
||||
if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) != EP_STATE_RUNNING)
|
||||
return 0;
|
||||
|
||||
hw_deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
|
||||
return (hw_deq !=
|
||||
xhci_trb_virt_to_dma(ep_ring->enq_seg, ep_ring->enqueue));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check transfer ring to guarantee there is enough room for the urb.
|
||||
* Update ISO URB start_frame and interval.
|
||||
|
@ -3983,10 +3961,12 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
}
|
||||
|
||||
/* Calculate the start frame and put it in urb->start_frame. */
|
||||
if (HCC_CFC(xhci->hcc_params) &&
|
||||
ep_ring_is_processing(xhci, slot_id, ep_index)) {
|
||||
urb->start_frame = xep->next_frame_id;
|
||||
goto skip_start_over;
|
||||
if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
|
||||
if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) ==
|
||||
EP_STATE_RUNNING) {
|
||||
urb->start_frame = xep->next_frame_id;
|
||||
goto skip_start_over;
|
||||
}
|
||||
}
|
||||
|
||||
start_frame = readl(&xhci->run_regs->microframe_index);
|
||||
|
|
|
@ -175,6 +175,16 @@ int xhci_reset(struct xhci_hcd *xhci)
|
|||
command |= CMD_RESET;
|
||||
writel(command, &xhci->op_regs->command);
|
||||
|
||||
/* Existing Intel xHCI controllers require a delay of 1 mS,
|
||||
* after setting the CMD_RESET bit, and before accessing any
|
||||
* HC registers. This allows the HC to complete the
|
||||
* reset operation and be ready for HC register access.
|
||||
* Without this delay, the subsequent HC register access,
|
||||
* may result in a system hang very rarely.
|
||||
*/
|
||||
if (xhci->quirks & XHCI_INTEL_HOST)
|
||||
udelay(1000);
|
||||
|
||||
ret = xhci_handshake(&xhci->op_regs->command,
|
||||
CMD_RESET, 0, 10 * 1000 * 1000);
|
||||
if (ret)
|
||||
|
|
|
@ -132,7 +132,7 @@ static inline struct musb *dev_to_musb(struct device *dev)
|
|||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CONFIG_BLACKFIN
|
||||
static int musb_ulpi_read(struct usb_phy *phy, u32 offset)
|
||||
static int musb_ulpi_read(struct usb_phy *phy, u32 reg)
|
||||
{
|
||||
void __iomem *addr = phy->io_priv;
|
||||
int i = 0;
|
||||
|
@ -151,7 +151,7 @@ static int musb_ulpi_read(struct usb_phy *phy, u32 offset)
|
|||
* ULPICarKitControlDisableUTMI after clearing POWER_SUSPENDM.
|
||||
*/
|
||||
|
||||
musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset);
|
||||
musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)reg);
|
||||
musb_writeb(addr, MUSB_ULPI_REG_CONTROL,
|
||||
MUSB_ULPI_REG_REQ | MUSB_ULPI_RDN_WR);
|
||||
|
||||
|
@ -176,7 +176,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data)
|
||||
static int musb_ulpi_write(struct usb_phy *phy, u32 val, u32 reg)
|
||||
{
|
||||
void __iomem *addr = phy->io_priv;
|
||||
int i = 0;
|
||||
|
@ -191,8 +191,8 @@ static int musb_ulpi_write(struct usb_phy *phy, u32 offset, u32 data)
|
|||
power &= ~MUSB_POWER_SUSPENDM;
|
||||
musb_writeb(addr, MUSB_POWER, power);
|
||||
|
||||
musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)offset);
|
||||
musb_writeb(addr, MUSB_ULPI_REG_DATA, (u8)data);
|
||||
musb_writeb(addr, MUSB_ULPI_REG_ADDR, (u8)reg);
|
||||
musb_writeb(addr, MUSB_ULPI_REG_DATA, (u8)val);
|
||||
musb_writeb(addr, MUSB_ULPI_REG_CONTROL, MUSB_ULPI_REG_REQ);
|
||||
|
||||
while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL)
|
||||
|
@ -1668,7 +1668,7 @@ EXPORT_SYMBOL_GPL(musb_interrupt);
|
|||
static bool use_dma = 1;
|
||||
|
||||
/* "modprobe ... use_dma=0" etc */
|
||||
module_param(use_dma, bool, 0);
|
||||
module_param(use_dma, bool, 0644);
|
||||
MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
|
||||
|
||||
void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
|
||||
|
|
|
@ -112,22 +112,32 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
|
|||
struct musb *musb = ep->musb;
|
||||
void __iomem *epio = ep->regs;
|
||||
u16 csr;
|
||||
u16 lastcsr = 0;
|
||||
int retries = 1000;
|
||||
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
while (csr & MUSB_TXCSR_FIFONOTEMPTY) {
|
||||
if (csr != lastcsr)
|
||||
dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
|
||||
lastcsr = csr;
|
||||
csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_TXPKTRDY;
|
||||
musb_writew(epio, MUSB_TXCSR, csr);
|
||||
csr = musb_readw(epio, MUSB_TXCSR);
|
||||
if (WARN(retries-- < 1,
|
||||
|
||||
/*
|
||||
* FIXME: sometimes the tx fifo flush failed, it has been
|
||||
* observed during device disconnect on AM335x.
|
||||
*
|
||||
* To reproduce the issue, ensure tx urb(s) are queued when
|
||||
* unplug the usb device which is connected to AM335x usb
|
||||
* host port.
|
||||
*
|
||||
* I found using a usb-ethernet device and running iperf
|
||||
* (client on AM335x) has very high chance to trigger it.
|
||||
*
|
||||
* Better to turn on dev_dbg() in musb_cleanup_urb() with
|
||||
* CPPI enabled to see the issue when aborting the tx channel.
|
||||
*/
|
||||
if (dev_WARN_ONCE(musb->controller, retries-- < 1,
|
||||
"Could not flush host TX%d fifo: csr: %04x\n",
|
||||
ep->epnum, csr))
|
||||
return;
|
||||
mdelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ config AB8500_USB
|
|||
config FSL_USB2_OTG
|
||||
bool "Freescale USB OTG Transceiver Driver"
|
||||
depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
|
||||
select USB_OTG
|
||||
select USB_PHY
|
||||
help
|
||||
Enable this to support Freescale USB OTG transceiver.
|
||||
|
@ -168,8 +167,7 @@ config USB_QCOM_8X16_PHY
|
|||
|
||||
config USB_MV_OTG
|
||||
tristate "Marvell USB OTG support"
|
||||
depends on USB_EHCI_MV && USB_MV_UDC && PM
|
||||
select USB_OTG
|
||||
depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG
|
||||
select USB_PHY
|
||||
help
|
||||
Say Y here if you want to build Marvell USB OTG transciever
|
||||
|
|
|
@ -452,10 +452,13 @@ static int mxs_phy_probe(struct platform_device *pdev)
|
|||
struct clk *clk;
|
||||
struct mxs_phy *mxs_phy;
|
||||
int ret;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxs_phy_dt_ids, &pdev->dev);
|
||||
const struct of_device_id *of_id;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev);
|
||||
if (!of_id)
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
|
|
|
@ -105,7 +105,6 @@ static int omap_otg_probe(struct platform_device *pdev)
|
|||
extcon = extcon_get_extcon_dev(config->extcon);
|
||||
if (!extcon)
|
||||
return -EPROBE_DEFER;
|
||||
otg_dev->extcon = extcon;
|
||||
|
||||
otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
|
||||
if (!otg_dev)
|
||||
|
@ -115,6 +114,7 @@ static int omap_otg_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(otg_dev->base))
|
||||
return PTR_ERR(otg_dev->base);
|
||||
|
||||
otg_dev->extcon = extcon;
|
||||
otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
|
||||
otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
|
||||
|
||||
|
|
|
@ -161,6 +161,7 @@ static void option_instat_callback(struct urb *urb);
|
|||
#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED 0x9001
|
||||
#define NOVATELWIRELESS_PRODUCT_E362 0x9010
|
||||
#define NOVATELWIRELESS_PRODUCT_E371 0x9011
|
||||
#define NOVATELWIRELESS_PRODUCT_U620L 0x9022
|
||||
#define NOVATELWIRELESS_PRODUCT_G2 0xA010
|
||||
#define NOVATELWIRELESS_PRODUCT_MC551 0xB001
|
||||
|
||||
|
@ -354,6 +355,7 @@ static void option_instat_callback(struct urb *urb);
|
|||
/* This is the 4G XS Stick W14 a.k.a. Mobilcom Debitel Surf-Stick *
|
||||
* It seems to contain a Qualcomm QSC6240/6290 chipset */
|
||||
#define FOUR_G_SYSTEMS_PRODUCT_W14 0x9603
|
||||
#define FOUR_G_SYSTEMS_PRODUCT_W100 0x9b01
|
||||
|
||||
/* iBall 3.5G connect wireless modem */
|
||||
#define IBALL_3_5G_CONNECT 0x9605
|
||||
|
@ -519,6 +521,11 @@ static const struct option_blacklist_info four_g_w14_blacklist = {
|
|||
.sendsetup = BIT(0) | BIT(1),
|
||||
};
|
||||
|
||||
static const struct option_blacklist_info four_g_w100_blacklist = {
|
||||
.sendsetup = BIT(1) | BIT(2),
|
||||
.reserved = BIT(3),
|
||||
};
|
||||
|
||||
static const struct option_blacklist_info alcatel_x200_blacklist = {
|
||||
.sendsetup = BIT(0) | BIT(1),
|
||||
.reserved = BIT(4),
|
||||
|
@ -1052,6 +1059,7 @@ static const struct usb_device_id option_ids[] = {
|
|||
{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC551, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E362, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E371, 0xff, 0xff, 0xff) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U620L, 0xff, 0x00, 0x00) },
|
||||
|
||||
{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) },
|
||||
{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) },
|
||||
|
@ -1641,6 +1649,9 @@ static const struct usb_device_id option_ids[] = {
|
|||
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14),
|
||||
.driver_info = (kernel_ulong_t)&four_g_w14_blacklist
|
||||
},
|
||||
{ USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100),
|
||||
.driver_info = (kernel_ulong_t)&four_g_w100_blacklist
|
||||
},
|
||||
{ USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) },
|
||||
{ USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) },
|
||||
{ USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) },
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#define DRIVER_AUTHOR "Qualcomm Inc"
|
||||
#define DRIVER_DESC "Qualcomm USB Serial driver"
|
||||
|
||||
#define QUECTEL_EC20_PID 0x9215
|
||||
|
||||
/* standard device layouts supported by this driver */
|
||||
enum qcserial_layouts {
|
||||
QCSERIAL_G2K = 0, /* Gobi 2000 */
|
||||
|
@ -171,6 +173,38 @@ static const struct usb_device_id id_table[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
static int handle_quectel_ec20(struct device *dev, int ifnum)
|
||||
{
|
||||
int altsetting = 0;
|
||||
|
||||
/*
|
||||
* Quectel EC20 Mini PCIe LTE module layout:
|
||||
* 0: DM/DIAG (use libqcdm from ModemManager for communication)
|
||||
* 1: NMEA
|
||||
* 2: AT-capable modem port
|
||||
* 3: Modem interface
|
||||
* 4: NDIS
|
||||
*/
|
||||
switch (ifnum) {
|
||||
case 0:
|
||||
dev_dbg(dev, "Quectel EC20 DM/DIAG interface found\n");
|
||||
break;
|
||||
case 1:
|
||||
dev_dbg(dev, "Quectel EC20 NMEA GPS interface found\n");
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
dev_dbg(dev, "Quectel EC20 Modem port found\n");
|
||||
break;
|
||||
case 4:
|
||||
/* Don't claim the QMI/net interface */
|
||||
altsetting = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return altsetting;
|
||||
}
|
||||
|
||||
static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_host_interface *intf = serial->interface->cur_altsetting;
|
||||
|
@ -181,6 +215,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
|
|||
int altsetting = -1;
|
||||
bool sendsetup = false;
|
||||
|
||||
/* we only support vendor specific functions */
|
||||
if (intf->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
|
||||
goto done;
|
||||
|
||||
nintf = serial->dev->actconfig->desc.bNumInterfaces;
|
||||
dev_dbg(dev, "Num Interfaces = %d\n", nintf);
|
||||
ifnum = intf->desc.bInterfaceNumber;
|
||||
|
@ -240,6 +278,12 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
|
|||
altsetting = -1;
|
||||
break;
|
||||
case QCSERIAL_G2K:
|
||||
/* handle non-standard layouts */
|
||||
if (nintf == 5 && id->idProduct == QUECTEL_EC20_PID) {
|
||||
altsetting = handle_quectel_ec20(dev, ifnum);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gobi 2K+ USB layout:
|
||||
* 0: QMI/net
|
||||
|
@ -301,29 +345,39 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
|
|||
break;
|
||||
case QCSERIAL_HWI:
|
||||
/*
|
||||
* Huawei layout:
|
||||
* 0: AT-capable modem port
|
||||
* 1: DM/DIAG
|
||||
* 2: AT-capable modem port
|
||||
* 3: CCID-compatible PCSC interface
|
||||
* 4: QMI/net
|
||||
* 5: NMEA
|
||||
* Huawei devices map functions by subclass + protocol
|
||||
* instead of interface numbers. The protocol identify
|
||||
* a specific function, while the subclass indicate a
|
||||
* specific firmware source
|
||||
*
|
||||
* This is a blacklist of functions known to be
|
||||
* non-serial. The rest are assumed to be serial and
|
||||
* will be handled by this driver
|
||||
*/
|
||||
switch (ifnum) {
|
||||
case 0:
|
||||
case 2:
|
||||
dev_dbg(dev, "Modem port found\n");
|
||||
break;
|
||||
case 1:
|
||||
dev_dbg(dev, "DM/DIAG interface found\n");
|
||||
break;
|
||||
case 5:
|
||||
dev_dbg(dev, "NMEA GPS interface found\n");
|
||||
break;
|
||||
default:
|
||||
/* don't claim any unsupported interface */
|
||||
switch (intf->desc.bInterfaceProtocol) {
|
||||
/* QMI combined (qmi_wwan) */
|
||||
case 0x07:
|
||||
case 0x37:
|
||||
case 0x67:
|
||||
/* QMI data (qmi_wwan) */
|
||||
case 0x08:
|
||||
case 0x38:
|
||||
case 0x68:
|
||||
/* QMI control (qmi_wwan) */
|
||||
case 0x09:
|
||||
case 0x39:
|
||||
case 0x69:
|
||||
/* NCM like (huawei_cdc_ncm) */
|
||||
case 0x16:
|
||||
case 0x46:
|
||||
case 0x76:
|
||||
altsetting = -1;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "Huawei type serial port found (%02x/%02x/%02x)\n",
|
||||
intf->desc.bInterfaceClass,
|
||||
intf->desc.bInterfaceSubClass,
|
||||
intf->desc.bInterfaceProtocol);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -159,6 +159,7 @@ static const struct usb_device_id ti_id_table_3410[] = {
|
|||
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STEREO_PLUG_ID) },
|
||||
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
|
||||
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
|
@ -191,6 +192,7 @@ static const struct usb_device_id ti_id_table_combined[] = {
|
|||
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) },
|
||||
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
|
||||
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
#define ABBOTT_PRODUCT_ID ABBOTT_STEREO_PLUG_ID
|
||||
#define ABBOTT_STRIP_PORT_ID 0x3420
|
||||
|
||||
/* Honeywell vendor and product IDs */
|
||||
#define HONEYWELL_VENDOR_ID 0x10ac
|
||||
#define HONEYWELL_HGI80_PRODUCT_ID 0x0102 /* Honeywell HGI80 */
|
||||
|
||||
/* Commands */
|
||||
#define TI_GET_VERSION 0x01
|
||||
#define TI_GET_PORT_STATUS 0x02
|
||||
|
|
Загрузка…
Ссылка в новой задаче