summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/sleep/main.c5
-rw-r--r--drivers/acpi/sleep/proc.c5
-rw-r--r--drivers/ata/ahci.c23
-rw-r--r--drivers/ata/libata-sff.c30
-rw-r--r--drivers/ata/sata_mv.c2
-rw-r--r--drivers/ata/sata_sil24.c1
-rw-r--r--drivers/ata/sata_uli.c1
-rw-r--r--drivers/auxdisplay/Kconfig2
-rw-r--r--drivers/auxdisplay/cfag12864b.c4
-rw-r--r--drivers/auxdisplay/cfag12864bfb.c4
-rw-r--r--drivers/auxdisplay/ks0108.c4
-rw-r--r--drivers/base/node.c4
-rw-r--r--drivers/base/power/trace.c2
-rw-r--r--drivers/block/cciss.c70
-rw-r--r--drivers/char/agp/amd64-agp.c85
-rw-r--r--drivers/char/drm/i915_irq.c4
-rw-r--r--drivers/char/hvc_xen.c61
-rw-r--r--drivers/char/tty_io.c2
-rw-r--r--drivers/connector/connector.c40
-rw-r--r--drivers/gpio/Kconfig14
-rw-r--r--drivers/gpio/pca953x.c2
-rw-r--r--drivers/hwmon/hdaps.c6
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c28
-rw-r--r--drivers/ide/Kconfig7
-rw-r--r--drivers/ide/arm/Makefile1
-rw-r--r--drivers/ide/arm/bast-ide.c90
-rw-r--r--drivers/ide/ide-probe.c2
-rw-r--r--drivers/ide/ide-proc.c2
-rw-r--r--drivers/ide/ide.c24
-rw-r--r--drivers/input/ff-core.c1
-rw-r--r--drivers/input/xen-kbdfront.c20
-rw-r--r--drivers/lguest/lg.h1
-rw-r--r--drivers/md/dm-crypt.c1
-rw-r--r--drivers/md/md.c6
-rw-r--r--drivers/md/raid10.c2
-rw-r--r--drivers/md/raid5.c7
-rw-r--r--drivers/media/video/Kconfig8
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/uvc/Makefile3
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c1256
-rw-r--r--drivers/media/video/uvc/uvc_driver.c1955
-rw-r--r--drivers/media/video/uvc/uvc_isight.c134
-rw-r--r--drivers/media/video/uvc/uvc_queue.c477
-rw-r--r--drivers/media/video/uvc/uvc_status.c207
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c1105
-rw-r--r--drivers/media/video/uvc/uvc_video.c934
-rw-r--r--drivers/media/video/uvc/uvcvideo.h796
-rw-r--r--drivers/mmc/host/pxamci.c13
-rw-r--r--drivers/mmc/host/sdhci.c34
-rw-r--r--drivers/net/3c59x.c5
-rw-r--r--drivers/net/e100.c2
-rw-r--r--drivers/net/e1000/e1000_ethtool.c2
-rw-r--r--drivers/net/e1000e/netdev.c3
-rw-r--r--drivers/net/hamradio/dmascc.c2
-rw-r--r--drivers/net/igb/igb_main.c3
-rw-r--r--drivers/net/ipg.c16
-rw-r--r--drivers/net/ixgbe/ixgbe_main.c3
-rw-r--r--drivers/net/netxen/netxen_nic_main.c18
-rw-r--r--drivers/net/pcmcia/axnet_cs.c2
-rw-r--r--drivers/net/pcmcia/pcnet_cs.c3
-rw-r--r--drivers/net/qla3xxx.c2
-rw-r--r--drivers/net/r6040.c2
-rw-r--r--drivers/net/s2io.c35
-rw-r--r--drivers/net/s2io.h4
-rw-r--r--drivers/net/tc35815.c4
-rw-r--r--drivers/net/wan/x25_asy.c3
-rw-r--r--drivers/net/wireless/b43/leds.c3
-rw-r--r--drivers/net/wireless/b43/main.c12
-rw-r--r--drivers/net/wireless/b43legacy/dma.c2
-rw-r--r--drivers/net/wireless/b43legacy/main.c6
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_rx.c8
-rw-r--r--drivers/net/wireless/hostap/hostap_ap.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_cs.c8
-rw-r--r--drivers/net/wireless/hostap/hostap_hw.c10
-rw-r--r--drivers/net/wireless/hostap/hostap_main.c5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c33
-rw-r--r--drivers/net/wireless/iwlwifi/iwl4965-base.c39
-rw-r--r--drivers/net/wireless/prism54/islpci_eth.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c36
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h1
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c38
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c4
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c36
-rw-r--r--drivers/pci/access.c14
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c17
-rw-r--r--drivers/pci/pci-sysfs.c4
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--drivers/pci/quirks.c43
-rw-r--r--drivers/rtc/interface.c102
-rw-r--r--drivers/rtc/rtc-sa1100.c4
-rw-r--r--drivers/rtc/rtc-x1205.c111
-rw-r--r--drivers/scsi/esp_scsi.c22
-rw-r--r--drivers/scsi/ses.c2
-rw-r--r--drivers/serial/serial_core.c4
-rw-r--r--drivers/spi/spidev.c10
-rw-r--r--drivers/usb/class/cdc-acm.c3
-rw-r--r--drivers/usb/core/hcd.c47
-rw-r--r--drivers/usb/core/hcd.h2
-rw-r--r--drivers/usb/core/hub.c24
-rw-r--r--drivers/usb/host/ehci.h19
-rw-r--r--drivers/usb/host/ohci-at91.c1
-rw-r--r--drivers/usb/host/ohci-au1xxx.c1
-rw-r--r--drivers/usb/host/ohci-ep93xx.c1
-rw-r--r--drivers/usb/host/ohci-hcd.c15
-rw-r--r--drivers/usb/host/ohci-hub.c53
-rw-r--r--drivers/usb/host/ohci-lh7a404.c1
-rw-r--r--drivers/usb/host/ohci-omap.c1
-rw-r--r--drivers/usb/host/ohci-pci.c1
-rw-r--r--drivers/usb/host/ohci-pnx4008.c1
-rw-r--r--drivers/usb/host/ohci-pnx8550.c1
-rw-r--r--drivers/usb/host/ohci-ppc-of.c1
-rw-r--r--drivers/usb/host/ohci-ppc-soc.c1
-rw-r--r--drivers/usb/host/ohci-ps3.c1
-rw-r--r--drivers/usb/host/ohci-pxa27x.c1
-rw-r--r--drivers/usb/host/ohci-q.c12
-rw-r--r--drivers/usb/host/ohci-s3c2410.c1
-rw-r--r--drivers/usb/host/ohci-sa1111.c1
-rw-r--r--drivers/usb/host/ohci-sh.c1
-rw-r--r--drivers/usb/host/ohci-sm501.c1
-rw-r--r--drivers/usb/host/ohci-ssb.c1
-rw-r--r--drivers/usb/host/u132-hcd.c11
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c1
-rw-r--r--drivers/usb/serial/ftdi_sio.h3
-rw-r--r--drivers/usb/serial/ipaq.c7
-rw-r--r--drivers/usb/serial/option.c1
-rw-r--r--drivers/usb/serial/pl2303.c1
-rw-r--r--drivers/usb/serial/pl2303.h1
-rw-r--r--drivers/usb/storage/unusual_devs.h8
-rw-r--r--drivers/video/Kconfig3
-rw-r--r--drivers/video/fsl-diu-fb.c29
-rw-r--r--drivers/video/pxafb.c52
-rw-r--r--drivers/video/w100fb.c1
-rw-r--r--drivers/video/xen-fbfront.c211
-rw-r--r--drivers/xen/Makefile2
-rw-r--r--drivers/xen/balloon.c10
-rw-r--r--drivers/xen/events.c114
-rw-r--r--drivers/xen/grant-table.c4
-rw-r--r--drivers/xen/manage.c252
-rw-r--r--drivers/xen/xenbus/xenbus_comms.c23
140 files changed, 8565 insertions, 539 deletions
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index c3b0cd88d09f..495c63a3e0af 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -36,9 +36,8 @@ static int acpi_sleep_prepare(u32 acpi_state)
if (!acpi_wakeup_address) {
return -EFAULT;
}
- acpi_set_firmware_waking_vector((acpi_physical_address)
- virt_to_phys((void *)
- acpi_wakeup_address));
+ acpi_set_firmware_waking_vector(
+ (acpi_physical_address)acpi_wakeup_address);
}
ACPI_FLUSH_CPU_CACHE();
diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c
index 224c57c03381..4ebbba2b6b19 100644
--- a/drivers/acpi/sleep/proc.c
+++ b/drivers/acpi/sleep/proc.c
@@ -315,8 +315,11 @@ acpi_system_write_alarm(struct file *file,
cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control);
if (acpi_gbl_FADT.month_alarm)
cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control);
- if (acpi_gbl_FADT.century)
+ if (acpi_gbl_FADT.century) {
+ if (adjust)
+ yr += cmos_bcd_read(acpi_gbl_FADT.century, rtc_control) * 100;
cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control);
+ }
/* enable the rtc alarm interrupt */
rtc_control |= RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 6a4a2a25d97a..5e6468a7ca4b 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1777,7 +1777,7 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
struct ahci_host_priv *hpriv;
unsigned int i, handled = 0;
void __iomem *mmio;
- u32 irq_stat, irq_ack = 0;
+ u32 irq_stat, irq_masked;
VPRINTK("ENTER\n");
@@ -1786,16 +1786,17 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
/* sigh. 0xffffffff is a valid return from h/w */
irq_stat = readl(mmio + HOST_IRQ_STAT);
- irq_stat &= hpriv->port_map;
if (!irq_stat)
return IRQ_NONE;
+ irq_masked = irq_stat & hpriv->port_map;
+
spin_lock(&host->lock);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap;
- if (!(irq_stat & (1 << i)))
+ if (!(irq_masked & (1 << i)))
continue;
ap = host->ports[i];
@@ -1809,14 +1810,20 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance)
"interrupt on disabled port %u\n", i);
}
- irq_ack |= (1 << i);
- }
-
- if (irq_ack) {
- writel(irq_ack, mmio + HOST_IRQ_STAT);
handled = 1;
}
+ /* HOST_IRQ_STAT behaves as level triggered latch meaning that
+ * it should be cleared after all the port events are cleared;
+ * otherwise, it will raise a spurious interrupt after each
+ * valid one. Please read section 10.6.2 of ahci 1.1 for more
+ * information.
+ *
+ * Also, use the unmasked value to clear interrupt as spurious
+ * pending event on a dummy port might cause screaming IRQ.
+ */
+ writel(irq_stat, mmio + HOST_IRQ_STAT);
+
spin_unlock(&host->lock);
VPRINTK("EXIT\n");
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 215d18672a5a..c0908c225483 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -1094,6 +1094,7 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq)
int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
u8 status, int in_wq)
{
+ struct ata_eh_info *ehi = &ap->link.eh_info;
unsigned long flags = 0;
int poll_next;
@@ -1125,9 +1126,12 @@ fsm_start:
if (likely(status & (ATA_ERR | ATA_DF)))
/* device stops HSM for abort/error */
qc->err_mask |= AC_ERR_DEV;
- else
+ else {
/* HSM violation. Let EH handle this */
+ ata_ehi_push_desc(ehi,
+ "ST_FIRST: !(DRQ|ERR|DF)");
qc->err_mask |= AC_ERR_HSM;
+ }
ap->hsm_task_state = HSM_ST_ERR;
goto fsm_start;
@@ -1146,9 +1150,9 @@ fsm_start:
* the CDB.
*/
if (!(qc->dev->horkage & ATA_HORKAGE_STUCK_ERR)) {
- ata_port_printk(ap, KERN_WARNING,
- "DRQ=1 with device error, "
- "dev_stat 0x%X\n", status);
+ ata_ehi_push_desc(ehi, "ST_FIRST: "
+ "DRQ=1 with device error, "
+ "dev_stat 0x%X", status);
qc->err_mask |= AC_ERR_HSM;
ap->hsm_task_state = HSM_ST_ERR;
goto fsm_start;
@@ -1205,9 +1209,9 @@ fsm_start:
* let the EH abort the command or reset the device.
*/
if (unlikely(status & (ATA_ERR | ATA_DF))) {
- ata_port_printk(ap, KERN_WARNING, "DRQ=1 with "
- "device error, dev_stat 0x%X\n",
- status);
+ ata_ehi_push_desc(ehi, "ST-ATAPI: "
+ "DRQ=1 with device error, "
+ "dev_stat 0x%X", status);
qc->err_mask |= AC_ERR_HSM;
ap->hsm_task_state = HSM_ST_ERR;
goto fsm_start;
@@ -1226,13 +1230,17 @@ fsm_start:
if (likely(status & (ATA_ERR | ATA_DF)))
/* device stops HSM for abort/error */
qc->err_mask |= AC_ERR_DEV;
- else
+ else {
/* HSM violation. Let EH handle this.
* Phantom devices also trigger this
* condition. Mark hint.
*/
+ ata_ehi_push_desc(ehi, "ST-ATA: "
+ "DRQ=1 with device error, "
+ "dev_stat 0x%X", status);
qc->err_mask |= AC_ERR_HSM |
AC_ERR_NODEV_HINT;
+ }
ap->hsm_task_state = HSM_ST_ERR;
goto fsm_start;
@@ -1257,8 +1265,12 @@ fsm_start:
status = ata_wait_idle(ap);
}
- if (status & (ATA_BUSY | ATA_DRQ))
+ if (status & (ATA_BUSY | ATA_DRQ)) {
+ ata_ehi_push_desc(ehi, "ST-ATA: "
+ "BUSY|DRQ persists on ERR|DF, "
+ "dev_stat 0x%X", status);
qc->err_mask |= AC_ERR_HSM;
+ }
/* ata_pio_sectors() might change the
* state to HSM_ST_LAST. so, the state
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 28092bc50146..ad169ffbc4cb 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -1607,7 +1607,7 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc)
* Much of the time, this could just work regardless.
* So for now, just log the incident, and allow the attempt.
*/
- if (limit_warnings && (qc->nbytes / qc->sect_size) > 1) {
+ if (limit_warnings > 0 && (qc->nbytes / qc->sect_size) > 1) {
--limit_warnings;
ata_link_printk(qc->dev->link, KERN_WARNING, DRV_NAME
": attempting PIO w/multiple DRQ: "
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index 8ee6b5b4ede7..84ffcc26a74b 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -370,6 +370,7 @@ static const struct pci_device_id sil24_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x3124), BID_SIL3124 },
{ PCI_VDEVICE(CMD, 0x3132), BID_SIL3132 },
{ PCI_VDEVICE(CMD, 0x0242), BID_SIL3132 },
+ { PCI_VDEVICE(CMD, 0x0244), BID_SIL3132 },
{ PCI_VDEVICE(CMD, 0x3131), BID_SIL3131 },
{ PCI_VDEVICE(CMD, 0x3531), BID_SIL3131 },
diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
index f277cea904ce..db529b849948 100644
--- a/drivers/ata/sata_uli.c
+++ b/drivers/ata/sata_uli.c
@@ -83,6 +83,7 @@ static struct ata_port_operations uli_ops = {
.inherits = &ata_bmdma_port_ops,
.scr_read = uli_scr_read,
.scr_write = uli_scr_write,
+ .hardreset = ATA_OP_NULL,
};
static const struct ata_port_info uli_port_info = {
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 043353bd0600..14b9d5f4c203 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -64,7 +64,7 @@ config KS0108_DELAY
Amount of time the ks0108 should wait between each control write
to the parallel port.
- If your driver seems to miss random writings, increment this.
+ If your LCD seems to miss random writings, increment this.
If you don't know what I'm talking about, ignore it.
diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c
index 80bb06105387..683509f013ab 100644
--- a/drivers/auxdisplay/cfag12864b.c
+++ b/drivers/auxdisplay/cfag12864b.c
@@ -5,7 +5,7 @@
* License: GPLv2
* Depends: ks0108
*
- * Author: Copyright (C) Miguel Ojeda Sandonis <maxextreme@gmail.com>
+ * Author: Copyright (C) Miguel Ojeda Sandonis
* Date: 2006-10-31
*
* This program is free software; you can redistribute it and/or modify
@@ -398,5 +398,5 @@ module_init(cfag12864b_init);
module_exit(cfag12864b_exit);
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>");
MODULE_DESCRIPTION("cfag12864b LCD driver");
diff --git a/drivers/auxdisplay/cfag12864bfb.c b/drivers/auxdisplay/cfag12864bfb.c
index 307c190699e0..fe3a865be4e5 100644
--- a/drivers/auxdisplay/cfag12864bfb.c
+++ b/drivers/auxdisplay/cfag12864bfb.c
@@ -5,7 +5,7 @@
* License: GPLv2
* Depends: cfag12864b
*
- * Author: Copyright (C) Miguel Ojeda Sandonis <maxextreme@gmail.com>
+ * Author: Copyright (C) Miguel Ojeda Sandonis
* Date: 2006-10-31
*
* This program is free software; you can redistribute it and/or modify
@@ -186,5 +186,5 @@ module_init(cfag12864bfb_init);
module_exit(cfag12864bfb_exit);
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>");
MODULE_DESCRIPTION("cfag12864b LCD framebuffer driver");
diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c
index e6c3646ef18c..5b93852392b8 100644
--- a/drivers/auxdisplay/ks0108.c
+++ b/drivers/auxdisplay/ks0108.c
@@ -5,7 +5,7 @@
* License: GPLv2
* Depends: parport
*
- * Author: Copyright (C) Miguel Ojeda Sandonis <maxextreme@gmail.com>
+ * Author: Copyright (C) Miguel Ojeda Sandonis
* Date: 2006-10-31
*
* This program is free software; you can redistribute it and/or modify
@@ -173,6 +173,6 @@ module_init(ks0108_init);
module_exit(ks0108_exit);
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>");
+MODULE_AUTHOR("Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>");
MODULE_DESCRIPTION("ks0108 LCD Controller driver");
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 39f3d1b3a213..0f867a083338 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -84,8 +84,8 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
nid, K(i.totalram),
nid, K(i.freeram),
nid, K(i.totalram - i.freeram),
- nid, node_page_state(nid, NR_ACTIVE),
- nid, node_page_state(nid, NR_INACTIVE),
+ nid, K(node_page_state(nid, NR_ACTIVE)),
+ nid, K(node_page_state(nid, NR_INACTIVE)),
#ifdef CONFIG_HIGHMEM
nid, K(i.totalhigh),
nid, K(i.freehigh),
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
index 2b4b392dcbc1..87a7f1d02578 100644
--- a/drivers/base/power/trace.c
+++ b/drivers/base/power/trace.c
@@ -153,7 +153,7 @@ EXPORT_SYMBOL(set_trace_device);
* it's not any guarantee, but it's a high _likelihood_ that
* the match is valid).
*/
-void generate_resume_trace(void *tracedata, unsigned int user)
+void generate_resume_trace(const void *tracedata, unsigned int user)
{
unsigned short lineno = *(unsigned short *)tracedata;
const char *file = *(const char **)(tracedata + 2);
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 5f1e1cc6165a..d81632cd7d06 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -106,35 +106,34 @@ MODULE_DEVICE_TABLE(pci, cciss_pci_device_id);
/* board_id = Subsystem Device ID & Vendor ID
* product = Marketing Name for the board
* access = Address of the struct of function pointers
- * nr_cmds = Number of commands supported by controller
*/
static struct board_type products[] = {
- {0x40700E11, "Smart Array 5300", &SA5_access, 512},
- {0x40800E11, "Smart Array 5i", &SA5B_access, 512},
- {0x40820E11, "Smart Array 532", &SA5B_access, 512},
- {0x40830E11, "Smart Array 5312", &SA5B_access, 512},
- {0x409A0E11, "Smart Array 641", &SA5_access, 512},
- {0x409B0E11, "Smart Array 642", &SA5_access, 512},
- {0x409C0E11, "Smart Array 6400", &SA5_access, 512},
- {0x409D0E11, "Smart Array 6400 EM", &SA5_access, 512},
- {0x40910E11, "Smart Array 6i", &SA5_access, 512},
- {0x3225103C, "Smart Array P600", &SA5_access, 512},
- {0x3223103C, "Smart Array P800", &SA5_access, 512},
- {0x3234103C, "Smart Array P400", &SA5_access, 512},
- {0x3235103C, "Smart Array P400i", &SA5_access, 512},
- {0x3211103C, "Smart Array E200i", &SA5_access, 120},
- {0x3212103C, "Smart Array E200", &SA5_access, 120},
- {0x3213103C, "Smart Array E200i", &SA5_access, 120},
- {0x3214103C, "Smart Array E200i", &SA5_access, 120},
- {0x3215103C, "Smart Array E200i", &SA5_access, 120},
- {0x3237103C, "Smart Array E500", &SA5_access, 512},
- {0x323D103C, "Smart Array P700m", &SA5_access, 512},
- {0x3241103C, "Smart Array P212", &SA5_access, 384},
- {0x3243103C, "Smart Array P410", &SA5_access, 384},
- {0x3245103C, "Smart Array P410i", &SA5_access, 384},
- {0x3247103C, "Smart Array P411", &SA5_access, 384},
- {0x3249103C, "Smart Array P812", &SA5_access, 384},
- {0xFFFF103C, "Unknown Smart Array", &SA5_access, 120},
+ {0x40700E11, "Smart Array 5300", &SA5_access},
+ {0x40800E11, "Smart Array 5i", &SA5B_access},
+ {0x40820E11, "Smart Array 532", &SA5B_access},
+ {0x40830E11, "Smart Array 5312", &SA5B_access},
+ {0x409A0E11, "Smart Array 641", &SA5_access},
+ {0x409B0E11, "Smart Array 642", &SA5_access},
+ {0x409C0E11, "Smart Array 6400", &SA5_access},
+ {0x409D0E11, "Smart Array 6400 EM", &SA5_access},
+ {0x40910E11, "Smart Array 6i", &SA5_access},
+ {0x3225103C, "Smart Array P600", &SA5_access},
+ {0x3223103C, "Smart Array P800", &SA5_access},
+ {0x3234103C, "Smart Array P400", &SA5_access},
+ {0x3235103C, "Smart Array P400i", &SA5_access},
+ {0x3211103C, "Smart Array E200i", &SA5_access},
+ {0x3212103C, "Smart Array E200", &SA5_access},
+ {0x3213103C, "Smart Array E200i", &SA5_access},
+ {0x3214103C, "Smart Array E200i", &SA5_access},
+ {0x3215103C, "Smart Array E200i", &SA5_access},
+ {0x3237103C, "Smart Array E500", &SA5_access},
+ {0x323D103C, "Smart Array P700m", &SA5_access},
+ {0x3241103C, "Smart Array P212", &SA5_access},
+ {0x3243103C, "Smart Array P410", &SA5_access},
+ {0x3245103C, "Smart Array P410i", &SA5_access},
+ {0x3247103C, "Smart Array P411", &SA5_access},
+ {0x3249103C, "Smart Array P812", &SA5_access},
+ {0xFFFF103C, "Unknown Smart Array", &SA5_access},
};
/* How long to wait (in milliseconds) for board to go into simple mode */
@@ -3086,11 +3085,20 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
print_cfg_table(c->cfgtable);
#endif /* CCISS_DEBUG */
+ /* Some controllers support Zero Memory Raid (ZMR).
+ * When configured in ZMR mode the number of supported
+ * commands drops to 64. So instead of just setting an
+ * arbitrary value we make the driver a little smarter.
+ * We read the config table to tell us how many commands
+ * are supported on the controller then subtract 4 to
+ * leave a little room for ioctl calls.
+ */
+ c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
for (i = 0; i < ARRAY_SIZE(products); i++) {
if (board_id == products[i].board_id) {
c->product_name = products[i].product_name;
c->access = *(products[i].access);
- c->nr_cmds = products[i].nr_cmds;
+ c->nr_cmds = c->max_commands - 4;
break;
}
}
@@ -3110,7 +3118,7 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
if (subsystem_vendor_id == PCI_VENDOR_ID_HP) {
c->product_name = products[i-1].product_name;
c->access = *(products[i-1].access);
- c->nr_cmds = products[i-1].nr_cmds;
+ c->nr_cmds = c->max_commands - 4;
printk(KERN_WARNING "cciss: This is an unknown "
"Smart Array controller.\n"
"cciss: Please update to the latest driver "
@@ -3546,6 +3554,10 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
for (j = 0; j <= hba[i]->highest_lun; j++)
add_disk(hba[i]->gendisk[j]);
+ /* we must register the controller even if no disks exist */
+ if (hba[i]->highest_lun == -1)
+ add_disk(hba[i]->gendisk[0]);
+
return 1;
clean4:
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index 13665db363d6..481ffe87c716 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -16,28 +16,9 @@
#include <asm/page.h> /* PAGE_SIZE */
#include <asm/e820.h>
#include <asm/k8.h>
+#include <asm/gart.h>
#include "agp.h"
-/* PTE bits. */
-#define GPTE_VALID 1
-#define GPTE_COHERENT 2
-
-/* Aperture control register bits. */
-#define GARTEN (1<<0)
-#define DISGARTCPU (1<<4)
-#define DISGARTIO (1<<5)
-
-/* GART cache control register bits. */
-#define INVGART (1<<0)
-#define GARTPTEERR (1<<1)
-
-/* K8 On-cpu GART registers */
-#define AMD64_GARTAPERTURECTL 0x90
-#define AMD64_GARTAPERTUREBASE 0x94
-#define AMD64_GARTTABLEBASE 0x98
-#define AMD64_GARTCACHECTL 0x9c
-#define AMD64_GARTEN (1<<0)
-
/* NVIDIA K8 registers */
#define NVIDIA_X86_64_0_APBASE 0x10
#define NVIDIA_X86_64_1_APBASE1 0x50
@@ -165,29 +146,18 @@ static int amd64_fetch_size(void)
* In a multiprocessor x86-64 system, this function gets
* called once for each CPU.
*/
-static u64 amd64_configure (struct pci_dev *hammer, u64 gatt_table)
+static u64 amd64_configure(struct pci_dev *hammer, u64 gatt_table)
{
u64 aperturebase;
u32 tmp;
- u64 addr, aper_base;
+ u64 aper_base;
/* Address to map to */
- pci_read_config_dword (hammer, AMD64_GARTAPERTUREBASE, &tmp);
+ pci_read_config_dword(hammer, AMD64_GARTAPERTUREBASE, &tmp);
aperturebase = tmp << 25;
aper_base = (aperturebase & PCI_BASE_ADDRESS_MEM_MASK);
- /* address of the mappings table */
- addr = (u64) gatt_table;
- addr >>= 12;
- tmp = (u32) addr<<4;
- tmp &= ~0xf;
- pci_write_config_dword (hammer, AMD64_GARTTABLEBASE, tmp);
-
- /* Enable GART translation for this hammer. */
- pci_read_config_dword(hammer, AMD64_GARTAPERTURECTL, &tmp);
- tmp |= GARTEN;
- tmp &= ~(DISGARTCPU | DISGARTIO);
- pci_write_config_dword(hammer, AMD64_GARTAPERTURECTL, tmp);
+ enable_gart_translation(hammer, gatt_table);
return aper_base;
}
@@ -226,9 +196,9 @@ static void amd64_cleanup(void)
for (i = 0; i < num_k8_northbridges; i++) {
struct pci_dev *dev = k8_northbridges[i];
/* disable gart translation */
- pci_read_config_dword (dev, AMD64_GARTAPERTURECTL, &tmp);
+ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &tmp);
tmp &= ~AMD64_GARTEN;
- pci_write_config_dword (dev, AMD64_GARTAPERTURECTL, tmp);
+ pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, tmp);
}
}
@@ -258,24 +228,10 @@ static const struct agp_bridge_driver amd_8151_driver = {
};
/* Some basic sanity checks for the aperture. */
-static int __devinit aperture_valid(u64 aper, u32 size)
+static int __devinit agp_aperture_valid(u64 aper, u32 size)
{
- if (aper == 0) {
- printk(KERN_ERR PFX "No aperture\n");
- return 0;
- }
- if (size < 32*1024*1024) {
- printk(KERN_ERR PFX "Aperture too small (%d MB)\n", size>>20);
- return 0;
- }
- if ((u64)aper + size > 0x100000000ULL) {
- printk(KERN_ERR PFX "Aperture out of bounds\n");
+ if (!aperture_valid(aper, size, 32*1024*1024))
return 0;
- }
- if (e820_any_mapped(aper, aper + size, E820_RAM)) {
- printk(KERN_ERR PFX "Aperture pointing to RAM\n");
- return 0;
- }
/* Request the Aperture. This catches cases when someone else
already put a mapping in there - happens with some very broken BIOS
@@ -308,11 +264,11 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp,
u32 nb_order, nb_base;
u16 apsize;
- pci_read_config_dword(nb, 0x90, &nb_order);
+ pci_read_config_dword(nb, AMD64_GARTAPERTURECTL, &nb_order);
nb_order = (nb_order >> 1) & 7;
- pci_read_config_dword(nb, 0x94, &nb_base);
+ pci_read_config_dword(nb, AMD64_GARTAPERTUREBASE, &nb_base);
nb_aper = nb_base << 25;
- if (aperture_valid(nb_aper, (32*1024*1024)<<nb_order)) {
+ if (agp_aperture_valid(nb_aper, (32*1024*1024)<<nb_order)) {
return 0;
}
@@ -331,12 +287,23 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp,
pci_read_config_dword(agp, 0x10, &aper_low);
pci_read_config_dword(agp, 0x14, &aper_hi);
aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
+
+ /*
+ * On some sick chips APSIZE is 0. This means it wants 4G
+ * so let double check that order, and lets trust the AMD NB settings
+ */
+ if (order >=0 && aper + (32ULL<<(20 + order)) > 0x100000000ULL) {
+ printk(KERN_INFO "Aperture size %u MB is not right, using settings from NB\n",
+ 32 << order);
+ order = nb_order;
+ }
+
printk(KERN_INFO PFX "Aperture from AGP @ %Lx size %u MB\n", aper, 32 << order);
- if (order < 0 || !aperture_valid(aper, (32*1024*1024)<<order))
+ if (order < 0 || !agp_aperture_valid(aper, (32*1024*1024)<<order))
return -1;
- pci_write_config_dword(nb, 0x90, order << 1);
- pci_write_config_dword(nb, 0x94, aper >> 25);
+ pci_write_config_dword(nb, AMD64_GARTAPERTURECTL, order << 1);
+ pci_write_config_dword(nb, AMD64_GARTAPERTUREBASE, aper >> 25);
return 0;
}
diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c
index f7f16e7a8bf3..df036118b8b1 100644
--- a/drivers/char/drm/i915_irq.c
+++ b/drivers/char/drm/i915_irq.c
@@ -62,11 +62,11 @@ static void i915_vblank_tasklet(struct drm_device *dev)
u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24);
RING_LOCALS;
- if (sarea_priv->front_tiled) {
+ if (IS_I965G(dev) && sarea_priv->front_tiled) {
cmd |= XY_SRC_COPY_BLT_DST_TILED;
dst_pitch >>= 2;
}
- if (sarea_priv->back_tiled) {
+ if (IS_I965G(dev) && sarea_priv->back_tiled) {
cmd |= XY_SRC_COPY_BLT_SRC_TILED;
src_pitch >>= 2;
}
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index dd68f8541c2d..db2ae4216279 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -39,9 +39,14 @@ static int xencons_irq;
/* ------------------------------------------------------------------ */
+static unsigned long console_pfn = ~0ul;
+
static inline struct xencons_interface *xencons_interface(void)
{
- return mfn_to_virt(xen_start_info->console.domU.mfn);
+ if (console_pfn == ~0ul)
+ return mfn_to_virt(xen_start_info->console.domU.mfn);
+ else
+ return __va(console_pfn << PAGE_SHIFT);
}
static inline void notify_daemon(void)
@@ -101,20 +106,32 @@ static int __init xen_init(void)
{
struct hvc_struct *hp;
- if (!is_running_on_xen())
- return 0;
+ if (!is_running_on_xen() ||
+ is_initial_xendomain() ||
+ !xen_start_info->console.domU.evtchn)
+ return -ENODEV;
xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn);
if (xencons_irq < 0)
- xencons_irq = 0 /* NO_IRQ */;
+ xencons_irq = 0; /* NO_IRQ */
+
hp = hvc_alloc(HVC_COOKIE, xencons_irq, &hvc_ops, 256);
if (IS_ERR(hp))
return PTR_ERR(hp);
hvc = hp;
+
+ console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn);
+
return 0;
}
+void xen_console_resume(void)
+{
+ if (xencons_irq)
+ rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq);
+}
+
static void __exit xen_fini(void)
{
if (hvc)
@@ -134,12 +151,28 @@ module_init(xen_init);
module_exit(xen_fini);
console_initcall(xen_cons_init);
+static void raw_console_write(const char *str, int len)
+{
+ while(len > 0) {
+ int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str);
+ if (rc <= 0)
+ break;
+
+ str += rc;
+ len -= rc;
+ }
+}
+
+#ifdef CONFIG_EARLY_PRINTK
static void xenboot_write_console(struct console *console, const char *string,
unsigned len)
{
unsigned int linelen, off = 0;
const char *pos;
+ raw_console_write(string, len);
+
+ write_console(0, "(early) ", 8);
while (off < len && NULL != (pos = strchr(string+off, '\n'))) {
linelen = pos-string+off;
if (off + linelen > len)
@@ -155,5 +188,23 @@ static void xenboot_write_console(struct console *console, const char *string,
struct console xenboot_console = {
.name = "xenboot",
.write = xenboot_write_console,
- .flags = CON_PRINTBUFFER | CON_BOOT,
+ .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
};
+#endif /* CONFIG_EARLY_PRINTK */
+
+void xen_raw_console_write(const char *str)
+{
+ raw_console_write(str, strlen(str));
+}
+
+void xen_raw_printk(const char *fmt, ...)
+{
+ static char buf[512];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ xen_raw_console_write(buf);
+}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index e94bee032314..750131010af0 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -3322,7 +3322,7 @@ static int send_break(struct tty_struct *tty, unsigned int duration)
msleep_interruptible(duration);
tty->ops->break_ctl(tty, 0);
tty_write_unlock(tty);
- if (!signal_pending(current))
+ if (signal_pending(current))
return -EINTR;
return 0;
}
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index 85e2ba7fcfba..bf4830082a13 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -27,6 +27,8 @@
#include <linux/moduleparam.h>
#include <linux/connector.h>
#include <linux/mutex.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
#include <net/sock.h>
@@ -403,6 +405,40 @@ static void cn_callback(void *data)
mutex_unlock(&notify_lock);
}
+static int cn_proc_show(struct seq_file *m, void *v)
+{
+ struct cn_queue_dev *dev = cdev.cbdev;
+ struct cn_callback_entry *cbq;
+
+ seq_printf(m, "Name ID\n");
+
+ spin_lock_bh(&dev->queue_lock);
+
+ list_for_each_entry(cbq, &dev->queue_list, callback_entry) {
+ seq_printf(m, "%-15s %u:%u\n",
+ cbq->id.name,
+ cbq->id.id.idx,
+ cbq->id.id.val);
+ }
+
+ spin_unlock_bh(&dev->queue_lock);
+
+ return 0;
+}
+
+static int cn_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cn_proc_show, NULL);
+}
+
+static const struct file_operations cn_file_ops = {
+ .owner = THIS_MODULE,
+ .open = cn_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release
+};
+
static int __devinit cn_init(void)
{
struct cn_dev *dev = &cdev;
@@ -434,6 +470,8 @@ static int __devinit cn_init(void)
return -EINVAL;
}
+ proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops);
+
return 0;
}
@@ -443,6 +481,8 @@ static void __devexit cn_fini(void)
cn_already_initialized = 0;
+ proc_net_remove(&init_net, "connector");
+
cn_del_callback(&dev->id);
cn_queue_free_dev(dev->cbdev);
netlink_kernel_release(dev->nls);
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index bbd28342e771..008c38ba774f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -28,12 +28,18 @@ config DEBUG_GPIO
comment "I2C GPIO expanders:"
config GPIO_PCA953X
- tristate "PCA953x I/O ports"
+ tristate "PCA953x, PCA955x, and MAX7310 I/O ports"
depends on I2C
help
- Say yes here to support the PCA9534 (8-bit), PCA9535 (16-bit),
- PCA9536 (4-bit), PCA9537 (4-bit), PCA9538 (8-bit), and PCA9539
- (16-bit) I/O ports. These parts are made by NXP and TI.
+ Say yes here to provide access to several register-oriented
+ SMBus I/O expanders, made mostly by NXP or TI. Compatible
+ models include:
+
+ 4 bits: pca9536, pca9537
+
+ 8 bits: max7310, pca9534, pca9538, pca9554, pca9557
+
+ 16 bits: pca9535, pca9539, pca9555
This driver can also be built as a module. If so, the module
will be called pca953x.
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
index 7e40e8a55edf..a380730b61ab 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/pca953x.c
@@ -33,7 +33,7 @@ static const struct i2c_device_id pca953x_id[] = {
{ "pca9554", 8, },
{ "pca9555", 16, },
{ "pca9557", 8, },
- /* REVISIT several pca955x parts should work here too */
+ { "max7310", 8, },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca953x_id);
diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c
index 26df06f840eb..50f22690d611 100644
--- a/drivers/hwmon/hdaps.c
+++ b/drivers/hwmon/hdaps.c
@@ -516,17 +516,23 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = {
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i"),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61"),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s"),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m"),
+ HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p"),
{ .ident = NULL }
};
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 1305ef190fc1..9e8c875437be 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -290,12 +290,12 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
* bus, or started a new i2c message
*/
- if (iicstat & S3C2410_IICSTAT_LASTBIT &&
+ if (iicstat & S3C2410_IICSTAT_LASTBIT &&
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
/* ack was not received... */
dev_dbg(i2c->dev, "ack was not received\n");
- s3c24xx_i2c_stop(i2c, -EREMOTEIO);
+ s3c24xx_i2c_stop(i2c, -ENXIO);
goto out_ack;
}
@@ -305,7 +305,7 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
i2c->state = STATE_WRITE;
/* terminate the transfer if there is nothing to do
- * (used by the i2c probe to find devices */
+ * as this is used by the i2c probe to find devices. */
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
s3c24xx_i2c_stop(i2c, 0);
@@ -323,7 +323,17 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
* end of the message, and if so, work out what to do
*/
+ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+ if (iicstat & S3C2410_IICSTAT_LASTBIT) {
+ dev_dbg(i2c->dev, "WRITE: No Ack\n");
+
+ s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
+ goto out_ack;
+ }
+ }
+
retry_write:
+
if (!is_msgend(i2c)) {
byte = i2c->msg->buf[i2c->msg_ptr++];
writeb(byte, i2c->regs + S3C2410_IICDS);
@@ -377,17 +387,6 @@ static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
* going to do any more read/write
*/
- if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) &&
- !(is_msglast(i2c) && is_lastmsg(i2c))) {
-
- if (iicstat & S3C2410_IICSTAT_LASTBIT) {
- dev_dbg(i2c->dev, "READ: No Ack\n");
-
- s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
- goto out_ack;
- }
- }
-
byte = readb(i2c->regs + S3C2410_IICDS);
i2c->msg->buf[i2c->msg_ptr++] = byte;
@@ -949,3 +948,4 @@ MODULE_DESCRIPTION("S3C24XX I2C Bus driver");
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s3c2410-i2c");
+MODULE_ALIAS("platform:s3c2440-i2c");
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 8e07de23d220..1607536ff5fb 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -823,6 +823,13 @@ config BLK_DEV_IDE_RAPIDE
Say Y here if you want to support the Yellowstone RapIDE controller
manufactured for use with Acorn computers.
+config BLK_DEV_IDE_BAST
+ tristate "Simtec BAST / Thorcom VR1000 IDE support"
+ depends on ARM && (ARCH_BAST || MACH_VR1000)
+ help
+ Say Y here if you want to support the onboard IDE channels on the
+ Simtec BAST or the Thorcom VR1000
+
config IDE_H8300
tristate "H8300 IDE support"
depends on H8300
diff --git a/drivers/ide/arm/Makefile b/drivers/ide/arm/Makefile
index 5bc26053afa6..936e7b0237f5 100644
--- a/drivers/ide/arm/Makefile
+++ b/drivers/ide/arm/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o
obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o
+obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o
obj-$(CONFIG_BLK_DEV_PALMCHIP_BK3710) += palm_bk3710.o
ifeq ($(CONFIG_IDE_ARM), m)
diff --git a/drivers/ide/arm/bast-ide.c b/drivers/ide/arm/bast-ide.c
new file mode 100644
index 000000000000..8e8c28104b45
--- /dev/null
+++ b/drivers/ide/arm/bast-ide.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2003-2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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/errno.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/mach-types.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/arch/map.h>
+#include <asm/arch/bast-map.h>
+#include <asm/arch/bast-irq.h>
+
+#define DRV_NAME "bast-ide"
+
+static int __init bastide_register(unsigned int base, unsigned int aux, int irq)
+{
+ ide_hwif_t *hwif;
+ hw_regs_t hw;
+ int i;
+ u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
+
+ memset(&hw, 0, sizeof(hw));
+
+ base += BAST_IDE_CS;
+ aux += BAST_IDE_CS;
+
+ for (i = 0; i <= 7; i++) {
+ hw.io_ports_array[i] = (unsigned long)base;
+ base += 0x20;
+ }
+
+ hw.io_ports.ctl_addr = aux + (6 * 0x20);
+ hw.irq = irq;
+ hw.chipset = ide_generic;
+
+ hwif = ide_find_port();
+ if (hwif == NULL)
+ goto out;
+
+ i = hwif->index;
+
+ ide_init_port_data(hwif, i);
+ ide_init_port_hw(hwif, &hw);
+ hwif->port_ops = NULL;
+
+ idx[0] = i;
+
+ ide_device_add(idx, NULL);
+out:
+ return 0;
+}
+
+static int __init bastide_init(void)
+{
+ unsigned long base = BAST_VA_IDEPRI + BAST_IDE_CS;
+
+ /* we can treat the VR1000 and the BAST the same */
+
+ if (!(machine_is_bast() || machine_is_vr1000()))
+ return 0;
+
+ printk("BAST: IDE driver, (c) 2003-2004 Simtec Electronics\n");
+
+ if (!request_mem_region(base, 0x400000, DRV_NAME)) {
+ printk(KERN_ERR "%s: resources busy\n", DRV_NAME);
+ return -EBUSY;
+ }
+
+ bastide_register(BAST_VA_IDEPRI, BAST_VA_IDEPRIAUX, IRQ_IDE0);
+ bastide_register(BAST_VA_IDESEC, BAST_VA_IDESECAUX, IRQ_IDE1);
+
+ return 0;
+}
+
+module_init(bastide_init);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Simtec BAST / Thorcom VR1000 IDE driver");
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 380fa0c8cc84..d27061b39324 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -646,8 +646,6 @@ static int ide_register_port(ide_hwif_t *hwif)
goto out;
}
- get_device(&hwif->gendev);
-
hwif->portdev = device_create_drvdata(ide_port_class, &hwif->gendev,
MKDEV(0, 0), hwif, hwif->name);
if (IS_ERR(hwif->portdev)) {
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index 55ec7f798772..8af88bf0969b 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -76,7 +76,7 @@ static int proc_ide_read_mate
ide_hwif_t *hwif = (ide_hwif_t *) data;
int len;
- if (hwif && hwif->mate && hwif->mate->present)
+ if (hwif && hwif->mate)
len = sprintf(page, "%s\n", hwif->mate->name);
else
len = sprintf(page, "(none)\n");
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index c758dcb13b14..300431d080a9 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -315,13 +315,14 @@ void ide_unregister(ide_hwif_t *hwif)
BUG_ON(in_interrupt());
BUG_ON(irqs_disabled());
+
mutex_lock(&ide_cfg_mtx);
- spin_lock_irq(&ide_lock);
- if (!hwif->present)
- goto abort;
- __ide_port_unregister_devices(hwif);
- hwif->present = 0;
+ spin_lock_irq(&ide_lock);
+ if (hwif->present) {
+ __ide_port_unregister_devices(hwif);
+ hwif->present = 0;
+ }
spin_unlock_irq(&ide_lock);
ide_proc_unregister_port(hwif);
@@ -351,16 +352,15 @@ void ide_unregister(ide_hwif_t *hwif)
blk_unregister_region(MKDEV(hwif->major, 0), MAX_DRIVES<<PARTN_BITS);
kfree(hwif->sg_table);
unregister_blkdev(hwif->major, hwif->name);
- spin_lock_irq(&ide_lock);
if (hwif->dma_base)
ide_release_dma_engine(hwif);
+ spin_lock_irq(&ide_lock);
/* restore hwif data to pristine status */
ide_init_port_data(hwif, hwif->index);
-
-abort:
spin_unlock_irq(&ide_lock);
+
mutex_unlock(&ide_cfg_mtx);
}
@@ -1094,13 +1094,6 @@ struct bus_type ide_bus_type = {
EXPORT_SYMBOL_GPL(ide_bus_type);
-static void ide_port_class_release(struct device *portdev)
-{
- ide_hwif_t *hwif = dev_get_drvdata(portdev);
-
- put_device(&hwif->gendev);
-}
-
int ide_vlb_clk;
EXPORT_SYMBOL_GPL(ide_vlb_clk);
@@ -1305,7 +1298,6 @@ static int __init ide_init(void)
ret = PTR_ERR(ide_port_class);
goto out_port_class;
}
- ide_port_class->dev_release = ide_port_class_release;
init_ide_data();
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index 4c01464ec8f3..72c63e5dd630 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -28,6 +28,7 @@
#include <linux/input.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/sched.h>
/*
* Check that the effect_id is a valid effect and whether the user
diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c
index 0f47f4697cdf..9ce3b3baf3a2 100644
--- a/drivers/input/xen-kbdfront.c
+++ b/drivers/input/xen-kbdfront.c
@@ -66,6 +66,9 @@ static irqreturn_t input_handler(int rq, void *dev_id)
case XENKBD_TYPE_MOTION:
input_report_rel(dev, REL_X, event->motion.rel_x);
input_report_rel(dev, REL_Y, event->motion.rel_y);
+ if (event->motion.rel_z)
+ input_report_rel(dev, REL_WHEEL,
+ -event->motion.rel_z);
break;
case XENKBD_TYPE_KEY:
dev = NULL;
@@ -84,6 +87,9 @@ static irqreturn_t input_handler(int rq, void *dev_id)
case XENKBD_TYPE_POS:
input_report_abs(dev, ABS_X, event->pos.abs_x);
input_report_abs(dev, ABS_Y, event->pos.abs_y);
+ if (event->pos.rel_z)
+ input_report_rel(dev, REL_WHEEL,
+ -event->pos.rel_z);
break;
}
if (dev)
@@ -152,7 +158,7 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev,
ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
for (i = BTN_LEFT; i <= BTN_TASK; i++)
set_bit(i, ptr->keybit);
- ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL);
input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
@@ -294,6 +300,16 @@ InitWait:
*/
if (dev->state != XenbusStateConnected)
goto InitWait; /* no InitWait seen yet, fudge it */
+
+ /* Set input abs params to match backend screen res */
+ if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "width", "%d", &val) > 0)
+ input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
+
+ if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "height", "%d", &val) > 0)
+ input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
+
break;
case XenbusStateClosing:
@@ -337,4 +353,6 @@ static void __exit xenkbd_cleanup(void)
module_init(xenkbd_init);
module_exit(xenkbd_cleanup);
+MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vkbd");
diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h
index 005bd045d2eb..5faefeaf6790 100644
--- a/drivers/lguest/lg.h
+++ b/drivers/lguest/lg.h
@@ -136,7 +136,6 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user);
* first step in the migration to the kernel types. pte_pfn is already defined
* in the kernel. */
#define pgd_flags(x) (pgd_val(x) & ~PAGE_MASK)
-#define pte_flags(x) (pte_val(x) & ~PAGE_MASK)
#define pgd_pfn(x) (pgd_val(x) >> PAGE_SHIFT)
/* interrupts_and_traps.c: */
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 835def11419d..ab6a61db63ce 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -432,6 +432,7 @@ static int crypt_convert(struct crypt_config *cc,
case 0:
atomic_dec(&ctx->pending);
ctx->sector++;
+ cond_resched();
continue;
/* error */
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 7cf512a34ccf..2580ac1b9b0f 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -3897,8 +3897,10 @@ static void autorun_devices(int part)
md_probe(dev, NULL, NULL);
mddev = mddev_find(dev);
- if (!mddev) {
- printk(KERN_ERR
+ if (!mddev || !mddev->gendisk) {
+ if (mddev)
+ mddev_put(mddev);
+ printk(KERN_ERR
"md: cannot allocate memory for md drive.\n");
break;
}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 1de17da34a95..a71277b640ab 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2137,6 +2137,8 @@ static int run(mddev_t *mddev)
!test_bit(In_sync, &disk->rdev->flags)) {
disk->head_position = 0;
mddev->degraded++;
+ if (disk->rdev)
+ conf->fullsync = 1;
}
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index c37e256b1176..54c8ee28fcc4 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2898,6 +2898,8 @@ static void handle_stripe5(struct stripe_head *sh)
for (i = conf->raid_disks; i--; ) {
set_bit(R5_Wantwrite, &sh->dev[i].flags);
+ set_bit(R5_LOCKED, &dev->flags);
+ s.locked++;
if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending))
sh->ops.count++;
}
@@ -2911,6 +2913,7 @@ static void handle_stripe5(struct stripe_head *sh)
conf->raid_disks);
s.locked += handle_write_operations5(sh, 1, 1);
} else if (s.expanded &&
+ s.locked == 0 &&
!test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) {
clear_bit(STRIPE_EXPAND_READY, &sh->state);
atomic_dec(&conf->reshape_stripes);
@@ -4305,7 +4308,9 @@ static int run(mddev_t *mddev)
" disk %d\n", bdevname(rdev->bdev,b),
raid_disk);
working_disks++;
- }
+ } else
+ /* Cannot rely on bitmap to complete recovery */
+ conf->fullsync = 1;
}
/*
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 3b26fbd3e558..5ccb0aeca8cc 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -793,6 +793,14 @@ menuconfig V4L_USB_DRIVERS
if V4L_USB_DRIVERS && USB
+config USB_VIDEO_CLASS
+ tristate "USB Video Class (UVC)"
+ ---help---
+ Support for the USB Video Class (UVC). Currently only video
+ input devices, such as webcams, are supported.
+
+ For more information see: <http://linux-uvc.berlios.de/>
+
source "drivers/media/video/pvrusb2/Kconfig"
source "drivers/media/video/em28xx/Kconfig"
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index dff0d6abe917..ecbbfaab24d5 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -136,6 +136,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
obj-$(CONFIG_VIDEO_AU0828) += au0828/
+obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/
+
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile
new file mode 100644
index 000000000000..968c1994eda0
--- /dev/null
+++ b/drivers/media/video/uvc/Makefile
@@ -0,0 +1,3 @@
+uvcvideo-objs := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
+ uvc_status.o uvc_isight.o
+obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
new file mode 100644
index 000000000000..f0ee46d15540
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -0,0 +1,1256 @@
+/*
+ * uvc_ctrl.c -- USB Video Class driver - Controls
+ *
+ * Copyright (C) 2005-2008
+ * Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+#include "uvcvideo.h"
+
+#define UVC_CTRL_NDATA 2
+#define UVC_CTRL_DATA_CURRENT 0
+#define UVC_CTRL_DATA_BACKUP 1
+
+/* ------------------------------------------------------------------------
+ * Control, formats, ...
+ */
+
+static struct uvc_control_info uvc_ctrls[] = {
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_BRIGHTNESS_CONTROL,
+ .index = 0,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_CONTRAST_CONTROL,
+ .index = 1,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_HUE_CONTROL,
+ .index = 2,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_SATURATION_CONTROL,
+ .index = 3,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_SHARPNESS_CONTROL,
+ .index = 4,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_GAMMA_CONTROL,
+ .index = 5,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_BACKLIGHT_COMPENSATION_CONTROL,
+ .index = 8,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_GAIN_CONTROL,
+ .index = 9,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_POWER_LINE_FREQUENCY_CONTROL,
+ .index = 10,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_HUE_AUTO_CONTROL,
+ .index = 11,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_AE_MODE_CONTROL,
+ .index = 1,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_GET_RES
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_AE_PRIORITY_CONTROL,
+ .index = 2,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
+ .index = 3,
+ .size = 4,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_FOCUS_ABSOLUTE_CONTROL,
+ .index = 5,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_FOCUS_AUTO_CONTROL,
+ .index = 17,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
+ .index = 12,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
+ .index = 6,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+ .index = 13,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL,
+ .index = 7,
+ .size = 4,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+};
+
+static struct uvc_menu_info power_line_frequency_controls[] = {
+ { 0, "Disabled" },
+ { 1, "50 Hz" },
+ { 2, "60 Hz" },
+};
+
+static struct uvc_menu_info exposure_auto_controls[] = {
+ { 1, "Manual Mode" },
+ { 2, "Auto Mode" },
+ { 4, "Shutter Priority Mode" },
+ { 8, "Aperture Priority Mode" },
+};
+
+static struct uvc_control_mapping uvc_ctrl_mappings[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_BRIGHTNESS_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_CONTRAST_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_HUE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_SATURATION_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .name = "Sharpness",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_SHARPNESS_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_GAMMA,
+ .name = "Gamma",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_GAMMA_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+ .name = "Backlight Compensation",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_BACKLIGHT_COMPENSATION_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_GAIN,
+ .name = "Gain",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_GAIN_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .name = "Power Line Frequency",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_POWER_LINE_FREQUENCY_CONTROL,
+ .size = 2,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_MENU,
+ .data_type = UVC_CTRL_DATA_TYPE_ENUM,
+ .menu_info = power_line_frequency_controls,
+ .menu_count = ARRAY_SIZE(power_line_frequency_controls),
+ },
+ {
+ .id = V4L2_CID_HUE_AUTO,
+ .name = "Hue, Auto",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_HUE_AUTO_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .name = "Exposure, Auto",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_AE_MODE_CONTROL,
+ .size = 4,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_MENU,
+ .data_type = UVC_CTRL_DATA_TYPE_BITMASK,
+ .menu_info = exposure_auto_controls,
+ .menu_count = ARRAY_SIZE(exposure_auto_controls),
+ },
+ {
+ .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
+ .name = "Exposure, Auto Priority",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_AE_PRIORITY_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_EXPOSURE_ABSOLUTE,
+ .name = "Exposure (Absolute)",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
+ .size = 32,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .name = "White Balance Temperature, Auto",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ .name = "White Balance Temperature",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .name = "White Balance Component, Auto",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .name = "White Balance Blue Component",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .name = "White Balance Red Component",
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL,
+ .size = 16,
+ .offset = 16,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_FOCUS_ABSOLUTE,
+ .name = "Focus (absolute)",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_FOCUS_ABSOLUTE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_FOCUS_AUTO,
+ .name = "Focus, Auto",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_FOCUS_AUTO_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
+};
+
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
+{
+ return ctrl->data + id * ctrl->info->size;
+}
+
+static inline int uvc_get_bit(const __u8 *data, int bit)
+{
+ return (data[bit >> 3] >> (bit & 7)) & 1;
+}
+
+/* Extract the bit string specified by mapping->offset and mapping->size
+ * from the little-endian data stored at 'data' and return the result as
+ * a signed 32bit integer. Sign extension will be performed if the mapping
+ * references a signed data type.
+ */
+static __s32 uvc_get_le_value(const __u8 *data,
+ struct uvc_control_mapping *mapping)
+{
+ int bits = mapping->size;
+ int offset = mapping->offset;
+ __s32 value = 0;
+ __u8 mask;
+
+ data += offset / 8;
+ offset &= 7;
+ mask = ((1LL << bits) - 1) << offset;
+
+ for (; bits > 0; data++) {
+ __u8 byte = *data & mask;
+ value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
+ bits -= 8 - (offset > 0 ? offset : 0);
+ offset -= 8;
+ mask = (1 << bits) - 1;
+ }
+
+ /* Sign-extend the value if needed */
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
+ value |= -(value & (1 << (mapping->size - 1)));
+
+ return value;
+}
+
+/* Set the bit string specified by mapping->offset and mapping->size
+ * in the little-endian data stored at 'data' to the value 'value'.
+ */
+static void uvc_set_le_value(__s32 value, __u8 *data,
+ struct uvc_control_mapping *mapping)
+{
+ int bits = mapping->size;
+ int offset = mapping->offset;
+ __u8 mask;
+
+ data += offset / 8;
+ offset &= 7;
+
+ for (; bits > 0; data++) {
+ mask = ((1LL << bits) - 1) << offset;
+ *data = (*data & ~mask) | ((value << offset) & mask);
+ value >>= offset ? offset : 8;
+ bits -= 8 - offset;
+ offset = 0;
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * Terminal and unit management
+ */
+
+static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
+static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
+static const __u8 uvc_media_transport_input_guid[16] =
+ UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
+
+static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16])
+{
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case ITT_CAMERA:
+ return memcmp(uvc_camera_guid, guid, 16) == 0;
+
+ case ITT_MEDIA_TRANSPORT_INPUT:
+ return memcmp(uvc_media_transport_input_guid, guid, 16) == 0;
+
+ case VC_PROCESSING_UNIT:
+ return memcmp(uvc_processing_guid, guid, 16) == 0;
+
+ case VC_EXTENSION_UNIT:
+ return memcmp(entity->extension.guidExtensionCode,
+ guid, 16) == 0;
+
+ default:
+ return 0;
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * UVC Controls
+ */
+
+static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
+ struct uvc_control_mapping **mapping, struct uvc_control **control,
+ int next)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *map;
+ unsigned int i;
+
+ if (entity == NULL)
+ return;
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+ if (ctrl->info == NULL)
+ continue;
+
+ list_for_each_entry(map, &ctrl->info->mappings, list) {
+ if ((map->id == v4l2_id) && !next) {
+ *control = ctrl;
+ *mapping = map;
+ return;
+ }
+
+ if ((*mapping == NULL || (*mapping)->id > map->id) &&
+ (map->id > v4l2_id) && next) {
+ *control = ctrl;
+ *mapping = map;
+ }
+ }
+ }
+}
+
+struct uvc_control *uvc_find_control(struct uvc_video_device *video,
+ __u32 v4l2_id, struct uvc_control_mapping **mapping)
+{
+ struct uvc_control *ctrl = NULL;
+ struct uvc_entity *entity;
+ int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
+
+ *mapping = NULL;
+
+ /* Mask the query flags. */
+ v4l2_id &= V4L2_CTRL_ID_MASK;
+
+ /* Find the control. */
+ __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next);
+ if (ctrl && !next)
+ return ctrl;
+
+ list_for_each_entry(entity, &video->iterms, chain) {
+ __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
+ if (ctrl && !next)
+ return ctrl;
+ }
+
+ list_for_each_entry(entity, &video->extensions, chain) {
+ __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
+ if (ctrl && !next)
+ return ctrl;
+ }
+
+ if (ctrl == NULL && !next)
+ uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n",
+ v4l2_id);
+
+ return ctrl;
+}
+
+int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
+ struct v4l2_queryctrl *v4l2_ctrl)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *mapping;
+ struct uvc_menu_info *menu;
+ unsigned int i;
+ __u8 data[8];
+ int ret;
+
+ ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping);
+ if (ctrl == NULL)
+ return -EINVAL;
+
+ v4l2_ctrl->id = mapping->id;
+ v4l2_ctrl->type = mapping->v4l2_type;
+ strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name);
+ v4l2_ctrl->flags = 0;
+
+ if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR))
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
+ if ((ret = uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id,
+ video->dev->intfnum, ctrl->info->selector,
+ &data, ctrl->info->size)) < 0)
+ return ret;
+ v4l2_ctrl->default_value = uvc_get_le_value(data, mapping);
+ }
+
+ if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
+ v4l2_ctrl->minimum = 0;
+ v4l2_ctrl->maximum = mapping->menu_count - 1;
+ v4l2_ctrl->step = 1;
+
+ menu = mapping->menu_info;
+ for (i = 0; i < mapping->menu_count; ++i, ++menu) {
+ if (menu->value == v4l2_ctrl->default_value) {
+ v4l2_ctrl->default_value = i;
+ break;
+ }
+ }
+
+ return 0;
+ }
+
+ if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
+ if ((ret = uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id,
+ video->dev->intfnum, ctrl->info->selector,
+ &data, ctrl->info->size)) < 0)
+ return ret;
+ v4l2_ctrl->minimum = uvc_get_le_value(data, mapping);
+ }
+ if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
+ if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id,
+ video->dev->intfnum, ctrl->info->selector,
+ &data, ctrl->info->size)) < 0)
+ return ret;
+ v4l2_ctrl->maximum = uvc_get_le_value(data, mapping);
+ }
+ if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
+ if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id,
+ video->dev->intfnum, ctrl->info->selector,
+ &data, ctrl->info->size)) < 0)
+ return ret;
+ v4l2_ctrl->step = uvc_get_le_value(data, mapping);
+ }
+
+ return 0;
+}
+
+
+/* --------------------------------------------------------------------------
+ * Control transactions
+ *
+ * To make extended set operations as atomic as the hardware allows, controls
+ * are handled using begin/commit/rollback operations.
+ *
+ * At the beginning of a set request, uvc_ctrl_begin should be called to
+ * initialize the request. This function acquires the control lock.
+ *
+ * When setting a control, the new value is stored in the control data field
+ * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for
+ * later processing. If the UVC and V4L2 control sizes differ, the current
+ * value is loaded from the hardware before storing the new value in the data
+ * field.
+ *
+ * After processing all controls in the transaction, uvc_ctrl_commit or
+ * uvc_ctrl_rollback must be called to apply the pending changes to the
+ * hardware or revert them. When applying changes, all controls marked as
+ * dirty will be modified in the UVC device, and the dirty flag will be
+ * cleared. When reverting controls, the control data field
+ * UVC_CTRL_DATA_CURRENT is reverted to its previous value
+ * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
+ * control lock.
+ */
+int uvc_ctrl_begin(struct uvc_video_device *video)
+{
+ return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0;
+}
+
+static int uvc_ctrl_commit_entity(struct uvc_device *dev,
+ struct uvc_entity *entity, int rollback)
+{
+ struct uvc_control *ctrl;
+ unsigned int i;
+ int ret;
+
+ if (entity == NULL)
+ return 0;
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+ if (ctrl->info == NULL || !ctrl->dirty)
+ continue;
+
+ if (!rollback)
+ ret = uvc_query_ctrl(dev, SET_CUR, ctrl->entity->id,
+ dev->intfnum, ctrl->info->selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info->size);
+ else
+ ret = 0;
+
+ if (rollback || ret < 0)
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+ ctrl->info->size);
+
+ if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
+ ctrl->loaded = 0;
+
+ ctrl->dirty = 0;
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback)
+{
+ struct uvc_entity *entity;
+ int ret = 0;
+
+ /* Find the control. */
+ ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback);
+ if (ret < 0)
+ goto done;
+
+ list_for_each_entry(entity, &video->iterms, chain) {
+ ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
+ if (ret < 0)
+ goto done;
+ }
+
+ list_for_each_entry(entity, &video->extensions, chain) {
+ ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
+ if (ret < 0)
+ goto done;
+ }
+
+done:
+ mutex_unlock(&video->ctrl_mutex);
+ return ret;
+}
+
+int uvc_ctrl_get(struct uvc_video_device *video,
+ struct v4l2_ext_control *xctrl)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *mapping;
+ struct uvc_menu_info *menu;
+ unsigned int i;
+ int ret;
+
+ ctrl = uvc_find_control(video, xctrl->id, &mapping);
+ if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
+ return -EINVAL;
+
+ if (!ctrl->loaded) {
+ ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id,
+ video->dev->intfnum, ctrl->info->selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info->size);
+ if (ret < 0)
+ return ret;
+
+ if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0)
+ ctrl->loaded = 1;
+ }
+
+ xctrl->value = uvc_get_le_value(
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+
+ if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
+ menu = mapping->menu_info;
+ for (i = 0; i < mapping->menu_count; ++i, ++menu) {
+ if (menu->value == xctrl->value) {
+ xctrl->value = i;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int uvc_ctrl_set(struct uvc_video_device *video,
+ struct v4l2_ext_control *xctrl)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *mapping;
+ s32 value = xctrl->value;
+ int ret;
+
+ ctrl = uvc_find_control(video, xctrl->id, &mapping);
+ if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
+ return -EINVAL;
+
+ if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
+ if (value < 0 || value >= mapping->menu_count)
+ return -EINVAL;
+ value = mapping->menu_info[value].value;
+ }
+
+ if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) {
+ if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) {
+ memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ 0, ctrl->info->size);
+ } else {
+ ret = uvc_query_ctrl(video->dev, GET_CUR,
+ ctrl->entity->id, video->dev->intfnum,
+ ctrl->info->selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info->size);
+ if (ret < 0)
+ return ret;
+ }
+
+ if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0)
+ ctrl->loaded = 1;
+ }
+
+ if (!ctrl->dirty) {
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info->size);
+ }
+
+ uvc_set_le_value(value,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+
+ ctrl->dirty = 1;
+ ctrl->modified = 1;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Dynamic controls
+ */
+
+int uvc_xu_ctrl_query(struct uvc_video_device *video,
+ struct uvc_xu_control *xctrl, int set)
+{
+ struct uvc_entity *entity;
+ struct uvc_control *ctrl = NULL;
+ unsigned int i, found = 0;
+ __u8 *data;
+ int ret;
+
+ /* Find the extension unit. */
+ list_for_each_entry(entity, &video->extensions, chain) {
+ if (entity->id == xctrl->unit)
+ break;
+ }
+
+ if (entity->id != xctrl->unit) {
+ uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n",
+ xctrl->unit);
+ return -EINVAL;
+ }
+
+ /* Find the control. */
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+ if (ctrl->info == NULL)
+ continue;
+
+ if (ctrl->info->selector == xctrl->selector) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ uvc_trace(UVC_TRACE_CONTROL,
+ "Control " UVC_GUID_FORMAT "/%u not found.\n",
+ UVC_GUID_ARGS(entity->extension.guidExtensionCode),
+ xctrl->selector);
+ return -EINVAL;
+ }
+
+ /* Validate control data size. */
+ if (ctrl->info->size != xctrl->size)
+ return -EINVAL;
+
+ if ((set && !(ctrl->info->flags & UVC_CONTROL_SET_CUR)) ||
+ (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR)))
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&video->ctrl_mutex))
+ return -ERESTARTSYS;
+
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ xctrl->size);
+ data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);
+
+ if (set && copy_from_user(data, xctrl->data, xctrl->size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = uvc_query_ctrl(video->dev, set ? SET_CUR : GET_CUR, xctrl->unit,
+ video->dev->intfnum, xctrl->selector, data,
+ xctrl->size);
+ if (ret < 0)
+ goto out;
+
+ if (!set && copy_to_user(xctrl->data, data, xctrl->size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+out:
+ if (ret)
+ memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+ xctrl->size);
+
+ mutex_unlock(&video->ctrl_mutex);
+ return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+/*
+ * Restore control values after resume, skipping controls that haven't been
+ * changed.
+ *
+ * TODO
+ * - Don't restore modified controls that are back to their default value.
+ * - Handle restore order (Auto-Exposure Mode should be restored before
+ * Exposure Time).
+ */
+int uvc_ctrl_resume_device(struct uvc_device *dev)
+{
+ struct uvc_control *ctrl;
+ struct uvc_entity *entity;
+ unsigned int i;
+ int ret;
+
+ /* Walk the entities list and restore controls when possible. */
+ list_for_each_entry(entity, &dev->entities, list) {
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+
+ if (ctrl->info == NULL || !ctrl->modified ||
+ (ctrl->info->flags & UVC_CONTROL_RESTORE) == 0)
+ continue;
+
+ printk(KERN_INFO "restoring control " UVC_GUID_FORMAT
+ "/%u/%u\n", UVC_GUID_ARGS(ctrl->info->entity),
+ ctrl->info->index, ctrl->info->selector);
+ ctrl->dirty = 1;
+ }
+
+ ret = uvc_ctrl_commit_entity(dev, entity, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Control and mapping handling
+ */
+
+static void uvc_ctrl_add_ctrl(struct uvc_device *dev,
+ struct uvc_control_info *info)
+{
+ struct uvc_entity *entity;
+ struct uvc_control *ctrl = NULL;
+ int ret, found = 0;
+ unsigned int i;
+
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (!uvc_entity_match_guid(entity, info->entity))
+ continue;
+
+ for (i = 0; i < entity->ncontrols; ++i) {
+ ctrl = &entity->controls[i];
+ if (ctrl->index == info->index) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ break;
+ }
+
+ if (!found)
+ return;
+
+ if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) {
+ /* Check if the device control information and length match
+ * the user supplied information.
+ */
+ __u32 flags;
+ __le16 size;
+ __u8 inf;
+
+ if ((ret = uvc_query_ctrl(dev, GET_LEN, ctrl->entity->id,
+ dev->intfnum, info->selector, (__u8 *)&size, 2)) < 0) {
+ uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on "
+ "control " UVC_GUID_FORMAT "/%u (%d).\n",
+ UVC_GUID_ARGS(info->entity), info->selector,
+ ret);
+ return;
+ }
+
+ if (info->size != le16_to_cpu(size)) {
+ uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT
+ "/%u size doesn't match user supplied "
+ "value.\n", UVC_GUID_ARGS(info->entity),
+ info->selector);
+ return;
+ }
+
+ if ((ret = uvc_query_ctrl(dev, GET_INFO, ctrl->entity->id,
+ dev->intfnum, info->selector, &inf, 1)) < 0) {
+ uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on "
+ "control " UVC_GUID_FORMAT "/%u (%d).\n",
+ UVC_GUID_ARGS(info->entity), info->selector,
+ ret);
+ return;
+ }
+
+ flags = info->flags;
+ if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) ||
+ ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) {
+ uvc_trace(UVC_TRACE_CONTROL, "Control "
+ UVC_GUID_FORMAT "/%u flags don't match "
+ "supported operations.\n",
+ UVC_GUID_ARGS(info->entity), info->selector);
+ return;
+ }
+ }
+
+ ctrl->info = info;
+ ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL);
+ uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u "
+ "to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity),
+ ctrl->info->selector, dev->udev->devpath, entity->id);
+}
+
+/*
+ * Add an item to the UVC control information list, and instantiate a control
+ * structure for each device that supports the control.
+ */
+int uvc_ctrl_add_info(struct uvc_control_info *info)
+{
+ struct uvc_control_info *ctrl;
+ struct uvc_device *dev;
+ int ret = 0;
+
+ /* Find matching controls by walking the devices, entities and
+ * controls list.
+ */
+ mutex_lock(&uvc_driver.ctrl_mutex);
+
+ /* First check if the list contains a control matching the new one.
+ * Bail out if it does.
+ */
+ list_for_each_entry(ctrl, &uvc_driver.controls, list) {
+ if (memcmp(ctrl->entity, info->entity, 16))
+ continue;
+
+ if (ctrl->selector == info->selector) {
+ uvc_trace(UVC_TRACE_CONTROL, "Control "
+ UVC_GUID_FORMAT "/%u is already defined.\n",
+ UVC_GUID_ARGS(info->entity), info->selector);
+ ret = -EEXIST;
+ goto end;
+ }
+ if (ctrl->index == info->index) {
+ uvc_trace(UVC_TRACE_CONTROL, "Control "
+ UVC_GUID_FORMAT "/%u would overwrite index "
+ "%d.\n", UVC_GUID_ARGS(info->entity),
+ info->selector, info->index);
+ ret = -EEXIST;
+ goto end;
+ }
+ }
+
+ list_for_each_entry(dev, &uvc_driver.devices, list)
+ uvc_ctrl_add_ctrl(dev, info);
+
+ INIT_LIST_HEAD(&info->mappings);
+ list_add_tail(&info->list, &uvc_driver.controls);
+end:
+ mutex_unlock(&uvc_driver.ctrl_mutex);
+ return ret;
+}
+
+int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
+{
+ struct uvc_control_info *info;
+ struct uvc_control_mapping *map;
+ int ret = -EINVAL;
+
+ if (mapping->id & ~V4L2_CTRL_ID_MASK) {
+ uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with "
+ "invalid control id 0x%08x\n", mapping->name,
+ mapping->id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&uvc_driver.ctrl_mutex);
+ list_for_each_entry(info, &uvc_driver.controls, list) {
+ if (memcmp(info->entity, mapping->entity, 16) ||
+ info->selector != mapping->selector)
+ continue;
+
+ if (info->size * 8 < mapping->size + mapping->offset) {
+ uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would "
+ "overflow control " UVC_GUID_FORMAT "/%u\n",
+ mapping->name, UVC_GUID_ARGS(info->entity),
+ info->selector);
+ ret = -EOVERFLOW;
+ goto end;
+ }
+
+ /* Check if the list contains a mapping matching the new one.
+ * Bail out if it does.
+ */
+ list_for_each_entry(map, &info->mappings, list) {
+ if (map->id == mapping->id) {
+ uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is "
+ "already defined.\n", mapping->name);
+ ret = -EEXIST;
+ goto end;
+ }
+ }
+
+ mapping->ctrl = info;
+ list_add_tail(&mapping->list, &info->mappings);
+ uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control "
+ UVC_GUID_FORMAT "/%u.\n", mapping->name,
+ UVC_GUID_ARGS(info->entity), info->selector);
+
+ ret = 0;
+ break;
+ }
+end:
+ mutex_unlock(&uvc_driver.ctrl_mutex);
+ return ret;
+}
+
+/*
+ * Initialize device controls.
+ */
+int uvc_ctrl_init_device(struct uvc_device *dev)
+{
+ struct uvc_control_info *info;
+ struct uvc_control *ctrl;
+ struct uvc_entity *entity;
+ unsigned int i;
+
+ /* Walk the entities list and instantiate controls */
+ list_for_each_entry(entity, &dev->entities, list) {
+ unsigned int bControlSize = 0, ncontrols = 0;
+ __u8 *bmControls = NULL;
+
+ if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) {
+ bmControls = entity->extension.bmControls;
+ bControlSize = entity->extension.bControlSize;
+ } else if (UVC_ENTITY_TYPE(entity) == VC_PROCESSING_UNIT) {
+ bmControls = entity->processing.bmControls;
+ bControlSize = entity->processing.bControlSize;
+ } else if (UVC_ENTITY_TYPE(entity) == ITT_CAMERA) {
+ bmControls = entity->camera.bmControls;
+ bControlSize = entity->camera.bControlSize;
+ }
+
+ for (i = 0; i < bControlSize; ++i)
+ ncontrols += hweight8(bmControls[i]);
+
+ if (ncontrols == 0)
+ continue;
+
+ entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL);
+ if (entity->controls == NULL)
+ return -ENOMEM;
+
+ entity->ncontrols = ncontrols;
+
+ ctrl = entity->controls;
+ for (i = 0; i < bControlSize * 8; ++i) {
+ if (uvc_get_bit(bmControls, i) == 0)
+ continue;
+
+ ctrl->entity = entity;
+ ctrl->index = i;
+ ctrl++;
+ }
+ }
+
+ /* Walk the controls info list and associate them with the device
+ * controls, then add the device to the global device list. This has
+ * to be done while holding the controls lock, to make sure
+ * uvc_ctrl_add_info() will not get called in-between.
+ */
+ mutex_lock(&uvc_driver.ctrl_mutex);
+ list_for_each_entry(info, &uvc_driver.controls, list)
+ uvc_ctrl_add_ctrl(dev, info);
+
+ list_add_tail(&dev->list, &uvc_driver.devices);
+ mutex_unlock(&uvc_driver.ctrl_mutex);
+
+ return 0;
+}
+
+/*
+ * Cleanup device controls.
+ */
+void uvc_ctrl_cleanup_device(struct uvc_device *dev)
+{
+ struct uvc_entity *entity;
+ unsigned int i;
+
+ /* Remove the device from the global devices list */
+ mutex_lock(&uvc_driver.ctrl_mutex);
+ if (dev->list.next != NULL)
+ list_del(&dev->list);
+ mutex_unlock(&uvc_driver.ctrl_mutex);
+
+ list_for_each_entry(entity, &dev->entities, list) {
+ for (i = 0; i < entity->ncontrols; ++i)
+ kfree(entity->controls[i].data);
+
+ kfree(entity->controls);
+ }
+}
+
+void uvc_ctrl_init(void)
+{
+ struct uvc_control_info *ctrl = uvc_ctrls;
+ struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls);
+ struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
+ struct uvc_control_mapping *mend =
+ mapping + ARRAY_SIZE(uvc_ctrl_mappings);
+
+ for (; ctrl < cend; ++ctrl)
+ uvc_ctrl_add_info(ctrl);
+
+ for (; mapping < mend; ++mapping)
+ uvc_ctrl_add_mapping(mapping);
+}
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
new file mode 100644
index 000000000000..60ced589f898
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -0,0 +1,1955 @@
+/*
+ * uvc_driver.c -- USB Video Class driver
+ *
+ * Copyright (C) 2005-2008
+ * Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This driver aims to support video input devices compliant with the 'USB
+ * Video Class' specification.
+ *
+ * The driver doesn't support the deprecated v4l1 interface. It implements the
+ * mmap capture method only, and doesn't do any image format conversion in
+ * software. If your user-space application doesn't support YUYV or MJPEG, fix
+ * it :-). Please note that the MJPEG data have been stripped from their
+ * Huffman tables (DHT marker), you will need to add it back if your JPEG
+ * codec can't handle MJPEG data.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+#define DRIVER_AUTHOR "Laurent Pinchart <laurent.pinchart@skynet.be>"
+#define DRIVER_DESC "USB Video Class driver"
+#ifndef DRIVER_VERSION
+#define DRIVER_VERSION "v0.1.0"
+#endif
+
+static unsigned int uvc_quirks_param;
+unsigned int uvc_trace_param;
+
+/* ------------------------------------------------------------------------
+ * Control, formats, ...
+ */
+
+static struct uvc_format_desc uvc_fmts[] = {
+ {
+ .name = "YUV 4:2:2 (YUYV)",
+ .guid = UVC_GUID_FORMAT_YUY2,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .name = "YUV 4:2:0 (NV12)",
+ .guid = UVC_GUID_FORMAT_NV12,
+ .fcc = V4L2_PIX_FMT_NV12,
+ },
+ {
+ .name = "MJPEG",
+ .guid = UVC_GUID_FORMAT_MJPEG,
+ .fcc = V4L2_PIX_FMT_MJPEG,
+ },
+ {
+ .name = "YVU 4:2:0 (YV12)",
+ .guid = UVC_GUID_FORMAT_YV12,
+ .fcc = V4L2_PIX_FMT_YVU420,
+ },
+ {
+ .name = "YUV 4:2:0 (I420)",
+ .guid = UVC_GUID_FORMAT_I420,
+ .fcc = V4L2_PIX_FMT_YUV420,
+ },
+ {
+ .name = "YUV 4:2:2 (UYVY)",
+ .guid = UVC_GUID_FORMAT_UYVY,
+ .fcc = V4L2_PIX_FMT_UYVY,
+ },
+ {
+ .name = "Greyscale",
+ .guid = UVC_GUID_FORMAT_Y800,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .name = "RGB Bayer",
+ .guid = UVC_GUID_FORMAT_BY8,
+ .fcc = V4L2_PIX_FMT_SBGGR8,
+ },
+};
+
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
+ __u8 epaddr)
+{
+ struct usb_host_endpoint *ep;
+ unsigned int i;
+
+ for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
+ ep = &alts->endpoint[i];
+ if (ep->desc.bEndpointAddress == epaddr)
+ return ep;
+ }
+
+ return NULL;
+}
+
+static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
+{
+ unsigned int len = ARRAY_SIZE(uvc_fmts);
+ unsigned int i;
+
+ for (i = 0; i < len; ++i) {
+ if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
+ return &uvc_fmts[i];
+ }
+
+ return NULL;
+}
+
+static __u32 uvc_colorspace(const __u8 primaries)
+{
+ static const __u8 colorprimaries[] = {
+ 0,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_COLORSPACE_470_SYSTEM_M,
+ V4L2_COLORSPACE_470_SYSTEM_BG,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_COLORSPACE_SMPTE240M,
+ };
+
+ if (primaries < ARRAY_SIZE(colorprimaries))
+ return colorprimaries[primaries];
+
+ return 0;
+}
+
+/* Simplify a fraction using a simple continued fraction decomposition. The
+ * idea here is to convert fractions such as 333333/10000000 to 1/30 using
+ * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
+ * arbitrary parameters to remove non-significative terms from the simple
+ * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
+ * respectively seems to give nice results.
+ */
+void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
+ unsigned int n_terms, unsigned int threshold)
+{
+ uint32_t *an;
+ uint32_t x, y, r;
+ unsigned int i, n;
+
+ an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);
+ if (an == NULL)
+ return;
+
+ /* Convert the fraction to a simple continued fraction. See
+ * http://mathforum.org/dr.math/faq/faq.fractions.html
+ * Stop if the current term is bigger than or equal to the given
+ * threshold.
+ */
+ x = *numerator;
+ y = *denominator;
+
+ for (n = 0; n < n_terms && y != 0; ++n) {
+ an[n] = x / y;
+ if (an[n] >= threshold) {
+ if (n < 2)
+ n++;
+ break;
+ }
+
+ r = x - an[n] * y;
+ x = y;
+ y = r;
+ }
+
+ /* Expand the simple continued fraction back to an integer fraction. */
+ x = 0;
+ y = 1;
+
+ for (i = n; i > 0; --i) {
+ r = y;
+ y = an[i-1] * y + x;
+ x = r;
+ }
+
+ *numerator = y;
+ *denominator = x;
+ kfree(an);
+}
+
+/* Convert a fraction to a frame interval in 100ns multiples. The idea here is
+ * to compute numerator / denominator * 10000000 using 32 bit fixed point
+ * arithmetic only.
+ */
+uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
+{
+ uint32_t multiplier;
+
+ /* Saturate the result if the operation would overflow. */
+ if (denominator == 0 ||
+ numerator/denominator >= ((uint32_t)-1)/10000000)
+ return (uint32_t)-1;
+
+ /* Divide both the denominator and the multiplier by two until
+ * numerator * multiplier doesn't overflow. If anyone knows a better
+ * algorithm please let me know.
+ */
+ multiplier = 10000000;
+ while (numerator > ((uint32_t)-1)/multiplier) {
+ multiplier /= 2;
+ denominator /= 2;
+ }
+
+ return denominator ? numerator * multiplier / denominator : 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Terminal and unit management
+ */
+
+static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
+{
+ struct uvc_entity *entity;
+
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (entity->id == id)
+ return entity;
+ }
+
+ return NULL;
+}
+
+static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
+ int id, struct uvc_entity *entity)
+{
+ unsigned int i;
+
+ if (entity == NULL)
+ entity = list_entry(&dev->entities, struct uvc_entity, list);
+
+ list_for_each_entry_continue(entity, &dev->entities, list) {
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case TT_STREAMING:
+ if (entity->output.bSourceID == id)
+ return entity;
+ break;
+
+ case VC_PROCESSING_UNIT:
+ if (entity->processing.bSourceID == id)
+ return entity;
+ break;
+
+ case VC_SELECTOR_UNIT:
+ for (i = 0; i < entity->selector.bNrInPins; ++i)
+ if (entity->selector.baSourceID[i] == id)
+ return entity;
+ break;
+
+ case VC_EXTENSION_UNIT:
+ for (i = 0; i < entity->extension.bNrInPins; ++i)
+ if (entity->extension.baSourceID[i] == id)
+ return entity;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+/* ------------------------------------------------------------------------
+ * Descriptors handling
+ */
+
+static int uvc_parse_format(struct uvc_device *dev,
+ struct uvc_streaming *streaming, struct uvc_format *format,
+ __u32 **intervals, unsigned char *buffer, int buflen)
+{
+ struct usb_interface *intf = streaming->intf;
+ struct usb_host_interface *alts = intf->cur_altsetting;
+ struct uvc_format_desc *fmtdesc;
+ struct uvc_frame *frame;
+ const unsigned char *start = buffer;
+ unsigned int interval;
+ unsigned int i, n;
+ __u8 ftype;
+
+ format->type = buffer[2];
+ format->index = buffer[3];
+
+ switch (buffer[2]) {
+ case VS_FORMAT_UNCOMPRESSED:
+ case VS_FORMAT_FRAME_BASED:
+ if (buflen < 27) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Find the format descriptor from its GUID. */
+ fmtdesc = uvc_format_by_guid(&buffer[5]);
+
+ if (fmtdesc != NULL) {
+ strncpy(format->name, fmtdesc->name,
+ sizeof format->name);
+ format->fcc = fmtdesc->fcc;
+ } else {
+ uvc_printk(KERN_INFO, "Unknown video format "
+ UVC_GUID_FORMAT "\n",
+ UVC_GUID_ARGS(&buffer[5]));
+ snprintf(format->name, sizeof format->name,
+ UVC_GUID_FORMAT, UVC_GUID_ARGS(&buffer[5]));
+ format->fcc = 0;
+ }
+
+ format->bpp = buffer[21];
+ if (buffer[2] == VS_FORMAT_UNCOMPRESSED) {
+ ftype = VS_FRAME_UNCOMPRESSED;
+ } else {
+ ftype = VS_FRAME_FRAME_BASED;
+ if (buffer[27])
+ format->flags = UVC_FMT_FLAG_COMPRESSED;
+ }
+ break;
+
+ case VS_FORMAT_MJPEG:
+ if (buflen < 11) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ strncpy(format->name, "MJPEG", sizeof format->name);
+ format->fcc = V4L2_PIX_FMT_MJPEG;
+ format->flags = UVC_FMT_FLAG_COMPRESSED;
+ format->bpp = 0;
+ ftype = VS_FRAME_MJPEG;
+ break;
+
+ case VS_FORMAT_DV:
+ if (buflen < 9) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ switch (buffer[8] & 0x7f) {
+ case 0:
+ strncpy(format->name, "SD-DV", sizeof format->name);
+ break;
+ case 1:
+ strncpy(format->name, "SDL-DV", sizeof format->name);
+ break;
+ case 2:
+ strncpy(format->name, "HD-DV", sizeof format->name);
+ break;
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d: unknown DV format %u\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber, buffer[8]);
+ return -EINVAL;
+ }
+
+ strncat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
+ sizeof format->name);
+
+ format->fcc = V4L2_PIX_FMT_DV;
+ format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
+ format->bpp = 0;
+ ftype = 0;
+
+ /* Create a dummy frame descriptor. */
+ frame = &format->frame[0];
+ memset(&format->frame[0], 0, sizeof format->frame[0]);
+ frame->bFrameIntervalType = 1;
+ frame->dwDefaultFrameInterval = 1;
+ frame->dwFrameInterval = *intervals;
+ *(*intervals)++ = 1;
+ format->nframes = 1;
+ break;
+
+ case VS_FORMAT_MPEG2TS:
+ case VS_FORMAT_STREAM_BASED:
+ /* Not supported yet. */
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d unsupported format %u\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber,
+ buffer[2]);
+ return -EINVAL;
+ }
+
+ uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+
+ /* Parse the frame descriptors. Only uncompressed, MJPEG and frame
+ * based formats have frame descriptors.
+ */
+ while (buflen > 2 && buffer[2] == ftype) {
+ frame = &format->frame[format->nframes];
+
+ if (ftype != VS_FRAME_FRAME_BASED)
+ n = buflen > 25 ? buffer[25] : 0;
+ else
+ n = buflen > 21 ? buffer[21] : 0;
+
+ n = n ? n : 3;
+
+ if (buflen < 26 + 4*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d FRAME error\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ frame->bFrameIndex = buffer[3];
+ frame->bmCapabilities = buffer[4];
+ frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]);
+ frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]);
+ frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]);
+ frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]);
+ if (ftype != VS_FRAME_FRAME_BASED) {
+ frame->dwMaxVideoFrameBufferSize =
+ le32_to_cpup((__le32 *)&buffer[17]);
+ frame->dwDefaultFrameInterval =
+ le32_to_cpup((__le32 *)&buffer[21]);
+ frame->bFrameIntervalType = buffer[25];
+ } else {
+ frame->dwMaxVideoFrameBufferSize = 0;
+ frame->dwDefaultFrameInterval =
+ le32_to_cpup((__le32 *)&buffer[17]);
+ frame->bFrameIntervalType = buffer[21];
+ }
+ frame->dwFrameInterval = *intervals;
+
+ /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize
+ * completely. Observed behaviours range from setting the
+ * value to 1.1x the actual frame size of hardwiring the
+ * 16 low bits to 0. This results in a higher than necessary
+ * memory usage as well as a wrong image size information. For
+ * uncompressed formats this can be fixed by computing the
+ * value from the frame size.
+ */
+ if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
+ frame->dwMaxVideoFrameBufferSize = format->bpp
+ * frame->wWidth * frame->wHeight / 8;
+
+ /* Some bogus devices report dwMinFrameInterval equal to
+ * dwMaxFrameInterval and have dwFrameIntervalStep set to
+ * zero. Setting all null intervals to 1 fixes the problem and
+ * some other divisions by zero which could happen.
+ */
+ for (i = 0; i < n; ++i) {
+ interval = le32_to_cpup((__le32 *)&buffer[26+4*i]);
+ *(*intervals)++ = interval ? interval : 1;
+ }
+
+ /* Make sure that the default frame interval stays between
+ * the boundaries.
+ */
+ n -= frame->bFrameIntervalType ? 1 : 2;
+ frame->dwDefaultFrameInterval =
+ min(frame->dwFrameInterval[n],
+ max(frame->dwFrameInterval[0],
+ frame->dwDefaultFrameInterval));
+
+ uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
+ frame->wWidth, frame->wHeight,
+ 10000000/frame->dwDefaultFrameInterval,
+ (100000000/frame->dwDefaultFrameInterval)%10);
+
+ format->nframes++;
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen > 2 && buffer[2] == VS_STILL_IMAGE_FRAME) {
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen > 2 && buffer[2] == VS_COLORFORMAT) {
+ if (buflen < 6) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d COLORFORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ format->colorspace = uvc_colorspace(buffer[3]);
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ return buffer - start;
+}
+
+static int uvc_parse_streaming(struct uvc_device *dev,
+ struct usb_interface *intf)
+{
+ struct uvc_streaming *streaming = NULL;
+ struct uvc_format *format;
+ struct uvc_frame *frame;
+ struct usb_host_interface *alts = &intf->altsetting[0];
+ unsigned char *_buffer, *buffer = alts->extra;
+ int _buflen, buflen = alts->extralen;
+ unsigned int nformats = 0, nframes = 0, nintervals = 0;
+ unsigned int size, i, n, p;
+ __u32 *interval;
+ __u16 psize;
+ int ret = -EINVAL;
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass
+ != SC_VIDEOSTREAMING) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
+ "video streaming interface\n", dev->udev->devnum,
+ intf->altsetting[0].desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
+ "claimed\n", dev->udev->devnum,
+ intf->altsetting[0].desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
+ if (streaming == NULL) {
+ usb_driver_release_interface(&uvc_driver.driver, intf);
+ return -EINVAL;
+ }
+
+ mutex_init(&streaming->mutex);
+ streaming->intf = usb_get_intf(intf);
+ streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ /* The Pico iMage webcam has its class-specific interface descriptors
+ * after the endpoint descriptors.
+ */
+ if (buflen == 0) {
+ for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
+ struct usb_host_endpoint *ep = &alts->endpoint[i];
+
+ if (ep->extralen == 0)
+ continue;
+
+ if (ep->extralen > 2 &&
+ ep->extra[1] == USB_DT_CS_INTERFACE) {
+ uvc_trace(UVC_TRACE_DESCR, "trying extra data "
+ "from endpoint %u.\n", i);
+ buffer = alts->endpoint[i].extra;
+ buflen = alts->endpoint[i].extralen;
+ break;
+ }
+ }
+ }
+
+ /* Skip the standard interface descriptors. */
+ while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen <= 2) {
+ uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
+ "interface descriptors found.\n");
+ goto error;
+ }
+
+ /* Parse the header descriptor. */
+ if (buffer[2] == VS_OUTPUT_HEADER) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d OUTPUT HEADER descriptor is not supported.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ goto error;
+ } else if (buffer[2] == VS_INPUT_HEADER) {
+ p = buflen >= 5 ? buffer[3] : 0;
+ n = buflen >= 12 ? buffer[12] : 0;
+
+ if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d INPUT HEADER descriptor is "
+ "invalid.\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ streaming->header.bNumFormats = p;
+ streaming->header.bEndpointAddress = buffer[6];
+ streaming->header.bmInfo = buffer[7];
+ streaming->header.bTerminalLink = buffer[8];
+ streaming->header.bStillCaptureMethod = buffer[9];
+ streaming->header.bTriggerSupport = buffer[10];
+ streaming->header.bTriggerUsage = buffer[11];
+ streaming->header.bControlSize = n;
+
+ streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
+ if (streaming->header.bmaControls == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ memcpy(streaming->header.bmaControls, &buffer[13], p*n);
+ } else {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d HEADER descriptor not found.\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+
+ _buffer = buffer;
+ _buflen = buflen;
+
+ /* Count the format and frame descriptors. */
+ while (_buflen > 2) {
+ switch (_buffer[2]) {
+ case VS_FORMAT_UNCOMPRESSED:
+ case VS_FORMAT_MJPEG:
+ case VS_FORMAT_FRAME_BASED:
+ nformats++;
+ break;
+
+ case VS_FORMAT_DV:
+ /* DV format has no frame descriptor. We will create a
+ * dummy frame descriptor with a dummy frame interval.
+ */
+ nformats++;
+ nframes++;
+ nintervals++;
+ break;
+
+ case VS_FORMAT_MPEG2TS:
+ case VS_FORMAT_STREAM_BASED:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT %u is not supported.\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber, _buffer[2]);
+ break;
+
+ case VS_FRAME_UNCOMPRESSED:
+ case VS_FRAME_MJPEG:
+ nframes++;
+ if (_buflen > 25)
+ nintervals += _buffer[25] ? _buffer[25] : 3;
+ break;
+
+ case VS_FRAME_FRAME_BASED:
+ nframes++;
+ if (_buflen > 21)
+ nintervals += _buffer[21] ? _buffer[21] : 3;
+ break;
+ }
+
+ _buflen -= _buffer[0];
+ _buffer += _buffer[0];
+ }
+
+ if (nformats == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d has no supported formats defined.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ size = nformats * sizeof *format + nframes * sizeof *frame
+ + nintervals * sizeof *interval;
+ format = kzalloc(size, GFP_KERNEL);
+ if (format == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ frame = (struct uvc_frame *)&format[nformats];
+ interval = (__u32 *)&frame[nframes];
+
+ streaming->format = format;
+ streaming->nformats = nformats;
+
+ /* Parse the format descriptors. */
+ while (buflen > 2) {
+ switch (buffer[2]) {
+ case VS_FORMAT_UNCOMPRESSED:
+ case VS_FORMAT_MJPEG:
+ case VS_FORMAT_DV:
+ case VS_FORMAT_FRAME_BASED:
+ format->frame = frame;
+ ret = uvc_parse_format(dev, streaming, format,
+ &interval, buffer, buflen);
+ if (ret < 0)
+ goto error;
+
+ frame += format->nframes;
+ format++;
+
+ buflen -= ret;
+ buffer += ret;
+ continue;
+
+ default:
+ break;
+ }
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ /* Parse the alternate settings to find the maximum bandwidth. */
+ for (i = 0; i < intf->num_altsetting; ++i) {
+ struct usb_host_endpoint *ep;
+ alts = &intf->altsetting[i];
+ ep = uvc_find_endpoint(alts,
+ streaming->header.bEndpointAddress);
+ if (ep == NULL)
+ continue;
+
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ if (psize > streaming->maxpsize)
+ streaming->maxpsize = psize;
+ }
+
+ list_add_tail(&streaming->list, &dev->streaming);
+ return 0;
+
+error:
+ usb_driver_release_interface(&uvc_driver.driver, intf);
+ usb_put_intf(intf);
+ kfree(streaming->format);
+ kfree(streaming->header.bmaControls);
+ kfree(streaming);
+ return ret;
+}
+
+/* Parse vendor-specific extensions. */
+static int uvc_parse_vendor_control(struct uvc_device *dev,
+ const unsigned char *buffer, int buflen)
+{
+ struct usb_device *udev = dev->udev;
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ struct uvc_entity *unit;
+ unsigned int n, p;
+ int handled = 0;
+
+ switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
+ case 0x046d: /* Logitech */
+ if (buffer[1] != 0x41 || buffer[2] != 0x01)
+ break;
+
+ /* Logitech implements several vendor specific functions
+ * through vendor specific extension units (LXU).
+ *
+ * The LXU descriptors are similar to XU descriptors
+ * (see "USB Device Video Class for Video Devices", section
+ * 3.7.2.6 "Extension Unit Descriptor") with the following
+ * differences:
+ *
+ * ----------------------------------------------------------
+ * 0 bLength 1 Number
+ * Size of this descriptor, in bytes: 24+p+n*2
+ * ----------------------------------------------------------
+ * 23+p+n bmControlsType N Bitmap
+ * Individual bits in the set are defined:
+ * 0: Absolute
+ * 1: Relative
+ *
+ * This bitset is mapped exactly the same as bmControls.
+ * ----------------------------------------------------------
+ * 23+p+n*2 bReserved 1 Boolean
+ * ----------------------------------------------------------
+ * 24+p+n*2 iExtension 1 Index
+ * Index of a string descriptor that describes this
+ * extension unit.
+ * ----------------------------------------------------------
+ */
+ p = buflen >= 22 ? buffer[21] : 0;
+ n = buflen >= 25 + p ? buffer[22+p] : 0;
+
+ if (buflen < 25 + p + 2*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d EXTENSION_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ break;
+ }
+
+ unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ unit->id = buffer[3];
+ unit->type = VC_EXTENSION_UNIT;
+ memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
+ unit->extension.bNumControls = buffer[20];
+ unit->extension.bNrInPins =
+ le16_to_cpup((__le16 *)&buffer[21]);
+ unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
+ memcpy(unit->extension.baSourceID, &buffer[22], p);
+ unit->extension.bControlSize = buffer[22+p];
+ unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;
+ unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit
+ + p + n;
+ memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
+
+ if (buffer[24+p+2*n] != 0)
+ usb_string(udev, buffer[24+p+2*n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Extension %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ handled = 1;
+ break;
+ }
+
+ return handled;
+}
+
+static int uvc_parse_standard_control(struct uvc_device *dev,
+ const unsigned char *buffer, int buflen)
+{
+ struct usb_device *udev = dev->udev;
+ struct uvc_entity *unit, *term;
+ struct usb_interface *intf;
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ unsigned int i, n, p, len;
+ __u16 type;
+
+ switch (buffer[2]) {
+ case VC_HEADER:
+ n = buflen >= 12 ? buffer[11] : 0;
+
+ if (buflen < 12 || buflen < 12 + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d HEADER error\n", udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]);
+ dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]);
+
+ /* Parse all USB Video Streaming interfaces. */
+ for (i = 0; i < n; ++i) {
+ intf = usb_ifnum_to_if(udev, buffer[12+i]);
+ if (intf == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d "
+ "interface %d doesn't exists\n",
+ udev->devnum, i);
+ continue;
+ }
+
+ uvc_parse_streaming(dev, intf);
+ }
+ break;
+
+ case VC_INPUT_TERMINAL:
+ if (buflen < 8) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Make sure the terminal type MSB is not null, otherwise it
+ * could be confused with a unit.
+ */
+ type = le16_to_cpup((__le16 *)&buffer[4]);
+ if ((type & 0xff00) == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL %d has invalid "
+ "type 0x%04x, skipping\n", udev->devnum,
+ alts->desc.bInterfaceNumber,
+ buffer[3], type);
+ return 0;
+ }
+
+ n = 0;
+ p = 0;
+ len = 8;
+
+ if (type == ITT_CAMERA) {
+ n = buflen >= 15 ? buffer[14] : 0;
+ len = 15;
+
+ } else if (type == ITT_MEDIA_TRANSPORT_INPUT) {
+ n = buflen >= 9 ? buffer[8] : 0;
+ p = buflen >= 10 + n ? buffer[9+n] : 0;
+ len = 10;
+ }
+
+ if (buflen < len + n + p) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ term = kzalloc(sizeof *term + n + p, GFP_KERNEL);
+ if (term == NULL)
+ return -ENOMEM;
+
+ term->id = buffer[3];
+ term->type = type | UVC_TERM_INPUT;
+
+ if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) {
+ term->camera.bControlSize = n;
+ term->camera.bmControls = (__u8 *)term + sizeof *term;
+ term->camera.wObjectiveFocalLengthMin =
+ le16_to_cpup((__le16 *)&buffer[8]);
+ term->camera.wObjectiveFocalLengthMax =
+ le16_to_cpup((__le16 *)&buffer[10]);
+ term->camera.wOcularFocalLength =
+ le16_to_cpup((__le16 *)&buffer[12]);
+ memcpy(term->camera.bmControls, &buffer[15], n);
+ } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) {
+ term->media.bControlSize = n;
+ term->media.bmControls = (__u8 *)term + sizeof *term;
+ term->media.bTransportModeSize = p;
+ term->media.bmTransportModes = (__u8 *)term
+ + sizeof *term + n;
+ memcpy(term->media.bmControls, &buffer[9], n);
+ memcpy(term->media.bmTransportModes, &buffer[10+n], p);
+ }
+
+ if (buffer[7] != 0)
+ usb_string(udev, buffer[7], term->name,
+ sizeof term->name);
+ else if (UVC_ENTITY_TYPE(term) == ITT_CAMERA)
+ sprintf(term->name, "Camera %u", buffer[3]);
+ else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT)
+ sprintf(term->name, "Media %u", buffer[3]);
+ else
+ sprintf(term->name, "Input %u", buffer[3]);
+
+ list_add_tail(&term->list, &dev->entities);
+ break;
+
+ case VC_OUTPUT_TERMINAL:
+ if (buflen < 9) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d OUTPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Make sure the terminal type MSB is not null, otherwise it
+ * could be confused with a unit.
+ */
+ type = le16_to_cpup((__le16 *)&buffer[4]);
+ if ((type & 0xff00) == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d OUTPUT_TERMINAL %d has invalid "
+ "type 0x%04x, skipping\n", udev->devnum,
+ alts->desc.bInterfaceNumber, buffer[3], type);
+ return 0;
+ }
+
+ term = kzalloc(sizeof *term, GFP_KERNEL);
+ if (term == NULL)
+ return -ENOMEM;
+
+ term->id = buffer[3];
+ term->type = type | UVC_TERM_OUTPUT;
+ term->output.bSourceID = buffer[7];
+
+ if (buffer[8] != 0)
+ usb_string(udev, buffer[8], term->name,
+ sizeof term->name);
+ else
+ sprintf(term->name, "Output %u", buffer[3]);
+
+ list_add_tail(&term->list, &dev->entities);
+ break;
+
+ case VC_SELECTOR_UNIT:
+ p = buflen >= 5 ? buffer[4] : 0;
+
+ if (buflen < 5 || buflen < 6 + p) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d SELECTOR_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = kzalloc(sizeof *unit + p, GFP_KERNEL);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ unit->id = buffer[3];
+ unit->type = buffer[2];
+ unit->selector.bNrInPins = buffer[4];
+ unit->selector.baSourceID = (__u8 *)unit + sizeof *unit;
+ memcpy(unit->selector.baSourceID, &buffer[5], p);
+
+ if (buffer[5+p] != 0)
+ usb_string(udev, buffer[5+p], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Selector %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ case VC_PROCESSING_UNIT:
+ n = buflen >= 8 ? buffer[7] : 0;
+ p = dev->uvc_version >= 0x0110 ? 10 : 9;
+
+ if (buflen < p + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d PROCESSING_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = kzalloc(sizeof *unit + n, GFP_KERNEL);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ unit->id = buffer[3];
+ unit->type = buffer[2];
+ unit->processing.bSourceID = buffer[4];
+ unit->processing.wMaxMultiplier =
+ le16_to_cpup((__le16 *)&buffer[5]);
+ unit->processing.bControlSize = buffer[7];
+ unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
+ memcpy(unit->processing.bmControls, &buffer[8], n);
+ if (dev->uvc_version >= 0x0110)
+ unit->processing.bmVideoStandards = buffer[9+n];
+
+ if (buffer[8+n] != 0)
+ usb_string(udev, buffer[8+n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Processing %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ case VC_EXTENSION_UNIT:
+ p = buflen >= 22 ? buffer[21] : 0;
+ n = buflen >= 24 + p ? buffer[22+p] : 0;
+
+ if (buflen < 24 + p + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d EXTENSION_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ unit->id = buffer[3];
+ unit->type = buffer[2];
+ memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
+ unit->extension.bNumControls = buffer[20];
+ unit->extension.bNrInPins =
+ le16_to_cpup((__le16 *)&buffer[21]);
+ unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
+ memcpy(unit->extension.baSourceID, &buffer[22], p);
+ unit->extension.bControlSize = buffer[22+p];
+ unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;
+ memcpy(unit->extension.bmControls, &buffer[23+p], n);
+
+ if (buffer[23+p+n] != 0)
+ usb_string(udev, buffer[23+p+n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Extension %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
+ "descriptor (%u)\n", buffer[2]);
+ break;
+ }
+
+ return 0;
+}
+
+static int uvc_parse_control(struct uvc_device *dev)
+{
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ unsigned char *buffer = alts->extra;
+ int buflen = alts->extralen;
+ int ret;
+
+ /* Parse the default alternate setting only, as the UVC specification
+ * defines a single alternate setting, the default alternate setting
+ * zero.
+ */
+
+ while (buflen > 2) {
+ if (uvc_parse_vendor_control(dev, buffer, buflen) ||
+ buffer[1] != USB_DT_CS_INTERFACE)
+ goto next_descriptor;
+
+ if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
+ return ret;
+
+next_descriptor:
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ /* Check if the optional status endpoint is present. */
+ if (alts->desc.bNumEndpoints == 1) {
+ struct usb_host_endpoint *ep = &alts->endpoint[0];
+ struct usb_endpoint_descriptor *desc = &ep->desc;
+
+ if (usb_endpoint_is_int_in(desc) &&
+ le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
+ desc->bInterval != 0) {
+ uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
+ "(addr %02x).\n", desc->bEndpointAddress);
+ dev->int_ep = ep;
+ }
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * USB probe and disconnect
+ */
+
+/*
+ * Unregister the video devices.
+ */
+static void uvc_unregister_video(struct uvc_device *dev)
+{
+ if (dev->video.vdev) {
+ if (dev->video.vdev->minor == -1)
+ video_device_release(dev->video.vdev);
+ else
+ video_unregister_device(dev->video.vdev);
+ dev->video.vdev = NULL;
+ }
+}
+
+/*
+ * Scan the UVC descriptors to locate a chain starting at an Output Terminal
+ * and containing the following units:
+ *
+ * - a USB Streaming Output Terminal
+ * - zero or one Processing Unit
+ * - zero, one or mode single-input Selector Units
+ * - zero or one multiple-input Selector Units, provided all inputs are
+ * connected to input terminals
+ * - zero, one or mode single-input Extension Units
+ * - one Camera Input Terminal, or one or more External terminals.
+ *
+ * A side forward scan is made on each detected entity to check for additional
+ * extension units.
+ */
+static int uvc_scan_chain_entity(struct uvc_video_device *video,
+ struct uvc_entity *entity)
+{
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case VC_EXTENSION_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- XU %d", entity->id);
+
+ if (entity->extension.bNrInPins != 1) {
+ uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
+ "than 1 input pin.\n", entity->id);
+ return -1;
+ }
+
+ list_add_tail(&entity->chain, &video->extensions);
+ break;
+
+ case VC_PROCESSING_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- PU %d", entity->id);
+
+ if (video->processing != NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found multiple "
+ "Processing Units in chain.\n");
+ return -1;
+ }
+
+ video->processing = entity;
+ break;
+
+ case VC_SELECTOR_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- SU %d", entity->id);
+
+ /* Single-input selector units are ignored. */
+ if (entity->selector.bNrInPins == 1)
+ break;
+
+ if (video->selector != NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
+ "Units in chain.\n");
+ return -1;
+ }
+
+ video->selector = entity;
+ break;
+
+ case ITT_VENDOR_SPECIFIC:
+ case ITT_CAMERA:
+ case ITT_MEDIA_TRANSPORT_INPUT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT %d\n", entity->id);
+
+ list_add_tail(&entity->chain, &video->iterms);
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
+ "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int uvc_scan_chain_forward(struct uvc_video_device *video,
+ struct uvc_entity *entity, struct uvc_entity *prev)
+{
+ struct uvc_entity *forward;
+ int found;
+
+ /* Forward scan */
+ forward = NULL;
+ found = 0;
+
+ while (1) {
+ forward = uvc_entity_by_reference(video->dev, entity->id,
+ forward);
+ if (forward == NULL)
+ break;
+
+ if (UVC_ENTITY_TYPE(forward) != VC_EXTENSION_UNIT ||
+ forward == prev)
+ continue;
+
+ if (forward->extension.bNrInPins != 1) {
+ uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has"
+ "more than 1 input pin.\n", entity->id);
+ return -1;
+ }
+
+ list_add_tail(&forward->chain, &video->extensions);
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
+ if (!found)
+ printk(" (-> XU");
+
+ printk(" %d", forward->id);
+ found = 1;
+ }
+ }
+ if (found)
+ printk(")");
+
+ return 0;
+}
+
+static int uvc_scan_chain_backward(struct uvc_video_device *video,
+ struct uvc_entity *entity)
+{
+ struct uvc_entity *term;
+ int id = -1, i;
+
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case VC_EXTENSION_UNIT:
+ id = entity->extension.baSourceID[0];
+ break;
+
+ case VC_PROCESSING_UNIT:
+ id = entity->processing.bSourceID;
+ break;
+
+ case VC_SELECTOR_UNIT:
+ /* Single-input selector units are ignored. */
+ if (entity->selector.bNrInPins == 1) {
+ id = entity->selector.baSourceID[0];
+ break;
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT");
+
+ video->selector = entity;
+ for (i = 0; i < entity->selector.bNrInPins; ++i) {
+ id = entity->selector.baSourceID[i];
+ term = uvc_entity_by_id(video->dev, id);
+ if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
+ uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
+ "input %d isn't connected to an "
+ "input terminal\n", entity->id, i);
+ return -1;
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" %d", term->id);
+
+ list_add_tail(&term->chain, &video->iterms);
+ uvc_scan_chain_forward(video, term, entity);
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk("\n");
+
+ id = 0;
+ break;
+ }
+
+ return id;
+}
+
+static int uvc_scan_chain(struct uvc_video_device *video)
+{
+ struct uvc_entity *entity, *prev;
+ int id;
+
+ entity = video->oterm;
+ uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+ id = entity->output.bSourceID;
+ while (id != 0) {
+ prev = entity;
+ entity = uvc_entity_by_id(video->dev, id);
+ if (entity == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "unknown entity %d.\n", id);
+ return -1;
+ }
+
+ /* Process entity */
+ if (uvc_scan_chain_entity(video, entity) < 0)
+ return -1;
+
+ /* Forward scan */
+ if (uvc_scan_chain_forward(video, entity, prev) < 0)
+ return -1;
+
+ /* Stop when a terminal is found. */
+ if (!UVC_ENTITY_IS_UNIT(entity))
+ break;
+
+ /* Backward scan */
+ id = uvc_scan_chain_backward(video, entity);
+ if (id < 0)
+ return id;
+ }
+
+ /* Initialize the video buffers queue. */
+ uvc_queue_init(&video->queue);
+
+ return 0;
+}
+
+/*
+ * Register the video devices.
+ *
+ * The driver currently supports a single video device per control interface
+ * only. The terminal and units must match the following structure:
+ *
+ * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ *
+ * The Extension Units, if present, must have a single input pin. The
+ * Processing Unit and Extension Units can be in any order. Additional
+ * Extension Units connected to the main chain as single-unit branches are
+ * also supported.
+ */
+static int uvc_register_video(struct uvc_device *dev)
+{
+ struct video_device *vdev;
+ struct uvc_entity *term;
+ int found = 0, ret;
+
+ /* Check if the control interface matches the structure we expect. */
+ list_for_each_entry(term, &dev->entities, list) {
+ struct uvc_streaming *streaming;
+
+ if (UVC_ENTITY_TYPE(term) != TT_STREAMING)
+ continue;
+
+ memset(&dev->video, 0, sizeof dev->video);
+ mutex_init(&dev->video.ctrl_mutex);
+ INIT_LIST_HEAD(&dev->video.iterms);
+ INIT_LIST_HEAD(&dev->video.extensions);
+ dev->video.oterm = term;
+ dev->video.dev = dev;
+ if (uvc_scan_chain(&dev->video) < 0)
+ continue;
+
+ list_for_each_entry(streaming, &dev->streaming, list) {
+ if (streaming->header.bTerminalLink == term->id) {
+ dev->video.streaming = streaming;
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ uvc_printk(KERN_INFO, "No valid video chain found.\n");
+ return -1;
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
+ uvc_printk(KERN_INFO, "Found a valid video chain (");
+ list_for_each_entry(term, &dev->video.iterms, chain) {
+ printk("%d", term->id);
+ if (term->chain.next != &dev->video.iterms)
+ printk(",");
+ }
+ printk(" -> %d).\n", dev->video.oterm->id);
+ }
+
+ /* Initialize the streaming interface with default streaming
+ * parameters.
+ */
+ if ((ret = uvc_video_init(&dev->video)) < 0) {
+ uvc_printk(KERN_ERR, "Failed to initialize the device "
+ "(%d).\n", ret);
+ return ret;
+ }
+
+ /* Register the device with V4L. */
+ vdev = video_device_alloc();
+ if (vdev == NULL)
+ return -1;
+
+ /* We already hold a reference to dev->udev. The video device will be
+ * unregistered before the reference is released, so we don't need to
+ * get another one.
+ */
+ vdev->dev = &dev->intf->dev;
+ vdev->type = 0;
+ vdev->type2 = 0;
+ vdev->minor = -1;
+ vdev->fops = &uvc_fops;
+ vdev->release = video_device_release;
+ strncpy(vdev->name, dev->name, sizeof vdev->name);
+
+ /* Set the driver data before calling video_register_device, otherwise
+ * uvc_v4l2_open might race us.
+ *
+ * FIXME: usb_set_intfdata hasn't been called so far. Is that a
+ * problem ? Does any function which could be called here get
+ * a pointer to the usb_interface ?
+ */
+ dev->video.vdev = vdev;
+ video_set_drvdata(vdev, &dev->video);
+
+ if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
+ dev->video.vdev = NULL;
+ video_device_release(vdev);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Delete the UVC device.
+ *
+ * Called by the kernel when the last reference to the uvc_device structure
+ * is released.
+ *
+ * Unregistering the video devices is done here because every opened instance
+ * must be closed before the device can be unregistered. An alternative would
+ * have been to use another reference count for uvc_v4l2_open/uvc_release, and
+ * unregister the video devices on disconnect when that reference count drops
+ * to zero.
+ *
+ * As this function is called after or during disconnect(), all URBs have
+ * already been canceled by the USB core. There is no need to kill the
+ * interrupt URB manually.
+ */
+void uvc_delete(struct kref *kref)
+{
+ struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
+ struct list_head *p, *n;
+
+ /* Unregister the video device */
+ uvc_unregister_video(dev);
+ usb_put_intf(dev->intf);
+ usb_put_dev(dev->udev);
+
+ uvc_status_cleanup(dev);
+ uvc_ctrl_cleanup_device(dev);
+
+ list_for_each_safe(p, n, &dev->entities) {
+ struct uvc_entity *entity;
+ entity = list_entry(p, struct uvc_entity, list);
+ kfree(entity);
+ }
+
+ list_for_each_safe(p, n, &dev->streaming) {
+ struct uvc_streaming *streaming;
+ streaming = list_entry(p, struct uvc_streaming, list);
+ usb_driver_release_interface(&uvc_driver.driver,
+ streaming->intf);
+ usb_put_intf(streaming->intf);
+ kfree(streaming->format);
+ kfree(streaming->header.bmaControls);
+ kfree(streaming);
+ }
+
+ kfree(dev);
+}
+
+static int uvc_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct uvc_device *dev;
+ int ret;
+
+ if (id->idVendor && id->idProduct)
+ uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
+ "(%04x:%04x)\n", udev->devpath, id->idVendor,
+ id->idProduct);
+ else
+ uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
+ udev->devpath);
+
+ /* Allocate memory for the device and initialize it */
+ if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&dev->entities);
+ INIT_LIST_HEAD(&dev->streaming);
+ kref_init(&dev->kref);
+
+ dev->udev = usb_get_dev(udev);
+ dev->intf = usb_get_intf(intf);
+ dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+ dev->quirks = id->driver_info | uvc_quirks_param;
+
+ if (udev->product != NULL)
+ strncpy(dev->name, udev->product, sizeof dev->name);
+ else
+ snprintf(dev->name, sizeof dev->name,
+ "UVC Camera (%04x:%04x)",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ /* Parse the Video Class control descriptor */
+ if (uvc_parse_control(dev) < 0) {
+ uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
+ "descriptors.\n");
+ goto error;
+ }
+
+ uvc_printk(KERN_INFO, "Found UVC %u.%02u device %s (%04x:%04x)\n",
+ dev->uvc_version >> 8, dev->uvc_version & 0xff,
+ udev->product ? udev->product : "<unnamed>",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ if (uvc_quirks_param != 0) {
+ uvc_printk(KERN_INFO, "Forcing device quirks 0x%x by module "
+ "parameter for testing purpose.\n", uvc_quirks_param);
+ uvc_printk(KERN_INFO, "Please report required quirks to the "
+ "linux-uvc-devel mailing list.\n");
+ }
+
+ /* Initialize controls */
+ if (uvc_ctrl_init_device(dev) < 0)
+ goto error;
+
+ /* Register the video devices */
+ if (uvc_register_video(dev) < 0)
+ goto error;
+
+ /* Save our data pointer in the interface data */
+ usb_set_intfdata(intf, dev);
+
+ /* Initialize the interrupt URB */
+ if ((ret = uvc_status_init(dev)) < 0) {
+ uvc_printk(KERN_INFO, "Unable to initialize the status "
+ "endpoint (%d), status interrupt will not be "
+ "supported.\n", ret);
+ }
+
+ uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
+ return 0;
+
+error:
+ kref_put(&dev->kref, uvc_delete);
+ return -ENODEV;
+}
+
+static void uvc_disconnect(struct usb_interface *intf)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+
+ /* Set the USB interface data to NULL. This can be done outside the
+ * lock, as there's no other reader.
+ */
+ usb_set_intfdata(intf, NULL);
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOSTREAMING)
+ return;
+
+ /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide
+ * lock is needed to prevent uvc_disconnect from releasing its
+ * reference to the uvc_device instance after uvc_v4l2_open() received
+ * the pointer to the device (video_devdata) but before it got the
+ * chance to increase the reference count (kref_get).
+ */
+ mutex_lock(&uvc_driver.open_mutex);
+
+ dev->state |= UVC_DEV_DISCONNECTED;
+ kref_put(&dev->kref, uvc_delete);
+
+ mutex_unlock(&uvc_driver.open_mutex);
+}
+
+static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ /* Controls are cached on the fly so they don't need to be saved. */
+ if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL)
+ return uvc_status_suspend(dev);
+
+ if (dev->video.streaming->intf != intf) {
+ uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB "
+ "interface mismatch.\n");
+ return -EINVAL;
+ }
+
+ return uvc_video_suspend(&dev->video);
+}
+
+static int uvc_resume(struct usb_interface *intf)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+ int ret;
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) {
+ if ((ret = uvc_ctrl_resume_device(dev)) < 0)
+ return ret;
+
+ return uvc_status_resume(dev);
+ }
+
+ if (dev->video.streaming->intf != intf) {
+ uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB "
+ "interface mismatch.\n");
+ return -EINVAL;
+ }
+
+ return uvc_video_resume(&dev->video);
+}
+
+/* ------------------------------------------------------------------------
+ * Driver initialization and cleanup
+ */
+
+/*
+ * The Logitech cameras listed below have their interface class set to
+ * VENDOR_SPEC because they don't announce themselves as UVC devices, even
+ * though they are compliant.
+ */
+static struct usb_device_id uvc_ids[] = {
+ /* ALi M5606 (Clevo M540SR) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0402,
+ .idProduct = 0x5606,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Creative Live! Optia */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x041e,
+ .idProduct = 0x4057,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Microsoft Lifecam NX-6000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x045e,
+ .idProduct = 0x00f8,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Microsoft Lifecam VX-7000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x045e,
+ .idProduct = 0x0723,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Logitech Quickcam Fusion */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c1,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Orbit MP */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Pro for Notebook */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c3,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Pro 5000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c5,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam OEM Dell Notebook */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c6,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam OEM Cisco VT Camera II */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c7,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Apple Built-In iSight */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05ac,
+ .idProduct = 0x8501,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_BUILTIN_ISIGHT },
+ /* Genesys Logic USB 2.0 PC Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05e3,
+ .idProduct = 0x0505,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Silicon Motion SM371 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x090c,
+ .idProduct = 0xb371,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* MT6227 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0e8d,
+ .idProduct = 0x0004,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Syntek (HP Spartan) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x5212,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Asus U3S) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a33,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Ecamm Pico iMage */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18cd,
+ .idProduct = 0xcafe,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS },
+ /* Bodelin ProScopeHR */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_DEV_HI
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x19ab,
+ .idProduct = 0x1000,
+ .bcdDevice_hi = 0x0126,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STATUS_INTERVAL },
+ /* SiGma Micro USB Web Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1c4f,
+ .idProduct = 0x3000,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_IGNORE_SELECTOR_UNIT},
+ /* Acer OEM Webcam - Unknown vendor */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0100,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Packard Bell OEM Webcam */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0101,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Acer Crystal Eye webcam */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0102,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Acer OrbiCam - Unknown vendor */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0200,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Generic USB Video Class */
+ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, uvc_ids);
+
+struct uvc_driver uvc_driver = {
+ .driver = {
+ .name = "uvcvideo",
+ .probe = uvc_probe,
+ .disconnect = uvc_disconnect,
+ .suspend = uvc_suspend,
+ .resume = uvc_resume,
+ .id_table = uvc_ids,
+ .supports_autosuspend = 1,
+ },
+};
+
+static int __init uvc_init(void)
+{
+ int result;
+
+ INIT_LIST_HEAD(&uvc_driver.devices);
+ INIT_LIST_HEAD(&uvc_driver.controls);
+ mutex_init(&uvc_driver.open_mutex);
+ mutex_init(&uvc_driver.ctrl_mutex);
+
+ uvc_ctrl_init();
+
+ result = usb_register(&uvc_driver.driver);
+ if (result == 0)
+ printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
+ return result;
+}
+
+static void __exit uvc_cleanup(void)
+{
+ usb_deregister(&uvc_driver.driver);
+}
+
+module_init(uvc_init);
+module_exit(uvc_cleanup);
+
+module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(quirks, "Forced device quirks");
+module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c
new file mode 100644
index 000000000000..37bdefdbead5
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_isight.c
@@ -0,0 +1,134 @@
+/*
+ * uvc_isight.c -- USB Video Class driver - iSight support
+ *
+ * Copyright (C) 2006-2007
+ * Ivan N. Zlatev <contact@i-nz.net>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include "uvcvideo.h"
+
+/* Built-in iSight webcams implements most of UVC 1.0 except a
+ * different packet format. Instead of sending a header at the
+ * beginning of each isochronous transfer payload, the webcam sends a
+ * single header per image (on its own in a packet), followed by
+ * packets containing data only.
+ *
+ * Offset Size (bytes) Description
+ * ------------------------------------------------------------------
+ * 0x00 1 Header length
+ * 0x01 1 Flags (UVC-compliant)
+ * 0x02 4 Always equal to '11223344'
+ * 0x06 8 Always equal to 'deadbeefdeadface'
+ * 0x0e 16 Unknown
+ *
+ * The header can be prefixed by an optional, unknown-purpose byte.
+ */
+
+static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
+ const __u8 *data, unsigned int len)
+{
+ static const __u8 hdr[] = {
+ 0x11, 0x22, 0x33, 0x44,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xfa, 0xce
+ };
+
+ unsigned int maxlen, nbytes;
+ __u8 *mem;
+ int is_header = 0;
+
+ if (buf == NULL)
+ return 0;
+
+ if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) ||
+ (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) {
+ uvc_trace(UVC_TRACE_FRAME, "iSight header found\n");
+ is_header = 1;
+ }
+
+ /* Synchronize to the input stream by waiting for a header packet. */
+ if (buf->state != UVC_BUF_STATE_ACTIVE) {
+ if (!is_header) {
+ uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of "
+ "sync).\n");
+ return 0;
+ }
+
+ buf->state = UVC_BUF_STATE_ACTIVE;
+ }
+
+ /* Mark the buffer as done if we're at the beginning of a new frame.
+ *
+ * Empty buffers (bytesused == 0) don't trigger end of frame detection
+ * as it doesn't make sense to return an empty buffer.
+ */
+ if (is_header && buf->buf.bytesused != 0) {
+ buf->state = UVC_BUF_STATE_DONE;
+ return -EAGAIN;
+ }
+
+ /* Copy the video data to the buffer. Skip header packets, as they
+ * contain no data.
+ */
+ if (!is_header) {
+ maxlen = buf->buf.length - buf->buf.bytesused;
+ mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+ nbytes = min(len, maxlen);
+ memcpy(mem, data, nbytes);
+ buf->buf.bytesused += nbytes;
+
+ if (len > maxlen || buf->buf.bytesused == buf->buf.length) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete "
+ "(overflow).\n");
+ buf->state = UVC_BUF_STATE_DONE;
+ }
+ }
+
+ return 0;
+}
+
+void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
+ struct uvc_buffer *buf)
+{
+ int ret, i;
+
+ for (i = 0; i < urb->number_of_packets; ++i) {
+ if (urb->iso_frame_desc[i].status < 0) {
+ uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
+ "lost (%d).\n",
+ urb->iso_frame_desc[i].status);
+ }
+
+ /* Decode the payload packet.
+ * uvc_video_decode is entered twice when a frame transition
+ * has been detected because the end of frame can only be
+ * reliably detected when the first packet of the new frame
+ * is processed. The first pass detects the transition and
+ * closes the previous frame's buffer, the second pass
+ * processes the data of the first payload of the new frame.
+ */
+ do {
+ ret = isight_decode(&video->queue, buf,
+ urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+
+ if (buf == NULL)
+ break;
+
+ if (buf->state == UVC_BUF_STATE_DONE ||
+ buf->state == UVC_BUF_STATE_ERROR)
+ buf = uvc_queue_next_buffer(&video->queue, buf);
+ } while (ret == -EAGAIN);
+ }
+}
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
new file mode 100644
index 000000000000..0923f0e3b3d4
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -0,0 +1,477 @@
+/*
+ * uvc_queue.c -- USB Video Class driver - Buffers management
+ *
+ * Copyright (C) 2005-2008
+ * Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * Video buffers queue management.
+ *
+ * Video queues is initialized by uvc_queue_init(). The function performs
+ * basic initialization of the uvc_video_queue struct and never fails.
+ *
+ * Video buffer allocation and freeing are performed by uvc_alloc_buffers and
+ * uvc_free_buffers respectively. The former acquires the video queue lock,
+ * while the later must be called with the lock held (so that allocation can
+ * free previously allocated buffers). Trying to free buffers that are mapped
+ * to user space will return -EBUSY.
+ *
+ * Video buffers are managed using two queues. However, unlike most USB video
+ * drivers which use an in queue and an out queue, we use a main queue which
+ * holds all queued buffers (both 'empty' and 'done' buffers), and an irq
+ * queue which holds empty buffers. This design (copied from video-buf)
+ * minimizes locking in interrupt, as only one queue is shared between
+ * interrupt and user contexts.
+ *
+ * Use cases
+ * ---------
+ *
+ * Unless stated otherwise, all operations which modify the irq buffers queue
+ * are protected by the irq spinlock.
+ *
+ * 1. The user queues the buffers, starts streaming and dequeues a buffer.
+ *
+ * The buffers are added to the main and irq queues. Both operations are
+ * protected by the queue lock, and the latert is protected by the irq
+ * spinlock as well.
+ *
+ * The completion handler fetches a buffer from the irq queue and fills it
+ * with video data. If no buffer is available (irq queue empty), the handler
+ * returns immediately.
+ *
+ * When the buffer is full, the completion handler removes it from the irq
+ * queue, marks it as ready (UVC_BUF_STATE_DONE) and wake its wait queue.
+ * At that point, any process waiting on the buffer will be woken up. If a
+ * process tries to dequeue a buffer after it has been marked ready, the
+ * dequeing will succeed immediately.
+ *
+ * 2. Buffers are queued, user is waiting on a buffer and the device gets
+ * disconnected.
+ *
+ * When the device is disconnected, the kernel calls the completion handler
+ * with an appropriate status code. The handler marks all buffers in the
+ * irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so
+ * that any process waiting on a buffer gets woken up.
+ *
+ * Waking up up the first buffer on the irq list is not enough, as the
+ * process waiting on the buffer might restart the dequeue operation
+ * immediately.
+ *
+ */
+
+void uvc_queue_init(struct uvc_video_queue *queue)
+{
+ mutex_init(&queue->mutex);
+ spin_lock_init(&queue->irqlock);
+ INIT_LIST_HEAD(&queue->mainqueue);
+ INIT_LIST_HEAD(&queue->irqqueue);
+}
+
+/*
+ * Allocate the video buffers.
+ *
+ * Pages are reserved to make sure they will not be swaped, as they will be
+ * filled in URB completion handler.
+ *
+ * Buffers will be individually mapped, so they must all be page aligned.
+ */
+int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
+ unsigned int buflength)
+{
+ unsigned int bufsize = PAGE_ALIGN(buflength);
+ unsigned int i;
+ void *mem = NULL;
+ int ret;
+
+ if (nbuffers > UVC_MAX_VIDEO_BUFFERS)
+ nbuffers = UVC_MAX_VIDEO_BUFFERS;
+
+ mutex_lock(&queue->mutex);
+
+ if ((ret = uvc_free_buffers(queue)) < 0)
+ goto done;
+
+ /* Bail out if no buffers should be allocated. */
+ if (nbuffers == 0)
+ goto done;
+
+ /* Decrement the number of buffers until allocation succeeds. */
+ for (; nbuffers > 0; --nbuffers) {
+ mem = vmalloc_32(nbuffers * bufsize);
+ if (mem != NULL)
+ break;
+ }
+
+ if (mem == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < nbuffers; ++i) {
+ memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
+ queue->buffer[i].buf.index = i;
+ queue->buffer[i].buf.m.offset = i * bufsize;
+ queue->buffer[i].buf.length = buflength;
+ queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ queue->buffer[i].buf.sequence = 0;
+ queue->buffer[i].buf.field = V4L2_FIELD_NONE;
+ queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
+ queue->buffer[i].buf.flags = 0;
+ init_waitqueue_head(&queue->buffer[i].wait);
+ }
+
+ queue->mem = mem;
+ queue->count = nbuffers;
+ queue->buf_size = bufsize;
+ ret = nbuffers;
+
+done:
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+
+/*
+ * Free the video buffers.
+ *
+ * This function must be called with the queue lock held.
+ */
+int uvc_free_buffers(struct uvc_video_queue *queue)
+{
+ unsigned int i;
+
+ for (i = 0; i < queue->count; ++i) {
+ if (queue->buffer[i].vma_use_count != 0)
+ return -EBUSY;
+ }
+
+ if (queue->count) {
+ vfree(queue->mem);
+ queue->count = 0;
+ }
+
+ return 0;
+}
+
+static void __uvc_query_buffer(struct uvc_buffer *buf,
+ struct v4l2_buffer *v4l2_buf)
+{
+ memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);
+
+ if (buf->vma_use_count)
+ v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ switch (buf->state) {
+ case UVC_BUF_STATE_ERROR:
+ case UVC_BUF_STATE_DONE:
+ v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
+ break;
+ case UVC_BUF_STATE_QUEUED:
+ case UVC_BUF_STATE_ACTIVE:
+ v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ break;
+ case UVC_BUF_STATE_IDLE:
+ default:
+ break;
+ }
+}
+
+int uvc_query_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf)
+{
+ int ret = 0;
+
+ mutex_lock(&queue->mutex);
+ if (v4l2_buf->index >= queue->count) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf);
+
+done:
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+
+/*
+ * Queue a video buffer. Attempting to queue a buffer that has already been
+ * queued will return -EINVAL.
+ */
+int uvc_queue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf)
+{
+ struct uvc_buffer *buf;
+ unsigned long flags;
+ int ret = 0;
+
+ uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
+
+ if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ v4l2_buf->memory != V4L2_MEMORY_MMAP) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
+ "and/or memory (%u).\n", v4l2_buf->type,
+ v4l2_buf->memory);
+ return -EINVAL;
+ }
+
+ mutex_lock(&queue->mutex);
+ if (v4l2_buf->index >= queue->count) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ buf = &queue->buffer[v4l2_buf->index];
+ if (buf->state != UVC_BUF_STATE_IDLE) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state "
+ "(%u).\n", buf->state);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ if (queue->flags & UVC_QUEUE_DISCONNECTED) {
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+ ret = -ENODEV;
+ goto done;
+ }
+ buf->state = UVC_BUF_STATE_QUEUED;
+ buf->buf.bytesused = 0;
+ list_add_tail(&buf->stream, &queue->mainqueue);
+ list_add_tail(&buf->queue, &queue->irqqueue);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+done:
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+
+static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking)
+{
+ if (nonblocking) {
+ return (buf->state != UVC_BUF_STATE_QUEUED &&
+ buf->state != UVC_BUF_STATE_ACTIVE)
+ ? 0 : -EAGAIN;
+ }
+
+ return wait_event_interruptible(buf->wait,
+ buf->state != UVC_BUF_STATE_QUEUED &&
+ buf->state != UVC_BUF_STATE_ACTIVE);
+}
+
+/*
+ * Dequeue a video buffer. If nonblocking is false, block until a buffer is
+ * available.
+ */
+int uvc_dequeue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf, int nonblocking)
+{
+ struct uvc_buffer *buf;
+ int ret = 0;
+
+ if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ v4l2_buf->memory != V4L2_MEMORY_MMAP) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
+ "and/or memory (%u).\n", v4l2_buf->type,
+ v4l2_buf->memory);
+ return -EINVAL;
+ }
+
+ mutex_lock(&queue->mutex);
+ if (list_empty(&queue->mainqueue)) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
+ if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0)
+ goto done;
+
+ uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n",
+ buf->buf.index, buf->state, buf->buf.bytesused);
+
+ switch (buf->state) {
+ case UVC_BUF_STATE_ERROR:
+ uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data "
+ "(transmission error).\n");
+ ret = -EIO;
+ case UVC_BUF_STATE_DONE:
+ buf->state = UVC_BUF_STATE_IDLE;
+ break;
+
+ case UVC_BUF_STATE_IDLE:
+ case UVC_BUF_STATE_QUEUED:
+ case UVC_BUF_STATE_ACTIVE:
+ default:
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u "
+ "(driver bug?).\n", buf->state);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ list_del(&buf->stream);
+ __uvc_query_buffer(buf, v4l2_buf);
+
+done:
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+
+/*
+ * Poll the video queue.
+ *
+ * This function implements video queue polling and is intended to be used by
+ * the device poll handler.
+ */
+unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
+ poll_table *wait)
+{
+ struct uvc_buffer *buf;
+ unsigned int mask = 0;
+
+ mutex_lock(&queue->mutex);
+ if (list_empty(&queue->mainqueue)) {
+ mask |= POLLERR;
+ goto done;
+ }
+ buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
+
+ poll_wait(file, &buf->wait, wait);
+ if (buf->state == UVC_BUF_STATE_DONE ||
+ buf->state == UVC_BUF_STATE_ERROR)
+ mask |= POLLIN | POLLRDNORM;
+
+done:
+ mutex_unlock(&queue->mutex);
+ return mask;
+}
+
+/*
+ * Enable or disable the video buffers queue.
+ *
+ * The queue must be enabled before starting video acquisition and must be
+ * disabled after stopping it. This ensures that the video buffers queue
+ * state can be properly initialized before buffers are accessed from the
+ * interrupt handler.
+ *
+ * Enabling the video queue initializes parameters (such as sequence number,
+ * sync pattern, ...). If the queue is already enabled, return -EBUSY.
+ *
+ * Disabling the video queue cancels the queue and removes all buffers from
+ * the main queue.
+ *
+ * This function can't be called from interrupt context. Use
+ * uvc_queue_cancel() instead.
+ */
+int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
+{
+ unsigned int i;
+ int ret = 0;
+
+ mutex_lock(&queue->mutex);
+ if (enable) {
+ if (uvc_queue_streaming(queue)) {
+ ret = -EBUSY;
+ goto done;
+ }
+ queue->sequence = 0;
+ queue->flags |= UVC_QUEUE_STREAMING;
+ } else {
+ uvc_queue_cancel(queue, 0);
+ INIT_LIST_HEAD(&queue->mainqueue);
+
+ for (i = 0; i < queue->count; ++i)
+ queue->buffer[i].state = UVC_BUF_STATE_IDLE;
+
+ queue->flags &= ~UVC_QUEUE_STREAMING;
+ }
+
+done:
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+
+/*
+ * Cancel the video buffers queue.
+ *
+ * Cancelling the queue marks all buffers on the irq queue as erroneous,
+ * wakes them up and remove them from the queue.
+ *
+ * If the disconnect parameter is set, further calls to uvc_queue_buffer will
+ * fail with -ENODEV.
+ *
+ * This function acquires the irq spinlock and can be called from interrupt
+ * context.
+ */
+void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
+{
+ struct uvc_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ while (!list_empty(&queue->irqqueue)) {
+ buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+ queue);
+ list_del(&buf->queue);
+ buf->state = UVC_BUF_STATE_ERROR;
+ wake_up(&buf->wait);
+ }
+ /* This must be protected by the irqlock spinlock to avoid race
+ * conditions between uvc_queue_buffer and the disconnection event that
+ * could result in an interruptible wait in uvc_dequeue_buffer. Do not
+ * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
+ * state outside the queue code.
+ */
+ if (disconnect)
+ queue->flags |= UVC_QUEUE_DISCONNECTED;
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf)
+{
+ struct uvc_buffer *nextbuf;
+ unsigned long flags;
+
+ if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
+ buf->buf.length != buf->buf.bytesused) {
+ buf->state = UVC_BUF_STATE_QUEUED;
+ buf->buf.bytesused = 0;
+ return buf;
+ }
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ list_del(&buf->queue);
+ if (!list_empty(&queue->irqqueue))
+ nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+ queue);
+ else
+ nextbuf = NULL;
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ buf->buf.sequence = queue->sequence++;
+ do_gettimeofday(&buf->buf.timestamp);
+
+ wake_up(&buf->wait);
+ return nextbuf;
+}
diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c
new file mode 100644
index 000000000000..be9084e5eace
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_status.c
@@ -0,0 +1,207 @@
+/*
+ * uvc_status.c -- USB Video Class driver - Status endpoint
+ *
+ * Copyright (C) 2007-2008
+ * Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+
+#include "uvcvideo.h"
+
+/* --------------------------------------------------------------------------
+ * Input device
+ */
+static int uvc_input_init(struct uvc_device *dev)
+{
+ struct usb_device *udev = dev->udev;
+ struct input_dev *input;
+ char *phys = NULL;
+ int ret;
+
+ input = input_allocate_device();
+ if (input == NULL)
+ return -ENOMEM;
+
+ phys = kmalloc(6 + strlen(udev->bus->bus_name) + strlen(udev->devpath),
+ GFP_KERNEL);
+ if (phys == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ sprintf(phys, "usb-%s-%s", udev->bus->bus_name, udev->devpath);
+
+ input->name = dev->name;
+ input->phys = phys;
+ usb_to_input_id(udev, &input->id);
+ input->dev.parent = &dev->intf->dev;
+
+ set_bit(EV_KEY, input->evbit);
+ set_bit(BTN_0, input->keybit);
+
+ if ((ret = input_register_device(input)) < 0)
+ goto error;
+
+ dev->input = input;
+ return 0;
+
+error:
+ input_free_device(input);
+ kfree(phys);
+ return ret;
+}
+
+static void uvc_input_cleanup(struct uvc_device *dev)
+{
+ if (dev->input)
+ input_unregister_device(dev->input);
+}
+
+/* --------------------------------------------------------------------------
+ * Status interrupt endpoint
+ */
+static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
+{
+ if (len < 3) {
+ uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event "
+ "received.\n");
+ return;
+ }
+
+ if (data[2] == 0) {
+ if (len < 4)
+ return;
+ uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
+ data[1], data[3] ? "pressed" : "released", len);
+ if (dev->input)
+ input_report_key(dev->input, BTN_0, data[3]);
+ } else {
+ uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x "
+ "len %d.\n", data[1], data[2], data[3], len);
+ }
+}
+
+static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len)
+{
+ char *attrs[3] = { "value", "info", "failure" };
+
+ if (len < 6 || data[2] != 0 || data[4] > 2) {
+ uvc_trace(UVC_TRACE_STATUS, "Invalid control status event "
+ "received.\n");
+ return;
+ }
+
+ uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",
+ data[1], data[3], attrs[data[4]], len);
+}
+
+static void uvc_status_complete(struct urb *urb)
+{
+ struct uvc_device *dev = urb->context;
+ int len, ret;
+
+ switch (urb->status) {
+ case 0:
+ break;
+
+ case -ENOENT: /* usb_kill_urb() called. */
+ case -ECONNRESET: /* usb_unlink_urb() called. */
+ case -ESHUTDOWN: /* The endpoint is being disabled. */
+ case -EPROTO: /* Device is disconnected (reported by some
+ * host controller). */
+ return;
+
+ default:
+ uvc_printk(KERN_WARNING, "Non-zero status (%d) in status "
+ "completion handler.\n", urb->status);
+ return;
+ }
+
+ len = urb->actual_length;
+ if (len > 0) {
+ switch (dev->status[0] & 0x0f) {
+ case UVC_STATUS_TYPE_CONTROL:
+ uvc_event_control(dev, dev->status, len);
+ break;
+
+ case UVC_STATUS_TYPE_STREAMING:
+ uvc_event_streaming(dev, dev->status, len);
+ break;
+
+ default:
+ uvc_printk(KERN_INFO, "unknown event type %u.\n",
+ dev->status[0]);
+ break;
+ }
+ }
+
+ /* Resubmit the URB. */
+ urb->interval = dev->int_ep->desc.bInterval;
+ if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
+ ret);
+ }
+}
+
+int uvc_status_init(struct uvc_device *dev)
+{
+ struct usb_host_endpoint *ep = dev->int_ep;
+ unsigned int pipe;
+ int interval;
+
+ if (ep == NULL)
+ return 0;
+
+ uvc_input_init(dev);
+
+ dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (dev->int_urb == NULL)
+ return -ENOMEM;
+
+ pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
+
+ /* For high-speed interrupt endpoints, the bInterval value is used as
+ * an exponent of two. Some developers forgot about it.
+ */
+ interval = ep->desc.bInterval;
+ if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&
+ (dev->quirks & UVC_QUIRK_STATUS_INTERVAL))
+ interval = fls(interval) - 1;
+
+ usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
+ dev->status, sizeof dev->status, uvc_status_complete,
+ dev, interval);
+
+ return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+}
+
+void uvc_status_cleanup(struct uvc_device *dev)
+{
+ usb_kill_urb(dev->int_urb);
+ usb_free_urb(dev->int_urb);
+ uvc_input_cleanup(dev);
+}
+
+int uvc_status_suspend(struct uvc_device *dev)
+{
+ usb_kill_urb(dev->int_urb);
+ return 0;
+}
+
+int uvc_status_resume(struct uvc_device *dev)
+{
+ if (dev->int_urb == NULL)
+ return 0;
+
+ return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+}
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
new file mode 100644
index 000000000000..2e0a66575bb4
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -0,0 +1,1105 @@
+/*
+ * uvc_v4l2.c -- USB Video Class driver - V4L2 API
+ *
+ * Copyright (C) 2005-2008
+ * Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * V4L2 interface
+ */
+
+/*
+ * Mapping V4L2 controls to UVC controls can be straighforward if done well.
+ * Most of the UVC controls exist in V4L2, and can be mapped directly. Some
+ * must be grouped (for instance the Red Balance, Blue Balance and Do White
+ * Balance V4L2 controls use the White Balance Component UVC control) or
+ * otherwise translated. The approach we take here is to use a translation
+ * table for the controls which can be mapped directly, and handle the others
+ * manually.
+ */
+static int uvc_v4l2_query_menu(struct uvc_video_device *video,
+ struct v4l2_querymenu *query_menu)
+{
+ struct uvc_menu_info *menu_info;
+ struct uvc_control_mapping *mapping;
+ struct uvc_control *ctrl;
+
+ ctrl = uvc_find_control(video, query_menu->id, &mapping);
+ if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU)
+ return -EINVAL;
+
+ if (query_menu->index >= mapping->menu_count)
+ return -EINVAL;
+
+ menu_info = &mapping->menu_info[query_menu->index];
+ strncpy(query_menu->name, menu_info->name, 32);
+ return 0;
+}
+
+/*
+ * Find the frame interval closest to the requested frame interval for the
+ * given frame format and size. This should be done by the device as part of
+ * the Video Probe and Commit negotiation, but some hardware don't implement
+ * that feature.
+ */
+static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
+{
+ unsigned int i;
+
+ if (frame->bFrameIntervalType) {
+ __u32 best = -1, dist;
+
+ for (i = 0; i < frame->bFrameIntervalType; ++i) {
+ dist = interval > frame->dwFrameInterval[i]
+ ? interval - frame->dwFrameInterval[i]
+ : frame->dwFrameInterval[i] - interval;
+
+ if (dist > best)
+ break;
+
+ best = dist;
+ }
+
+ interval = frame->dwFrameInterval[i-1];
+ } else {
+ const __u32 min = frame->dwFrameInterval[0];
+ const __u32 max = frame->dwFrameInterval[1];
+ const __u32 step = frame->dwFrameInterval[2];
+
+ interval = min + (interval - min + step/2) / step * step;
+ if (interval > max)
+ interval = max;
+ }
+
+ return interval;
+}
+
+static int uvc_v4l2_try_format(struct uvc_video_device *video,
+ struct v4l2_format *fmt, struct uvc_streaming_control *probe,
+ struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
+{
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ __u16 rw, rh;
+ unsigned int d, maxd;
+ unsigned int i;
+ __u32 interval;
+ int ret = 0;
+ __u8 *fcc;
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
+ uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
+ fmt->fmt.pix.pixelformat,
+ fcc[0], fcc[1], fcc[2], fcc[3],
+ fmt->fmt.pix.width, fmt->fmt.pix.height);
+
+ /* Check if the hardware supports the requested format. */
+ for (i = 0; i < video->streaming->nformats; ++i) {
+ format = &video->streaming->format[i];
+ if (format->fcc == fmt->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) {
+ uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n",
+ fmt->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ /* Find the closest image size. The distance between image sizes is
+ * the size in pixels of the non-overlapping regions between the
+ * requested size and the frame-specified size.
+ */
+ rw = fmt->fmt.pix.width;
+ rh = fmt->fmt.pix.height;
+ maxd = (unsigned int)-1;
+
+ for (i = 0; i < format->nframes; ++i) {
+ __u16 w = format->frame[i].wWidth;
+ __u16 h = format->frame[i].wHeight;
+
+ d = min(w, rw) * min(h, rh);
+ d = w*h + rw*rh - 2*d;
+ if (d < maxd) {
+ maxd = d;
+ frame = &format->frame[i];
+ }
+
+ if (maxd == 0)
+ break;
+ }
+
+ if (frame == NULL) {
+ uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n",
+ fmt->fmt.pix.width, fmt->fmt.pix.height);
+ return -EINVAL;
+ }
+
+ /* Use the default frame interval. */
+ interval = frame->dwDefaultFrameInterval;
+ uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us "
+ "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval,
+ (100000000/interval)%10);
+
+ /* Set the format index, frame index and frame interval. */
+ memset(probe, 0, sizeof *probe);
+ probe->bmHint = 1; /* dwFrameInterval */
+ probe->bFormatIndex = format->index;
+ probe->bFrameIndex = frame->bFrameIndex;
+ probe->dwFrameInterval = uvc_try_frame_interval(frame, interval);
+ /* Some webcams stall the probe control set request when the
+ * dwMaxVideoFrameSize field is set to zero. The UVC specification
+ * clearly states that the field is read-only from the host, so this
+ * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by
+ * the webcam to work around the problem.
+ *
+ * The workaround could probably be enabled for all webcams, so the
+ * quirk can be removed if needed. It's currently useful to detect
+ * webcam bugs and fix them before they hit the market (providing
+ * developers test their webcams with the Linux driver as well as with
+ * the Windows driver).
+ */
+ if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
+ probe->dwMaxVideoFrameSize =
+ video->streaming->ctrl.dwMaxVideoFrameSize;
+
+ /* Probe the device */
+ if ((ret = uvc_probe_video(video, probe)) < 0)
+ goto done;
+
+ fmt->fmt.pix.width = frame->wWidth;
+ fmt->fmt.pix.height = frame->wHeight;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
+ fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;
+ fmt->fmt.pix.colorspace = format->colorspace;
+ fmt->fmt.pix.priv = 0;
+
+ if (uvc_format != NULL)
+ *uvc_format = format;
+ if (uvc_frame != NULL)
+ *uvc_frame = frame;
+
+done:
+ return ret;
+}
+
+static int uvc_v4l2_get_format(struct uvc_video_device *video,
+ struct v4l2_format *fmt)
+{
+ struct uvc_format *format = video->streaming->cur_format;
+ struct uvc_frame *frame = video->streaming->cur_frame;
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (format == NULL || frame == NULL)
+ return -EINVAL;
+
+ fmt->fmt.pix.pixelformat = format->fcc;
+ fmt->fmt.pix.width = frame->wWidth;
+ fmt->fmt.pix.height = frame->wHeight;
+ fmt->fmt.pix.field = V4L2_FIELD_NONE;
+ fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
+ fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize;
+ fmt->fmt.pix.colorspace = format->colorspace;
+ fmt->fmt.pix.priv = 0;
+
+ return 0;
+}
+
+static int uvc_v4l2_set_format(struct uvc_video_device *video,
+ struct v4l2_format *fmt)
+{
+ struct uvc_streaming_control probe;
+ struct uvc_format *format;
+ struct uvc_frame *frame;
+ int ret;
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (uvc_queue_streaming(&video->queue))
+ return -EBUSY;
+
+ ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);
+ if (ret < 0)
+ return ret;
+
+ if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
+ return ret;
+
+ memcpy(&video->streaming->ctrl, &probe, sizeof probe);
+ video->streaming->cur_format = format;
+ video->streaming->cur_frame = frame;
+
+ return 0;
+}
+
+static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
+ struct v4l2_streamparm *parm)
+{
+ uint32_t numerator, denominator;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ numerator = video->streaming->ctrl.dwFrameInterval;
+ denominator = 10000000;
+ uvc_simplify_fraction(&numerator, &denominator, 8, 333);
+
+ memset(parm, 0, sizeof *parm);
+ parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.capturemode = 0;
+ parm->parm.capture.timeperframe.numerator = numerator;
+ parm->parm.capture.timeperframe.denominator = denominator;
+ parm->parm.capture.extendedmode = 0;
+ parm->parm.capture.readbuffers = 0;
+
+ return 0;
+}
+
+static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
+ struct v4l2_streamparm *parm)
+{
+ struct uvc_frame *frame = video->streaming->cur_frame;
+ struct uvc_streaming_control probe;
+ uint32_t interval;
+ int ret;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (uvc_queue_streaming(&video->queue))
+ return -EBUSY;
+
+ memcpy(&probe, &video->streaming->ctrl, sizeof probe);
+ interval = uvc_fraction_to_interval(
+ parm->parm.capture.timeperframe.numerator,
+ parm->parm.capture.timeperframe.denominator);
+
+ uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
+ parm->parm.capture.timeperframe.numerator,
+ parm->parm.capture.timeperframe.denominator,
+ interval);
+ probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
+
+ /* Probe the device with the new settings. */
+ if ((ret = uvc_probe_video(video, &probe)) < 0)
+ return ret;
+
+ /* Commit the new settings. */
+ if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
+ return ret;
+
+ memcpy(&video->streaming->ctrl, &probe, sizeof probe);
+
+ /* Return the actual frame period. */
+ parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval;
+ parm->parm.capture.timeperframe.denominator = 10000000;
+ uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator,
+ &parm->parm.capture.timeperframe.denominator,
+ 8, 333);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Privilege management
+ */
+
+/*
+ * Privilege management is the multiple-open implementation basis. The current
+ * implementation is completely transparent for the end-user and doesn't
+ * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls.
+ * Those ioctls enable finer control on the device (by making possible for a
+ * user to request exclusive access to a device), but are not mature yet.
+ * Switching to the V4L2 priority mechanism might be considered in the future
+ * if this situation changes.
+ *
+ * Each open instance of a UVC device can either be in a privileged or
+ * unprivileged state. Only a single instance can be in a privileged state at
+ * a given time. Trying to perform an operation which requires privileges will
+ * automatically acquire the required privileges if possible, or return -EBUSY
+ * otherwise. Privileges are dismissed when closing the instance.
+ *
+ * Operations which require privileges are:
+ *
+ * - VIDIOC_S_INPUT
+ * - VIDIOC_S_PARM
+ * - VIDIOC_S_FMT
+ * - VIDIOC_TRY_FMT
+ * - VIDIOC_REQBUFS
+ */
+static int uvc_acquire_privileges(struct uvc_fh *handle)
+{
+ int ret = 0;
+
+ /* Always succeed if the handle is already privileged. */
+ if (handle->state == UVC_HANDLE_ACTIVE)
+ return 0;
+
+ /* Check if the device already has a privileged handle. */
+ mutex_lock(&uvc_driver.open_mutex);
+ if (atomic_inc_return(&handle->device->active) != 1) {
+ atomic_dec(&handle->device->active);
+ ret = -EBUSY;
+ goto done;
+ }
+
+ handle->state = UVC_HANDLE_ACTIVE;
+
+done:
+ mutex_unlock(&uvc_driver.open_mutex);
+ return ret;
+}
+
+static void uvc_dismiss_privileges(struct uvc_fh *handle)
+{
+ if (handle->state == UVC_HANDLE_ACTIVE)
+ atomic_dec(&handle->device->active);
+
+ handle->state = UVC_HANDLE_PASSIVE;
+}
+
+static int uvc_has_privileges(struct uvc_fh *handle)
+{
+ return handle->state == UVC_HANDLE_ACTIVE;
+}
+
+/* ------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int uvc_v4l2_open(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev;
+ struct uvc_video_device *video;
+ struct uvc_fh *handle;
+ int ret = 0;
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
+ mutex_lock(&uvc_driver.open_mutex);
+ vdev = video_devdata(file);
+ video = video_get_drvdata(vdev);
+
+ if (video->dev->state & UVC_DEV_DISCONNECTED) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = usb_autopm_get_interface(video->dev->intf);
+ if (ret < 0)
+ goto done;
+
+ /* Create the device handle. */
+ handle = kzalloc(sizeof *handle, GFP_KERNEL);
+ if (handle == NULL) {
+ usb_autopm_put_interface(video->dev->intf);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ handle->device = video;
+ handle->state = UVC_HANDLE_PASSIVE;
+ file->private_data = handle;
+
+ kref_get(&video->dev->kref);
+
+done:
+ mutex_unlock(&uvc_driver.open_mutex);
+ return ret;
+}
+
+static int uvc_v4l2_release(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_video_device *video = video_get_drvdata(vdev);
+ struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
+
+ /* Only free resources if this is a privileged handle. */
+ if (uvc_has_privileges(handle)) {
+ uvc_video_enable(video, 0);
+
+ mutex_lock(&video->queue.mutex);
+ if (uvc_free_buffers(&video->queue) < 0)
+ uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to "
+ "free buffers.\n");
+ mutex_unlock(&video->queue.mutex);
+ }
+
+ /* Release the file handle. */
+ uvc_dismiss_privileges(handle);
+ kfree(handle);
+ file->private_data = NULL;
+
+ usb_autopm_put_interface(video->dev->intf);
+ kref_put(&video->dev->kref, uvc_delete);
+ return 0;
+}
+
+static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_video_device *video = video_get_drvdata(vdev);
+ struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
+ int ret = 0;
+
+ if (uvc_trace_param & UVC_TRACE_IOCTL)
+ v4l_printk_ioctl(cmd);
+
+ switch (cmd) {
+ /* Query capabilities */
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ memset(cap, 0, sizeof *cap);
+ strncpy(cap->driver, "uvcvideo", sizeof cap->driver);
+ strncpy(cap->card, vdev->name, 32);
+ strncpy(cap->bus_info, video->dev->udev->bus->bus_name,
+ sizeof cap->bus_info);
+ cap->version = DRIVER_VERSION_NUMBER;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_STREAMING;
+ break;
+ }
+
+ /* Get, Set & Query control */
+ case VIDIOC_QUERYCTRL:
+ return uvc_query_v4l2_ctrl(video, arg);
+
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ struct v4l2_ext_control xctrl;
+
+ memset(&xctrl, 0, sizeof xctrl);
+ xctrl.id = ctrl->id;
+
+ uvc_ctrl_begin(video);
+ ret = uvc_ctrl_get(video, &xctrl);
+ uvc_ctrl_rollback(video);
+ if (ret >= 0)
+ ctrl->value = xctrl.value;
+ break;
+ }
+
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ struct v4l2_ext_control xctrl;
+
+ memset(&xctrl, 0, sizeof xctrl);
+ xctrl.id = ctrl->id;
+ xctrl.value = ctrl->value;
+
+ uvc_ctrl_begin(video);
+ ret = uvc_ctrl_set(video, &xctrl);
+ if (ret < 0) {
+ uvc_ctrl_rollback(video);
+ return ret;
+ }
+ ret = uvc_ctrl_commit(video);
+ break;
+ }
+
+ case VIDIOC_QUERYMENU:
+ return uvc_v4l2_query_menu(video, arg);
+
+ case VIDIOC_G_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctrls = arg;
+ struct v4l2_ext_control *ctrl = ctrls->controls;
+ unsigned int i;
+
+ uvc_ctrl_begin(video);
+ for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+ ret = uvc_ctrl_get(video, ctrl);
+ if (ret < 0) {
+ uvc_ctrl_rollback(video);
+ ctrls->error_idx = i;
+ return ret;
+ }
+ }
+ ctrls->error_idx = 0;
+ ret = uvc_ctrl_rollback(video);
+ break;
+ }
+
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctrls = arg;
+ struct v4l2_ext_control *ctrl = ctrls->controls;
+ unsigned int i;
+
+ ret = uvc_ctrl_begin(video);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+ ret = uvc_ctrl_set(video, ctrl);
+ if (ret < 0) {
+ uvc_ctrl_rollback(video);
+ ctrls->error_idx = i;
+ return ret;
+ }
+ }
+
+ ctrls->error_idx = 0;
+
+ if (cmd == VIDIOC_S_EXT_CTRLS)
+ ret = uvc_ctrl_commit(video);
+ else
+ ret = uvc_ctrl_rollback(video);
+ break;
+ }
+
+ /* Get, Set & Enum input */
+ case VIDIOC_ENUMINPUT:
+ {
+ const struct uvc_entity *selector = video->selector;
+ struct v4l2_input *input = arg;
+ struct uvc_entity *iterm = NULL;
+ u32 index = input->index;
+ int pin = 0;
+
+ if (selector == NULL ||
+ (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ if (index != 0)
+ return -EINVAL;
+ iterm = list_first_entry(&video->iterms,
+ struct uvc_entity, chain);
+ pin = iterm->id;
+ } else if (pin < selector->selector.bNrInPins) {
+ pin = selector->selector.baSourceID[index];
+ list_for_each_entry(iterm, video->iterms.next, chain) {
+ if (iterm->id == pin)
+ break;
+ }
+ }
+
+ if (iterm == NULL || iterm->id != pin)
+ return -EINVAL;
+
+ memset(input, 0, sizeof *input);
+ input->index = index;
+ strncpy(input->name, iterm->name, sizeof input->name);
+ if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA)
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ break;
+ }
+
+ case VIDIOC_G_INPUT:
+ {
+ u8 input;
+
+ if (video->selector == NULL ||
+ (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ *(int *)arg = 0;
+ break;
+ }
+
+ ret = uvc_query_ctrl(video->dev, GET_CUR, video->selector->id,
+ video->dev->intfnum, SU_INPUT_SELECT_CONTROL,
+ &input, 1);
+ if (ret < 0)
+ return ret;
+
+ *(int *)arg = input - 1;
+ break;
+ }
+
+ case VIDIOC_S_INPUT:
+ {
+ u8 input = *(u32 *)arg + 1;
+
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ if (video->selector == NULL ||
+ (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ if (input != 1)
+ return -EINVAL;
+ break;
+ }
+
+ if (input > video->selector->selector.bNrInPins)
+ return -EINVAL;
+
+ return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id,
+ video->dev->intfnum, SU_INPUT_SELECT_CONTROL,
+ &input, 1);
+ }
+
+ /* Try, Get, Set & Enum format */
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *fmt = arg;
+ struct uvc_format *format;
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fmt->index >= video->streaming->nformats)
+ return -EINVAL;
+
+ format = &video->streaming->format[fmt->index];
+ fmt->flags = 0;
+ if (format->flags & UVC_FMT_FLAG_COMPRESSED)
+ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ strncpy(fmt->description, format->name,
+ sizeof fmt->description);
+ fmt->description[sizeof fmt->description - 1] = 0;
+ fmt->pixelformat = format->fcc;
+ break;
+ }
+
+ case VIDIOC_TRY_FMT:
+ {
+ struct uvc_streaming_control probe;
+
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL);
+ }
+
+ case VIDIOC_S_FMT:
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ return uvc_v4l2_set_format(video, arg);
+
+ case VIDIOC_G_FMT:
+ return uvc_v4l2_get_format(video, arg);
+
+ /* Frame size enumeration */
+ case VIDIOC_ENUM_FRAMESIZES:
+ {
+ struct v4l2_frmsizeenum *fsize = arg;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame;
+ int i;
+
+ /* Look for the given pixel format */
+ for (i = 0; i < video->streaming->nformats; i++) {
+ if (video->streaming->format[i].fcc ==
+ fsize->pixel_format) {
+ format = &video->streaming->format[i];
+ break;
+ }
+ }
+ if (format == NULL)
+ return -EINVAL;
+
+ if (fsize->index >= format->nframes)
+ return -EINVAL;
+
+ frame = &format->frame[fsize->index];
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = frame->wWidth;
+ fsize->discrete.height = frame->wHeight;
+ break;
+ }
+
+ /* Frame interval enumeration */
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ {
+ struct v4l2_frmivalenum *fival = arg;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ int i;
+
+ /* Look for the given pixel format and frame size */
+ for (i = 0; i < video->streaming->nformats; i++) {
+ if (video->streaming->format[i].fcc ==
+ fival->pixel_format) {
+ format = &video->streaming->format[i];
+ break;
+ }
+ }
+ if (format == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < format->nframes; i++) {
+ if (format->frame[i].wWidth == fival->width &&
+ format->frame[i].wHeight == fival->height) {
+ frame = &format->frame[i];
+ break;
+ }
+ }
+ if (frame == NULL)
+ return -EINVAL;
+
+ if (frame->bFrameIntervalType) {
+ if (fival->index >= frame->bFrameIntervalType)
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator =
+ frame->dwFrameInterval[fival->index];
+ fival->discrete.denominator = 10000000;
+ uvc_simplify_fraction(&fival->discrete.numerator,
+ &fival->discrete.denominator, 8, 333);
+ } else {
+ fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+ fival->stepwise.min.numerator =
+ frame->dwFrameInterval[0];
+ fival->stepwise.min.denominator = 10000000;
+ fival->stepwise.max.numerator =
+ frame->dwFrameInterval[1];
+ fival->stepwise.max.denominator = 10000000;
+ fival->stepwise.step.numerator =
+ frame->dwFrameInterval[2];
+ fival->stepwise.step.denominator = 10000000;
+ uvc_simplify_fraction(&fival->stepwise.min.numerator,
+ &fival->stepwise.min.denominator, 8, 333);
+ uvc_simplify_fraction(&fival->stepwise.max.numerator,
+ &fival->stepwise.max.denominator, 8, 333);
+ uvc_simplify_fraction(&fival->stepwise.step.numerator,
+ &fival->stepwise.step.denominator, 8, 333);
+ }
+ break;
+ }
+
+ /* Get & Set streaming parameters */
+ case VIDIOC_G_PARM:
+ return uvc_v4l2_get_streamparm(video, arg);
+
+ case VIDIOC_S_PARM:
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ return uvc_v4l2_set_streamparm(video, arg);
+
+ /* Cropping and scaling */
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *ccap = arg;
+ struct uvc_frame *frame = video->streaming->cur_frame;
+
+ if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ ccap->bounds.left = 0;
+ ccap->bounds.top = 0;
+ ccap->bounds.width = frame->wWidth;
+ ccap->bounds.height = frame->wHeight;
+
+ ccap->defrect = ccap->bounds;
+
+ ccap->pixelaspect.numerator = 1;
+ ccap->pixelaspect.denominator = 1;
+ break;
+ }
+
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ return -EINVAL;
+
+ /* Buffers & streaming */
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *rb = arg;
+ unsigned int bufsize =
+ video->streaming->ctrl.dwMaxVideoFrameSize;
+
+ if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if ((ret = uvc_acquire_privileges(handle)) < 0)
+ return ret;
+
+ ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize);
+ if (ret < 0)
+ return ret;
+
+ if (!(video->streaming->cur_format->flags &
+ UVC_FMT_FLAG_COMPRESSED))
+ video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
+
+ rb->count = ret;
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_query_buffer(&video->queue, buf);
+ }
+
+ case VIDIOC_QBUF:
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_queue_buffer(&video->queue, arg);
+
+ case VIDIOC_DQBUF:
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_dequeue_buffer(&video->queue, arg,
+ file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_STREAMON:
+ {
+ int *type = arg;
+
+ if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ if ((ret = uvc_video_enable(video, 1)) < 0)
+ return ret;
+ break;
+ }
+
+ case VIDIOC_STREAMOFF:
+ {
+ int *type = arg;
+
+ if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_video_enable(video, 0);
+ }
+
+ /* Analog video standards make no sense for digital cameras. */
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+
+ case VIDIOC_OVERLAY:
+
+ case VIDIOC_ENUMAUDIO:
+ case VIDIOC_ENUMAUDOUT:
+
+ case VIDIOC_ENUMOUTPUT:
+ uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd);
+ return -EINVAL;
+
+ /* Dynamic controls. */
+ case UVCIOC_CTRL_ADD:
+ {
+ struct uvc_xu_control_info *xinfo = arg;
+ struct uvc_control_info *info;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ info = kmalloc(sizeof *info, GFP_KERNEL);
+ if (info == NULL)
+ return -ENOMEM;
+
+ memcpy(info->entity, xinfo->entity, sizeof info->entity);
+ info->index = xinfo->index;
+ info->selector = xinfo->selector;
+ info->size = xinfo->size;
+ info->flags = xinfo->flags;
+
+ info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
+ UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF;
+
+ ret = uvc_ctrl_add_info(info);
+ if (ret < 0)
+ kfree(info);
+ break;
+ }
+
+ case UVCIOC_CTRL_MAP:
+ {
+ struct uvc_xu_control_mapping *xmap = arg;
+ struct uvc_control_mapping *map;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ map = kmalloc(sizeof *map, GFP_KERNEL);
+ if (map == NULL)
+ return -ENOMEM;
+
+ map->id = xmap->id;
+ memcpy(map->name, xmap->name, sizeof map->name);
+ memcpy(map->entity, xmap->entity, sizeof map->entity);
+ map->selector = xmap->selector;
+ map->size = xmap->size;
+ map->offset = xmap->offset;
+ map->v4l2_type = xmap->v4l2_type;
+ map->data_type = xmap->data_type;
+
+ ret = uvc_ctrl_add_mapping(map);
+ if (ret < 0)
+ kfree(map);
+ break;
+ }
+
+ case UVCIOC_CTRL_GET:
+ return uvc_xu_ctrl_query(video, arg, 0);
+
+ case UVCIOC_CTRL_SET:
+ return uvc_xu_ctrl_query(video, arg, 1);
+
+ default:
+ if ((ret = v4l_compat_translate_ioctl(inode, file, cmd, arg,
+ uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD)
+ uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n",
+ cmd);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int uvc_v4l2_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n");
+ return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl);
+}
+
+static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n");
+ return -ENODEV;
+}
+
+/*
+ * VMA operations.
+ */
+static void uvc_vm_open(struct vm_area_struct *vma)
+{
+ struct uvc_buffer *buffer = vma->vm_private_data;
+ buffer->vma_use_count++;
+}
+
+static void uvc_vm_close(struct vm_area_struct *vma)
+{
+ struct uvc_buffer *buffer = vma->vm_private_data;
+ buffer->vma_use_count--;
+}
+
+static struct vm_operations_struct uvc_vm_ops = {
+ .open = uvc_vm_open,
+ .close = uvc_vm_close,
+};
+
+static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_video_device *video = video_get_drvdata(vdev);
+ struct uvc_buffer *buffer;
+ struct page *page;
+ unsigned long addr, start, size;
+ unsigned int i;
+ int ret = 0;
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n");
+
+ start = vma->vm_start;
+ size = vma->vm_end - vma->vm_start;
+
+ mutex_lock(&video->queue.mutex);
+
+ for (i = 0; i < video->queue.count; ++i) {
+ buffer = &video->queue.buffer[i];
+ if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+
+ if (i == video->queue.count || size != video->queue.buf_size) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /*
+ * VM_IO marks the area as being an mmaped region for I/O to a
+ * device. It also prevents the region from being core dumped.
+ */
+ vma->vm_flags |= VM_IO;
+
+ addr = (unsigned long)video->queue.mem + buffer->buf.m.offset;
+ while (size > 0) {
+ page = vmalloc_to_page((void *)addr);
+ if ((ret = vm_insert_page(vma, start, page)) < 0)
+ goto done;
+
+ start += PAGE_SIZE;
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &uvc_vm_ops;
+ vma->vm_private_data = buffer;
+ uvc_vm_open(vma);
+
+done:
+ mutex_unlock(&video->queue.mutex);
+ return ret;
+}
+
+static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct uvc_video_device *video = video_get_drvdata(vdev);
+
+ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
+
+ return uvc_queue_poll(&video->queue, file, wait);
+}
+
+struct file_operations uvc_fops = {
+ .owner = THIS_MODULE,
+ .open = uvc_v4l2_open,
+ .release = uvc_v4l2_release,
+ .ioctl = uvc_v4l2_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+ .read = uvc_v4l2_read,
+ .mmap = uvc_v4l2_mmap,
+ .poll = uvc_v4l2_poll,
+};
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
new file mode 100644
index 000000000000..6faf1fb21614
--- /dev/null
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -0,0 +1,934 @@
+/*
+ * uvc_video.c -- USB Video Class driver - Video handling
+ *
+ * Copyright (C) 2005-2008
+ * Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <asm/unaligned.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * UVC Controls
+ */
+
+static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size,
+ int timeout)
+{
+ __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ unsigned int pipe;
+ int ret;
+
+ pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
+ : usb_sndctrlpipe(dev->udev, 0);
+ type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
+
+ ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8,
+ unit << 8 | intfnum, data, size, timeout);
+
+ if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u "
+ "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret,
+ size);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size)
+{
+ return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
+ UVC_CTRL_CONTROL_TIMEOUT);
+}
+
+static void uvc_fixup_buffer_size(struct uvc_video_device *video,
+ struct uvc_streaming_control *ctrl)
+{
+ struct uvc_format *format;
+ struct uvc_frame *frame;
+
+ if (ctrl->bFormatIndex <= 0 ||
+ ctrl->bFormatIndex > video->streaming->nformats)
+ return;
+
+ format = &video->streaming->format[ctrl->bFormatIndex - 1];
+
+ if (ctrl->bFrameIndex <= 0 ||
+ ctrl->bFrameIndex > format->nframes)
+ return;
+
+ frame = &format->frame[ctrl->bFrameIndex - 1];
+
+ if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) ||
+ (ctrl->dwMaxVideoFrameSize == 0 &&
+ video->dev->uvc_version < 0x0110))
+ ctrl->dwMaxVideoFrameSize =
+ frame->dwMaxVideoFrameBufferSize;
+}
+
+static int uvc_get_video_ctrl(struct uvc_video_device *video,
+ struct uvc_streaming_control *ctrl, int probe, __u8 query)
+{
+ __u8 data[34];
+ __u8 size;
+ int ret;
+
+ size = video->dev->uvc_version >= 0x0110 ? 34 : 26;
+ ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum,
+ probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size,
+ UVC_CTRL_STREAMING_TIMEOUT);
+
+ if (ret < 0)
+ return ret;
+
+ ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
+ ctrl->bFormatIndex = data[2];
+ ctrl->bFrameIndex = data[3];
+ ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]);
+ ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]);
+ ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]);
+ ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
+ ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
+ ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
+ ctrl->dwMaxVideoFrameSize =
+ le32_to_cpu(get_unaligned((__le32 *)&data[18]));
+ ctrl->dwMaxPayloadTransferSize =
+ le32_to_cpu(get_unaligned((__le32 *)&data[22]));
+
+ if (size == 34) {
+ ctrl->dwClockFrequency =
+ le32_to_cpu(get_unaligned((__le32 *)&data[26]));
+ ctrl->bmFramingInfo = data[30];
+ ctrl->bPreferedVersion = data[31];
+ ctrl->bMinVersion = data[32];
+ ctrl->bMaxVersion = data[33];
+ } else {
+ ctrl->dwClockFrequency = video->dev->clock_frequency;
+ ctrl->bmFramingInfo = 0;
+ ctrl->bPreferedVersion = 0;
+ ctrl->bMinVersion = 0;
+ ctrl->bMaxVersion = 0;
+ }
+
+ /* Some broken devices return a null or wrong dwMaxVideoFrameSize.
+ * Try to get the value from the format and frame descriptor.
+ */
+ uvc_fixup_buffer_size(video, ctrl);
+
+ return 0;
+}
+
+int uvc_set_video_ctrl(struct uvc_video_device *video,
+ struct uvc_streaming_control *ctrl, int probe)
+{
+ __u8 data[34];
+ __u8 size;
+
+ size = video->dev->uvc_version >= 0x0110 ? 34 : 26;
+ memset(data, 0, sizeof data);
+
+ *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
+ data[2] = ctrl->bFormatIndex;
+ data[3] = ctrl->bFrameIndex;
+ *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
+ *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
+ *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
+ *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
+ *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
+ *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
+ /* Note: Some of the fields below are not required for IN devices (see
+ * UVC spec, 4.3.1.1), but we still copy them in case support for OUT
+ * devices is added in the future. */
+ put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize),
+ (__le32 *)&data[18]);
+ put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize),
+ (__le32 *)&data[22]);
+
+ if (size == 34) {
+ put_unaligned(cpu_to_le32(ctrl->dwClockFrequency),
+ (__le32 *)&data[26]);
+ data[30] = ctrl->bmFramingInfo;
+ data[31] = ctrl->bPreferedVersion;
+ data[32] = ctrl->bMinVersion;
+ data[33] = ctrl->bMaxVersion;
+ }
+
+ return __uvc_query_ctrl(video->dev, SET_CUR, 0,
+ video->streaming->intfnum,
+ probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size,
+ UVC_CTRL_STREAMING_TIMEOUT);
+}
+
+int uvc_probe_video(struct uvc_video_device *video,
+ struct uvc_streaming_control *probe)
+{
+ struct uvc_streaming_control probe_min, probe_max;
+ __u16 bandwidth;
+ unsigned int i;
+ int ret;
+
+ mutex_lock(&video->streaming->mutex);
+
+ /* Perform probing. The device should adjust the requested values
+ * according to its capabilities. However, some devices, namely the
+ * first generation UVC Logitech webcams, don't implement the Video
+ * Probe control properly, and just return the needed bandwidth. For
+ * that reason, if the needed bandwidth exceeds the maximum available
+ * bandwidth, try to lower the quality.
+ */
+ if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0)
+ goto done;
+
+ /* Get the minimum and maximum values for compression settings. */
+ if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) {
+ ret = uvc_get_video_ctrl(video, &probe_min, 1, GET_MIN);
+ if (ret < 0)
+ goto done;
+ ret = uvc_get_video_ctrl(video, &probe_max, 1, GET_MAX);
+ if (ret < 0)
+ goto done;
+
+ probe->wCompQuality = probe_max.wCompQuality;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0 ||
+ (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0)
+ goto done;
+
+ if (video->streaming->intf->num_altsetting == 1)
+ break;
+
+ bandwidth = probe->dwMaxPayloadTransferSize;
+ if (bandwidth <= video->streaming->maxpsize)
+ break;
+
+ if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) {
+ ret = -ENOSPC;
+ goto done;
+ }
+
+ /* TODO: negotiate compression parameters */
+ probe->wKeyFrameRate = probe_min.wKeyFrameRate;
+ probe->wPFrameRate = probe_min.wPFrameRate;
+ probe->wCompQuality = probe_max.wCompQuality;
+ probe->wCompWindowSize = probe_min.wCompWindowSize;
+ }
+
+done:
+ mutex_unlock(&video->streaming->mutex);
+ return ret;
+}
+
+/* ------------------------------------------------------------------------
+ * Video codecs
+ */
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
+/* Video payload decoding is handled by uvc_video_decode_start(),
+ * uvc_video_decode_data() and uvc_video_decode_end().
+ *
+ * uvc_video_decode_start is called with URB data at the start of a bulk or
+ * isochronous payload. It processes header data and returns the header size
+ * in bytes if successful. If an error occurs, it returns a negative error
+ * code. The following error codes have special meanings.
+ *
+ * - EAGAIN informs the caller that the current video buffer should be marked
+ * as done, and that the function should be called again with the same data
+ * and a new video buffer. This is used when end of frame conditions can be
+ * reliably detected at the beginning of the next frame only.
+ *
+ * If an error other than -EAGAIN is returned, the caller will drop the current
+ * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be
+ * made until the next payload. -ENODATA can be used to drop the current
+ * payload if no other error code is appropriate.
+ *
+ * uvc_video_decode_data is called for every URB with URB data. It copies the
+ * data to the video buffer.
+ *
+ * uvc_video_decode_end is called with header data at the end of a bulk or
+ * isochronous payload. It performs any additional header data processing and
+ * returns 0 or a negative error code if an error occured. As header data have
+ * already been processed by uvc_video_decode_start, this functions isn't
+ * required to perform sanity checks a second time.
+ *
+ * For isochronous transfers where a payload is always transfered in a single
+ * URB, the three functions will be called in a row.
+ *
+ * To let the decoder process header data and update its internal state even
+ * when no video buffer is available, uvc_video_decode_start must be prepared
+ * to be called with a NULL buf parameter. uvc_video_decode_data and
+ * uvc_video_decode_end will never be called with a NULL buffer.
+ */
+static int uvc_video_decode_start(struct uvc_video_device *video,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ __u8 fid;
+
+ /* Sanity checks:
+ * - packet must be at least 2 bytes long
+ * - bHeaderLength value must be at least 2 bytes (see above)
+ * - bHeaderLength value can't be larger than the packet size.
+ */
+ if (len < 2 || data[0] < 2 || data[0] > len)
+ return -EINVAL;
+
+ /* Skip payloads marked with the error bit ("error frames"). */
+ if (data[1] & UVC_STREAM_ERR) {
+ uvc_trace(UVC_TRACE_FRAME, "Dropping payload (error bit "
+ "set).\n");
+ return -ENODATA;
+ }
+
+ fid = data[1] & UVC_STREAM_FID;
+
+ /* Store the payload FID bit and return immediately when the buffer is
+ * NULL.
+ */
+ if (buf == NULL) {
+ video->last_fid = fid;
+ return -ENODATA;
+ }
+
+ /* Synchronize to the input stream by waiting for the FID bit to be
+ * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
+ * queue->last_fid is initialized to -1, so the first isochronous
+ * frame will always be in sync.
+ *
+ * If the device doesn't toggle the FID bit, invert video->last_fid
+ * when the EOF bit is set to force synchronisation on the next packet.
+ */
+ if (buf->state != UVC_BUF_STATE_ACTIVE) {
+ if (fid == video->last_fid) {
+ uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
+ "sync).\n");
+ if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
+ (data[1] & UVC_STREAM_EOF))
+ video->last_fid ^= UVC_STREAM_FID;
+ return -ENODATA;
+ }
+
+ /* TODO: Handle PTS and SCR. */
+ buf->state = UVC_BUF_STATE_ACTIVE;
+ }
+
+ /* Mark the buffer as done if we're at the beginning of a new frame.
+ * End of frame detection is better implemented by checking the EOF
+ * bit (FID bit toggling is delayed by one frame compared to the EOF
+ * bit), but some devices don't set the bit at end of frame (and the
+ * last payload can be lost anyway). We thus must check if the FID has
+ * been toggled.
+ *
+ * queue->last_fid is initialized to -1, so the first isochronous
+ * frame will never trigger an end of frame detection.
+ *
+ * Empty buffers (bytesused == 0) don't trigger end of frame detection
+ * as it doesn't make sense to return an empty buffer. This also
+ * avoids detecting and of frame conditions at FID toggling if the
+ * previous payload had the EOF bit set.
+ */
+ if (fid != video->last_fid && buf->buf.bytesused != 0) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
+ "toggled).\n");
+ buf->state = UVC_BUF_STATE_DONE;
+ return -EAGAIN;
+ }
+
+ video->last_fid = fid;
+
+ return data[0];
+}
+
+static void uvc_video_decode_data(struct uvc_video_device *video,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ struct uvc_video_queue *queue = &video->queue;
+ unsigned int maxlen, nbytes;
+ void *mem;
+
+ if (len <= 0)
+ return;
+
+ /* Copy the video data to the buffer. */
+ maxlen = buf->buf.length - buf->buf.bytesused;
+ mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+ nbytes = min((unsigned int)len, maxlen);
+ memcpy(mem, data, nbytes);
+ buf->buf.bytesused += nbytes;
+
+ /* Complete the current frame if the buffer size was exceeded. */
+ if (len > maxlen) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
+ buf->state = UVC_BUF_STATE_DONE;
+ }
+}
+
+static void uvc_video_decode_end(struct uvc_video_device *video,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ /* Mark the buffer as done if the EOF marker is set. */
+ if (data[1] & UVC_STREAM_EOF && buf->buf.bytesused != 0) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
+ if (data[0] == len)
+ uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
+ buf->state = UVC_BUF_STATE_DONE;
+ if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
+ video->last_fid ^= UVC_STREAM_FID;
+ }
+}
+
+/* ------------------------------------------------------------------------
+ * URB handling
+ */
+
+/*
+ * Completion handler for video URBs.
+ */
+static void uvc_video_decode_isoc(struct urb *urb,
+ struct uvc_video_device *video, struct uvc_buffer *buf)
+{
+ u8 *mem;
+ int ret, i;
+
+ for (i = 0; i < urb->number_of_packets; ++i) {
+ if (urb->iso_frame_desc[i].status < 0) {
+ uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
+ "lost (%d).\n", urb->iso_frame_desc[i].status);
+ continue;
+ }
+
+ /* Decode the payload header. */
+ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ do {
+ ret = uvc_video_decode_start(video, buf, mem,
+ urb->iso_frame_desc[i].actual_length);
+ if (ret == -EAGAIN)
+ buf = uvc_queue_next_buffer(&video->queue, buf);
+ } while (ret == -EAGAIN);
+
+ if (ret < 0)
+ continue;
+
+ /* Decode the payload data. */
+ uvc_video_decode_data(video, buf, mem + ret,
+ urb->iso_frame_desc[i].actual_length - ret);
+
+ /* Process the header again. */
+ uvc_video_decode_end(video, buf, mem, ret);
+
+ if (buf->state == UVC_BUF_STATE_DONE ||
+ buf->state == UVC_BUF_STATE_ERROR)
+ buf = uvc_queue_next_buffer(&video->queue, buf);
+ }
+}
+
+static void uvc_video_decode_bulk(struct urb *urb,
+ struct uvc_video_device *video, struct uvc_buffer *buf)
+{
+ u8 *mem;
+ int len, ret;
+
+ mem = urb->transfer_buffer;
+ len = urb->actual_length;
+ video->bulk.payload_size += len;
+
+ /* If the URB is the first of its payload, decode and save the
+ * header.
+ */
+ if (video->bulk.header_size == 0) {
+ do {
+ ret = uvc_video_decode_start(video, buf, mem, len);
+ if (ret == -EAGAIN)
+ buf = uvc_queue_next_buffer(&video->queue, buf);
+ } while (ret == -EAGAIN);
+
+ /* If an error occured skip the rest of the payload. */
+ if (ret < 0 || buf == NULL) {
+ video->bulk.skip_payload = 1;
+ return;
+ }
+
+ video->bulk.header_size = ret;
+ memcpy(video->bulk.header, mem, video->bulk.header_size);
+
+ mem += ret;
+ len -= ret;
+ }
+
+ /* The buffer queue might have been cancelled while a bulk transfer
+ * was in progress, so we can reach here with buf equal to NULL. Make
+ * sure buf is never dereferenced if NULL.
+ */
+
+ /* Process video data. */
+ if (!video->bulk.skip_payload && buf != NULL)
+ uvc_video_decode_data(video, buf, mem, len);
+
+ /* Detect the payload end by a URB smaller than the maximum size (or
+ * a payload size equal to the maximum) and process the header again.
+ */
+ if (urb->actual_length < urb->transfer_buffer_length ||
+ video->bulk.payload_size >= video->bulk.max_payload_size) {
+ if (!video->bulk.skip_payload && buf != NULL) {
+ uvc_video_decode_end(video, buf, video->bulk.header,
+ video->bulk.header_size);
+ if (buf->state == UVC_BUF_STATE_DONE ||
+ buf->state == UVC_BUF_STATE_ERROR)
+ buf = uvc_queue_next_buffer(&video->queue, buf);
+ }
+
+ video->bulk.header_size = 0;
+ video->bulk.skip_payload = 0;
+ video->bulk.payload_size = 0;
+ }
+}
+
+static void uvc_video_complete(struct urb *urb)
+{
+ struct uvc_video_device *video = urb->context;
+ struct uvc_video_queue *queue = &video->queue;
+ struct uvc_buffer *buf = NULL;
+ unsigned long flags;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ break;
+
+ default:
+ uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
+ "completion handler.\n", urb->status);
+
+ case -ENOENT: /* usb_kill_urb() called. */
+ if (video->frozen)
+ return;
+
+ case -ECONNRESET: /* usb_unlink_urb() called. */
+ case -ESHUTDOWN: /* The endpoint is being disabled. */
+ uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
+ return;
+ }
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ if (!list_empty(&queue->irqqueue))
+ buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+ queue);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ video->decode(urb, video, buf);
+
+ if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+ ret);
+ }
+}
+
+/*
+ * Uninitialize isochronous/bulk URBs and free transfer buffers.
+ */
+static void uvc_uninit_video(struct uvc_video_device *video)
+{
+ struct urb *urb;
+ unsigned int i;
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ if ((urb = video->urb[i]) == NULL)
+ continue;
+
+ usb_kill_urb(urb);
+ /* urb->transfer_buffer_length is not touched by USB core, so
+ * we can use it here as the buffer length.
+ */
+ if (video->urb_buffer[i]) {
+ usb_buffer_free(video->dev->udev,
+ urb->transfer_buffer_length,
+ video->urb_buffer[i], urb->transfer_dma);
+ video->urb_buffer[i] = NULL;
+ }
+
+ usb_free_urb(urb);
+ video->urb[i] = NULL;
+ }
+}
+
+/*
+ * Initialize isochronous URBs and allocate transfer buffers. The packet size
+ * is given by the endpoint.
+ */
+static int uvc_init_video_isoc(struct uvc_video_device *video,
+ struct usb_host_endpoint *ep)
+{
+ struct urb *urb;
+ unsigned int npackets, i, j;
+ __u16 psize;
+ __u32 size;
+
+ /* Compute the number of isochronous packets to allocate by dividing
+ * the maximum video frame size by the packet size. Limit the result
+ * to UVC_MAX_ISO_PACKETS.
+ */
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+
+ size = video->streaming->ctrl.dwMaxVideoFrameSize;
+ if (size > UVC_MAX_FRAME_SIZE)
+ return -EINVAL;
+
+ npackets = (size + psize - 1) / psize;
+ if (npackets > UVC_MAX_ISO_PACKETS)
+ npackets = UVC_MAX_ISO_PACKETS;
+
+ size = npackets * psize;
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = usb_alloc_urb(npackets, GFP_KERNEL);
+ if (urb == NULL) {
+ uvc_uninit_video(video);
+ return -ENOMEM;
+ }
+
+ video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
+ size, GFP_KERNEL, &urb->transfer_dma);
+ if (video->urb_buffer[i] == NULL) {
+ usb_free_urb(urb);
+ uvc_uninit_video(video);
+ return -ENOMEM;
+ }
+
+ urb->dev = video->dev->udev;
+ urb->context = video;
+ urb->pipe = usb_rcvisocpipe(video->dev->udev,
+ ep->desc.bEndpointAddress);
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = ep->desc.bInterval;
+ urb->transfer_buffer = video->urb_buffer[i];
+ urb->complete = uvc_video_complete;
+ urb->number_of_packets = npackets;
+ urb->transfer_buffer_length = size;
+
+ for (j = 0; j < npackets; ++j) {
+ urb->iso_frame_desc[j].offset = j * psize;
+ urb->iso_frame_desc[j].length = psize;
+ }
+
+ video->urb[i] = urb;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize bulk URBs and allocate transfer buffers. The packet size is
+ * given by the endpoint.
+ */
+static int uvc_init_video_bulk(struct uvc_video_device *video,
+ struct usb_host_endpoint *ep)
+{
+ struct urb *urb;
+ unsigned int pipe, i;
+ __u16 psize;
+ __u32 size;
+
+ /* Compute the bulk URB size. Some devices set the maximum payload
+ * size to a value too high for memory-constrained devices. We must
+ * then transfer the payload accross multiple URBs. To be consistant
+ * with isochronous mode, allocate maximum UVC_MAX_ISO_PACKETS per bulk
+ * URB.
+ */
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;
+ size = video->streaming->ctrl.dwMaxPayloadTransferSize;
+ video->bulk.max_payload_size = size;
+ if (size > psize * UVC_MAX_ISO_PACKETS)
+ size = psize * UVC_MAX_ISO_PACKETS;
+
+ pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress);
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (urb == NULL) {
+ uvc_uninit_video(video);
+ return -ENOMEM;
+ }
+
+ video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
+ size, GFP_KERNEL, &urb->transfer_dma);
+ if (video->urb_buffer[i] == NULL) {
+ usb_free_urb(urb);
+ uvc_uninit_video(video);
+ return -ENOMEM;
+ }
+
+ usb_fill_bulk_urb(urb, video->dev->udev, pipe,
+ video->urb_buffer[i], size, uvc_video_complete,
+ video);
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+
+ video->urb[i] = urb;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize isochronous/bulk URBs and allocate transfer buffers.
+ */
+static int uvc_init_video(struct uvc_video_device *video)
+{
+ struct usb_interface *intf = video->streaming->intf;
+ struct usb_host_interface *alts;
+ struct usb_host_endpoint *ep = NULL;
+ int intfnum = video->streaming->intfnum;
+ unsigned int bandwidth, psize, i;
+ int ret;
+
+ video->last_fid = -1;
+ video->bulk.header_size = 0;
+ video->bulk.skip_payload = 0;
+ video->bulk.payload_size = 0;
+
+ if (intf->num_altsetting > 1) {
+ /* Isochronous endpoint, select the alternate setting. */
+ bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize;
+
+ if (bandwidth == 0) {
+ uvc_printk(KERN_WARNING, "device %s requested null "
+ "bandwidth, defaulting to lowest.\n",
+ video->vdev->name);
+ bandwidth = 1;
+ }
+
+ for (i = 0; i < intf->num_altsetting; ++i) {
+ alts = &intf->altsetting[i];
+ ep = uvc_find_endpoint(alts,
+ video->streaming->header.bEndpointAddress);
+ if (ep == NULL)
+ continue;
+
+ /* Check if the bandwidth is high enough. */
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ if (psize >= bandwidth)
+ break;
+ }
+
+ if (i >= intf->num_altsetting)
+ return -EIO;
+
+ if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0)
+ return ret;
+
+ ret = uvc_init_video_isoc(video, ep);
+ } else {
+ /* Bulk endpoint, proceed to URB initialization. */
+ ep = uvc_find_endpoint(&intf->altsetting[0],
+ video->streaming->header.bEndpointAddress);
+ if (ep == NULL)
+ return -EIO;
+
+ ret = uvc_init_video_bulk(video, ep);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Submit the URBs. */
+ for (i = 0; i < UVC_URBS; ++i) {
+ if ((ret = usb_submit_urb(video->urb[i], GFP_KERNEL)) < 0) {
+ uvc_printk(KERN_ERR, "Failed to submit URB %u "
+ "(%d).\n", i, ret);
+ uvc_uninit_video(video);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+/*
+ * Stop streaming without disabling the video queue.
+ *
+ * To let userspace applications resume without trouble, we must not touch the
+ * video buffers in any way. We mark the device as frozen to make sure the URB
+ * completion handler won't try to cancel the queue when we kill the URBs.
+ */
+int uvc_video_suspend(struct uvc_video_device *video)
+{
+ if (!uvc_queue_streaming(&video->queue))
+ return 0;
+
+ video->frozen = 1;
+ uvc_uninit_video(video);
+ usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
+ return 0;
+}
+
+/*
+ * Reconfigure the video interface and restart streaming if it was enable
+ * before suspend.
+ *
+ * If an error occurs, disable the video queue. This will wake all pending
+ * buffers, making sure userspace applications are notified of the problem
+ * instead of waiting forever.
+ */
+int uvc_video_resume(struct uvc_video_device *video)
+{
+ int ret;
+
+ video->frozen = 0;
+
+ if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) {
+ uvc_queue_enable(&video->queue, 0);
+ return ret;
+ }
+
+ if (!uvc_queue_streaming(&video->queue))
+ return 0;
+
+ if ((ret = uvc_init_video(video)) < 0)
+ uvc_queue_enable(&video->queue, 0);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------
+ * Video device
+ */
+
+/*
+ * Initialize the UVC video device by retrieving the default format and
+ * committing it.
+ *
+ * Some cameras (namely the Fuji Finepix) set the format and frame
+ * indexes to zero. The UVC standard doesn't clearly make this a spec
+ * violation, so try to silently fix the values if possible.
+ *
+ * This function is called before registering the device with V4L.
+ */
+int uvc_video_init(struct uvc_video_device *video)
+{
+ struct uvc_streaming_control *probe = &video->streaming->ctrl;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ unsigned int i;
+ int ret;
+
+ if (video->streaming->nformats == 0) {
+ uvc_printk(KERN_INFO, "No supported video formats found.\n");
+ return -EINVAL;
+ }
+
+ /* Alternate setting 0 should be the default, yet the XBox Live Vision
+ * Cam (and possibly other devices) crash or otherwise misbehave if
+ * they don't receive a SET_INTERFACE request before any other video
+ * control request.
+ */
+ usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
+
+ /* Some webcams don't suport GET_DEF request on the probe control. We
+ * fall back to GET_CUR if GET_DEF fails.
+ */
+ if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_DEF)) < 0 &&
+ (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0)
+ return ret;
+
+ /* Check if the default format descriptor exists. Use the first
+ * available format otherwise.
+ */
+ for (i = video->streaming->nformats; i > 0; --i) {
+ format = &video->streaming->format[i-1];
+ if (format->index == probe->bFormatIndex)
+ break;
+ }
+
+ if (format->nframes == 0) {
+ uvc_printk(KERN_INFO, "No frame descriptor found for the "
+ "default format.\n");
+ return -EINVAL;
+ }
+
+ /* Zero bFrameIndex might be correct. Stream-based formats (including
+ * MPEG-2 TS and DV) do not support frames but have a dummy frame
+ * descriptor with bFrameIndex set to zero. If the default frame
+ * descriptor is not found, use the first avalable frame.
+ */
+ for (i = format->nframes; i > 0; --i) {
+ frame = &format->frame[i-1];
+ if (frame->bFrameIndex == probe->bFrameIndex)
+ break;
+ }
+
+ /* Commit the default settings. */
+ probe->bFormatIndex = format->index;
+ probe->bFrameIndex = frame->bFrameIndex;
+ if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0)
+ return ret;
+
+ video->streaming->cur_format = format;
+ video->streaming->cur_frame = frame;
+ atomic_set(&video->active, 0);
+
+ /* Select the video decoding function */
+ if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
+ video->decode = uvc_video_decode_isight;
+ else if (video->streaming->intf->num_altsetting > 1)
+ video->decode = uvc_video_decode_isoc;
+ else
+ video->decode = uvc_video_decode_bulk;
+
+ return 0;
+}
+
+/*
+ * Enable or disable the video stream.
+ */
+int uvc_video_enable(struct uvc_video_device *video, int enable)
+{
+ int ret;
+
+ if (!enable) {
+ uvc_uninit_video(video);
+ usb_set_interface(video->dev->udev,
+ video->streaming->intfnum, 0);
+ uvc_queue_enable(&video->queue, 0);
+ return 0;
+ }
+
+ if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
+ return ret;
+
+ return uvc_init_video(video);
+}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
new file mode 100644
index 000000000000..a995a780db1c
--- /dev/null
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -0,0 +1,796 @@
+#ifndef _USB_VIDEO_H_
+#define _USB_VIDEO_H_
+
+#include <linux/kernel.h>
+#include <linux/videodev2.h>
+
+
+/*
+ * Dynamic controls
+ */
+
+/* Data types for UVC control data */
+#define UVC_CTRL_DATA_TYPE_RAW 0
+#define UVC_CTRL_DATA_TYPE_SIGNED 1
+#define UVC_CTRL_DATA_TYPE_UNSIGNED 2
+#define UVC_CTRL_DATA_TYPE_BOOLEAN 3
+#define UVC_CTRL_DATA_TYPE_ENUM 4
+#define UVC_CTRL_DATA_TYPE_BITMASK 5
+
+/* Control flags */
+#define UVC_CONTROL_SET_CUR (1 << 0)
+#define UVC_CONTROL_GET_CUR (1 << 1)
+#define UVC_CONTROL_GET_MIN (1 << 2)
+#define UVC_CONTROL_GET_MAX (1 << 3)
+#define UVC_CONTROL_GET_RES (1 << 4)
+#define UVC_CONTROL_GET_DEF (1 << 5)
+/* Control should be saved at suspend and restored at resume. */
+#define UVC_CONTROL_RESTORE (1 << 6)
+/* Control can be updated by the camera. */
+#define UVC_CONTROL_AUTO_UPDATE (1 << 7)
+
+#define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
+ UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
+ UVC_CONTROL_GET_DEF)
+
+struct uvc_xu_control_info {
+ __u8 entity[16];
+ __u8 index;
+ __u8 selector;
+ __u16 size;
+ __u32 flags;
+};
+
+struct uvc_xu_control_mapping {
+ __u32 id;
+ __u8 name[32];
+ __u8 entity[16];
+ __u8 selector;
+
+ __u8 size;
+ __u8 offset;
+ enum v4l2_ctrl_type v4l2_type;
+ __u32 data_type;
+};
+
+struct uvc_xu_control {
+ __u8 unit;
+ __u8 selector;
+ __u16 size;
+ __u8 __user *data;
+};
+
+#define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info)
+#define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
+#define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
+#define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
+
+#ifdef __KERNEL__
+
+#include <linux/poll.h>
+
+/* --------------------------------------------------------------------------
+ * UVC constants
+ */
+
+#define SC_UNDEFINED 0x00
+#define SC_VIDEOCONTROL 0x01
+#define SC_VIDEOSTREAMING 0x02
+#define SC_VIDEO_INTERFACE_COLLECTION 0x03
+
+#define PC_PROTOCOL_UNDEFINED 0x00
+
+#define CS_UNDEFINED 0x20
+#define CS_DEVICE 0x21
+#define CS_CONFIGURATION 0x22
+#define CS_STRING 0x23
+#define CS_INTERFACE 0x24
+#define CS_ENDPOINT 0x25
+
+/* VideoControl class specific interface descriptor */
+#define VC_DESCRIPTOR_UNDEFINED 0x00
+#define VC_HEADER 0x01
+#define VC_INPUT_TERMINAL 0x02
+#define VC_OUTPUT_TERMINAL 0x03
+#define VC_SELECTOR_UNIT 0x04
+#define VC_PROCESSING_UNIT 0x05
+#define VC_EXTENSION_UNIT 0x06
+
+/* VideoStreaming class specific interface descriptor */
+#define VS_UNDEFINED 0x00
+#define VS_INPUT_HEADER 0x01
+#define VS_OUTPUT_HEADER 0x02
+#define VS_STILL_IMAGE_FRAME 0x03
+#define VS_FORMAT_UNCOMPRESSED 0x04
+#define VS_FRAME_UNCOMPRESSED 0x05
+#define VS_FORMAT_MJPEG 0x06
+#define VS_FRAME_MJPEG 0x07
+#define VS_FORMAT_MPEG2TS 0x0a
+#define VS_FORMAT_DV 0x0c
+#define VS_COLORFORMAT 0x0d
+#define VS_FORMAT_FRAME_BASED 0x10
+#define VS_FRAME_FRAME_BASED 0x11
+#define VS_FORMAT_STREAM_BASED 0x12
+
+/* Endpoint type */
+#define EP_UNDEFINED 0x00
+#define EP_GENERAL 0x01
+#define EP_ENDPOINT 0x02
+#define EP_INTERRUPT 0x03
+
+/* Request codes */
+#define RC_UNDEFINED 0x00
+#define SET_CUR 0x01
+#define GET_CUR 0x81
+#define GET_MIN 0x82
+#define GET_MAX 0x83
+#define GET_RES 0x84
+#define GET_LEN 0x85
+#define GET_INFO 0x86
+#define GET_DEF 0x87
+
+/* VideoControl interface controls */
+#define VC_CONTROL_UNDEFINED 0x00
+#define VC_VIDEO_POWER_MODE_CONTROL 0x01
+#define VC_REQUEST_ERROR_CODE_CONTROL 0x02
+
+/* Terminal controls */
+#define TE_CONTROL_UNDEFINED 0x00
+
+/* Selector Unit controls */
+#define SU_CONTROL_UNDEFINED 0x00
+#define SU_INPUT_SELECT_CONTROL 0x01
+
+/* Camera Terminal controls */
+#define CT_CONTROL_UNDEFINED 0x00
+#define CT_SCANNING_MODE_CONTROL 0x01
+#define CT_AE_MODE_CONTROL 0x02
+#define CT_AE_PRIORITY_CONTROL 0x03
+#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04
+#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05
+#define CT_FOCUS_ABSOLUTE_CONTROL 0x06
+#define CT_FOCUS_RELATIVE_CONTROL 0x07
+#define CT_FOCUS_AUTO_CONTROL 0x08
+#define CT_IRIS_ABSOLUTE_CONTROL 0x09
+#define CT_IRIS_RELATIVE_CONTROL 0x0a
+#define CT_ZOOM_ABSOLUTE_CONTROL 0x0b
+#define CT_ZOOM_RELATIVE_CONTROL 0x0c
+#define CT_PANTILT_ABSOLUTE_CONTROL 0x0d
+#define CT_PANTILT_RELATIVE_CONTROL 0x0e
+#define CT_ROLL_ABSOLUTE_CONTROL 0x0f
+#define CT_ROLL_RELATIVE_CONTROL 0x10
+#define CT_PRIVACY_CONTROL 0x11
+
+/* Processing Unit controls */
+#define PU_CONTROL_UNDEFINED 0x00
+#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01
+#define PU_BRIGHTNESS_CONTROL 0x02
+#define PU_CONTRAST_CONTROL 0x03
+#define PU_GAIN_CONTROL 0x04
+#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05
+#define PU_HUE_CONTROL 0x06
+#define PU_SATURATION_CONTROL 0x07
+#define PU_SHARPNESS_CONTROL 0x08
+#define PU_GAMMA_CONTROL 0x09
+#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a
+#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b
+#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c
+#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d
+#define PU_DIGITAL_MULTIPLIER_CONTROL 0x0e
+#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f
+#define PU_HUE_AUTO_CONTROL 0x10
+#define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11
+#define PU_ANALOG_LOCK_STATUS_CONTROL 0x12
+
+#define LXU_MOTOR_PANTILT_RELATIVE_CONTROL 0x01
+#define LXU_MOTOR_PANTILT_RESET_CONTROL 0x02
+#define LXU_MOTOR_FOCUS_MOTOR_CONTROL 0x03
+
+/* VideoStreaming interface controls */
+#define VS_CONTROL_UNDEFINED 0x00
+#define VS_PROBE_CONTROL 0x01
+#define VS_COMMIT_CONTROL 0x02
+#define VS_STILL_PROBE_CONTROL 0x03
+#define VS_STILL_COMMIT_CONTROL 0x04
+#define VS_STILL_IMAGE_TRIGGER_CONTROL 0x05
+#define VS_STREAM_ERROR_CODE_CONTROL 0x06
+#define VS_GENERATE_KEY_FRAME_CONTROL 0x07
+#define VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08
+#define VS_SYNC_DELAY_CONTROL 0x09
+
+#define TT_VENDOR_SPECIFIC 0x0100
+#define TT_STREAMING 0x0101
+
+/* Input Terminal types */
+#define ITT_VENDOR_SPECIFIC 0x0200
+#define ITT_CAMERA 0x0201
+#define ITT_MEDIA_TRANSPORT_INPUT 0x0202
+
+/* Output Terminal types */
+#define OTT_VENDOR_SPECIFIC 0x0300
+#define OTT_DISPLAY 0x0301
+#define OTT_MEDIA_TRANSPORT_OUTPUT 0x0302
+
+/* External Terminal types */
+#define EXTERNAL_VENDOR_SPECIFIC 0x0400
+#define COMPOSITE_CONNECTOR 0x0401
+#define SVIDEO_CONNECTOR 0x0402
+#define COMPONENT_CONNECTOR 0x0403
+
+#define UVC_TERM_INPUT 0x0000
+#define UVC_TERM_OUTPUT 0x8000
+
+#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff)
+#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
+#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0)
+#define UVC_ENTITY_IS_ITERM(entity) \
+ (((entity)->type & 0x8000) == UVC_TERM_INPUT)
+#define UVC_ENTITY_IS_OTERM(entity) \
+ (((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
+
+#define UVC_STATUS_TYPE_CONTROL 1
+#define UVC_STATUS_TYPE_STREAMING 2
+
+/* ------------------------------------------------------------------------
+ * GUIDs
+ */
+#define UVC_GUID_UVC_CAMERA \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
+#define UVC_GUID_UVC_OUTPUT \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}
+#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
+#define UVC_GUID_UVC_PROCESSING \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}
+#define UVC_GUID_UVC_SELECTOR \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
+
+#define UVC_GUID_LOGITECH_DEV_INFO \
+ {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+ 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e}
+#define UVC_GUID_LOGITECH_USER_HW \
+ {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+ 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f}
+#define UVC_GUID_LOGITECH_VIDEO \
+ {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+ 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x50}
+#define UVC_GUID_LOGITECH_MOTOR \
+ {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+ 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56}
+
+#define UVC_GUID_FORMAT_MJPEG \
+ { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YUY2 \
+ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_NV12 \
+ { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YV12 \
+ { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_I420 \
+ { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_UYVY \
+ { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y800 \
+ { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_BY8 \
+ { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+
+
+/* ------------------------------------------------------------------------
+ * Driver specific constants.
+ */
+
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0)
+
+/* Number of isochronous URBs. */
+#define UVC_URBS 5
+/* Maximum number of packets per isochronous URB. */
+#define UVC_MAX_ISO_PACKETS 40
+/* Maximum frame size in bytes, for sanity checking. */
+#define UVC_MAX_FRAME_SIZE (16*1024*1024)
+/* Maximum number of video buffers. */
+#define UVC_MAX_VIDEO_BUFFERS 32
+
+#define UVC_CTRL_CONTROL_TIMEOUT 300
+#define UVC_CTRL_STREAMING_TIMEOUT 1000
+
+/* Devices quirks */
+#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
+#define UVC_QUIRK_PROBE_MINMAX 0x00000002
+#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004
+#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008
+#define UVC_QUIRK_STREAM_NO_FID 0x00000010
+#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
+
+/* Format flags */
+#define UVC_FMT_FLAG_COMPRESSED 0x00000001
+#define UVC_FMT_FLAG_STREAM 0x00000002
+
+/* ------------------------------------------------------------------------
+ * Structures.
+ */
+
+struct uvc_device;
+
+/* TODO: Put the most frequently accessed fields at the beginning of
+ * structures to maximize cache efficiency.
+ */
+struct uvc_streaming_control {
+ __u16 bmHint;
+ __u8 bFormatIndex;
+ __u8 bFrameIndex;
+ __u32 dwFrameInterval;
+ __u16 wKeyFrameRate;
+ __u16 wPFrameRate;
+ __u16 wCompQuality;
+ __u16 wCompWindowSize;
+ __u16 wDelay;
+ __u32 dwMaxVideoFrameSize;
+ __u32 dwMaxPayloadTransferSize;
+ __u32 dwClockFrequency;
+ __u8 bmFramingInfo;
+ __u8 bPreferedVersion;
+ __u8 bMinVersion;
+ __u8 bMaxVersion;
+};
+
+struct uvc_menu_info {
+ __u32 value;
+ __u8 name[32];
+};
+
+struct uvc_control_info {
+ struct list_head list;
+ struct list_head mappings;
+
+ __u8 entity[16];
+ __u8 index;
+ __u8 selector;
+
+ __u16 size;
+ __u32 flags;
+};
+
+struct uvc_control_mapping {
+ struct list_head list;
+
+ struct uvc_control_info *ctrl;
+
+ __u32 id;
+ __u8 name[32];
+ __u8 entity[16];
+ __u8 selector;
+
+ __u8 size;
+ __u8 offset;
+ enum v4l2_ctrl_type v4l2_type;
+ __u32 data_type;
+
+ struct uvc_menu_info *menu_info;
+ __u32 menu_count;
+};
+
+struct uvc_control {
+ struct uvc_entity *entity;
+ struct uvc_control_info *info;
+
+ __u8 index; /* Used to match the uvc_control entry with a
+ uvc_control_info. */
+ __u8 dirty : 1,
+ loaded : 1,
+ modified : 1;
+
+ __u8 *data;
+};
+
+struct uvc_format_desc {
+ char *name;
+ __u8 guid[16];
+ __u32 fcc;
+};
+
+/* The term 'entity' refers to both UVC units and UVC terminals.
+ *
+ * The type field is either the terminal type (wTerminalType in the terminal
+ * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor).
+ * As the bDescriptorSubtype field is one byte long, the type value will
+ * always have a null MSB for units. All terminal types defined by the UVC
+ * specification have a non-null MSB, so it is safe to use the MSB to
+ * differentiate between units and terminals as long as the descriptor parsing
+ * code makes sure terminal types have a non-null MSB.
+ *
+ * For terminals, the type's most significant bit stores the terminal
+ * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should
+ * always be accessed with the UVC_ENTITY_* macros and never directly.
+ */
+
+struct uvc_entity {
+ struct list_head list; /* Entity as part of a UVC device. */
+ struct list_head chain; /* Entity as part of a video device
+ * chain. */
+ __u8 id;
+ __u16 type;
+ char name[64];
+
+ union {
+ struct {
+ __u16 wObjectiveFocalLengthMin;
+ __u16 wObjectiveFocalLengthMax;
+ __u16 wOcularFocalLength;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ } camera;
+
+ struct {
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 bTransportModeSize;
+ __u8 *bmTransportModes;
+ } media;
+
+ struct {
+ __u8 bSourceID;
+ } output;
+
+ struct {
+ __u8 bSourceID;
+ __u16 wMaxMultiplier;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 bmVideoStandards;
+ } processing;
+
+ struct {
+ __u8 bNrInPins;
+ __u8 *baSourceID;
+ } selector;
+
+ struct {
+ __u8 guidExtensionCode[16];
+ __u8 bNumControls;
+ __u8 bNrInPins;
+ __u8 *baSourceID;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 *bmControlsType;
+ } extension;
+ };
+
+ unsigned int ncontrols;
+ struct uvc_control *controls;
+};
+
+struct uvc_frame {
+ __u8 bFrameIndex;
+ __u8 bmCapabilities;
+ __u16 wWidth;
+ __u16 wHeight;
+ __u32 dwMinBitRate;
+ __u32 dwMaxBitRate;
+ __u32 dwMaxVideoFrameBufferSize;
+ __u8 bFrameIntervalType;
+ __u32 dwDefaultFrameInterval;
+ __u32 *dwFrameInterval;
+};
+
+struct uvc_format {
+ __u8 type;
+ __u8 index;
+ __u8 bpp;
+ __u8 colorspace;
+ __u32 fcc;
+ __u32 flags;
+
+ char name[32];
+
+ unsigned int nframes;
+ struct uvc_frame *frame;
+};
+
+struct uvc_streaming_header {
+ __u8 bNumFormats;
+ __u8 bEndpointAddress;
+ __u8 bTerminalLink;
+ __u8 bControlSize;
+ __u8 *bmaControls;
+ /* The following fields are used by input headers only. */
+ __u8 bmInfo;
+ __u8 bStillCaptureMethod;
+ __u8 bTriggerSupport;
+ __u8 bTriggerUsage;
+};
+
+struct uvc_streaming {
+ struct list_head list;
+
+ struct usb_interface *intf;
+ int intfnum;
+ __u16 maxpsize;
+
+ struct uvc_streaming_header header;
+
+ unsigned int nformats;
+ struct uvc_format *format;
+
+ struct uvc_streaming_control ctrl;
+ struct uvc_format *cur_format;
+ struct uvc_frame *cur_frame;
+
+ struct mutex mutex;
+};
+
+enum uvc_buffer_state {
+ UVC_BUF_STATE_IDLE = 0,
+ UVC_BUF_STATE_QUEUED = 1,
+ UVC_BUF_STATE_ACTIVE = 2,
+ UVC_BUF_STATE_DONE = 3,
+ UVC_BUF_STATE_ERROR = 4,
+};
+
+struct uvc_buffer {
+ unsigned long vma_use_count;
+ struct list_head stream;
+
+ /* Touched by interrupt handler. */
+ struct v4l2_buffer buf;
+ struct list_head queue;
+ wait_queue_head_t wait;
+ enum uvc_buffer_state state;
+};
+
+#define UVC_QUEUE_STREAMING (1 << 0)
+#define UVC_QUEUE_DISCONNECTED (1 << 1)
+#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
+
+struct uvc_video_queue {
+ void *mem;
+ unsigned int flags;
+ __u32 sequence;
+
+ unsigned int count;
+ unsigned int buf_size;
+ struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
+ struct mutex mutex; /* protects buffers and mainqueue */
+ spinlock_t irqlock; /* protects irqqueue */
+
+ struct list_head mainqueue;
+ struct list_head irqqueue;
+};
+
+struct uvc_video_device {
+ struct uvc_device *dev;
+ struct video_device *vdev;
+ atomic_t active;
+ unsigned int frozen : 1;
+
+ struct list_head iterms;
+ struct uvc_entity *oterm;
+ struct uvc_entity *processing;
+ struct uvc_entity *selector;
+ struct list_head extensions;
+ struct mutex ctrl_mutex;
+
+ struct uvc_video_queue queue;
+
+ /* Video streaming object, must always be non-NULL. */
+ struct uvc_streaming *streaming;
+
+ void (*decode) (struct urb *urb, struct uvc_video_device *video,
+ struct uvc_buffer *buf);
+
+ /* Context data used by the bulk completion handler. */
+ struct {
+ __u8 header[256];
+ unsigned int header_size;
+ int skip_payload;
+ __u32 payload_size;
+ __u32 max_payload_size;
+ } bulk;
+
+ struct urb *urb[UVC_URBS];
+ char *urb_buffer[UVC_URBS];
+
+ __u8 last_fid;
+};
+
+enum uvc_device_state {
+ UVC_DEV_DISCONNECTED = 1,
+};
+
+struct uvc_device {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ __u32 quirks;
+ int intfnum;
+ char name[32];
+
+ enum uvc_device_state state;
+ struct kref kref;
+ struct list_head list;
+
+ /* Video control interface */
+ __u16 uvc_version;
+ __u32 clock_frequency;
+
+ struct list_head entities;
+
+ struct uvc_video_device video;
+
+ /* Status Interrupt Endpoint */
+ struct usb_host_endpoint *int_ep;
+ struct urb *int_urb;
+ __u8 status[16];
+ struct input_dev *input;
+
+ /* Video Streaming interfaces */
+ struct list_head streaming;
+};
+
+enum uvc_handle_state {
+ UVC_HANDLE_PASSIVE = 0,
+ UVC_HANDLE_ACTIVE = 1,
+};
+
+struct uvc_fh {
+ struct uvc_video_device *device;
+ enum uvc_handle_state state;
+};
+
+struct uvc_driver {
+ struct usb_driver driver;
+
+ struct mutex open_mutex; /* protects from open/disconnect race */
+
+ struct list_head devices; /* struct uvc_device list */
+ struct list_head controls; /* struct uvc_control_info list */
+ struct mutex ctrl_mutex; /* protects controls and devices
+ lists */
+};
+
+/* ------------------------------------------------------------------------
+ * Debugging, printing and logging
+ */
+
+#define UVC_TRACE_PROBE (1 << 0)
+#define UVC_TRACE_DESCR (1 << 1)
+#define UVC_TRACE_CONTROL (1 << 2)
+#define UVC_TRACE_FORMAT (1 << 3)
+#define UVC_TRACE_CAPTURE (1 << 4)
+#define UVC_TRACE_CALLS (1 << 5)
+#define UVC_TRACE_IOCTL (1 << 6)
+#define UVC_TRACE_FRAME (1 << 7)
+#define UVC_TRACE_SUSPEND (1 << 8)
+#define UVC_TRACE_STATUS (1 << 9)
+
+extern unsigned int uvc_trace_param;
+
+#define uvc_trace(flag, msg...) \
+ do { \
+ if (uvc_trace_param & flag) \
+ printk(KERN_DEBUG "uvcvideo: " msg); \
+ } while (0)
+
+#define uvc_printk(level, msg...) \
+ printk(level "uvcvideo: " msg)
+
+#define UVC_GUID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \
+ "%02x%02x%02x%02x%02x%02x"
+#define UVC_GUID_ARGS(guid) \
+ (guid)[3], (guid)[2], (guid)[1], (guid)[0], \
+ (guid)[5], (guid)[4], \
+ (guid)[7], (guid)[6], \
+ (guid)[8], (guid)[9], \
+ (guid)[10], (guid)[11], (guid)[12], \
+ (guid)[13], (guid)[14], (guid)[15]
+
+/* --------------------------------------------------------------------------
+ * Internal functions.
+ */
+
+/* Core driver */
+extern struct uvc_driver uvc_driver;
+extern void uvc_delete(struct kref *kref);
+
+/* Video buffers queue management. */
+extern void uvc_queue_init(struct uvc_video_queue *queue);
+extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
+ unsigned int nbuffers, unsigned int buflength);
+extern int uvc_free_buffers(struct uvc_video_queue *queue);
+extern int uvc_query_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf);
+extern int uvc_queue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf);
+extern int uvc_dequeue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf, int nonblocking);
+extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable);
+extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
+extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf);
+extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
+ struct file *file, poll_table *wait);
+static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
+{
+ return queue->flags & UVC_QUEUE_STREAMING;
+}
+
+/* V4L2 interface */
+extern struct file_operations uvc_fops;
+
+/* Video */
+extern int uvc_video_init(struct uvc_video_device *video);
+extern int uvc_video_suspend(struct uvc_video_device *video);
+extern int uvc_video_resume(struct uvc_video_device *video);
+extern int uvc_video_enable(struct uvc_video_device *video, int enable);
+extern int uvc_probe_video(struct uvc_video_device *video,
+ struct uvc_streaming_control *probe);
+extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size);
+extern int uvc_set_video_ctrl(struct uvc_video_device *video,
+ struct uvc_streaming_control *ctrl, int probe);
+
+/* Status */
+extern int uvc_status_init(struct uvc_device *dev);
+extern void uvc_status_cleanup(struct uvc_device *dev);
+extern int uvc_status_suspend(struct uvc_device *dev);
+extern int uvc_status_resume(struct uvc_device *dev);
+
+/* Controls */
+extern struct uvc_control *uvc_find_control(struct uvc_video_device *video,
+ __u32 v4l2_id, struct uvc_control_mapping **mapping);
+extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
+ struct v4l2_queryctrl *v4l2_ctrl);
+
+extern int uvc_ctrl_add_info(struct uvc_control_info *info);
+extern int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping);
+extern int uvc_ctrl_init_device(struct uvc_device *dev);
+extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
+extern int uvc_ctrl_resume_device(struct uvc_device *dev);
+extern void uvc_ctrl_init(void);
+
+extern int uvc_ctrl_begin(struct uvc_video_device *video);
+extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback);
+static inline int uvc_ctrl_commit(struct uvc_video_device *video)
+{
+ return __uvc_ctrl_commit(video, 0);
+}
+static inline int uvc_ctrl_rollback(struct uvc_video_device *video)
+{
+ return __uvc_ctrl_commit(video, 1);
+}
+
+extern int uvc_ctrl_get(struct uvc_video_device *video,
+ struct v4l2_ext_control *xctrl);
+extern int uvc_ctrl_set(struct uvc_video_device *video,
+ struct v4l2_ext_control *xctrl);
+
+extern int uvc_xu_ctrl_query(struct uvc_video_device *video,
+ struct uvc_xu_control *ctrl, int set);
+
+/* Utility functions */
+extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
+ unsigned int n_terms, unsigned int threshold);
+extern uint32_t uvc_fraction_to_interval(uint32_t numerator,
+ uint32_t denominator);
+extern struct usb_host_endpoint *uvc_find_endpoint(
+ struct usb_host_interface *alts, __u8 epaddr);
+
+/* Quirks support */
+void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
+ struct uvc_buffer *buf);
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 65210fca37ed..d89475d36988 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -114,6 +114,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
unsigned int nob = data->blocks;
unsigned long long clks;
unsigned int timeout;
+ bool dalgn = 0;
u32 dcmd;
int i;
@@ -152,6 +153,9 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
host->sg_cpu[i].dcmd = dcmd | length;
if (length & 31 && !(data->flags & MMC_DATA_READ))
host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN;
+ /* Not aligned to 8-byte boundary? */
+ if (sg_dma_address(&data->sg[i]) & 0x7)
+ dalgn = 1;
if (data->flags & MMC_DATA_READ) {
host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
@@ -165,6 +169,15 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
wmb();
+ /*
+ * The PXA27x DMA controller encounters overhead when working with
+ * unaligned (to 8-byte boundaries) data, so switch on byte alignment
+ * mode only if we have unaligned data.
+ */
+ if (dalgn)
+ DALGN |= (1 << host->dma);
+ else
+ DALGN &= (1 << host->dma);
DDADR(host->dma) = host->sg_dma;
DCSR(host->dma) = DCSR_RUN;
}
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 07c2048b230b..b413aa6c246b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -55,6 +55,10 @@ static unsigned int debug_quirks = 0;
#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<7)
/* Controller needs to be reset after each request to stay stable */
#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8)
+/* Controller needs voltage and power writes to happen separately */
+#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9)
+/* Controller has an off-by-one issue with timeout value */
+#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL (1<<10)
static const struct pci_device_id pci_ids[] __devinitdata = {
{
@@ -115,7 +119,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
- SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
+ SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
+ SDHCI_QUIRK_BROKEN_DMA,
},
{
@@ -124,7 +129,17 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
- SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
+ SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
+ SDHCI_QUIRK_BROKEN_DMA,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_MARVELL,
+ .device = PCI_DEVICE_ID_MARVELL_CAFE_SD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
+ SDHCI_QUIRK_INCR_TIMEOUT_CONTROL,
},
{
@@ -469,6 +484,13 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
break;
}
+ /*
+ * Compensate for an off-by-one error in the CaFe hardware; otherwise,
+ * a too-small count gives us interrupt timeouts.
+ */
+ if ((host->chip->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL))
+ count++;
+
if (count >= 0xF) {
printk(KERN_WARNING "%s: Too large timeout requested!\n",
mmc_hostname(host->mmc));
@@ -774,6 +796,14 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
BUG();
}
+ /*
+ * At least the CaFe chip gets confused if we set the voltage
+ * and set turn on power at the same time, so set the voltage first.
+ */
+ if ((host->chip->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
+ writeb(pwr & ~SDHCI_POWER_ON,
+ host->ioaddr + SDHCI_POWER_CONTROL);
+
writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
out:
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 2edda8cc7f99..aabad8ce7458 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -1768,9 +1768,10 @@ vortex_timer(unsigned long data)
case XCVR_MII: case XCVR_NWAY:
{
ok = 1;
- spin_lock_bh(&vp->lock);
+ /* Interrupts are already disabled */
+ spin_lock(&vp->lock);
vortex_check_media(dev, 0);
- spin_unlock_bh(&vp->lock);
+ spin_unlock(&vp->lock);
}
break;
default: /* Other media types handled by Tx timeouts. */
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index f3cba5e24ec5..1037b1332312 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1803,6 +1803,8 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)
if (rx->prev->skb) {
struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data;
put_unaligned_le32(rx->dma_addr, &prev_rfd->link);
+ pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr,
+ sizeof(struct rfd), PCI_DMA_TODEVICE);
}
return 0;
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c
index 701531e72e7b..a3f6a9c72ec8 100644
--- a/drivers/net/e1000/e1000_ethtool.c
+++ b/drivers/net/e1000/e1000_ethtool.c
@@ -347,7 +347,7 @@ e1000_set_tso(struct net_device *netdev, u32 data)
else
netdev->features &= ~NETIF_F_TSO;
- if (data)
+ if (data && (adapter->hw.mac_type > e1000_82547_rev_2))
netdev->features |= NETIF_F_TSO6;
else
netdev->features &= ~NETIF_F_TSO6;
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index cab1835173cd..648a87bbf467 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -2535,7 +2535,8 @@ void e1000e_down(struct e1000_adapter *adapter)
adapter->link_speed = 0;
adapter->link_duplex = 0;
- e1000e_reset(adapter);
+ if (!pci_channel_offline(adapter->pdev))
+ e1000e_reset(adapter);
e1000_clean_tx_ring(adapter);
e1000_clean_rx_ring(adapter);
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index 0b94833e23f7..e8cfadefa4b6 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -1077,8 +1077,6 @@ static inline void rx_off(struct scc_priv *priv)
static void start_timer(struct scc_priv *priv, int t, int r15)
{
- unsigned long flags;
-
outb(priv->tmr_mode, priv->tmr_ctrl);
if (t == 0) {
tm_isr(priv);
diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c
index ae398f04c7b4..e79a26a886c8 100644
--- a/drivers/net/igb/igb_main.c
+++ b/drivers/net/igb/igb_main.c
@@ -718,7 +718,8 @@ void igb_down(struct igb_adapter *adapter)
adapter->link_speed = 0;
adapter->link_duplex = 0;
- igb_reset(adapter);
+ if (!pci_channel_offline(adapter->pdev))
+ igb_reset(adapter);
igb_clean_all_tx_rings(adapter);
igb_clean_all_rx_rings(adapter);
}
diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c
index 679a0826780e..2c03f4e2ccc4 100644
--- a/drivers/net/ipg.c
+++ b/drivers/net/ipg.c
@@ -1271,7 +1271,7 @@ static void ipg_nic_rx_with_end(struct net_device *dev,
framelen = le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFRAMELEN;
- endframeLen = framelen - jumbo->current_size;
+ endframelen = framelen - jumbo->current_size;
/*
if (framelen > IPG_RXFRAG_SIZE)
framelen=IPG_RXFRAG_SIZE;
@@ -1279,8 +1279,8 @@ static void ipg_nic_rx_with_end(struct net_device *dev,
if (framelen > IPG_RXSUPPORT_SIZE)
dev_kfree_skb_irq(jumbo->skb);
else {
- memcpy(skb_put(jumbo->skb, endframeLen),
- skb->data, endframeLen);
+ memcpy(skb_put(jumbo->skb, endframelen),
+ skb->data, endframelen);
jumbo->skb->protocol =
eth_type_trans(jumbo->skb, dev);
@@ -1352,16 +1352,16 @@ static int ipg_nic_rx(struct net_device *dev)
switch (ipg_nic_rx_check_frame_type(dev)) {
case FRAME_WITH_START_WITH_END:
- ipg_nic_rx_with_start_and_end(dev, tp, rxfd, entry);
+ ipg_nic_rx_with_start_and_end(dev, sp, rxfd, entry);
break;
case FRAME_WITH_START:
- ipg_nic_rx_with_start(dev, tp, rxfd, entry);
+ ipg_nic_rx_with_start(dev, sp, rxfd, entry);
break;
case FRAME_WITH_END:
- ipg_nic_rx_with_end(dev, tp, rxfd, entry);
+ ipg_nic_rx_with_end(dev, sp, rxfd, entry);
break;
case FRAME_NO_START_NO_END:
- ipg_nic_rx_no_start_no_end(dev, tp, rxfd, entry);
+ ipg_nic_rx_no_start_no_end(dev, sp, rxfd, entry);
break;
}
}
@@ -1808,7 +1808,7 @@ static int ipg_nic_open(struct net_device *dev)
/* initialize JUMBO Frame control variable */
sp->jumbo.found_start = 0;
sp->jumbo.current_size = 0;
- sp->jumbo.skb = 0;
+ sp->jumbo.skb = NULL;
dev->mtu = IPG_TXFRAG_SIZE;
#endif
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 7b859220c255..8f0460901153 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -1969,7 +1969,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
netif_carrier_off(netdev);
netif_stop_queue(netdev);
- ixgbe_reset(adapter);
+ if (!pci_channel_offline(adapter->pdev))
+ ixgbe_reset(adapter);
ixgbe_clean_all_tx_rings(adapter);
ixgbe_clean_all_rx_rings(adapter);
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 6797ed069f1f..63cd67b931e7 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -71,14 +71,18 @@ static irqreturn_t netxen_intr(int irq, void *data);
static irqreturn_t netxen_msi_intr(int irq, void *data);
/* PCI Device ID Table */
+#define ENTRY(device) \
+ {PCI_DEVICE(0x4040, (device)), \
+ .class = PCI_CLASS_NETWORK_ETHERNET << 8, .class_mask = ~0}
+
static struct pci_device_id netxen_pci_tbl[] __devinitdata = {
- {PCI_DEVICE(0x4040, 0x0001), PCI_DEVICE_CLASS(0x020000, ~0)},
- {PCI_DEVICE(0x4040, 0x0002), PCI_DEVICE_CLASS(0x020000, ~0)},
- {PCI_DEVICE(0x4040, 0x0003), PCI_DEVICE_CLASS(0x020000, ~0)},
- {PCI_DEVICE(0x4040, 0x0004), PCI_DEVICE_CLASS(0x020000, ~0)},
- {PCI_DEVICE(0x4040, 0x0005), PCI_DEVICE_CLASS(0x020000, ~0)},
- {PCI_DEVICE(0x4040, 0x0024), PCI_DEVICE_CLASS(0x020000, ~0)},
- {PCI_DEVICE(0x4040, 0x0025), PCI_DEVICE_CLASS(0x020000, ~0)},
+ ENTRY(0x0001),
+ ENTRY(0x0002),
+ ENTRY(0x0003),
+ ENTRY(0x0004),
+ ENTRY(0x0005),
+ ENTRY(0x0024),
+ ENTRY(0x0025),
{0,}
};
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index ce95c5d168fe..70d012e90dcf 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -525,12 +525,14 @@ static int axnet_open(struct net_device *dev)
int ret;
axnet_dev_t *info = PRIV(dev);
struct pcmcia_device *link = info->p_dev;
+ unsigned int nic_base = dev->base_addr;
DEBUG(2, "axnet_open('%s')\n", dev->name);
if (!pcmcia_dev_present(link))
return -ENODEV;
+ outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev);
if (ret)
return ret;
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c
index fd8158a86f64..2d4c4ad89b8d 100644
--- a/drivers/net/pcmcia/pcnet_cs.c
+++ b/drivers/net/pcmcia/pcnet_cs.c
@@ -969,6 +969,7 @@ static int pcnet_open(struct net_device *dev)
int ret;
pcnet_dev_t *info = PRIV(dev);
struct pcmcia_device *link = info->p_dev;
+ unsigned int nic_base = dev->base_addr;
DEBUG(2, "pcnet_open('%s')\n", dev->name);
@@ -976,6 +977,8 @@ static int pcnet_open(struct net_device *dev)
return -ENODEV;
set_misc_reg(dev);
+
+ outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev_info, dev);
if (ret)
return ret;
diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c
index b7f7b2227d56..bccee68bd48a 100644
--- a/drivers/net/qla3xxx.c
+++ b/drivers/net/qla3xxx.c
@@ -3701,7 +3701,9 @@ static int ql_cycle_adapter(struct ql3_adapter *qdev, int reset)
printk(KERN_ERR PFX
"%s: Driver up/down cycle failed, "
"closing device\n",qdev->ndev->name);
+ rtnl_lock();
dev_close(qdev->ndev);
+ rtnl_unlock();
return -1;
}
return 0;
diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c
index 858b191517b3..504a48ff73c8 100644
--- a/drivers/net/r6040.c
+++ b/drivers/net/r6040.c
@@ -273,7 +273,7 @@ static void r6040_init_ring_desc(struct r6040_descriptor *desc_ring,
dma_addr_t mapping = desc_dma;
while (size-- > 0) {
- mapping += sizeof(sizeof(*desc));
+ mapping += sizeof(*desc);
desc->ndesc = cpu_to_le32(mapping);
desc->vndescp = desc + 1;
desc++;
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
index b5c1e663417d..ae7b697456b4 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -2625,9 +2625,7 @@ static int fill_rx_buffers(struct ring_info *ring)
rxdp1->Buffer0_ptr = pci_map_single
(ring->pdev, skb->data, size - NET_IP_ALIGN,
PCI_DMA_FROMDEVICE);
- if( (rxdp1->Buffer0_ptr == 0) ||
- (rxdp1->Buffer0_ptr ==
- DMA_ERROR_CODE))
+ if(pci_dma_mapping_error(rxdp1->Buffer0_ptr))
goto pci_map_failed;
rxdp->Control_2 =
@@ -2657,6 +2655,7 @@ static int fill_rx_buffers(struct ring_info *ring)
skb->data = (void *) (unsigned long)tmp;
skb_reset_tail_pointer(skb);
+ /* AK: check is wrong. 0 can be valid dma address */
if (!(rxdp3->Buffer0_ptr))
rxdp3->Buffer0_ptr =
pci_map_single(ring->pdev, ba->ba_0,
@@ -2665,8 +2664,7 @@ static int fill_rx_buffers(struct ring_info *ring)
pci_dma_sync_single_for_device(ring->pdev,
(dma_addr_t) rxdp3->Buffer0_ptr,
BUF0_LEN, PCI_DMA_FROMDEVICE);
- if( (rxdp3->Buffer0_ptr == 0) ||
- (rxdp3->Buffer0_ptr == DMA_ERROR_CODE))
+ if (pci_dma_mapping_error(rxdp3->Buffer0_ptr))
goto pci_map_failed;
rxdp->Control_2 = SET_BUFFER0_SIZE_3(BUF0_LEN);
@@ -2681,18 +2679,17 @@ static int fill_rx_buffers(struct ring_info *ring)
(ring->pdev, skb->data, ring->mtu + 4,
PCI_DMA_FROMDEVICE);
- if( (rxdp3->Buffer2_ptr == 0) ||
- (rxdp3->Buffer2_ptr == DMA_ERROR_CODE))
+ if (pci_dma_mapping_error(rxdp3->Buffer2_ptr))
goto pci_map_failed;
+ /* AK: check is wrong */
if (!rxdp3->Buffer1_ptr)
rxdp3->Buffer1_ptr =
pci_map_single(ring->pdev,
ba->ba_1, BUF1_LEN,
PCI_DMA_FROMDEVICE);
- if( (rxdp3->Buffer1_ptr == 0) ||
- (rxdp3->Buffer1_ptr == DMA_ERROR_CODE)) {
+ if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) {
pci_unmap_single
(ring->pdev,
(dma_addr_t)(unsigned long)
@@ -4264,16 +4261,14 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
txdp->Buffer_Pointer = pci_map_single(sp->pdev,
fifo->ufo_in_band_v,
sizeof(u64), PCI_DMA_TODEVICE);
- if((txdp->Buffer_Pointer == 0) ||
- (txdp->Buffer_Pointer == DMA_ERROR_CODE))
+ if (pci_dma_mapping_error(txdp->Buffer_Pointer))
goto pci_map_failed;
txdp++;
}
txdp->Buffer_Pointer = pci_map_single
(sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE);
- if((txdp->Buffer_Pointer == 0) ||
- (txdp->Buffer_Pointer == DMA_ERROR_CODE))
+ if (pci_dma_mapping_error(txdp->Buffer_Pointer))
goto pci_map_failed;
txdp->Host_Control = (unsigned long) skb;
@@ -6884,10 +6879,8 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp,
pci_map_single( sp->pdev, (*skb)->data,
size - NET_IP_ALIGN,
PCI_DMA_FROMDEVICE);
- if( (rxdp1->Buffer0_ptr == 0) ||
- (rxdp1->Buffer0_ptr == DMA_ERROR_CODE)) {
+ if (pci_dma_mapping_error(rxdp1->Buffer0_ptr))
goto memalloc_failed;
- }
rxdp->Host_Control = (unsigned long) (*skb);
}
} else if ((sp->rxd_mode == RXD_MODE_3B) && (rxdp->Host_Control == 0)) {
@@ -6913,15 +6906,12 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp,
pci_map_single(sp->pdev, (*skb)->data,
dev->mtu + 4,
PCI_DMA_FROMDEVICE);
- if( (rxdp3->Buffer2_ptr == 0) ||
- (rxdp3->Buffer2_ptr == DMA_ERROR_CODE)) {
+ if (pci_dma_mapping_error(rxdp3->Buffer2_ptr))
goto memalloc_failed;
- }
rxdp3->Buffer0_ptr = *temp0 =
pci_map_single( sp->pdev, ba->ba_0, BUF0_LEN,
PCI_DMA_FROMDEVICE);
- if( (rxdp3->Buffer0_ptr == 0) ||
- (rxdp3->Buffer0_ptr == DMA_ERROR_CODE)) {
+ if (pci_dma_mapping_error(rxdp3->Buffer0_ptr)) {
pci_unmap_single (sp->pdev,
(dma_addr_t)rxdp3->Buffer2_ptr,
dev->mtu + 4, PCI_DMA_FROMDEVICE);
@@ -6933,8 +6923,7 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp,
rxdp3->Buffer1_ptr = *temp1 =
pci_map_single(sp->pdev, ba->ba_1, BUF1_LEN,
PCI_DMA_FROMDEVICE);
- if( (rxdp3->Buffer1_ptr == 0) ||
- (rxdp3->Buffer1_ptr == DMA_ERROR_CODE)) {
+ if (pci_dma_mapping_error(rxdp3->Buffer1_ptr)) {
pci_unmap_single (sp->pdev,
(dma_addr_t)rxdp3->Buffer0_ptr,
BUF0_LEN, PCI_DMA_FROMDEVICE);
diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h
index 4706f7f9acb6..1827b6686c98 100644
--- a/drivers/net/s2io.h
+++ b/drivers/net/s2io.h
@@ -75,10 +75,6 @@ static int debug_level = ERR_DBG;
/* DEBUG message print. */
#define DBG_PRINT(dbg_level, args...) if(!(debug_level<dbg_level)) printk(args)
-#ifndef DMA_ERROR_CODE
-#define DMA_ERROR_CODE (~(dma_addr_t)0x0)
-#endif
-
/* Protocol assist features of the NIC */
#define L3_CKSUM_OK 0xFFFF
#define L4_CKSUM_OK 0xFFFF
diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c
index 10e4e85da3fc..b07b8cbadeaf 100644
--- a/drivers/net/tc35815.c
+++ b/drivers/net/tc35815.c
@@ -1394,6 +1394,7 @@ tc35815_open(struct net_device *dev)
tc35815_chip_init(dev);
spin_unlock_irq(&lp->lock);
+ netif_carrier_off(dev);
/* schedule a link state check */
phy_start(lp->phy_dev);
@@ -1735,7 +1736,6 @@ tc35815_rx(struct net_device *dev)
skb = lp->rx_skbs[cur_bd].skb;
prefetch(skb->data);
lp->rx_skbs[cur_bd].skb = NULL;
- lp->fbl_count--;
pci_unmap_single(lp->pci_dev,
lp->rx_skbs[cur_bd].skb_dma,
RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
@@ -1791,6 +1791,7 @@ tc35815_rx(struct net_device *dev)
#ifdef TC35815_USE_PACKEDBUFFER
while (lp->fbl_curid != id)
#else
+ lp->fbl_count--;
while (lp->fbl_count < RX_BUF_NUM)
#endif
{
@@ -2453,6 +2454,7 @@ static int tc35815_resume(struct pci_dev *pdev)
return 0;
pci_set_power_state(pdev, PCI_D0);
tc35815_restart(dev);
+ netif_carrier_off(dev);
if (lp->phy_dev)
phy_start(lp->phy_dev);
netif_device_attach(dev);
diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c
index 249e18053d5f..069f8bb0a99f 100644
--- a/drivers/net/wan/x25_asy.c
+++ b/drivers/net/wan/x25_asy.c
@@ -32,6 +32,7 @@
#include <linux/x25.h>
#include <linux/lapb.h>
#include <linux/init.h>
+#include <linux/rtnetlink.h>
#include "x25_asy.h"
#include <net/x25device.h>
@@ -601,8 +602,10 @@ static void x25_asy_close_tty(struct tty_struct *tty)
if (!sl || sl->magic != X25_ASY_MAGIC)
return;
+ rtnl_lock();
if (sl->dev->flags & IFF_UP)
dev_close(sl->dev);
+ rtnl_unlock();
tty->disc_data = NULL;
sl->tty = NULL;
diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c
index 36a9c42df835..76f4c7bad8b8 100644
--- a/drivers/net/wireless/b43/leds.c
+++ b/drivers/net/wireless/b43/leds.c
@@ -72,6 +72,9 @@ static void b43_led_brightness_set(struct led_classdev *led_dev,
struct b43_wldev *dev = led->dev;
bool radio_enabled;
+ if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED))
+ return;
+
/* Checking the radio-enabled status here is slightly racy,
* but we want to avoid the locking overhead and we don't care
* whether the LED has the wrong state for a second. */
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index fa4b0d8b74a2..a70827793086 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -2883,12 +2883,11 @@ static int b43_op_tx(struct ieee80211_hw *hw,
if (unlikely(skb->len < 2 + 2 + 6)) {
/* Too short, this can't be a valid frame. */
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
+ goto drop_packet;
}
B43_WARN_ON(skb_shinfo(skb)->nr_frags);
if (unlikely(!dev))
- return NETDEV_TX_BUSY;
+ goto drop_packet;
/* Transmissions on seperate queues can run concurrently. */
read_lock_irqsave(&wl->tx_lock, flags);
@@ -2904,7 +2903,12 @@ static int b43_op_tx(struct ieee80211_hw *hw,
read_unlock_irqrestore(&wl->tx_lock, flags);
if (unlikely(err))
- return NETDEV_TX_BUSY;
+ goto drop_packet;
+ return NETDEV_TX_OK;
+
+drop_packet:
+ /* We can not transmit this packet. Drop it. */
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c
index c990f87b107a..93ddc1cbcc8b 100644
--- a/drivers/net/wireless/b43legacy/dma.c
+++ b/drivers/net/wireless/b43legacy/dma.c
@@ -876,6 +876,7 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev,
if (!ring)
goto out;
ring->type = type;
+ ring->dev = dev;
nr_slots = B43legacy_RXRING_SLOTS;
if (for_tx)
@@ -922,7 +923,6 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev,
DMA_TO_DEVICE);
}
- ring->dev = dev;
ring->nr_slots = nr_slots;
ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index);
ring->index = controller_index;
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 204077c13870..3e612d0a13e8 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -2378,8 +2378,10 @@ static int b43legacy_op_tx(struct ieee80211_hw *hw,
} else
err = b43legacy_dma_tx(dev, skb, ctl);
out:
- if (unlikely(err))
- return NETDEV_TX_BUSY;
+ if (unlikely(err)) {
+ /* Drop the packet. */
+ dev_kfree_skb_any(skb);
+ }
return NETDEV_TX_OK;
}
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c
index 4fd73809602e..020f450e9dba 100644
--- a/drivers/net/wireless/hostap/hostap_80211_rx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_rx.c
@@ -64,7 +64,7 @@ int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
int hdrlen, phdrlen, head_need, tail_need;
u16 fc;
int prism_header, ret;
- struct ieee80211_hdr_4addr *hdr;
+ struct ieee80211_hdr_4addr *fhdr;
iface = netdev_priv(dev);
local = iface->local;
@@ -83,8 +83,8 @@ int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
phdrlen = 0;
}
- hdr = (struct ieee80211_hdr_4addr *) skb->data;
- fc = le16_to_cpu(hdr->frame_ctl);
+ fhdr = (struct ieee80211_hdr_4addr *) skb->data;
+ fc = le16_to_cpu(fhdr->frame_ctl);
if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) {
printk(KERN_DEBUG "%s: dropped management frame with header "
@@ -551,7 +551,7 @@ hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr_4addr *hdr,
hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff ||
hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) {
/* RA (or BSSID) is not ours - drop */
- PDEBUG(DEBUG_EXTRA, "%s: received WDS frame with "
+ PDEBUG(DEBUG_EXTRA2, "%s: received WDS frame with "
"not own or broadcast %s=%s\n",
local->dev->name,
fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID",
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
index 0acd9589c48c..ab981afd481d 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/hostap/hostap_ap.c
@@ -1930,7 +1930,7 @@ static void handle_pspoll(local_info_t *local,
PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n");
return;
}
- aid &= ~BIT(15) & ~BIT(14);
+ aid &= ~(BIT(15) | BIT(14));
if (aid == 0 || aid > MAX_AID_TABLE_SIZE) {
PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid);
return;
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index ed4317a17cbb..80039a0ae027 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -533,10 +533,10 @@ static void prism2_detach(struct pcmcia_device *link)
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
#define CFG_CHECK2(fn, retf) \
-do { int ret = (retf); \
-if (ret != 0) { \
- PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \
- cs_error(link, fn, ret); \
+do { int _ret = (retf); \
+if (_ret != 0) { \
+ PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", _ret); \
+ cs_error(link, fn, _ret); \
goto next_entry; \
} \
} while (0)
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c
index cdf90c40f11b..936f52e3d95c 100644
--- a/drivers/net/wireless/hostap/hostap_hw.c
+++ b/drivers/net/wireless/hostap/hostap_hw.c
@@ -2835,7 +2835,7 @@ static void hostap_passive_scan(unsigned long data)
{
local_info_t *local = (local_info_t *) data;
struct net_device *dev = local->dev;
- u16 channel;
+ u16 chan;
if (local->passive_scan_interval <= 0)
return;
@@ -2872,11 +2872,11 @@ static void hostap_passive_scan(unsigned long data)
printk(KERN_DEBUG "%s: passive scan channel %d\n",
dev->name, local->passive_scan_channel);
- channel = local->passive_scan_channel;
+ chan = local->passive_scan_channel;
local->passive_scan_state = PASSIVE_SCAN_WAIT;
local->passive_scan_timer.expires = jiffies + HZ / 10;
} else {
- channel = local->channel;
+ chan = local->channel;
local->passive_scan_state = PASSIVE_SCAN_LISTEN;
local->passive_scan_timer.expires = jiffies +
local->passive_scan_interval * HZ;
@@ -2884,9 +2884,9 @@ static void hostap_passive_scan(unsigned long data)
if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST |
(HFA384X_TEST_CHANGE_CHANNEL << 8),
- channel, NULL, 0))
+ chan, NULL, 0))
printk(KERN_ERR "%s: passive scan channel set %d "
- "failed\n", dev->name, channel);
+ "failed\n", dev->name, chan);
add_timer(&local->passive_scan_timer);
}
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c
index f7aec9309d04..a38e85f334df 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/hostap/hostap_main.c
@@ -594,7 +594,8 @@ void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx)
}
-int hostap_80211_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+static int hostap_80211_header_parse(const struct sk_buff *skb,
+ unsigned char *haddr)
{
struct hostap_interface *iface = netdev_priv(skb->dev);
local_info_t *local = iface->local;
@@ -857,7 +858,6 @@ const struct header_ops hostap_80211_ops = {
.rebuild = eth_rebuild_header,
.cache = eth_header_cache,
.cache_update = eth_header_cache_update,
-
.parse = hostap_80211_header_parse,
};
EXPORT_SYMBOL(hostap_80211_ops);
@@ -1150,7 +1150,6 @@ EXPORT_SYMBOL(hostap_set_roaming);
EXPORT_SYMBOL(hostap_set_auth_algs);
EXPORT_SYMBOL(hostap_dump_rx_header);
EXPORT_SYMBOL(hostap_dump_tx_header);
-EXPORT_SYMBOL(hostap_80211_header_parse);
EXPORT_SYMBOL(hostap_80211_get_hdrlen);
EXPORT_SYMBOL(hostap_get_stats);
EXPORT_SYMBOL(hostap_setup_dev);
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 13925b627e3b..b1b3c523185d 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -2227,7 +2227,10 @@ static int iwl3945_scan_initiate(struct iwl3945_priv *priv)
}
IWL_DEBUG_INFO("Starting scan...\n");
- priv->scan_bands = 2;
+ if (priv->cfg->sku & IWL_SKU_G)
+ priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ);
+ if (priv->cfg->sku & IWL_SKU_A)
+ priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
set_bit(STATUS_SCANNING, &priv->status);
priv->scan_start = jiffies;
priv->scan_pass_start = priv->scan_start;
@@ -3352,13 +3355,18 @@ static void iwl3945_rx_scan_complete_notif(struct iwl3945_priv *priv,
cancel_delayed_work(&priv->scan_check);
IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n",
- (priv->scan_bands == 2) ? "2.4" : "5.2",
+ (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ?
+ "2.4" : "5.2",
jiffies_to_msecs(elapsed_jiffies
(priv->scan_pass_start, jiffies)));
- /* Remove this scanned band from the list
- * of pending bands to scan */
- priv->scan_bands--;
+ /* Remove this scanned band from the list of pending
+ * bands to scan, band G precedes A in order of scanning
+ * as seen in iwl3945_bg_request_scan */
+ if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ))
+ priv->scan_bands &= ~BIT(IEEE80211_BAND_2GHZ);
+ else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ))
+ priv->scan_bands &= ~BIT(IEEE80211_BAND_5GHZ);
/* If a request to abort was given, or the scan did not succeed
* then we reset the scan state machine and terminate,
@@ -4972,7 +4980,7 @@ static int iwl3945_get_channels_for_scan(struct iwl3945_priv *priv,
ch_info = iwl3945_get_channel_info(priv, band, scan_ch->channel);
if (!is_channel_valid(ch_info)) {
- IWL_DEBUG_SCAN("Channel %d is INVALID for this SKU.\n",
+ IWL_DEBUG_SCAN("Channel %d is INVALID for this band.\n",
scan_ch->channel);
continue;
}
@@ -6315,21 +6323,16 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
/* flags + rate selection */
- switch (priv->scan_bands) {
- case 2:
+ if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
scan->tx_cmd.rate = IWL_RATE_1M_PLCP;
scan->good_CRC_th = 0;
band = IEEE80211_BAND_2GHZ;
- break;
-
- case 1:
+ } else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) {
scan->tx_cmd.rate = IWL_RATE_6M_PLCP;
scan->good_CRC_th = IWL_GOOD_CRC_TH;
band = IEEE80211_BAND_5GHZ;
- break;
-
- default:
+ } else {
IWL_WARNING("Invalid scan band count\n");
goto done;
}
@@ -6770,7 +6773,7 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
ch_info = iwl3945_get_channel_info(priv, conf->channel->band,
conf->channel->hw_value);
if (!is_channel_valid(ch_info)) {
- IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this SKU.\n",
+ IWL_DEBUG_SCAN("Channel %d [%d] is INVALID for this band.\n",
conf->channel->hw_value, conf->channel->band);
IWL_DEBUG_MAC80211("leave - invalid channel\n");
spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c
index 883b42f7e998..5ed16ce78468 100644
--- a/drivers/net/wireless/iwlwifi/iwl4965-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c
@@ -1774,7 +1774,10 @@ static int iwl4965_scan_initiate(struct iwl_priv *priv)
}
IWL_DEBUG_INFO("Starting scan...\n");
- priv->scan_bands = 2;
+ if (priv->cfg->sku & IWL_SKU_G)
+ priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ);
+ if (priv->cfg->sku & IWL_SKU_A)
+ priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
set_bit(STATUS_SCANNING, &priv->status);
priv->scan_start = jiffies;
priv->scan_pass_start = priv->scan_start;
@@ -3023,8 +3026,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
if (index != -1) {
- int freed = iwl4965_tx_queue_reclaim(priv, txq_id, index);
#ifdef CONFIG_IWL4965_HT
+ int freed = iwl4965_tx_queue_reclaim(priv, txq_id, index);
+
if (tid != MAX_TID_COUNT)
priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
if (iwl4965_queue_space(&txq->q) > txq->q.low_mark &&
@@ -3276,13 +3280,18 @@ static void iwl4965_rx_scan_complete_notif(struct iwl_priv *priv,
cancel_delayed_work(&priv->scan_check);
IWL_DEBUG_INFO("Scan pass on %sGHz took %dms\n",
- (priv->scan_bands == 2) ? "2.4" : "5.2",
+ (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ?
+ "2.4" : "5.2",
jiffies_to_msecs(elapsed_jiffies
(priv->scan_pass_start, jiffies)));
- /* Remove this scanned band from the list
- * of pending bands to scan */
- priv->scan_bands--;
+ /* Remove this scanned band from the list of pending
+ * bands to scan, band G precedes A in order of scanning
+ * as seen in iwl_bg_request_scan */
+ if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ))
+ priv->scan_bands &= ~BIT(IEEE80211_BAND_2GHZ);
+ else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ))
+ priv->scan_bands &= ~BIT(IEEE80211_BAND_5GHZ);
/* If a request to abort was given, or the scan did not succeed
* then we reset the scan state machine and terminate,
@@ -3292,7 +3301,7 @@ static void iwl4965_rx_scan_complete_notif(struct iwl_priv *priv,
clear_bit(STATUS_SCAN_ABORTING, &priv->status);
} else {
/* If there are more bands on this scan pass reschedule */
- if (priv->scan_bands > 0)
+ if (priv->scan_bands)
goto reschedule;
}
@@ -4635,10 +4644,9 @@ static int iwl4965_get_channels_for_scan(struct iwl_priv *priv,
scan_ch->channel = ieee80211_frequency_to_channel(channels[i].center_freq);
- ch_info = iwl_get_channel_info(priv, band,
- scan_ch->channel);
+ ch_info = iwl_get_channel_info(priv, band, scan_ch->channel);
if (!is_channel_valid(ch_info)) {
- IWL_DEBUG_SCAN("Channel %d is INVALID for this SKU.\n",
+ IWL_DEBUG_SCAN("Channel %d is INVALID for this band.\n",
scan_ch->channel);
continue;
}
@@ -5830,8 +5838,7 @@ static void iwl4965_bg_request_scan(struct work_struct *data)
scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
- switch (priv->scan_bands) {
- case 2:
+ if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
scan->tx_cmd.rate_n_flags =
iwl4965_hw_set_rate_n_flags(IWL_RATE_1M_PLCP,
@@ -5839,17 +5846,13 @@ static void iwl4965_bg_request_scan(struct work_struct *data)
scan->good_CRC_th = 0;
band = IEEE80211_BAND_2GHZ;
- break;
-
- case 1:
+ } else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) {
scan->tx_cmd.rate_n_flags =
iwl4965_hw_set_rate_n_flags(IWL_RATE_6M_PLCP,
RATE_MCS_ANT_B_MSK);
scan->good_CRC_th = IWL_GOOD_CRC_TH;
band = IEEE80211_BAND_5GHZ;
- break;
-
- default:
+ } else {
IWL_WARNING("Invalid scan band count\n");
goto done;
}
diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c
index 762e85bef55d..e43bae97ed8f 100644
--- a/drivers/net/wireless/prism54/islpci_eth.c
+++ b/drivers/net/wireless/prism54/islpci_eth.c
@@ -290,7 +290,7 @@ islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb)
avs->version = cpu_to_be32(P80211CAPTURE_VERSION);
avs->length = cpu_to_be32(sizeof (struct avs_80211_1_header));
- avs->mactime = cpu_to_be64(le64_to_cpu(clock));
+ avs->mactime = cpu_to_be64(clock);
avs->hosttime = cpu_to_be64(jiffies);
avs->phytype = cpu_to_be32(6); /*OFDM: 6 for (g), 8 for (a) */
avs->channel = cpu_to_be32(channel_of_freq(freq));
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index fdbd0ef2be4b..61e59c17a60a 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -138,11 +138,8 @@ static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev,
* Wait until the BBP becomes ready.
*/
reg = rt2500usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR8 register busy. Write failed.\n");
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
- return;
- }
+ if (rt2x00_get_field16(reg, PHY_CSR8_BUSY))
+ goto exit_fail;
/*
* Write the data into the BBP.
@@ -155,6 +152,13 @@ static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev,
rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
mutex_unlock(&rt2x00dev->usb_cache_mutex);
+
+ return;
+
+exit_fail:
+ mutex_unlock(&rt2x00dev->usb_cache_mutex);
+
+ ERROR(rt2x00dev, "PHY_CSR8 register busy. Write failed.\n");
}
static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev,
@@ -168,10 +172,8 @@ static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev,
* Wait until the BBP becomes ready.
*/
reg = rt2500usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n");
- return;
- }
+ if (rt2x00_get_field16(reg, PHY_CSR8_BUSY))
+ goto exit_fail;
/*
* Write the request into the BBP.
@@ -186,17 +188,21 @@ static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev,
* Wait until the BBP becomes ready.
*/
reg = rt2500usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n");
- *value = 0xff;
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
- return;
- }
+ if (rt2x00_get_field16(reg, PHY_CSR8_BUSY))
+ goto exit_fail;
rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, &reg);
*value = rt2x00_get_field16(reg, PHY_CSR7_DATA);
mutex_unlock(&rt2x00dev->usb_cache_mutex);
+
+ return;
+
+exit_fail:
+ mutex_unlock(&rt2x00dev->usb_cache_mutex);
+
+ ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n");
+ *value = 0xff;
}
static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 611d98320593..b4bf1e09cf9a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -821,6 +821,7 @@ struct rt2x00_dev {
/*
* Scheduled work.
*/
+ struct workqueue_struct *workqueue;
struct work_struct intf_work;
struct work_struct filter_work;
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 2673d568bcac..c997d4f28ab3 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -75,7 +75,7 @@ static void rt2x00lib_start_link_tuner(struct rt2x00_dev *rt2x00dev)
rt2x00lib_reset_link_tuner(rt2x00dev);
- queue_delayed_work(rt2x00dev->hw->workqueue,
+ queue_delayed_work(rt2x00dev->workqueue,
&rt2x00dev->link.work, LINK_TUNE_INTERVAL);
}
@@ -137,14 +137,6 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
return;
/*
- * Stop all scheduled work.
- */
- if (work_pending(&rt2x00dev->intf_work))
- cancel_work_sync(&rt2x00dev->intf_work);
- if (work_pending(&rt2x00dev->filter_work))
- cancel_work_sync(&rt2x00dev->filter_work);
-
- /*
* Stop the TX queues.
*/
ieee80211_stop_queues(rt2x00dev->hw);
@@ -398,8 +390,8 @@ static void rt2x00lib_link_tuner(struct work_struct *work)
* Increase tuner counter, and reschedule the next link tuner run.
*/
rt2x00dev->link.count++;
- queue_delayed_work(rt2x00dev->hw->workqueue, &rt2x00dev->link.work,
- LINK_TUNE_INTERVAL);
+ queue_delayed_work(rt2x00dev->workqueue,
+ &rt2x00dev->link.work, LINK_TUNE_INTERVAL);
}
static void rt2x00lib_packetfilter_scheduled(struct work_struct *work)
@@ -433,6 +425,15 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,
spin_unlock(&intf->lock);
+ /*
+ * It is possible the radio was disabled while the work had been
+ * scheduled. If that happens we should return here immediately,
+ * note that in the spinlock protected area above the delayed_flags
+ * have been cleared correctly.
+ */
+ if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags))
+ return;
+
if (delayed_flags & DELAYED_UPDATE_BEACON) {
skb = ieee80211_beacon_get(rt2x00dev->hw, vif, &control);
if (skb && rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw,
@@ -441,7 +442,7 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,
}
if (delayed_flags & DELAYED_CONFIG_ERP)
- rt2x00lib_config_erp(rt2x00dev, intf, &intf->conf);
+ rt2x00lib_config_erp(rt2x00dev, intf, &conf);
if (delayed_flags & DELAYED_LED_ASSOC)
rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated);
@@ -487,7 +488,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
rt2x00lib_beacondone_iter,
rt2x00dev);
- queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->intf_work);
+ queue_work(rt2x00dev->workqueue, &rt2x00dev->intf_work);
}
EXPORT_SYMBOL_GPL(rt2x00lib_beacondone);
@@ -1130,6 +1131,10 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
/*
* Initialize configuration work.
*/
+ rt2x00dev->workqueue = create_singlethread_workqueue("rt2x00lib");
+ if (!rt2x00dev->workqueue)
+ goto exit;
+
INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);
INIT_WORK(&rt2x00dev->filter_work, rt2x00lib_packetfilter_scheduled);
INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00lib_link_tuner);
@@ -1190,6 +1195,13 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
rt2x00leds_unregister(rt2x00dev);
/*
+ * Stop all queued work. Note that most tasks will already be halted
+ * during rt2x00lib_disable_radio() and rt2x00lib_uninitialize().
+ */
+ flush_workqueue(rt2x00dev->workqueue);
+ destroy_workqueue(rt2x00dev->workqueue);
+
+ /*
* Free ieee80211_hw memory.
*/
rt2x00lib_remove_hw(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 87e280a21971..9cb023edd2e9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -428,7 +428,7 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw,
if (!test_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags))
rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags);
else
- queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->filter_work);
+ queue_work(rt2x00dev->workqueue, &rt2x00dev->filter_work);
}
EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter);
@@ -509,7 +509,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
memcpy(&intf->conf, bss_conf, sizeof(*bss_conf));
if (delayed) {
intf->delayed_flags |= delayed;
- queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->intf_work);
+ queue_work(rt2x00dev->workqueue, &rt2x00dev->intf_work);
}
spin_unlock(&intf->lock);
}
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index fff8386e816b..83cc0147f698 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -134,11 +134,8 @@ static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev,
* Wait until the BBP becomes ready.
*/
reg = rt73usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n");
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
- return;
- }
+ if (rt2x00_get_field32(reg, PHY_CSR3_BUSY))
+ goto exit_fail;
/*
* Write the data into the BBP.
@@ -151,6 +148,13 @@ static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev,
rt73usb_register_write_lock(rt2x00dev, PHY_CSR3, reg);
mutex_unlock(&rt2x00dev->usb_cache_mutex);
+
+ return;
+
+exit_fail:
+ mutex_unlock(&rt2x00dev->usb_cache_mutex);
+
+ ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n");
}
static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev,
@@ -164,11 +168,8 @@ static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev,
* Wait until the BBP becomes ready.
*/
reg = rt73usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n");
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
- return;
- }
+ if (rt2x00_get_field32(reg, PHY_CSR3_BUSY))
+ goto exit_fail;
/*
* Write the request into the BBP.
@@ -184,14 +185,19 @@ static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev,
* Wait until the BBP becomes ready.
*/
reg = rt73usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n");
- *value = 0xff;
- return;
- }
+ if (rt2x00_get_field32(reg, PHY_CSR3_BUSY))
+ goto exit_fail;
*value = rt2x00_get_field32(reg, PHY_CSR3_VALUE);
mutex_unlock(&rt2x00dev->usb_cache_mutex);
+
+ return;
+
+exit_fail:
+ mutex_unlock(&rt2x00dev->usb_cache_mutex);
+
+ ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n");
+ *value = 0xff;
}
static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index ec8f7002b09d..39bb96b413ef 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -178,8 +178,7 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size,
int ret;
int begin, end, i;
- if (pos < 0 || pos > PCI_VPD_PCI22_SIZE ||
- size > PCI_VPD_PCI22_SIZE - pos)
+ if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos)
return -EINVAL;
if (size == 0)
return 0;
@@ -223,8 +222,8 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size,
u32 val;
int ret;
- if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 ||
- size > PCI_VPD_PCI22_SIZE - pos || size < 4)
+ if (pos < 0 || pos > vpd->base.len || pos & 3 ||
+ size > vpd->base.len - pos || size < 4)
return -EINVAL;
val = (u8) *buf++;
@@ -255,11 +254,6 @@ out:
return 4;
}
-static int pci_vpd_pci22_get_size(struct pci_dev *dev)
-{
- return PCI_VPD_PCI22_SIZE;
-}
-
static void pci_vpd_pci22_release(struct pci_dev *dev)
{
kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
@@ -268,7 +262,6 @@ static void pci_vpd_pci22_release(struct pci_dev *dev)
static struct pci_vpd_ops pci_vpd_pci22_ops = {
.read = pci_vpd_pci22_read,
.write = pci_vpd_pci22_write,
- .get_size = pci_vpd_pci22_get_size,
.release = pci_vpd_pci22_release,
};
@@ -284,6 +277,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev)
if (!vpd)
return -ENOMEM;
+ vpd->base.len = PCI_VPD_PCI22_SIZE;
vpd->base.ops = &pci_vpd_pci22_ops;
spin_lock_init(&vpd->lock);
vpd->cap = cap;
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 648596d469f6..91156f85a926 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -700,9 +700,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
cleanup_p2p_bridge, NULL, NULL);
- if (!(bridge = acpiphp_handle_to_bridge(handle)))
- return AE_OK;
- cleanup_bridge(bridge);
+ bridge = acpiphp_handle_to_bridge(handle);
+ if (bridge)
+ cleanup_bridge(bridge);
+
return AE_OK;
}
@@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle)
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
(u32)1, cleanup_p2p_bridge, NULL, NULL);
+ /*
+ * On root bridges with hotplug slots directly underneath (ie,
+ * no p2p bridge inbetween), we call cleanup_bridge().
+ *
+ * The else clause cleans up root bridges that either had no
+ * hotplug slots at all, or had a p2p bridge underneath.
+ */
bridge = acpiphp_handle_to_bridge(handle);
if (bridge)
cleanup_bridge(bridge);
+ else
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ handle_hotplug_event_bridge);
}
static struct pci_dev * get_apic_pci_info(acpi_handle handle)
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 6f3c7446c329..9c718583a237 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -736,9 +736,9 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
if (attr) {
pdev->vpd->attr = attr;
- attr->size = pdev->vpd->ops->get_size(pdev);
+ attr->size = pdev->vpd->len;
attr->attr.name = "vpd";
- attr->attr.mode = S_IRUGO | S_IWUSR;
+ attr->attr.mode = S_IRUSR | S_IWUSR;
attr->read = pci_read_vpd;
attr->write = pci_write_vpd;
retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 0a497c1b4227..00408c97e5fc 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -21,11 +21,11 @@ extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
struct pci_vpd_ops {
int (*read)(struct pci_dev *dev, int pos, int size, char *buf);
int (*write)(struct pci_dev *dev, int pos, int size, const char *buf);
- int (*get_size)(struct pci_dev *dev);
void (*release)(struct pci_dev *dev);
};
struct pci_vpd {
+ unsigned int len;
struct pci_vpd_ops *ops;
struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
};
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index dabb563f51d9..338a3f94b4d4 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1670,6 +1670,48 @@ static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
+/*
+ * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
+ * VPD end tag will hang the device. This problem was initially
+ * observed when a vpd entry was created in sysfs
+ * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry
+ * will dump 32k of data. Reading a full 32k will cause an access
+ * beyond the VPD end tag causing the device to hang. Once the device
+ * is hung, the bnx2 driver will not be able to reset the device.
+ * We believe that it is legal to read beyond the end tag and
+ * therefore the solution is to limit the read/write length.
+ */
+static void __devinit quirk_brcm_570x_limit_vpd(struct pci_dev *dev)
+{
+ /* Only disable the VPD capability for 5706, 5708, and 5709 rev. A */
+ if ((dev->device == PCI_DEVICE_ID_NX2_5706) ||
+ (dev->device == PCI_DEVICE_ID_NX2_5708) ||
+ ((dev->device == PCI_DEVICE_ID_NX2_5709) &&
+ (dev->revision & 0xf0) == 0x0)) {
+ if (dev->vpd)
+ dev->vpd->len = 0x80;
+ }
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5706S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5708S,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709,
+ quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+ PCI_DEVICE_ID_NX2_5709S,
+ quirk_brcm_570x_limit_vpd);
+
#ifdef CONFIG_PCI_MSI
/* Some chipsets do not support MSI. We cannot easily rely on setting
* PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually
@@ -1685,6 +1727,7 @@ static void __init quirk_disable_all_msi(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_disable_all_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS400_200, quirk_disable_all_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS480, quirk_disable_all_msi);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3336, quirk_disable_all_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VT3351, quirk_disable_all_msi);
/* Disable MSI on chipsets that are known to not support it */
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 7e3ad4f3b343..58b7336640ff 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -126,12 +126,25 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
int err;
struct rtc_time before, now;
int first_time = 1;
+ unsigned long t_now, t_alm;
+ enum { none, day, month, year } missing = none;
+ unsigned days;
- /* The lower level RTC driver may not be capable of filling
- * in all fields of the rtc_time struct (eg. rtc-cmos),
- * and so might instead return -1 in some fields.
- * We deal with that here by grabbing a current RTC timestamp
- * and using values from that for any missing (-1) values.
+ /* The lower level RTC driver may return -1 in some fields,
+ * creating invalid alarm->time values, for reasons like:
+ *
+ * - The hardware may not be capable of filling them in;
+ * many alarms match only on time-of-day fields, not
+ * day/month/year calendar data.
+ *
+ * - Some hardware uses illegal values as "wildcard" match
+ * values, which non-Linux firmware (like a BIOS) may try
+ * to set up as e.g. "alarm 15 minutes after each hour".
+ * Linux uses only oneshot alarms.
+ *
+ * When we see that here, we deal with it by using values from
+ * a current RTC timestamp for any missing (-1) values. The
+ * RTC driver prevents "periodic alarm" modes.
*
* But this can be racey, because some fields of the RTC timestamp
* may have wrapped in the interval since we read the RTC alarm,
@@ -174,6 +187,10 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
if (!alarm->enabled)
return 0;
+ /* full-function RTCs won't have such missing fields */
+ if (rtc_valid_tm(&alarm->time) == 0)
+ return 0;
+
/* get the "after" timestamp, to detect wrapped fields */
err = rtc_read_time(rtc, &now);
if (err < 0)
@@ -183,22 +200,85 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
} while ( before.tm_min != now.tm_min
|| before.tm_hour != now.tm_hour
|| before.tm_mon != now.tm_mon
- || before.tm_year != now.tm_year
- || before.tm_isdst != now.tm_isdst);
+ || before.tm_year != now.tm_year);
- /* Fill in any missing alarm fields using the timestamp */
+ /* Fill in the missing alarm fields using the timestamp; we
+ * know there's at least one since alarm->time is invalid.
+ */
if (alarm->time.tm_sec == -1)
alarm->time.tm_sec = now.tm_sec;
if (alarm->time.tm_min == -1)
alarm->time.tm_min = now.tm_min;
if (alarm->time.tm_hour == -1)
alarm->time.tm_hour = now.tm_hour;
- if (alarm->time.tm_mday == -1)
+
+ /* For simplicity, only support date rollover for now */
+ if (alarm->time.tm_mday == -1) {
alarm->time.tm_mday = now.tm_mday;
- if (alarm->time.tm_mon == -1)
+ missing = day;
+ }
+ if (alarm->time.tm_mon == -1) {
alarm->time.tm_mon = now.tm_mon;
- if (alarm->time.tm_year == -1)
+ if (missing == none)
+ missing = month;
+ }
+ if (alarm->time.tm_year == -1) {
alarm->time.tm_year = now.tm_year;
+ if (missing == none)
+ missing = year;
+ }
+
+ /* with luck, no rollover is needed */
+ rtc_tm_to_time(&now, &t_now);
+ rtc_tm_to_time(&alarm->time, &t_alm);
+ if (t_now < t_alm)
+ goto done;
+
+ switch (missing) {
+
+ /* 24 hour rollover ... if it's now 10am Monday, an alarm that
+ * that will trigger at 5am will do so at 5am Tuesday, which
+ * could also be in the next month or year. This is a common
+ * case, especially for PCs.
+ */
+ case day:
+ dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day");
+ t_alm += 24 * 60 * 60;
+ rtc_time_to_tm(t_alm, &alarm->time);
+ break;
+
+ /* Month rollover ... if it's the 31th, an alarm on the 3rd will
+ * be next month. An alarm matching on the 30th, 29th, or 28th
+ * may end up in the month after that! Many newer PCs support
+ * this type of alarm.
+ */
+ case month:
+ dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month");
+ do {
+ if (alarm->time.tm_mon < 11)
+ alarm->time.tm_mon++;
+ else {
+ alarm->time.tm_mon = 0;
+ alarm->time.tm_year++;
+ }
+ days = rtc_month_days(alarm->time.tm_mon,
+ alarm->time.tm_year);
+ } while (days < alarm->time.tm_mday);
+ break;
+
+ /* Year rollover ... easy except for leap years! */
+ case year:
+ dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year");
+ do {
+ alarm->time.tm_year++;
+ } while (!rtc_valid_tm(&alarm->time));
+ break;
+
+ default:
+ dev_warn(&rtc->dev, "alarm rollover not handled\n");
+ }
+
+done:
return 0;
}
EXPORT_SYMBOL_GPL(rtc_read_alarm);
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index 82f62d25f921..67421b0d3a7b 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -331,14 +331,14 @@ static int sa1100_rtc_probe(struct platform_device *pdev)
RCNR = 0;
}
+ device_init_wakeup(&pdev->dev, 1);
+
rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
- device_init_wakeup(&pdev->dev, 1);
-
platform_set_drvdata(pdev, rtc);
return 0;
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index eaf55945f21b..7dcfba1bbfe1 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -71,6 +71,7 @@
#define X1205_SR_RTCF 0x01 /* Clock failure */
#define X1205_SR_WEL 0x02 /* Write Enable Latch */
#define X1205_SR_RWEL 0x04 /* Register Write Enable */
+#define X1205_SR_AL0 0x20 /* Alarm 0 match */
#define X1205_DTR_DTR0 0x01
#define X1205_DTR_DTR1 0x02
@@ -78,6 +79,8 @@
#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */
+#define X1205_INT_AL0E 0x20 /* Alarm 0 enable */
+
static struct i2c_driver x1205_driver;
/*
@@ -89,8 +92,8 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
unsigned char reg_base)
{
unsigned char dt_addr[2] = { 0, reg_base };
-
unsigned char buf[8];
+ int i;
struct i2c_msg msgs[] = {
{ client->addr, 0, 2, dt_addr }, /* setup read ptr */
@@ -98,7 +101,7 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
};
/* read date registers */
- if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
dev_err(&client->dev, "%s: read error\n", __func__);
return -EIO;
}
@@ -110,6 +113,11 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7]);
+ /* Mask out the enable bits if these are alarm registers */
+ if (reg_base < X1205_CCR_BASE)
+ for (i = 0; i <= 4; i++)
+ buf[i] &= 0x7F;
+
tm->tm_sec = BCD2BIN(buf[CCR_SEC]);
tm->tm_min = BCD2BIN(buf[CCR_MIN]);
tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */
@@ -138,7 +146,7 @@ static int x1205_get_status(struct i2c_client *client, unsigned char *sr)
};
/* read status register */
- if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
dev_err(&client->dev, "%s: read error\n", __func__);
return -EIO;
}
@@ -147,10 +155,11 @@ static int x1205_get_status(struct i2c_client *client, unsigned char *sr)
}
static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
- int datetoo, u8 reg_base)
+ int datetoo, u8 reg_base, unsigned char alm_enable)
{
- int i, xfer;
+ int i, xfer, nbytes;
unsigned char buf[8];
+ unsigned char rdata[10] = { 0, reg_base };
static const unsigned char wel[3] = { 0, X1205_REG_SR,
X1205_SR_WEL };
@@ -189,6 +198,11 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
buf[CCR_Y2K] = BIN2BCD(tm->tm_year / 100);
}
+ /* If writing alarm registers, set compare bits on registers 0-4 */
+ if (reg_base < X1205_CCR_BASE)
+ for (i = 0; i <= 4; i++)
+ buf[i] |= 0x80;
+
/* this sequence is required to unlock the chip */
if ((xfer = i2c_master_send(client, wel, 3)) != 3) {
dev_err(&client->dev, "%s: wel - %d\n", __func__, xfer);
@@ -200,19 +214,57 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
return -EIO;
}
+
/* write register's data */
- for (i = 0; i < (datetoo ? 8 : 3); i++) {
- unsigned char rdata[3] = { 0, reg_base + i, buf[i] };
+ if (datetoo)
+ nbytes = 8;
+ else
+ nbytes = 3;
+ for (i = 0; i < nbytes; i++)
+ rdata[2+i] = buf[i];
+
+ xfer = i2c_master_send(client, rdata, nbytes+2);
+ if (xfer != nbytes+2) {
+ dev_err(&client->dev,
+ "%s: result=%d addr=%02x, data=%02x\n",
+ __func__,
+ xfer, rdata[1], rdata[2]);
+ return -EIO;
+ }
+
+ /* If we wrote to the nonvolatile region, wait 10msec for write cycle*/
+ if (reg_base < X1205_CCR_BASE) {
+ unsigned char al0e[3] = { 0, X1205_REG_INT, 0 };
+
+ msleep(10);
- xfer = i2c_master_send(client, rdata, 3);
+ /* ...and set or clear the AL0E bit in the INT register */
+
+ /* Need to set RWEL again as the write has cleared it */
+ xfer = i2c_master_send(client, rwel, 3);
if (xfer != 3) {
dev_err(&client->dev,
- "%s: xfer=%d addr=%02x, data=%02x\n",
+ "%s: aloe rwel - %d\n",
__func__,
- xfer, rdata[1], rdata[2]);
+ xfer);
+ return -EIO;
+ }
+
+ if (alm_enable)
+ al0e[2] = X1205_INT_AL0E;
+
+ xfer = i2c_master_send(client, al0e, 3);
+ if (xfer != 3) {
+ dev_err(&client->dev,
+ "%s: al0e - %d\n",
+ __func__,
+ xfer);
return -EIO;
}
- };
+
+ /* and wait 10msec again for this write to complete */
+ msleep(10);
+ }
/* disable further writes */
if ((xfer = i2c_master_send(client, diswe, 3)) != 3) {
@@ -230,9 +282,9 @@ static int x1205_fix_osc(struct i2c_client *client)
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
- if ((err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE)) < 0)
- dev_err(&client->dev,
- "unable to restart the oscillator\n");
+ err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE, 0);
+ if (err < 0)
+ dev_err(&client->dev, "unable to restart the oscillator\n");
return err;
}
@@ -248,7 +300,7 @@ static int x1205_get_dtrim(struct i2c_client *client, int *trim)
};
/* read dtr register */
- if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
dev_err(&client->dev, "%s: read error\n", __func__);
return -EIO;
}
@@ -280,7 +332,7 @@ static int x1205_get_atrim(struct i2c_client *client, int *trim)
};
/* read atr register */
- if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
dev_err(&client->dev, "%s: read error\n", __func__);
return -EIO;
}
@@ -403,14 +455,33 @@ static int x1205_validate_client(struct i2c_client *client)
static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
- return x1205_get_datetime(to_i2c_client(dev),
- &alrm->time, X1205_ALM0_BASE);
+ int err;
+ unsigned char intreg, status;
+ static unsigned char int_addr[2] = { 0, X1205_REG_INT };
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_msg msgs[] = {
+ { client->addr, 0, 2, int_addr }, /* setup read ptr */
+ { client->addr, I2C_M_RD, 1, &intreg }, /* read INT register */
+ };
+
+ /* read interrupt register and status register */
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+ err = x1205_get_status(client, &status);
+ if (err == 0) {
+ alrm->pending = (status & X1205_SR_AL0) ? 1 : 0;
+ alrm->enabled = (intreg & X1205_INT_AL0E) ? 1 : 0;
+ err = x1205_get_datetime(client, &alrm->time, X1205_ALM0_BASE);
+ }
+ return err;
}
static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
return x1205_set_datetime(to_i2c_client(dev),
- &alrm->time, 1, X1205_ALM0_BASE);
+ &alrm->time, 1, X1205_ALM0_BASE, alrm->enabled);
}
static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -422,7 +493,7 @@ static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return x1205_set_datetime(to_i2c_client(dev),
- tm, 1, X1205_CCR_BASE);
+ tm, 1, X1205_CCR_BASE, 0);
}
static int x1205_rtc_proc(struct device *dev, struct seq_file *seq)
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
index a0b6d414953d..59fbef08d690 100644
--- a/drivers/scsi/esp_scsi.c
+++ b/drivers/scsi/esp_scsi.c
@@ -2359,6 +2359,24 @@ void scsi_esp_unregister(struct esp *esp)
}
EXPORT_SYMBOL(scsi_esp_unregister);
+static int esp_target_alloc(struct scsi_target *starget)
+{
+ struct esp *esp = shost_priv(dev_to_shost(&starget->dev));
+ struct esp_target_data *tp = &esp->target[starget->id];
+
+ tp->starget = starget;
+
+ return 0;
+}
+
+static void esp_target_destroy(struct scsi_target *starget)
+{
+ struct esp *esp = shost_priv(dev_to_shost(&starget->dev));
+ struct esp_target_data *tp = &esp->target[starget->id];
+
+ tp->starget = NULL;
+}
+
static int esp_slave_alloc(struct scsi_device *dev)
{
struct esp *esp = shost_priv(dev->host);
@@ -2370,8 +2388,6 @@ static int esp_slave_alloc(struct scsi_device *dev)
return -ENOMEM;
dev->hostdata = lp;
- tp->starget = dev->sdev_target;
-
spi_min_period(tp->starget) = esp->min_period;
spi_max_offset(tp->starget) = 15;
@@ -2608,6 +2624,8 @@ struct scsi_host_template scsi_esp_template = {
.name = "esp",
.info = esp_info,
.queuecommand = esp_queuecommand,
+ .target_alloc = esp_target_alloc,
+ .target_destroy = esp_target_destroy,
.slave_alloc = esp_slave_alloc,
.slave_configure = esp_slave_configure,
.slave_destroy = esp_slave_destroy,
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 45df83b9d847..0fe031f003e7 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -61,7 +61,7 @@ static int ses_probe(struct device *dev)
return err;
}
-#define SES_TIMEOUT 30
+#define SES_TIMEOUT (30 * HZ)
#define SES_RETRIES 3
static int ses_recv_diag(struct scsi_device *sdev, int page_code,
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index c9b64e73c987..42d2e108b679 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -1991,7 +1991,9 @@ struct uart_match {
static int serial_match_port(struct device *dev, void *data)
{
struct uart_match *match = data;
- dev_t devt = MKDEV(match->driver->major, match->driver->minor) + match->port->line;
+ struct tty_driver *tty_drv = match->driver->tty_driver;
+ dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) +
+ match->port->line;
return dev->devt == devt; /* Actually, only one tty per port */
}
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 799337f7fde1..f5b60c70389b 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -167,14 +167,14 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
mutex_lock(&spidev->buf_lock);
status = spidev_sync_read(spidev, count);
- if (status == 0) {
+ if (status > 0) {
unsigned long missing;
- missing = copy_to_user(buf, spidev->buffer, count);
- if (count && missing == count)
+ missing = copy_to_user(buf, spidev->buffer, status);
+ if (missing == status)
status = -EFAULT;
else
- status = count - missing;
+ status = status - missing;
}
mutex_unlock(&spidev->buf_lock);
@@ -200,8 +200,6 @@ spidev_write(struct file *filp, const char __user *buf,
missing = copy_from_user(spidev->buffer, buf, count);
if (missing == 0) {
status = spidev_sync_write(spidev, count);
- if (status == 0)
- status = count;
} else
status = -EFAULT;
mutex_unlock(&spidev->buf_lock);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 63c34043b4d9..c3201affa0b6 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1125,9 +1125,6 @@ static void stop_data_traffic(struct acm *acm)
for (i = 0; i < acm->rx_buflimit; i++)
usb_kill_urb(acm->ru[i].urb);
- INIT_LIST_HEAD(&acm->filled_read_bufs);
- INIT_LIST_HEAD(&acm->spare_read_bufs);
-
tasklet_enable(&acm->urb_task);
cancel_work_sync(&acm->work);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 09a53e7f3327..42a436478b78 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -924,6 +924,15 @@ static int register_root_hub(struct usb_hcd *hcd)
return retval;
}
+void usb_enable_root_hub_irq (struct usb_bus *bus)
+{
+ struct usb_hcd *hcd;
+
+ hcd = container_of (bus, struct usb_hcd, self);
+ if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT)
+ hcd->driver->hub_irq_enable (hcd);
+}
+
/*-------------------------------------------------------------------------*/
@@ -1684,19 +1693,30 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
- int start = hcd->state;
+ unsigned long flags;
+ irqreturn_t rc;
- if (unlikely(start == HC_STATE_HALT ||
- !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
- return IRQ_NONE;
- if (hcd->driver->irq (hcd) == IRQ_NONE)
- return IRQ_NONE;
+ /* IRQF_DISABLED doesn't work correctly with shared IRQs
+ * when the first handler doesn't use it. So let's just
+ * assume it's never used.
+ */
+ local_irq_save(flags);
- set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+ if (unlikely(hcd->state == HC_STATE_HALT ||
+ !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ rc = IRQ_NONE;
+ } else if (hcd->driver->irq(hcd) == IRQ_NONE) {
+ rc = IRQ_NONE;
+ } else {
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ if (unlikely(hcd->state == HC_STATE_HALT))
+ usb_hc_died(hcd);
+ rc = IRQ_HANDLED;
+ }
- if (unlikely(hcd->state == HC_STATE_HALT))
- usb_hc_died (hcd);
- return IRQ_HANDLED;
+ local_irq_restore(flags);
+ return rc;
}
/*-------------------------------------------------------------------------*/
@@ -1860,6 +1880,13 @@ int usb_add_hcd(struct usb_hcd *hcd,
/* enable irqs just before we start the controller */
if (hcd->driver->irq) {
+
+ /* IRQF_DISABLED doesn't work as advertised when used together
+ * with IRQF_SHARED. As usb_hcd_irq() will always disable
+ * interrupts we can remove it here.
+ */
+ irqflags &= ~IRQF_DISABLED;
+
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index a0bf5df6cb6f..b9de1569b39e 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -210,6 +210,8 @@ struct hc_driver {
int (*bus_suspend)(struct usb_hcd *);
int (*bus_resume)(struct usb_hcd *);
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
+ void (*hub_irq_enable)(struct usb_hcd *);
+ /* Needed only if port-change IRQs are level-triggered */
/* force handover of high-speed port to full-speed companion */
void (*relinquish_port)(struct usb_hcd *, int);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 94789be54ca3..4cfe32a16c37 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -713,18 +713,11 @@ static void hub_restart(struct usb_hub *hub, int type)
}
/* Was the power session lost while we were suspended? */
- switch (type) {
- case HUB_RESET_RESUME:
- portstatus = 0;
- portchange = USB_PORT_STAT_C_CONNECTION;
- break;
+ status = hub_port_status(hub, port1, &portstatus, &portchange);
- case HUB_RESET:
- case HUB_RESUME:
- status = hub_port_status(hub, port1,
- &portstatus, &portchange);
- break;
- }
+ /* If the device is gone, khubd will handle it later */
+ if (status == 0 && !(portstatus & USB_PORT_STAT_CONNECTION))
+ continue;
/* For "USB_PERSIST"-enabled children we must
* mark the child device for reset-resume and
@@ -2080,6 +2073,8 @@ int usb_port_resume(struct usb_device *udev)
}
clear_bit(port1, hub->busy_bits);
+ if (!hub->hdev->parent && !hub->busy_bits[0])
+ usb_enable_root_hub_irq(hub->hdev->bus);
if (status == 0)
status = finish_port_resume(udev);
@@ -3009,6 +3004,11 @@ static void hub_events(void)
hub->activating = 0;
+ /* If this is a root hub, tell the HCD it's okay to
+ * re-enable port-change interrupts now. */
+ if (!hdev->parent && !hub->busy_bits[0])
+ usb_enable_root_hub_irq(hdev->bus);
+
loop_autopm:
/* Allow autosuspend if we're not going to run again */
if (list_empty(&hub->event_list))
@@ -3234,6 +3234,8 @@ int usb_reset_device(struct usb_device *udev)
break;
}
clear_bit(port1, parent_hub->busy_bits);
+ if (!parent_hdev->parent && !parent_hub->busy_bits[0])
+ usb_enable_root_hub_irq(parent_hdev->bus);
if (ret < 0)
goto re_enumerate;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 35a03095757e..90245fd8bac4 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -177,6 +177,15 @@ timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
static inline void
timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
{
+ /* Don't override timeouts which shrink or (later) disable
+ * the async ring; just the I/O watchdog. Note that if a
+ * SHRINK were pending, OFF would never be requested.
+ */
+ if (timer_pending(&ehci->watchdog)
+ && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF))
+ & ehci->actions))
+ return;
+
if (!test_and_set_bit (action, &ehci->actions)) {
unsigned long t;
@@ -192,15 +201,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
t = EHCI_SHRINK_JIFFIES;
break;
}
- t += jiffies;
- // all timings except IAA watchdog can be overridden.
- // async queue SHRINK often precedes IAA. while it's ready
- // to go OFF neither can matter, and afterwards the IO
- // watchdog stops unless there's still periodic traffic.
- if (time_before_eq(t, ehci->watchdog.expires)
- && timer_pending (&ehci->watchdog))
- return;
- mod_timer (&ehci->watchdog, t);
+ mod_timer(&ehci->watchdog, t + jiffies);
}
}
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index c96db1153dcf..e534f9de0f05 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -261,6 +261,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index 1b9abdba920b..f90fe0c7373f 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -288,6 +288,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
index 06aadfb0ec29..5adaf36e47d0 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -135,6 +135,7 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
.get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 33f1c1c32edf..a8160d65f32b 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1054,7 +1054,7 @@ MODULE_LICENSE ("GPL");
#ifdef CONFIG_MFD_SM501
#include "ohci-sm501.c"
-#define PLATFORM_DRIVER ohci_hcd_sm501_driver
+#define SM501_OHCI_DRIVER ohci_hcd_sm501_driver
#endif
#if !defined(PCI_DRIVER) && \
@@ -1062,6 +1062,7 @@ MODULE_LICENSE ("GPL");
!defined(OF_PLATFORM_DRIVER) && \
!defined(SA1111_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && \
+ !defined(SM501_OHCI_DRIVER) && \
!defined(SSB_OHCI_DRIVER)
#error "missing bus glue for ohci-hcd"
#endif
@@ -1121,9 +1122,18 @@ static int __init ohci_hcd_mod_init(void)
goto error_ssb;
#endif
+#ifdef SM501_OHCI_DRIVER
+ retval = platform_driver_register(&SM501_OHCI_DRIVER);
+ if (retval < 0)
+ goto error_sm501;
+#endif
+
return retval;
/* Error path */
+#ifdef SM501_OHCI_DRIVER
+ error_sm501:
+#endif
#ifdef SSB_OHCI_DRIVER
error_ssb:
#endif
@@ -1159,6 +1169,9 @@ module_init(ohci_hcd_mod_init);
static void __exit ohci_hcd_mod_exit(void)
{
+#ifdef SM501_OHCI_DRIVER
+ platform_driver_unregister(&SM501_OHCI_DRIVER);
+#endif
#ifdef SSB_OHCI_DRIVER
ssb_driver_unregister(&SSB_OHCI_DRIVER);
#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 79a78029f896..b56739221d11 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -36,6 +36,18 @@
/*-------------------------------------------------------------------------*/
+/* hcd->hub_irq_enable() */
+static void ohci_rhsc_enable (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ spin_lock_irq(&ohci->lock);
+ if (!ohci->autostop)
+ del_timer(&hcd->rh_timer); /* Prevent next poll */
+ ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+ spin_unlock_irq(&ohci->lock);
+}
+
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
@@ -362,28 +374,18 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
int any_connected)
{
int poll_rh = 1;
- int rhsc;
- rhsc = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC;
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_OPER:
- /* If no status changes are pending, enable status-change
- * interrupts.
- */
- if (!rhsc && !changed) {
- rhsc = OHCI_INTR_RHSC;
- ohci_writel(ohci, rhsc, &ohci->regs->intrenable);
- }
-
- /* Keep on polling until we know a device is connected
- * and RHSC is enabled, or until we autostop.
- */
+ /* keep on polling until we know a device is connected
+ * and RHSC is enabled */
if (!ohci->autostop) {
if (any_connected ||
!device_may_wakeup(&ohci_to_hcd(ohci)
->self.root_hub->dev)) {
- if (rhsc)
+ if (ohci_readl(ohci, &ohci->regs->intrenable) &
+ OHCI_INTR_RHSC)
poll_rh = 0;
} else {
ohci->autostop = 1;
@@ -396,13 +398,12 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
ohci->autostop = 0;
ohci->next_statechange = jiffies +
STATECHANGE_DELAY;
- } else if (rhsc && time_after_eq(jiffies,
+ } else if (time_after_eq(jiffies,
ohci->next_statechange)
&& !ohci->ed_rm_list
&& !(ohci->hc_control &
OHCI_SCHED_ENABLES)) {
ohci_rh_suspend(ohci, 1);
- poll_rh = 0;
}
}
break;
@@ -416,12 +417,6 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
else
usb_hcd_resume_root_hub(ohci_to_hcd(ohci));
} else {
- if (!rhsc && (ohci->autostop ||
- ohci_to_hcd(ohci)->self.root_hub->
- do_remote_wakeup))
- ohci_writel(ohci, OHCI_INTR_RHSC,
- &ohci->regs->intrenable);
-
/* everything is idle, no need for polling */
poll_rh = 0;
}
@@ -443,16 +438,12 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci)
static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
int any_connected)
{
- /* If RHSC is enabled, don't poll */
- if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
- return 0;
+ int poll_rh = 1;
- /* If no status changes are pending, enable status-change interrupts */
- if (!changed) {
- ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
- return 0;
- }
- return 1;
+ /* keep on polling until RHSC is enabled */
+ if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
+ poll_rh = 0;
+ return poll_rh;
}
#endif /* CONFIG_PM */
diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c
index 96d14fa1d833..13c12ed22252 100644
--- a/drivers/usb/host/ohci-lh7a404.c
+++ b/drivers/usb/host/ohci-lh7a404.c
@@ -193,6 +193,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 6859fb5f1d6f..3a7c24c03671 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -466,6 +466,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 3bf175d95a23..4696cc912e16 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -327,6 +327,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index 664f07ee8732..28b458f20cc3 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -280,6 +280,7 @@ static const struct hc_driver ohci_pnx4008_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-pnx8550.c b/drivers/usb/host/ohci-pnx8550.c
index 28467e288a93..605d59cba28e 100644
--- a/drivers/usb/host/ohci-pnx8550.c
+++ b/drivers/usb/host/ohci-pnx8550.c
@@ -201,6 +201,7 @@ static const struct hc_driver ohci_pnx8550_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index 50e55db13636..a67252791223 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -72,6 +72,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c
index cd3398b675b2..523c30125577 100644
--- a/drivers/usb/host/ohci-ppc-soc.c
+++ b/drivers/usb/host/ohci-ppc-soc.c
@@ -172,6 +172,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
index bfdeb0d22d05..c1935ae537f8 100644
--- a/drivers/usb/host/ohci-ps3.c
+++ b/drivers/usb/host/ohci-ps3.c
@@ -68,6 +68,7 @@ static const struct hc_driver ps3_ohci_hc_driver = {
.get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
.start_port_reset = ohci_start_port_reset,
#if defined(CONFIG_PM)
.bus_suspend = ohci_bus_suspend,
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 70b0d4b459e7..d4ee27d92be8 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -298,6 +298,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 9c9f3b59186f..9b547407c934 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -952,6 +952,7 @@ rescan_this:
struct urb *urb;
urb_priv_t *urb_priv;
__hc32 savebits;
+ u32 tdINFO;
td = list_entry (entry, struct td, td_list);
urb = td->urb;
@@ -966,6 +967,17 @@ rescan_this:
savebits = *prev & ~cpu_to_hc32 (ohci, TD_MASK);
*prev = td->hwNextTD | savebits;
+ /* If this was unlinked, the TD may not have been
+ * retired ... so manually save the data toggle.
+ * The controller ignores the value we save for
+ * control and ISO endpoints.
+ */
+ tdINFO = hc32_to_cpup(ohci, &td->hwINFO);
+ if ((tdINFO & TD_T) == TD_T_DATA0)
+ ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_C);
+ else if ((tdINFO & TD_T) == TD_T_DATA1)
+ ed->hwHeadP |= cpu_to_hc32(ohci, ED_C);
+
/* HC may have partly processed this TD */
td_done (ohci, urb, td);
urb_priv->td_cnt++;
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index a73d2ff322e2..ead4772f0f27 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -466,6 +466,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
*/
.hub_status_data = ohci_s3c2410_hub_status_data,
.hub_control = ohci_s3c2410_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index 99438c65981b..0f48f2d99226 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -231,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c
index 60f03cc7ec4f..e7ee607278fe 100644
--- a/drivers/usb/host/ohci-sh.c
+++ b/drivers/usb/host/ohci-sh.c
@@ -68,6 +68,7 @@ static const struct hc_driver ohci_sh_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c
index e899a77dfb83..e610698c6b60 100644
--- a/drivers/usb/host/ohci-sm501.c
+++ b/drivers/usb/host/ohci-sm501.c
@@ -75,6 +75,7 @@ static const struct hc_driver ohci_sm501_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c
index c4265caec780..7275186db315 100644
--- a/drivers/usb/host/ohci-ssb.c
+++ b/drivers/usb/host/ohci-ssb.c
@@ -81,6 +81,7 @@ static const struct hc_driver ssb_ohci_hc_driver = {
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index f29307405bb3..9b6323f768b2 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -2934,6 +2934,16 @@ static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num)
return 0;
}
+static void u132_hub_irq_enable(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ } else if (u132->going > 0)
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+}
+
#ifdef CONFIG_PM
static int u132_bus_suspend(struct usb_hcd *hcd)
@@ -2985,6 +2995,7 @@ static struct hc_driver u132_hc_driver = {
.bus_suspend = u132_bus_suspend,
.bus_resume = u132_bus_resume,
.start_port_reset = u132_start_port_reset,
+ .hub_irq_enable = u132_hub_irq_enable,
};
/*
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index cb7fa0eaf3ae..33182f4c2267 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -3264,8 +3264,6 @@ static void sisusb_disconnect(struct usb_interface *intf)
/* decrement our usage count */
kref_put(&sisusb->kref, sisusb_delete);
-
- dev_info(&sisusb->sisusb_dev->dev, "Disconnected\n");
}
static struct usb_device_id sisusb_table [] = {
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 5234e7a3bd2c..0ff4a3971e45 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -637,6 +637,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
+ { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 06e0ecabb3eb..8302eca893ea 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -828,6 +828,9 @@
/* Propox devices */
#define FTDI_PROPOX_JTAGCABLEII_PID 0xD738
+/* Rig Expert Ukraine devices */
+#define FTDI_REU_TINY_PID 0xED22 /* RigExpert Tiny */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index ea924dc48496..d9fb3768a2d7 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -570,7 +570,12 @@ static struct usb_serial_driver ipaq_device = {
.description = "PocketPC PDA",
.usb_driver = &ipaq_driver,
.id_table = ipaq_id_table,
- .num_ports = 2,
+ /*
+ * some devices have an extra endpoint, which
+ * must be ignored as it would make the core
+ * create a second port which oopses when used
+ */
+ .num_ports = 1,
.open = ipaq_open,
.close = ipaq_close,
.attach = ipaq_startup,
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 43cfde83a93b..a73420dd052a 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -306,6 +306,7 @@ static struct usb_device_id option_ids[] = {
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
{ USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 103195abd417..2a0dd1b50dc4 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -57,6 +57,7 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index cff160abb130..6ac3bbcf7a22 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -15,6 +15,7 @@
#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2
#define PL2303_PRODUCT_ID_ALDIGA 0x0611
#define PL2303_PRODUCT_ID_MMX 0x0612
+#define PL2303_PRODUCT_ID_GPRS 0x0609
#define ATEN_VENDOR_ID 0x0557
#define ATEN_VENDOR_ID2 0x0547
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 45fe3663fa7f..39a7c11795c4 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -402,11 +402,19 @@ UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100,
US_FL_IGNORE_RESIDUE ),
#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB
+/* CY7C68300 : support atacb */
UNUSUAL_DEV( 0x04b4, 0x6830, 0x0000, 0x9999,
"Cypress",
"Cypress AT2LP",
US_SC_CYP_ATACB, US_PR_DEVICE, NULL,
0),
+
+/* CY7C68310 : support atacb and atacb2 */
+UNUSUAL_DEV( 0x04b4, 0x6831, 0x0000, 0x9999,
+ "Cypress",
+ "Cypress ISD-300LP",
+ US_SC_CYP_ATACB, US_PR_DEVICE, NULL,
+ 0),
#endif
/* Reported by Simon Levitt <simon@whattf.com>
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 002b61b4f0f6..e0c5f96b273d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1825,12 +1825,13 @@ config FB_FSL_DIU
config FB_W100
tristate "W100 frame buffer support"
- depends on FB && PXA_SHARPSL
+ depends on FB && ARCH_PXA
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
---help---
Frame buffer driver for the w100 as found on the Sharp SL-Cxx series.
+ It can also drive the w3220 chip found on iPAQ hx4700.
This driver is also available as a module ( = code which can be
inserted and removed from the running kernel whenever you want). The
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index 0a2785361ca3..712dabc6269f 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -286,7 +286,7 @@ static struct diu_pool pool;
* rheap and make the furture large allocation fail.
*/
-void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys)
+static void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys)
{
void *virt;
@@ -311,12 +311,12 @@ void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys)
memset(virt, 0, size);
}
- pr_debug("rh virt=%p phys=%lx\n", virt, *phys);
+ pr_debug("rh virt=%p phys=%llx\n", virt, (unsigned long long)*phys);
return virt;
}
-void fsl_diu_free(void *p, unsigned long size)
+static void fsl_diu_free(void *p, unsigned long size)
{
pr_debug("p=%p size=%lu\n", p, size);
@@ -770,7 +770,7 @@ static int map_video_memory(struct fb_info *info)
info->fix.smem_len = info->fix.line_length * info->var.yres_virtual;
pr_debug("MAP_VIDEO_MEMORY: smem_len = %d\n", info->fix.smem_len);
info->screen_base = fsl_diu_alloc(info->fix.smem_len, &phys);
- if (info->screen_base == 0) {
+ if (info->screen_base == NULL) {
printk(KERN_ERR "Unable to allocate fb memory\n");
return -ENOMEM;
}
@@ -788,7 +788,7 @@ static int map_video_memory(struct fb_info *info)
static void unmap_video_memory(struct fb_info *info)
{
fsl_diu_free(info->screen_base, info->fix.smem_len);
- info->screen_base = 0;
+ info->screen_base = NULL;
info->fix.smem_start = 0;
info->fix.smem_len = 0;
}
@@ -1158,7 +1158,7 @@ static int init_fbinfo(struct fb_info *info)
return 0;
}
-static int install_fb(struct fb_info *info)
+static int __devinit install_fb(struct fb_info *info)
{
int rc;
struct mfb_info *mfbi = info->par;
@@ -1233,7 +1233,7 @@ static int install_fb(struct fb_info *info)
return 0;
}
-static void __exit uninstall_fb(struct fb_info *info)
+static void uninstall_fb(struct fb_info *info)
{
struct mfb_info *mfbi = info->par;
@@ -1287,7 +1287,7 @@ static int request_irq_local(int irq)
/* Read to clear the status */
status = in_be32(&hw->int_status);
- ret = request_irq(irq, fsl_diu_isr, 0, "diu", 0);
+ ret = request_irq(irq, fsl_diu_isr, 0, "diu", NULL);
if (ret)
pr_info("Request diu IRQ failed.\n");
else {
@@ -1312,7 +1312,7 @@ static void free_irq_local(int irq)
/* Disable all LCDC interrupt */
out_be32(&hw->int_mask, 0x1f);
- free_irq(irq, 0);
+ free_irq(irq, NULL);
}
#ifdef CONFIG_PM
@@ -1324,7 +1324,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state)
{
struct fsl_diu_data *machine_data;
- machine_data = dev_get_drvdata(&ofdev->dev);
+ machine_data = dev_get_drvdata(&dev->dev);
disable_lcdc(machine_data->fsl_diu_info[0]);
return 0;
@@ -1334,7 +1334,7 @@ static int fsl_diu_resume(struct of_device *ofdev)
{
struct fsl_diu_data *machine_data;
- machine_data = dev_get_drvdata(&ofdev->dev);
+ machine_data = dev_get_drvdata(&dev->dev);
enable_lcdc(machine_data->fsl_diu_info[0]);
return 0;
@@ -1353,7 +1353,8 @@ static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align)
dma_addr_t paddr = 0;
ssize = size + bytes_align;
- buf->vaddr = dma_alloc_coherent(0, ssize, &paddr, GFP_DMA | __GFP_ZERO);
+ buf->vaddr = dma_alloc_coherent(NULL, ssize, &paddr, GFP_DMA |
+ __GFP_ZERO);
if (!buf->vaddr)
return -ENOMEM;
@@ -1371,7 +1372,7 @@ static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align)
static void free_buf(struct diu_addr *buf, u32 size, u32 bytes_align)
{
- dma_free_coherent(0, size + bytes_align,
+ dma_free_coherent(NULL, size + bytes_align,
buf->vaddr, (buf->paddr - buf->offset));
return;
}
@@ -1411,7 +1412,7 @@ static ssize_t show_monitor(struct device *device,
return diu_ops.show_monitor_port(machine_data->monitor_port, buf);
}
-static int fsl_diu_probe(struct of_device *ofdev,
+static int __devinit fsl_diu_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->node;
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index 7dcda187d9ba..fafe7db20d6d 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -1246,7 +1246,7 @@ static int pxafb_resume(struct platform_device *dev)
* cache. Once this area is remapped, all virtual memory
* access to the video memory should occur at the new region.
*/
-static int __init pxafb_map_video_memory(struct pxafb_info *fbi)
+static int __devinit pxafb_map_video_memory(struct pxafb_info *fbi)
{
/*
* We reserve one page for the palette, plus the size
@@ -1348,7 +1348,7 @@ decode_mode:
pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes);
}
-static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev)
+static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
{
struct pxafb_info *fbi;
void *addr;
@@ -1410,7 +1410,7 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev)
}
#ifdef CONFIG_FB_PXA_PARAMETERS
-static int __init parse_opt_mode(struct device *dev, const char *this_opt)
+static int __devinit parse_opt_mode(struct device *dev, const char *this_opt)
{
struct pxafb_mach_info *inf = dev->platform_data;
@@ -1469,7 +1469,7 @@ done:
return 0;
}
-static int __init parse_opt(struct device *dev, char *this_opt)
+static int __devinit parse_opt(struct device *dev, char *this_opt)
{
struct pxafb_mach_info *inf = dev->platform_data;
struct pxafb_mode_info *mode = &inf->modes[0];
@@ -1567,7 +1567,7 @@ static int __init parse_opt(struct device *dev, char *this_opt)
return 0;
}
-static int __init pxafb_parse_options(struct device *dev, char *options)
+static int __devinit pxafb_parse_options(struct device *dev, char *options)
{
char *this_opt;
int ret;
@@ -1588,8 +1588,8 @@ static int __init pxafb_parse_options(struct device *dev, char *options)
static char g_options[256] __devinitdata = "";
-#ifndef CONFIG_MODULES
-static int __devinit pxafb_setup_options(void)
+#ifndef MODULE
+static int __init pxafb_setup_options(void)
{
char *options = NULL;
@@ -1613,7 +1613,7 @@ MODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.txt)");
#define pxafb_setup_options() (0)
#endif
-static int __init pxafb_probe(struct platform_device *dev)
+static int __devinit pxafb_probe(struct platform_device *dev)
{
struct pxafb_info *fbi;
struct pxafb_mach_info *inf;
@@ -1685,14 +1685,14 @@ static int __init pxafb_probe(struct platform_device *dev)
if (r == NULL) {
dev_err(&dev->dev, "no I/O memory resource defined\n");
ret = -ENODEV;
- goto failed;
+ goto failed_fbi;
}
r = request_mem_region(r->start, r->end - r->start + 1, dev->name);
if (r == NULL) {
dev_err(&dev->dev, "failed to request I/O memory\n");
ret = -EBUSY;
- goto failed;
+ goto failed_fbi;
}
fbi->mmio_base = ioremap(r->start, r->end - r->start + 1);
@@ -1735,8 +1735,17 @@ static int __init pxafb_probe(struct platform_device *dev)
* This makes sure that our colour bitfield
* descriptors are correctly initialised.
*/
- pxafb_check_var(&fbi->fb.var, &fbi->fb);
- pxafb_set_par(&fbi->fb);
+ ret = pxafb_check_var(&fbi->fb.var, &fbi->fb);
+ if (ret) {
+ dev_err(&dev->dev, "failed to get suitable mode\n");
+ goto failed_free_irq;
+ }
+
+ ret = pxafb_set_par(&fbi->fb);
+ if (ret) {
+ dev_err(&dev->dev, "Failed to set parameters\n");
+ goto failed_free_irq;
+ }
platform_set_drvdata(dev, fbi);
@@ -1744,7 +1753,7 @@ static int __init pxafb_probe(struct platform_device *dev)
if (ret < 0) {
dev_err(&dev->dev,
"Failed to register framebuffer device: %d\n", ret);
- goto failed_free_irq;
+ goto failed_free_cmap;
}
#ifdef CONFIG_CPU_FREQ
@@ -1763,18 +1772,23 @@ static int __init pxafb_probe(struct platform_device *dev)
return 0;
+failed_free_cmap:
+ if (fbi->fb.cmap.len)
+ fb_dealloc_cmap(&fbi->fb.cmap);
failed_free_irq:
free_irq(irq, fbi);
-failed_free_res:
- release_mem_region(r->start, r->end - r->start + 1);
-failed_free_io:
- iounmap(fbi->mmio_base);
failed_free_mem:
dma_free_writecombine(&dev->dev, fbi->map_size,
fbi->map_cpu, fbi->map_dma);
-failed:
+failed_free_io:
+ iounmap(fbi->mmio_base);
+failed_free_res:
+ release_mem_region(r->start, r->end - r->start + 1);
+failed_fbi:
+ clk_put(fbi->clk);
platform_set_drvdata(dev, NULL);
kfree(fbi);
+failed:
return ret;
}
@@ -1787,7 +1801,7 @@ static struct platform_driver pxafb_driver = {
},
};
-static int __devinit pxafb_init(void)
+static int __init pxafb_init(void)
{
if (pxafb_setup_options())
return -EINVAL;
diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c
index 30469bf906e5..d0674f1e3f10 100644
--- a/drivers/video/w100fb.c
+++ b/drivers/video/w100fb.c
@@ -1003,6 +1003,7 @@ static struct w100_pll_info xtal_14318000[] = {
static struct w100_pll_info xtal_16000000[] = {
/*freq M N_int N_fac tfgoal lock_time */
{ 72, 1, 8, 0, 0xe0, 48}, /* tfgoal guessed */
+ { 80, 1, 9, 0, 0xe0, 13}, /* tfgoal guessed */
{ 95, 1, 10, 7, 0xe0, 38}, /* tfgoal guessed */
{ 96, 1, 11, 0, 0xe0, 36}, /* tfgoal guessed */
{ 0, 0, 0, 0, 0, 0},
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index 619a6f8d65a2..47ed39b52f9c 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -18,6 +18,7 @@
* frame buffer.
*/
+#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fb.h>
@@ -42,37 +43,68 @@ struct xenfb_info {
struct xenfb_page *page;
unsigned long *mfns;
int update_wanted; /* XENFB_TYPE_UPDATE wanted */
+ int feature_resize; /* XENFB_TYPE_RESIZE ok */
+ struct xenfb_resize resize; /* protected by resize_lock */
+ int resize_dpy; /* ditto */
+ spinlock_t resize_lock;
struct xenbus_device *xbdev;
};
-static u32 xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8;
+#define XENFB_DEFAULT_FB_LEN (XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8)
+enum { KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT };
+static int video[KPARAM_CNT] = { 2, XENFB_WIDTH, XENFB_HEIGHT };
+module_param_array(video, int, NULL, 0);
+MODULE_PARM_DESC(video,
+ "Video memory size in MB, width, height in pixels (default 2,800,600)");
+
+static void xenfb_make_preferred_console(void);
static int xenfb_remove(struct xenbus_device *);
-static void xenfb_init_shared_page(struct xenfb_info *);
+static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *);
static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
static void xenfb_disconnect_backend(struct xenfb_info *);
+static void xenfb_send_event(struct xenfb_info *info,
+ union xenfb_out_event *event)
+{
+ u32 prod;
+
+ prod = info->page->out_prod;
+ /* caller ensures !xenfb_queue_full() */
+ mb(); /* ensure ring space available */
+ XENFB_OUT_RING_REF(info->page, prod) = *event;
+ wmb(); /* ensure ring contents visible */
+ info->page->out_prod = prod + 1;
+
+ notify_remote_via_irq(info->irq);
+}
+
static void xenfb_do_update(struct xenfb_info *info,
int x, int y, int w, int h)
{
union xenfb_out_event event;
- u32 prod;
+ memset(&event, 0, sizeof(event));
event.type = XENFB_TYPE_UPDATE;
event.update.x = x;
event.update.y = y;
event.update.width = w;
event.update.height = h;
- prod = info->page->out_prod;
/* caller ensures !xenfb_queue_full() */
- mb(); /* ensure ring space available */
- XENFB_OUT_RING_REF(info->page, prod) = event;
- wmb(); /* ensure ring contents visible */
- info->page->out_prod = prod + 1;
+ xenfb_send_event(info, &event);
+}
- notify_remote_via_irq(info->irq);
+static void xenfb_do_resize(struct xenfb_info *info)
+{
+ union xenfb_out_event event;
+
+ memset(&event, 0, sizeof(event));
+ event.resize = info->resize;
+
+ /* caller ensures !xenfb_queue_full() */
+ xenfb_send_event(info, &event);
}
static int xenfb_queue_full(struct xenfb_info *info)
@@ -84,12 +116,28 @@ static int xenfb_queue_full(struct xenfb_info *info)
return prod - cons == XENFB_OUT_RING_LEN;
}
+static void xenfb_handle_resize_dpy(struct xenfb_info *info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->resize_lock, flags);
+ if (info->resize_dpy) {
+ if (!xenfb_queue_full(info)) {
+ info->resize_dpy = 0;
+ xenfb_do_resize(info);
+ }
+ }
+ spin_unlock_irqrestore(&info->resize_lock, flags);
+}
+
static void xenfb_refresh(struct xenfb_info *info,
int x1, int y1, int w, int h)
{
unsigned long flags;
- int y2 = y1 + h - 1;
int x2 = x1 + w - 1;
+ int y2 = y1 + h - 1;
+
+ xenfb_handle_resize_dpy(info);
if (!info->update_wanted)
return;
@@ -222,6 +270,57 @@ static ssize_t xenfb_write(struct fb_info *p, const char __user *buf,
return res;
}
+static int
+xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct xenfb_info *xenfb_info;
+ int required_mem_len;
+
+ xenfb_info = info->par;
+
+ if (!xenfb_info->feature_resize) {
+ if (var->xres == video[KPARAM_WIDTH] &&
+ var->yres == video[KPARAM_HEIGHT] &&
+ var->bits_per_pixel == xenfb_info->page->depth) {
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ /* Can't resize past initial width and height */
+ if (var->xres > video[KPARAM_WIDTH] || var->yres > video[KPARAM_HEIGHT])
+ return -EINVAL;
+
+ required_mem_len = var->xres * var->yres * xenfb_info->page->depth / 8;
+ if (var->bits_per_pixel == xenfb_info->page->depth &&
+ var->xres <= info->fix.line_length / (XENFB_DEPTH / 8) &&
+ required_mem_len <= info->fix.smem_len) {
+ var->xres_virtual = var->xres;
+ var->yres_virtual = var->yres;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int xenfb_set_par(struct fb_info *info)
+{
+ struct xenfb_info *xenfb_info;
+ unsigned long flags;
+
+ xenfb_info = info->par;
+
+ spin_lock_irqsave(&xenfb_info->resize_lock, flags);
+ xenfb_info->resize.type = XENFB_TYPE_RESIZE;
+ xenfb_info->resize.width = info->var.xres;
+ xenfb_info->resize.height = info->var.yres;
+ xenfb_info->resize.stride = info->fix.line_length;
+ xenfb_info->resize.depth = info->var.bits_per_pixel;
+ xenfb_info->resize.offset = 0;
+ xenfb_info->resize_dpy = 1;
+ spin_unlock_irqrestore(&xenfb_info->resize_lock, flags);
+ return 0;
+}
+
static struct fb_ops xenfb_fb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
@@ -230,6 +329,8 @@ static struct fb_ops xenfb_fb_ops = {
.fb_fillrect = xenfb_fillrect,
.fb_copyarea = xenfb_copyarea,
.fb_imageblit = xenfb_imageblit,
+ .fb_check_var = xenfb_check_var,
+ .fb_set_par = xenfb_set_par,
};
static irqreturn_t xenfb_event_handler(int rq, void *dev_id)
@@ -258,6 +359,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
{
struct xenfb_info *info;
struct fb_info *fb_info;
+ int fb_size;
+ int val;
int ret;
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -265,18 +368,35 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
return -ENOMEM;
}
+
+ /* Limit kernel param videoram amount to what is in xenstore */
+ if (xenbus_scanf(XBT_NIL, dev->otherend, "videoram", "%d", &val) == 1) {
+ if (val < video[KPARAM_MEM])
+ video[KPARAM_MEM] = val;
+ }
+
+ /* If requested res does not fit in available memory, use default */
+ fb_size = video[KPARAM_MEM] * 1024 * 1024;
+ if (video[KPARAM_WIDTH] * video[KPARAM_HEIGHT] * XENFB_DEPTH / 8
+ > fb_size) {
+ video[KPARAM_WIDTH] = XENFB_WIDTH;
+ video[KPARAM_HEIGHT] = XENFB_HEIGHT;
+ fb_size = XENFB_DEFAULT_FB_LEN;
+ }
+
dev->dev.driver_data = info;
info->xbdev = dev;
info->irq = -1;
info->x1 = info->y1 = INT_MAX;
spin_lock_init(&info->dirty_lock);
+ spin_lock_init(&info->resize_lock);
- info->fb = vmalloc(xenfb_mem_len);
+ info->fb = vmalloc(fb_size);
if (info->fb == NULL)
goto error_nomem;
- memset(info->fb, 0, xenfb_mem_len);
+ memset(info->fb, 0, fb_size);
- info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
if (!info->mfns)
@@ -287,8 +407,6 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
if (!info->page)
goto error_nomem;
- xenfb_init_shared_page(info);
-
/* abusing framebuffer_alloc() to allocate pseudo_palette */
fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
if (fb_info == NULL)
@@ -301,9 +419,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
fb_info->screen_base = info->fb;
fb_info->fbops = &xenfb_fb_ops;
- fb_info->var.xres_virtual = fb_info->var.xres = info->page->width;
- fb_info->var.yres_virtual = fb_info->var.yres = info->page->height;
- fb_info->var.bits_per_pixel = info->page->depth;
+ fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH];
+ fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT];
+ fb_info->var.bits_per_pixel = XENFB_DEPTH;
fb_info->var.red = (struct fb_bitfield){16, 8, 0};
fb_info->var.green = (struct fb_bitfield){8, 8, 0};
@@ -315,9 +433,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
fb_info->var.vmode = FB_VMODE_NONINTERLACED;
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
- fb_info->fix.line_length = info->page->line_length;
+ fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8;
fb_info->fix.smem_start = 0;
- fb_info->fix.smem_len = xenfb_mem_len;
+ fb_info->fix.smem_len = fb_size;
strcpy(fb_info->fix.id, "xen");
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.accel = FB_ACCEL_NONE;
@@ -334,6 +452,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
fb_info->fbdefio = &xenfb_defio;
fb_deferred_io_init(fb_info);
+ xenfb_init_shared_page(info, fb_info);
+
ret = register_framebuffer(fb_info);
if (ret) {
fb_deferred_io_cleanup(fb_info);
@@ -348,6 +468,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
if (ret < 0)
goto error;
+ xenfb_make_preferred_console();
return 0;
error_nomem:
@@ -358,12 +479,34 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
return ret;
}
+static __devinit void
+xenfb_make_preferred_console(void)
+{
+ struct console *c;
+
+ if (console_set_on_cmdline)
+ return;
+
+ acquire_console_sem();
+ for (c = console_drivers; c; c = c->next) {
+ if (!strcmp(c->name, "tty") && c->index == 0)
+ break;
+ }
+ release_console_sem();
+ if (c) {
+ unregister_console(c);
+ c->flags |= CON_CONSDEV;
+ c->flags &= ~CON_PRINTBUFFER; /* don't print again */
+ register_console(c);
+ }
+}
+
static int xenfb_resume(struct xenbus_device *dev)
{
struct xenfb_info *info = dev->dev.driver_data;
xenfb_disconnect_backend(info);
- xenfb_init_shared_page(info);
+ xenfb_init_shared_page(info, info->fb_info);
return xenfb_connect_backend(dev, info);
}
@@ -391,20 +534,23 @@ static unsigned long vmalloc_to_mfn(void *address)
return pfn_to_mfn(vmalloc_to_pfn(address));
}
-static void xenfb_init_shared_page(struct xenfb_info *info)
+static void xenfb_init_shared_page(struct xenfb_info *info,
+ struct fb_info *fb_info)
{
int i;
+ int epd = PAGE_SIZE / sizeof(info->mfns[0]);
for (i = 0; i < info->nr_pages; i++)
info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
- info->page->pd[0] = vmalloc_to_mfn(info->mfns);
- info->page->pd[1] = 0;
- info->page->width = XENFB_WIDTH;
- info->page->height = XENFB_HEIGHT;
- info->page->depth = XENFB_DEPTH;
- info->page->line_length = (info->page->depth / 8) * info->page->width;
- info->page->mem_length = xenfb_mem_len;
+ for (i = 0; i * epd < info->nr_pages; i++)
+ info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]);
+
+ info->page->width = fb_info->var.xres;
+ info->page->height = fb_info->var.yres;
+ info->page->depth = fb_info->var.bits_per_pixel;
+ info->page->line_length = fb_info->fix.line_length;
+ info->page->mem_length = fb_info->fix.smem_len;
info->page->in_cons = info->page->in_prod = 0;
info->page->out_cons = info->page->out_prod = 0;
}
@@ -504,6 +650,11 @@ InitWait:
val = 0;
if (val)
info->update_wanted = 1;
+
+ if (xenbus_scanf(XBT_NIL, dev->otherend,
+ "feature-resize", "%d", &val) < 0)
+ val = 0;
+ info->feature_resize = val;
break;
case XenbusStateClosing:
@@ -547,4 +698,6 @@ static void __exit xenfb_cleanup(void)
module_init(xenfb_init);
module_exit(xenfb_cleanup);
+MODULE_DESCRIPTION("Xen virtual framebuffer device frontend");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vfb");
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 37af04f1ffd9..363286c54290 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -1,4 +1,4 @@
-obj-y += grant-table.o features.o events.o
+obj-y += grant-table.o features.o events.o manage.o
obj-y += xenbus/
obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
obj-$(CONFIG_XEN_BALLOON) += balloon.o
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index ab25ba6cbbb9..591bc29b55f5 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -225,7 +225,7 @@ static int increase_reservation(unsigned long nr_pages)
page = balloon_next_page(page);
}
- reservation.extent_start = (unsigned long)frame_list;
+ set_xen_guest_handle(reservation.extent_start, frame_list);
reservation.nr_extents = nr_pages;
rc = HYPERVISOR_memory_op(
XENMEM_populate_physmap, &reservation);
@@ -321,7 +321,7 @@ static int decrease_reservation(unsigned long nr_pages)
balloon_append(pfn_to_page(pfn));
}
- reservation.extent_start = (unsigned long)frame_list;
+ set_xen_guest_handle(reservation.extent_start, frame_list);
reservation.nr_extents = nr_pages;
ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
BUG_ON(ret != nr_pages);
@@ -368,7 +368,7 @@ static void balloon_process(struct work_struct *work)
}
/* Resets the Xen limit, sets new target, and kicks off processing. */
-void balloon_set_new_target(unsigned long target)
+static void balloon_set_new_target(unsigned long target)
{
/* No need for lock. Not read-modify-write updates. */
balloon_stats.hard_limit = ~0UL;
@@ -483,7 +483,7 @@ static int dealloc_pte_fn(
.extent_order = 0,
.domid = DOMID_SELF
};
- reservation.extent_start = (unsigned long)&mfn;
+ set_xen_guest_handle(reservation.extent_start, &mfn);
set_pte_at(&init_mm, addr, pte, __pte_ma(0ull));
set_phys_to_machine(__pa(addr) >> PAGE_SHIFT, INVALID_P2M_ENTRY);
ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
@@ -519,7 +519,7 @@ static struct page **alloc_empty_pages_and_pagevec(int nr_pages)
.extent_order = 0,
.domid = DOMID_SELF
};
- reservation.extent_start = (unsigned long)&gmfn;
+ set_xen_guest_handle(reservation.extent_start, &gmfn);
ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
&reservation);
if (ret == 1)
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 76e5b7386af9..332dd63750a0 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -355,7 +355,7 @@ static void unbind_from_irq(unsigned int irq)
spin_lock(&irq_mapping_update_lock);
- if (VALID_EVTCHN(evtchn) && (--irq_bindcount[irq] == 0)) {
+ if ((--irq_bindcount[irq] == 0) && VALID_EVTCHN(evtchn)) {
close.port = evtchn;
if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0)
BUG();
@@ -375,7 +375,7 @@ static void unbind_from_irq(unsigned int irq)
evtchn_to_irq[evtchn] = -1;
irq_info[irq] = IRQ_UNBOUND;
- dynamic_irq_init(irq);
+ dynamic_irq_cleanup(irq);
}
spin_unlock(&irq_mapping_update_lock);
@@ -557,6 +557,33 @@ out:
put_cpu();
}
+/* Rebind a new event channel to an existing irq. */
+void rebind_evtchn_irq(int evtchn, int irq)
+{
+ /* Make sure the irq is masked, since the new event channel
+ will also be masked. */
+ disable_irq(irq);
+
+ spin_lock(&irq_mapping_update_lock);
+
+ /* After resume the irq<->evtchn mappings are all cleared out */
+ BUG_ON(evtchn_to_irq[evtchn] != -1);
+ /* Expect irq to have been bound before,
+ so the bindcount should be non-0 */
+ BUG_ON(irq_bindcount[irq] == 0);
+
+ evtchn_to_irq[evtchn] = irq;
+ irq_info[irq] = mk_irq_info(IRQT_EVTCHN, 0, evtchn);
+
+ spin_unlock(&irq_mapping_update_lock);
+
+ /* new event channels are always bound to cpu 0 */
+ irq_set_affinity(irq, cpumask_of_cpu(0));
+
+ /* Unmask the event channel. */
+ enable_irq(irq);
+}
+
/* Rebind an evtchn so that it gets delivered to a specific cpu */
static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
{
@@ -647,6 +674,89 @@ static int retrigger_dynirq(unsigned int irq)
return ret;
}
+static void restore_cpu_virqs(unsigned int cpu)
+{
+ struct evtchn_bind_virq bind_virq;
+ int virq, irq, evtchn;
+
+ for (virq = 0; virq < NR_VIRQS; virq++) {
+ if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1)
+ continue;
+
+ BUG_ON(irq_info[irq].type != IRQT_VIRQ);
+ BUG_ON(irq_info[irq].index != virq);
+
+ /* Get a new binding from Xen. */
+ bind_virq.virq = virq;
+ bind_virq.vcpu = cpu;
+ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
+ &bind_virq) != 0)
+ BUG();
+ evtchn = bind_virq.port;
+
+ /* Record the new mapping. */
+ evtchn_to_irq[evtchn] = irq;
+ irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn);
+ bind_evtchn_to_cpu(evtchn, cpu);
+
+ /* Ready for use. */
+ unmask_evtchn(evtchn);
+ }
+}
+
+static void restore_cpu_ipis(unsigned int cpu)
+{
+ struct evtchn_bind_ipi bind_ipi;
+ int ipi, irq, evtchn;
+
+ for (ipi = 0; ipi < XEN_NR_IPIS; ipi++) {
+ if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1)
+ continue;
+
+ BUG_ON(irq_info[irq].type != IRQT_IPI);
+ BUG_ON(irq_info[irq].index != ipi);
+
+ /* Get a new binding from Xen. */
+ bind_ipi.vcpu = cpu;
+ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi,
+ &bind_ipi) != 0)
+ BUG();
+ evtchn = bind_ipi.port;
+
+ /* Record the new mapping. */
+ evtchn_to_irq[evtchn] = irq;
+ irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn);
+ bind_evtchn_to_cpu(evtchn, cpu);
+
+ /* Ready for use. */
+ unmask_evtchn(evtchn);
+
+ }
+}
+
+void xen_irq_resume(void)
+{
+ unsigned int cpu, irq, evtchn;
+
+ init_evtchn_cpu_bindings();
+
+ /* New event-channel space is not 'live' yet. */
+ for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
+ mask_evtchn(evtchn);
+
+ /* No IRQ <-> event-channel mappings. */
+ for (irq = 0; irq < NR_IRQS; irq++)
+ irq_info[irq].evtchn = 0; /* zap event-channel binding */
+
+ for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
+ evtchn_to_irq[evtchn] = -1;
+
+ for_each_possible_cpu(cpu) {
+ restore_cpu_virqs(cpu);
+ restore_cpu_ipis(cpu);
+ }
+}
+
static struct irq_chip xen_dynamic_chip __read_mostly = {
.name = "xen-dyn",
.mask = disable_dynirq,
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 52b6b41b909d..e9e11168616a 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -471,14 +471,14 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
return 0;
}
-static int gnttab_resume(void)
+int gnttab_resume(void)
{
if (max_nr_grant_frames() < nr_grant_frames)
return -ENOSYS;
return gnttab_map(0, nr_grant_frames - 1);
}
-static int gnttab_suspend(void)
+int gnttab_suspend(void)
{
arch_gnttab_unmap_shared(shared, nr_grant_frames);
return 0;
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
new file mode 100644
index 000000000000..5b546e365f00
--- /dev/null
+++ b/drivers/xen/manage.c
@@ -0,0 +1,252 @@
+/*
+ * Handle extern requests for shutdown, reboot and sysrq
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/reboot.h>
+#include <linux/sysrq.h>
+#include <linux/stop_machine.h>
+#include <linux/freezer.h>
+
+#include <xen/xenbus.h>
+#include <xen/grant_table.h>
+#include <xen/events.h>
+#include <xen/hvc-console.h>
+#include <xen/xen-ops.h>
+
+#include <asm/xen/hypercall.h>
+#include <asm/xen/page.h>
+
+enum shutdown_state {
+ SHUTDOWN_INVALID = -1,
+ SHUTDOWN_POWEROFF = 0,
+ SHUTDOWN_SUSPEND = 2,
+ /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
+ report a crash, not be instructed to crash!
+ HALT is the same as POWEROFF, as far as we're concerned. The tools use
+ the distinction when we return the reason code to them. */
+ SHUTDOWN_HALT = 4,
+};
+
+/* Ignore multiple shutdown requests. */
+static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
+
+#ifdef CONFIG_PM_SLEEP
+static int xen_suspend(void *data)
+{
+ int *cancelled = data;
+ int err;
+
+ BUG_ON(!irqs_disabled());
+
+ load_cr3(swapper_pg_dir);
+
+ err = device_power_down(PMSG_SUSPEND);
+ if (err) {
+ printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n",
+ err);
+ return err;
+ }
+
+ xen_mm_pin_all();
+ gnttab_suspend();
+ xen_pre_suspend();
+
+ /*
+ * This hypercall returns 1 if suspend was cancelled
+ * or the domain was merely checkpointed, and 0 if it
+ * is resuming in a new domain.
+ */
+ *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
+
+ xen_post_suspend(*cancelled);
+ gnttab_resume();
+ xen_mm_unpin_all();
+
+ device_power_up();
+
+ if (!*cancelled) {
+ xen_irq_resume();
+ xen_console_resume();
+ }
+
+ return 0;
+}
+
+static void do_suspend(void)
+{
+ int err;
+ int cancelled = 1;
+
+ shutting_down = SHUTDOWN_SUSPEND;
+
+#ifdef CONFIG_PREEMPT
+ /* If the kernel is preemptible, we need to freeze all the processes
+ to prevent them from being in the middle of a pagetable update
+ during suspend. */
+ err = freeze_processes();
+ if (err) {
+ printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
+ return;
+ }
+#endif
+
+ err = device_suspend(PMSG_SUSPEND);
+ if (err) {
+ printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
+ goto out;
+ }
+
+ printk("suspending xenbus...\n");
+ /* XXX use normal device tree? */
+ xenbus_suspend();
+
+ err = stop_machine_run(xen_suspend, &cancelled, 0);
+ if (err) {
+ printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
+ goto out;
+ }
+
+ if (!cancelled)
+ xenbus_resume();
+ else
+ xenbus_suspend_cancel();
+
+ device_resume();
+
+ /* Make sure timer events get retriggered on all CPUs */
+ clock_was_set();
+out:
+#ifdef CONFIG_PREEMPT
+ thaw_processes();
+#endif
+ shutting_down = SHUTDOWN_INVALID;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static void shutdown_handler(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ char *str;
+ struct xenbus_transaction xbt;
+ int err;
+
+ if (shutting_down != SHUTDOWN_INVALID)
+ return;
+
+ again:
+ err = xenbus_transaction_start(&xbt);
+ if (err)
+ return;
+
+ str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
+ /* Ignore read errors and empty reads. */
+ if (XENBUS_IS_ERR_READ(str)) {
+ xenbus_transaction_end(xbt, 1);
+ return;
+ }
+
+ xenbus_write(xbt, "control", "shutdown", "");
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err == -EAGAIN) {
+ kfree(str);
+ goto again;
+ }
+
+ if (strcmp(str, "poweroff") == 0 ||
+ strcmp(str, "halt") == 0) {
+ shutting_down = SHUTDOWN_POWEROFF;
+ orderly_poweroff(false);
+ } else if (strcmp(str, "reboot") == 0) {
+ shutting_down = SHUTDOWN_POWEROFF; /* ? */
+ ctrl_alt_del();
+#ifdef CONFIG_PM_SLEEP
+ } else if (strcmp(str, "suspend") == 0) {
+ do_suspend();
+#endif
+ } else {
+ printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
+ shutting_down = SHUTDOWN_INVALID;
+ }
+
+ kfree(str);
+}
+
+static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
+ unsigned int len)
+{
+ char sysrq_key = '\0';
+ struct xenbus_transaction xbt;
+ int err;
+
+ again:
+ err = xenbus_transaction_start(&xbt);
+ if (err)
+ return;
+ if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
+ printk(KERN_ERR "Unable to read sysrq code in "
+ "control/sysrq\n");
+ xenbus_transaction_end(xbt, 1);
+ return;
+ }
+
+ if (sysrq_key != '\0')
+ xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
+
+ err = xenbus_transaction_end(xbt, 0);
+ if (err == -EAGAIN)
+ goto again;
+
+ if (sysrq_key != '\0')
+ handle_sysrq(sysrq_key, NULL);
+}
+
+static struct xenbus_watch shutdown_watch = {
+ .node = "control/shutdown",
+ .callback = shutdown_handler
+};
+
+static struct xenbus_watch sysrq_watch = {
+ .node = "control/sysrq",
+ .callback = sysrq_handler
+};
+
+static int setup_shutdown_watcher(void)
+{
+ int err;
+
+ err = register_xenbus_watch(&shutdown_watch);
+ if (err) {
+ printk(KERN_ERR "Failed to set shutdown watcher\n");
+ return err;
+ }
+
+ err = register_xenbus_watch(&sysrq_watch);
+ if (err) {
+ printk(KERN_ERR "Failed to set sysrq watcher\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int shutdown_event(struct notifier_block *notifier,
+ unsigned long event,
+ void *data)
+{
+ setup_shutdown_watcher();
+ return NOTIFY_DONE;
+}
+
+static int __init setup_shutdown_event(void)
+{
+ static struct notifier_block xenstore_notifier = {
+ .notifier_call = shutdown_event
+ };
+ register_xenstore_notifier(&xenstore_notifier);
+
+ return 0;
+}
+
+subsys_initcall(setup_shutdown_event);
diff --git a/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c
index 6efbe3f29ca5..090c61ee8fd0 100644
--- a/drivers/xen/xenbus/xenbus_comms.c
+++ b/drivers/xen/xenbus/xenbus_comms.c
@@ -203,7 +203,6 @@ int xb_read(void *data, unsigned len)
int xb_init_comms(void)
{
struct xenstore_domain_interface *intf = xen_store_interface;
- int err;
if (intf->req_prod != intf->req_cons)
printk(KERN_ERR "XENBUS request ring is not quiescent "
@@ -216,18 +215,20 @@ int xb_init_comms(void)
intf->rsp_cons = intf->rsp_prod;
}
- if (xenbus_irq)
- unbind_from_irqhandler(xenbus_irq, &xb_waitq);
+ if (xenbus_irq) {
+ /* Already have an irq; assume we're resuming */
+ rebind_evtchn_irq(xen_store_evtchn, xenbus_irq);
+ } else {
+ int err;
+ err = bind_evtchn_to_irqhandler(xen_store_evtchn, wake_waiting,
+ 0, "xenbus", &xb_waitq);
+ if (err <= 0) {
+ printk(KERN_ERR "XENBUS request irq failed %i\n", err);
+ return err;
+ }
- err = bind_evtchn_to_irqhandler(
- xen_store_evtchn, wake_waiting,
- 0, "xenbus", &xb_waitq);
- if (err <= 0) {
- printk(KERN_ERR "XENBUS request irq failed %i\n", err);
- return err;
+ xenbus_irq = err;
}
- xenbus_irq = err;
-
return 0;
}
OpenPOWER on IntegriCloud