You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: Revert "USB: EHCI: fix performance regression" USB: fsl_usb2_udc: fix recursive lock USB: usb-serial: option: Don't match Huawei driver CD images USB: pl2303: another product ID USB: add another scanner quirk USB: Add support for ROKR W5 in unusual_devs.h USB: Fix M600i unusual_devs entry USB: usb-storage: unusual_devs update for Cypress ATACB USB: EHCI: fix performance regression USB: EHCI: fix bug in Iso scheduling USB: EHCI: fix remote-wakeup regression USB: EHCI: suppress unwanted error messages USB: EHCI: fix up root-hub TT mess USB: add all configs to the "descriptors" attribute USB: fix possible deadlock involving sysfs attributes USB: Firmware loader driver for USB Apple iSight camera USB: FTDI_SIO : Add support for Matrix Orbital PID Range
This commit is contained in:
@@ -155,9 +155,6 @@ static int generic_probe(struct usb_device *udev)
|
||||
{
|
||||
int err, c;
|
||||
|
||||
/* put device-specific files into sysfs */
|
||||
usb_create_sysfs_dev_files(udev);
|
||||
|
||||
/* Choose and set the configuration. This registers the interfaces
|
||||
* with the driver core and lets interface drivers bind to them.
|
||||
*/
|
||||
@@ -189,8 +186,6 @@ static void generic_disconnect(struct usb_device *udev)
|
||||
* unconfigure the device */
|
||||
if (udev->actconfig)
|
||||
usb_set_configuration(udev, -1);
|
||||
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
@@ -213,6 +213,8 @@ struct hc_driver {
|
||||
|
||||
/* force handover of high-speed port to full-speed companion */
|
||||
void (*relinquish_port)(struct usb_hcd *, int);
|
||||
/* has a port been handed over to a companion? */
|
||||
int (*port_handed_over)(struct usb_hcd *, int);
|
||||
};
|
||||
|
||||
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||
|
||||
+14
-1
@@ -1326,6 +1326,12 @@ void usb_disconnect(struct usb_device **pdev)
|
||||
|
||||
usb_unlock_device(udev);
|
||||
|
||||
/* Remove the device-specific files from sysfs. This must be
|
||||
* done with udev unlocked, because some of the attribute
|
||||
* routines try to acquire the device lock.
|
||||
*/
|
||||
usb_remove_sysfs_dev_files(udev);
|
||||
|
||||
/* Unregister the device. The device driver is responsible
|
||||
* for removing the device files from usbfs and sysfs and for
|
||||
* de-configuring the device.
|
||||
@@ -1541,6 +1547,9 @@ int usb_new_device(struct usb_device *udev)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* put device-specific files into sysfs */
|
||||
usb_create_sysfs_dev_files(udev);
|
||||
|
||||
/* Tell the world! */
|
||||
announce_device(udev);
|
||||
return err;
|
||||
@@ -2744,7 +2753,11 @@ loop:
|
||||
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
|
||||
break;
|
||||
}
|
||||
dev_err(hub_dev, "unable to enumerate USB device on port %d\n", port1);
|
||||
if (hub->hdev->parent ||
|
||||
!hcd->driver->port_handed_over ||
|
||||
!(hcd->driver->port_handed_over)(hcd, port1))
|
||||
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
|
||||
port1);
|
||||
|
||||
done:
|
||||
hub_port_disable(hub, port1, 1);
|
||||
|
||||
@@ -47,6 +47,10 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* Edirol SD-20 */
|
||||
{ USB_DEVICE(0x0582, 0x0027), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
/* Avision AV600U */
|
||||
{ USB_DEVICE(0x0638, 0x0a13), .driver_info =
|
||||
USB_QUIRK_STRING_FETCH_255 },
|
||||
|
||||
/* M-Systems Flash Disk Pioneers */
|
||||
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
|
||||
|
||||
|
||||
+21
-23
@@ -588,35 +588,33 @@ read_descriptors(struct kobject *kobj, struct bin_attribute *attr,
|
||||
container_of(kobj, struct device, kobj));
|
||||
size_t nleft = count;
|
||||
size_t srclen, n;
|
||||
int cfgno;
|
||||
void *src;
|
||||
|
||||
usb_lock_device(udev);
|
||||
|
||||
/* The binary attribute begins with the device descriptor */
|
||||
srclen = sizeof(struct usb_device_descriptor);
|
||||
if (off < srclen) {
|
||||
n = min_t(size_t, nleft, srclen - off);
|
||||
memcpy(buf, off + (char *) &udev->descriptor, n);
|
||||
nleft -= n;
|
||||
buf += n;
|
||||
off = 0;
|
||||
} else {
|
||||
off -= srclen;
|
||||
}
|
||||
|
||||
/* Then follows the raw descriptor entry for the current
|
||||
* configuration (config plus subsidiary descriptors).
|
||||
/* The binary attribute begins with the device descriptor.
|
||||
* Following that are the raw descriptor entries for all the
|
||||
* configurations (config plus subsidiary descriptors).
|
||||
*/
|
||||
if (udev->actconfig) {
|
||||
int cfgno = udev->actconfig - udev->config;
|
||||
|
||||
srclen = __le16_to_cpu(udev->actconfig->desc.wTotalLength);
|
||||
for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
|
||||
nleft > 0; ++cfgno) {
|
||||
if (cfgno < 0) {
|
||||
src = &udev->descriptor;
|
||||
srclen = sizeof(struct usb_device_descriptor);
|
||||
} else {
|
||||
src = udev->rawdescriptors[cfgno];
|
||||
srclen = __le16_to_cpu(udev->config[cfgno].desc.
|
||||
wTotalLength);
|
||||
}
|
||||
if (off < srclen) {
|
||||
n = min_t(size_t, nleft, srclen - off);
|
||||
memcpy(buf, off + udev->rawdescriptors[cfgno], n);
|
||||
n = min(nleft, srclen - (size_t) off);
|
||||
memcpy(buf, src + off, n);
|
||||
nleft -= n;
|
||||
buf += n;
|
||||
off = 0;
|
||||
} else {
|
||||
off -= srclen;
|
||||
}
|
||||
}
|
||||
usb_unlock_device(udev);
|
||||
return count - nleft;
|
||||
}
|
||||
|
||||
|
||||
@@ -1627,7 +1627,9 @@ static int reset_queues(struct fsl_udc *udc)
|
||||
udc_reset_ep_queue(udc, pipe);
|
||||
|
||||
/* report disconnect; the driver is already quiesced */
|
||||
spin_unlock(&udc->lock);
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -223,6 +223,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@@ -269,7 +269,7 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
ehci->is_tdi_rh_tt = 1;
|
||||
hcd->has_tt = 1;
|
||||
|
||||
ehci->sbrn = 0x20;
|
||||
|
||||
@@ -295,10 +295,6 @@ static const struct hc_driver ehci_fsl_hc_driver = {
|
||||
*/
|
||||
.reset = ehci_fsl_setup,
|
||||
.start = ehci_run,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ehci_bus_suspend,
|
||||
.resume = ehci_bus_resume,
|
||||
#endif
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
@@ -322,6 +318,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
};
|
||||
|
||||
static int ehci_fsl_drv_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -609,7 +609,7 @@ static int ehci_hub_control (
|
||||
}
|
||||
break;
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
/* we auto-clear this feature */
|
||||
clear_bit(wIndex, &ehci->port_c_suspend);
|
||||
break;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
if (HCS_PPC (ehci->hcs_params))
|
||||
@@ -688,7 +688,7 @@ static int ehci_hub_control (
|
||||
/* resume completed? */
|
||||
else if (time_after_eq(jiffies,
|
||||
ehci->reset_done[wIndex])) {
|
||||
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
||||
set_bit(wIndex, &ehci->port_c_suspend);
|
||||
ehci->reset_done[wIndex] = 0;
|
||||
|
||||
/* stop resume signaling */
|
||||
@@ -765,6 +765,8 @@ static int ehci_hub_control (
|
||||
status |= 1 << USB_PORT_FEAT_RESET;
|
||||
if (temp & PORT_POWER)
|
||||
status |= 1 << USB_PORT_FEAT_POWER;
|
||||
if (test_bit(wIndex, &ehci->port_c_suspend))
|
||||
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
|
||||
|
||||
#ifndef VERBOSE_DEBUG
|
||||
if (status & ~0xffff) /* only if wPortChange is interesting */
|
||||
@@ -875,3 +877,13 @@ static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
|
||||
set_owner(ehci, --portnum, PORT_OWNER);
|
||||
}
|
||||
|
||||
static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
u32 __iomem *reg;
|
||||
|
||||
if (ehci_is_TDI(ehci))
|
||||
return 0;
|
||||
reg = &ehci->regs->port_status[portnum - 1];
|
||||
return ehci_readl(ehci, reg) & PORT_OWNER;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ static int ixp4xx_ehci_init(struct usb_hcd *hcd)
|
||||
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
|
||||
ehci->is_tdi_rh_tt = 1;
|
||||
hcd->has_tt = 1;
|
||||
ehci_reset(ehci);
|
||||
|
||||
retval = ehci_init(hcd);
|
||||
@@ -58,6 +58,8 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = {
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
#endif
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
};
|
||||
|
||||
static int ixp4xx_ehci_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -139,10 +139,6 @@ static const struct hc_driver ehci_orion_hc_driver = {
|
||||
*/
|
||||
.reset = ehci_orion_setup,
|
||||
.start = ehci_run,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ehci_bus_suspend,
|
||||
.resume = ehci_bus_resume,
|
||||
#endif
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
@@ -165,6 +161,8 @@ static const struct hc_driver ehci_orion_hc_driver = {
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
};
|
||||
|
||||
static void __init
|
||||
@@ -250,7 +248,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
|
||||
ehci->regs = hcd->regs + 0x100 +
|
||||
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
ehci->is_tdi_rh_tt = 1;
|
||||
hcd->has_tt = 1;
|
||||
ehci->sbrn = 0x20;
|
||||
|
||||
/*
|
||||
|
||||
@@ -129,7 +129,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
|
||||
switch (pdev->vendor) {
|
||||
case PCI_VENDOR_ID_TDI:
|
||||
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
|
||||
ehci->is_tdi_rh_tt = 1;
|
||||
hcd->has_tt = 1;
|
||||
tdi_reset(ehci);
|
||||
}
|
||||
@@ -379,7 +378,8 @@ static const struct hc_driver ehci_pci_hc_driver = {
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@@ -76,6 +76,8 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
#endif
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ static const struct hc_driver ehci_ppc_soc_hc_driver = {
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
};
|
||||
|
||||
static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -73,6 +73,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
|
||||
.bus_resume = ehci_bus_resume,
|
||||
#endif
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
};
|
||||
|
||||
static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
|
||||
|
||||
@@ -1349,18 +1349,27 @@ iso_stream_schedule (
|
||||
/* when's the last uframe this urb could start? */
|
||||
max = now + mod;
|
||||
|
||||
/* typical case: reuse current schedule. stream is still active,
|
||||
* and no gaps from host falling behind (irq delays etc)
|
||||
/* Typical case: reuse current schedule, stream is still active.
|
||||
* Hopefully there are no gaps from the host falling behind
|
||||
* (irq delays etc), but if there are we'll take the next
|
||||
* slot in the schedule, implicitly assuming URB_ISO_ASAP.
|
||||
*/
|
||||
if (likely (!list_empty (&stream->td_list))) {
|
||||
start = stream->next_uframe;
|
||||
if (start < now)
|
||||
start += mod;
|
||||
if (likely ((start + sched->span) < max))
|
||||
goto ready;
|
||||
/* else fell behind; someday, try to reschedule */
|
||||
status = -EL2NSYNC;
|
||||
goto fail;
|
||||
|
||||
/* Fell behind (by up to twice the slop amount)? */
|
||||
if (start >= max - 2 * 8 * SCHEDULE_SLOP)
|
||||
start += stream->interval * DIV_ROUND_UP(
|
||||
max - start, stream->interval) - mod;
|
||||
|
||||
/* Tried to schedule too far into the future? */
|
||||
if (unlikely((start + sched->span) >= max)) {
|
||||
status = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
goto ready;
|
||||
}
|
||||
|
||||
/* need to schedule; when's the next (u)frame we could start?
|
||||
@@ -1613,6 +1622,9 @@ itd_complete (
|
||||
} else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
|
||||
desc->status = 0;
|
||||
desc->actual_length = EHCI_ITD_LENGTH (t);
|
||||
} else {
|
||||
/* URB was too late */
|
||||
desc->status = -EXDEV;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2095,7 +2107,7 @@ done:
|
||||
static void
|
||||
scan_periodic (struct ehci_hcd *ehci)
|
||||
{
|
||||
unsigned frame, clock, now_uframe, mod;
|
||||
unsigned now_uframe, frame, clock, clock_frame, mod;
|
||||
unsigned modified;
|
||||
|
||||
mod = ehci->periodic_size << 3;
|
||||
@@ -2111,6 +2123,7 @@ scan_periodic (struct ehci_hcd *ehci)
|
||||
else
|
||||
clock = now_uframe + mod - 1;
|
||||
clock %= mod;
|
||||
clock_frame = clock >> 3;
|
||||
|
||||
for (;;) {
|
||||
union ehci_shadow q, *q_p;
|
||||
@@ -2157,22 +2170,26 @@ restart:
|
||||
case Q_TYPE_ITD:
|
||||
/* If this ITD is still active, leave it for
|
||||
* later processing ... check the next entry.
|
||||
* No need to check for activity unless the
|
||||
* frame is current.
|
||||
*/
|
||||
rmb ();
|
||||
for (uf = 0; uf < 8 && live; uf++) {
|
||||
if (0 == (q.itd->hw_transaction [uf]
|
||||
& ITD_ACTIVE(ehci)))
|
||||
continue;
|
||||
incomplete = true;
|
||||
q_p = &q.itd->itd_next;
|
||||
hw_p = &q.itd->hw_next;
|
||||
type = Q_NEXT_TYPE(ehci,
|
||||
if (frame == clock_frame && live) {
|
||||
rmb();
|
||||
for (uf = 0; uf < 8; uf++) {
|
||||
if (q.itd->hw_transaction[uf] &
|
||||
ITD_ACTIVE(ehci))
|
||||
break;
|
||||
}
|
||||
if (uf < 8) {
|
||||
incomplete = true;
|
||||
q_p = &q.itd->itd_next;
|
||||
hw_p = &q.itd->hw_next;
|
||||
type = Q_NEXT_TYPE(ehci,
|
||||
q.itd->hw_next);
|
||||
q = *q_p;
|
||||
break;
|
||||
q = *q_p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (uf < 8 && live)
|
||||
break;
|
||||
|
||||
/* Take finished ITDs out of the schedule
|
||||
* and process them: recycle, maybe report
|
||||
@@ -2189,9 +2206,12 @@ restart:
|
||||
case Q_TYPE_SITD:
|
||||
/* If this SITD is still active, leave it for
|
||||
* later processing ... check the next entry.
|
||||
* No need to check for activity unless the
|
||||
* frame is current.
|
||||
*/
|
||||
if ((q.sitd->hw_results & SITD_ACTIVE(ehci))
|
||||
&& live) {
|
||||
if (frame == clock_frame && live &&
|
||||
(q.sitd->hw_results &
|
||||
SITD_ACTIVE(ehci))) {
|
||||
incomplete = true;
|
||||
q_p = &q.sitd->sitd_next;
|
||||
hw_p = &q.sitd->hw_next;
|
||||
@@ -2260,6 +2280,7 @@ restart:
|
||||
|
||||
/* rescan the rest of this frame, then ... */
|
||||
clock = now;
|
||||
clock_frame = clock >> 3;
|
||||
} else {
|
||||
now_uframe++;
|
||||
now_uframe %= mod;
|
||||
|
||||
@@ -97,6 +97,8 @@ struct ehci_hcd { /* one per controller */
|
||||
dedicated to the companion controller */
|
||||
unsigned long owned_ports; /* which ports are
|
||||
owned by the companion during a bus suspend */
|
||||
unsigned long port_c_suspend; /* which ports have
|
||||
the change-suspend feature turned on */
|
||||
|
||||
/* per-HC memory pools (could be per-bus, but ...) */
|
||||
struct dma_pool *qh_pool; /* qh per active urb */
|
||||
@@ -112,7 +114,6 @@ struct ehci_hcd { /* one per controller */
|
||||
u32 command;
|
||||
|
||||
/* SILICON QUIRKS */
|
||||
unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */
|
||||
unsigned no_selective_suspend:1;
|
||||
unsigned has_fsl_port_bug:1; /* FreeScale */
|
||||
unsigned big_endian_mmio:1;
|
||||
@@ -678,7 +679,7 @@ struct ehci_fstn {
|
||||
* needed (mostly in root hub code).
|
||||
*/
|
||||
|
||||
#define ehci_is_TDI(e) ((e)->is_tdi_rh_tt)
|
||||
#define ehci_is_TDI(e) (ehci_to_hcd(e)->has_tt)
|
||||
|
||||
/* Returns the speed of a device attached to a port on the root hub. */
|
||||
static inline unsigned int
|
||||
|
||||
@@ -269,3 +269,14 @@ config USB_TEST
|
||||
See <http://www.linux-usb.org/usbtest/> for more information,
|
||||
including sample test device firmware and "how to use it".
|
||||
|
||||
config USB_ISIGHTFW
|
||||
tristate "iSight firmware loading support"
|
||||
depends on USB
|
||||
help
|
||||
This driver loads firmware for USB Apple iSight cameras, allowing
|
||||
them to be driven by the USB video class driver available at
|
||||
http://linux-uvc.berlios.de
|
||||
|
||||
The firmware for this driver must be extracted from the MacOS
|
||||
driver beforehand. Tools for doing so are available at
|
||||
http://bersace03.free.fr
|
||||
|
||||
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_EMI62) += emi62.o
|
||||
obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
|
||||
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
|
||||
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
|
||||
obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o
|
||||
obj-$(CONFIG_USB_LCD) += usblcd.o
|
||||
obj-$(CONFIG_USB_LD) += ldusb.o
|
||||
obj-$(CONFIG_USB_LED) += usbled.o
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Driver for loading USB isight firmware
|
||||
*
|
||||
* Copyright (C) 2008 Matthew Garrett <mjg@redhat.com>
|
||||
*
|
||||
* 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, version 2.
|
||||
*
|
||||
* The USB isight cameras in recent Apples are roughly compatible with the USB
|
||||
* video class specification, and can be driven by uvcvideo. However, they
|
||||
* need firmware to be loaded beforehand. After firmware loading, the device
|
||||
* detaches from the USB bus and reattaches with a new device ID. It can then
|
||||
* be claimed by the uvc driver.
|
||||
*
|
||||
* The firmware is non-free and must be extracted by the user. Tools to do this
|
||||
* are available at http://bersace03.free.fr/ift/
|
||||
*
|
||||
* The isight firmware loading was reverse engineered by Johannes Berg
|
||||
* <johannes@sipsolutions.de>, and this driver is based on code by Ronald
|
||||
* Bultje <rbultje@ronald.bitfreak.net>
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static struct usb_device_id id_table[] = {
|
||||
{USB_DEVICE(0x05ac, 0x8300)},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
static int isight_firmware_load(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
int llen, len, req, ret = 0;
|
||||
const struct firmware *firmware;
|
||||
unsigned char *buf;
|
||||
unsigned char data[4];
|
||||
char *ptr;
|
||||
|
||||
if (request_firmware(&firmware, "isight.fw", &dev->dev) != 0) {
|
||||
printk(KERN_ERR "Unable to load isight firmware\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ptr = firmware->data;
|
||||
|
||||
if (usb_control_msg
|
||||
(dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\1", 1,
|
||||
300) != 1) {
|
||||
printk(KERN_ERR
|
||||
"Failed to initialise isight firmware loader\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
memcpy(data, ptr, 4);
|
||||
len = (data[0] << 8 | data[1]);
|
||||
req = (data[2] << 8 | data[3]);
|
||||
ptr += 4;
|
||||
|
||||
if (len == 0x8001)
|
||||
break; /* success */
|
||||
else if (len == 0)
|
||||
continue;
|
||||
|
||||
for (; len > 0; req += 50) {
|
||||
llen = len > 50 ? 50 : len;
|
||||
len -= llen;
|
||||
|
||||
buf = kmalloc(llen, GFP_KERNEL);
|
||||
memcpy(buf, ptr, llen);
|
||||
|
||||
ptr += llen;
|
||||
|
||||
if (usb_control_msg
|
||||
(dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, req, 0,
|
||||
buf, llen, 300) != llen) {
|
||||
printk(KERN_ERR
|
||||
"Failed to load isight firmware\n");
|
||||
kfree(buf);
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
}
|
||||
if (usb_control_msg
|
||||
(dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\0", 1,
|
||||
300) != 1) {
|
||||
printk(KERN_ERR "isight firmware loading completion failed\n");
|
||||
ret = -ENODEV;
|
||||
}
|
||||
out:
|
||||
release_firmware(firmware);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void isight_firmware_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
}
|
||||
|
||||
static struct usb_driver isight_firmware_driver = {
|
||||
.name = "isight_firmware",
|
||||
.probe = isight_firmware_load,
|
||||
.disconnect = isight_firmware_disconnect,
|
||||
.id_table = id_table,
|
||||
};
|
||||
|
||||
static int __init isight_firmware_init(void)
|
||||
{
|
||||
return usb_register(&isight_firmware_driver);
|
||||
}
|
||||
|
||||
static void __exit isight_firmware_exit(void)
|
||||
{
|
||||
usb_deregister(&isight_firmware_driver);
|
||||
}
|
||||
|
||||
module_init(isight_firmware_init);
|
||||
module_exit(isight_firmware_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user