diff options
Diffstat (limited to 'drivers')
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(¬ify_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, ®); *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; } |