diff options
Diffstat (limited to 'drivers/usb/misc/isight_firmware.c')
-rw-r--r-- | drivers/usb/misc/isight_firmware.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/drivers/usb/misc/isight_firmware.c b/drivers/usb/misc/isight_firmware.c new file mode 100644 index 000000000000..9f30aa1f8a5d --- /dev/null +++ b/drivers/usb/misc/isight_firmware.c | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * Driver for loading USB isight firmware | ||
3 | * | ||
4 | * Copyright (C) 2008 Matthew Garrett <mjg@redhat.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the Free | ||
8 | * Software Foundation, version 2. | ||
9 | * | ||
10 | * The USB isight cameras in recent Apples are roughly compatible with the USB | ||
11 | * video class specification, and can be driven by uvcvideo. However, they | ||
12 | * need firmware to be loaded beforehand. After firmware loading, the device | ||
13 | * detaches from the USB bus and reattaches with a new device ID. It can then | ||
14 | * be claimed by the uvc driver. | ||
15 | * | ||
16 | * The firmware is non-free and must be extracted by the user. Tools to do this | ||
17 | * are available at http://bersace03.free.fr/ift/ | ||
18 | * | ||
19 | * The isight firmware loading was reverse engineered by Johannes Berg | ||
20 | * <johannes@sipsolutions.de>, and this driver is based on code by Ronald | ||
21 | * Bultje <rbultje@ronald.bitfreak.net> | ||
22 | */ | ||
23 | |||
24 | #include <linux/usb.h> | ||
25 | #include <linux/firmware.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/module.h> | ||
28 | |||
29 | static struct usb_device_id id_table[] = { | ||
30 | {USB_DEVICE(0x05ac, 0x8300)}, | ||
31 | {}, | ||
32 | }; | ||
33 | |||
34 | MODULE_DEVICE_TABLE(usb, id_table); | ||
35 | |||
36 | static int isight_firmware_load(struct usb_interface *intf, | ||
37 | const struct usb_device_id *id) | ||
38 | { | ||
39 | struct usb_device *dev = interface_to_usbdev(intf); | ||
40 | int llen, len, req, ret = 0; | ||
41 | const struct firmware *firmware; | ||
42 | unsigned char *buf = kmalloc(50, GFP_KERNEL); | ||
43 | unsigned char data[4]; | ||
44 | u8 *ptr; | ||
45 | |||
46 | if (!buf) | ||
47 | return -ENOMEM; | ||
48 | |||
49 | if (request_firmware(&firmware, "isight.fw", &dev->dev) != 0) { | ||
50 | printk(KERN_ERR "Unable to load isight firmware\n"); | ||
51 | return -ENODEV; | ||
52 | } | ||
53 | |||
54 | ptr = firmware->data; | ||
55 | |||
56 | if (usb_control_msg | ||
57 | (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\1", 1, | ||
58 | 300) != 1) { | ||
59 | printk(KERN_ERR | ||
60 | "Failed to initialise isight firmware loader\n"); | ||
61 | ret = -ENODEV; | ||
62 | goto out; | ||
63 | } | ||
64 | |||
65 | while (ptr+4 <= firmware->data+firmware->size) { | ||
66 | memcpy(data, ptr, 4); | ||
67 | len = (data[0] << 8 | data[1]); | ||
68 | req = (data[2] << 8 | data[3]); | ||
69 | ptr += 4; | ||
70 | |||
71 | if (len == 0x8001) | ||
72 | break; /* success */ | ||
73 | else if (len == 0) | ||
74 | continue; | ||
75 | |||
76 | for (; len > 0; req += 50) { | ||
77 | llen = min(len, 50); | ||
78 | len -= llen; | ||
79 | if (ptr+llen > firmware->data+firmware->size) { | ||
80 | printk(KERN_ERR | ||
81 | "Malformed isight firmware"); | ||
82 | ret = -ENODEV; | ||
83 | goto out; | ||
84 | } | ||
85 | memcpy(buf, ptr, llen); | ||
86 | |||
87 | ptr += llen; | ||
88 | |||
89 | if (usb_control_msg | ||
90 | (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, req, 0, | ||
91 | buf, llen, 300) != llen) { | ||
92 | printk(KERN_ERR | ||
93 | "Failed to load isight firmware\n"); | ||
94 | kfree(buf); | ||
95 | ret = -ENODEV; | ||
96 | goto out; | ||
97 | } | ||
98 | |||
99 | } | ||
100 | } | ||
101 | |||
102 | if (usb_control_msg | ||
103 | (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\0", 1, | ||
104 | 300) != 1) { | ||
105 | printk(KERN_ERR "isight firmware loading completion failed\n"); | ||
106 | ret = -ENODEV; | ||
107 | } | ||
108 | |||
109 | out: | ||
110 | kfree(buf); | ||
111 | release_firmware(firmware); | ||
112 | return ret; | ||
113 | } | ||
114 | |||
115 | static void isight_firmware_disconnect(struct usb_interface *intf) | ||
116 | { | ||
117 | } | ||
118 | |||
119 | static struct usb_driver isight_firmware_driver = { | ||
120 | .name = "isight_firmware", | ||
121 | .probe = isight_firmware_load, | ||
122 | .disconnect = isight_firmware_disconnect, | ||
123 | .id_table = id_table, | ||
124 | }; | ||
125 | |||
126 | static int __init isight_firmware_init(void) | ||
127 | { | ||
128 | return usb_register(&isight_firmware_driver); | ||
129 | } | ||
130 | |||
131 | static void __exit isight_firmware_exit(void) | ||
132 | { | ||
133 | usb_deregister(&isight_firmware_driver); | ||
134 | } | ||
135 | |||
136 | module_init(isight_firmware_init); | ||
137 | module_exit(isight_firmware_exit); | ||
138 | |||
139 | MODULE_LICENSE("GPL"); | ||
140 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | ||