USB: Firmware loader driver for USB Apple iSight camera

Uninitialised Apple iSight drivers present with a distinctive USB ID.
Once firmware has been uploaded, they disconnect and reconnect with a
new ID. At this point they can be driven by the uvcvideo driver. As this
is unique to the Apple cameras and not functionality shared by any other
UVC devices, it makes sense to provide the firmware loading
functionality in a separate driver. This driver will read an isight.fw
file extracted from the Apple driver using the tools at
http://bersace03.free.fr/ift/ and upload it to the camera. It will also
handle the case where the device loses its firmware during hibernation
and must have it reloaded.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Matthew Garrett 2008-05-20 20:06:28 +01:00 committed by Greg Kroah-Hartman
parent ebb3770c01
commit 62d104d0de
3 changed files with 143 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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>");