From ccc9c8b91c2631da2cab46a6fcd9c3106dcb9abb Mon Sep 17 00:00:00 2001 From: Balaji Rao Date: Tue, 27 Jan 2009 19:22:38 +0530 Subject: pcf50633_charger: Fix typo container_of(psy, struct pcf50633_mbc, usb); should be container_of(psy, struct pcf50633_mbc, adapter); Signed-off-by: Balaji Rao Cc: Andy Green Signed-off-by: Anton Vorontsov --- drivers/power/pcf50633-charger.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index e988ec130fcd..41aec2acbb91 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c @@ -199,7 +199,8 @@ static int adapter_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb); + struct pcf50633_mbc *mbc = container_of(psy, + struct pcf50633_mbc, adapter); int ret = 0; switch (psp) { -- cgit v1.2.1 From ac048e1734699dd98f4bdf4daf2b9592d4a4d38e Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 3 Feb 2009 19:05:12 +1000 Subject: i915: fix unneeded locking in i915 LVDS get modes code. This code is always called under the lock from the higher layers, so need to go locking it here. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/intel_lvds.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index b36a5214d8df..cf8da644faf2 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -311,10 +311,8 @@ static int intel_lvds_get_modes(struct drm_connector *connector) if (dev_priv->panel_fixed_mode != NULL) { struct drm_display_mode *mode; - mutex_lock(&dev->mode_config.mutex); mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); drm_mode_probed_add(connector, mode); - mutex_unlock(&dev->mode_config.mutex); return 1; } -- cgit v1.2.1 From 0cd5c3c80a0ebd68c08312fa7d8c13149cc61c4c Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Wed, 4 Feb 2009 14:29:19 -0800 Subject: x86: disable intel_iommu support by default Due to recurring issues with DMAR support on certain platforms. There's a number of filesystem corruption incidents reported: https://bugzilla.redhat.com/show_bug.cgi?id=479996 http://bugzilla.kernel.org/show_bug.cgi?id=12578 Provide a Kconfig option to change whether it is enabled by default. If disabled, it can still be reenabled by passing intel_iommu=on to the kernel. Keep the .config option off by default. Signed-off-by: Kyle McMartin Signed-off-by: Andrew Morton Acked-By: David Woodhouse Signed-off-by: Ingo Molnar --- drivers/pci/intel-iommu.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 3dfecb20d5e7..f4b7c79023ff 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -268,7 +268,12 @@ static long list_size; static void domain_remove_dev_info(struct dmar_domain *domain); -int dmar_disabled; +#ifdef CONFIG_DMAR_DEFAULT_ON +int dmar_disabled = 0; +#else +int dmar_disabled = 1; +#endif /*CONFIG_DMAR_DEFAULT_ON*/ + static int __initdata dmar_map_gfx = 1; static int dmar_forcedac; static int intel_iommu_strict; @@ -284,9 +289,12 @@ static int __init intel_iommu_setup(char *str) if (!str) return -EINVAL; while (*str) { - if (!strncmp(str, "off", 3)) { + if (!strncmp(str, "on", 2)) { + dmar_disabled = 0; + printk(KERN_INFO "Intel-IOMMU: enabled\n"); + } else if (!strncmp(str, "off", 3)) { dmar_disabled = 1; - printk(KERN_INFO"Intel-IOMMU: disabled\n"); + printk(KERN_INFO "Intel-IOMMU: disabled\n"); } else if (!strncmp(str, "igfx_off", 8)) { dmar_map_gfx = 0; printk(KERN_INFO -- cgit v1.2.1 From 1ca3abdb6a4b87246b00292f048acd344325fd12 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Fri, 23 Jan 2009 09:25:02 -0500 Subject: [CPUFREQ] Make ignore_nice_load setting of ondemand work as expected. ondemand micro-accounting of idle time changes broke ignore_nice_load sysfs setting due to a thinko in the code. The bug entry: http://bugzilla.kernel.org/show_bug.cgi?id=12310 Reported-by: Jim Bray Signed-off-by: Venkatesh Pallipadi Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_ondemand.c | 47 ++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 6a2b036c9389..6f45b1658a67 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -117,11 +117,7 @@ static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.irq); busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.softirq); busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.steal); - - if (!dbs_tuners_ins.ignore_nice) { - busy_time = cputime64_add(busy_time, - kstat_cpu(cpu).cpustat.nice); - } + busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.nice); idle_time = cputime64_sub(cur_wall_time, busy_time); if (wall) @@ -137,23 +133,6 @@ static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) if (idle_time == -1ULL) return get_cpu_idle_time_jiffy(cpu, wall); - if (dbs_tuners_ins.ignore_nice) { - cputime64_t cur_nice; - unsigned long cur_nice_jiffies; - struct cpu_dbs_info_s *dbs_info; - - dbs_info = &per_cpu(cpu_dbs_info, cpu); - cur_nice = cputime64_sub(kstat_cpu(cpu).cpustat.nice, - dbs_info->prev_cpu_nice); - /* - * Assumption: nice time between sampling periods will be - * less than 2^32 jiffies for 32 bit sys - */ - cur_nice_jiffies = (unsigned long) - cputime64_to_jiffies64(cur_nice); - dbs_info->prev_cpu_nice = kstat_cpu(cpu).cpustat.nice; - return idle_time + jiffies_to_usecs(cur_nice_jiffies); - } return idle_time; } @@ -319,6 +298,9 @@ static ssize_t store_ignore_nice_load(struct cpufreq_policy *policy, dbs_info = &per_cpu(cpu_dbs_info, j); dbs_info->prev_cpu_idle = get_cpu_idle_time(j, &dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) + dbs_info->prev_cpu_nice = kstat_cpu(j).cpustat.nice; + } mutex_unlock(&dbs_mutex); @@ -419,6 +401,23 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) j_dbs_info->prev_cpu_idle); j_dbs_info->prev_cpu_idle = cur_idle_time; + if (dbs_tuners_ins.ignore_nice) { + cputime64_t cur_nice; + unsigned long cur_nice_jiffies; + + cur_nice = cputime64_sub(kstat_cpu(j).cpustat.nice, + j_dbs_info->prev_cpu_nice); + /* + * Assumption: nice time between sampling periods will + * be less than 2^32 jiffies for 32 bit sys + */ + cur_nice_jiffies = (unsigned long) + cputime64_to_jiffies64(cur_nice); + + j_dbs_info->prev_cpu_nice = kstat_cpu(j).cpustat.nice; + idle_time += jiffies_to_usecs(cur_nice_jiffies); + } + if (unlikely(!wall_time || wall_time < idle_time)) continue; @@ -575,6 +574,10 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j, &j_dbs_info->prev_cpu_wall); + if (dbs_tuners_ins.ignore_nice) { + j_dbs_info->prev_cpu_nice = + kstat_cpu(j).cpustat.nice; + } } this_dbs_info->cpu = cpu; /* -- cgit v1.2.1 From 355423d0849f4506bc71ab2738d38cb74429aaef Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Fri, 6 Feb 2009 21:49:57 -0800 Subject: r8169: Don't update statistics counters when interface is down Some Realtek chips (RTL8169sb/8110sb in my case) are unable to retrieve ethtool statistics when the interface is down. The process stays in endless loop in rtl8169_get_ethtool_stats. This is because these chips need to have receiver enabled (CmdRxEnb bit in ChipCmd register) that is cleared when the interface is going down. It's better to update statistics only when the interface is up and otherwise return copy of statistics grabbed when the interface was up (in rtl8169_close). It is interesting that PCI-E NICs (like 8168b/8111b...) are not affected. Signed-off-by: Ivan Vecera Acked-by: Francois Romieu Signed-off-by: David S. Miller --- drivers/net/r8169.c | 93 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 2c73ca606b35..0771eb6fc6eb 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -437,6 +437,22 @@ enum features { RTL_FEATURE_GMII = (1 << 2), }; +struct rtl8169_counters { + __le64 tx_packets; + __le64 rx_packets; + __le64 tx_errors; + __le32 rx_errors; + __le16 rx_missed; + __le16 align_errors; + __le32 tx_one_collision; + __le32 tx_multi_collision; + __le64 rx_unicast; + __le64 rx_broadcast; + __le32 rx_multicast; + __le16 tx_aborted; + __le16 tx_underun; +}; + struct rtl8169_private { void __iomem *mmio_addr; /* memory map physical address */ struct pci_dev *pci_dev; /* Index of PCI device */ @@ -480,6 +496,7 @@ struct rtl8169_private { unsigned features; struct mii_if_info mii; + struct rtl8169_counters counters; }; MODULE_AUTHOR("Realtek and the Linux r8169 crew "); @@ -1100,22 +1117,6 @@ static const char rtl8169_gstrings[][ETH_GSTRING_LEN] = { "tx_underrun", }; -struct rtl8169_counters { - __le64 tx_packets; - __le64 rx_packets; - __le64 tx_errors; - __le32 rx_errors; - __le16 rx_missed; - __le16 align_errors; - __le32 tx_one_collision; - __le32 tx_multi_collision; - __le64 rx_unicast; - __le64 rx_broadcast; - __le32 rx_multicast; - __le16 tx_aborted; - __le16 tx_underun; -}; - static int rtl8169_get_sset_count(struct net_device *dev, int sset) { switch (sset) { @@ -1126,16 +1127,21 @@ static int rtl8169_get_sset_count(struct net_device *dev, int sset) } } -static void rtl8169_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) +static void rtl8169_update_counters(struct net_device *dev) { struct rtl8169_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; struct rtl8169_counters *counters; dma_addr_t paddr; u32 cmd; + int wait = 1000; - ASSERT_RTNL(); + /* + * Some chips are unable to dump tally counters when the receiver + * is disabled. + */ + if ((RTL_R8(ChipCmd) & CmdRxEnb) == 0) + return; counters = pci_alloc_consistent(tp->pci_dev, sizeof(*counters), &paddr); if (!counters) @@ -1146,31 +1152,45 @@ static void rtl8169_get_ethtool_stats(struct net_device *dev, RTL_W32(CounterAddrLow, cmd); RTL_W32(CounterAddrLow, cmd | CounterDump); - while (RTL_R32(CounterAddrLow) & CounterDump) { - if (msleep_interruptible(1)) + while (wait--) { + if ((RTL_R32(CounterAddrLow) & CounterDump) == 0) { + /* copy updated counters */ + memcpy(&tp->counters, counters, sizeof(*counters)); break; + } + udelay(10); } RTL_W32(CounterAddrLow, 0); RTL_W32(CounterAddrHigh, 0); - data[0] = le64_to_cpu(counters->tx_packets); - data[1] = le64_to_cpu(counters->rx_packets); - data[2] = le64_to_cpu(counters->tx_errors); - data[3] = le32_to_cpu(counters->rx_errors); - data[4] = le16_to_cpu(counters->rx_missed); - data[5] = le16_to_cpu(counters->align_errors); - data[6] = le32_to_cpu(counters->tx_one_collision); - data[7] = le32_to_cpu(counters->tx_multi_collision); - data[8] = le64_to_cpu(counters->rx_unicast); - data[9] = le64_to_cpu(counters->rx_broadcast); - data[10] = le32_to_cpu(counters->rx_multicast); - data[11] = le16_to_cpu(counters->tx_aborted); - data[12] = le16_to_cpu(counters->tx_underun); - pci_free_consistent(tp->pci_dev, sizeof(*counters), counters, paddr); } +static void rtl8169_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + ASSERT_RTNL(); + + rtl8169_update_counters(dev); + + data[0] = le64_to_cpu(tp->counters.tx_packets); + data[1] = le64_to_cpu(tp->counters.rx_packets); + data[2] = le64_to_cpu(tp->counters.tx_errors); + data[3] = le32_to_cpu(tp->counters.rx_errors); + data[4] = le16_to_cpu(tp->counters.rx_missed); + data[5] = le16_to_cpu(tp->counters.align_errors); + data[6] = le32_to_cpu(tp->counters.tx_one_collision); + data[7] = le32_to_cpu(tp->counters.tx_multi_collision); + data[8] = le64_to_cpu(tp->counters.rx_unicast); + data[9] = le64_to_cpu(tp->counters.rx_broadcast); + data[10] = le32_to_cpu(tp->counters.rx_multicast); + data[11] = le16_to_cpu(tp->counters.tx_aborted); + data[12] = le16_to_cpu(tp->counters.tx_underun); +} + static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data) { switch(stringset) { @@ -3682,6 +3702,9 @@ static int rtl8169_close(struct net_device *dev) struct rtl8169_private *tp = netdev_priv(dev); struct pci_dev *pdev = tp->pci_dev; + /* update counters before going down */ + rtl8169_update_counters(dev); + rtl8169_down(dev); free_irq(dev->irq, dev); -- cgit v1.2.1 From 71822faa3bc0af5dbf5e333a2d085f1ed7cd809f Mon Sep 17 00:00:00 2001 From: Ilkka Virta Date: Fri, 6 Feb 2009 22:00:36 -0800 Subject: sungem: Soft lockup in sungem on Netra AC200 when switching interface up From: Ilkka Virta In the lockup situation the driver seems to go off in an eternal storm of interrupts right after calling request_irq(). It doesn't actually do anything interesting in the interrupt handler. Since connecting the link afterwards works, something later in initialization must fix this. Looking at gem_do_start() and gem_open(), it seems that the only thing done while opening the device after the request_irq(), is a call to napi_enable(). I don't know what the ordering requirements are for the initialization, but I boldly tried to move the napi_enable() call inside gem_do_start() before the link state is checked and interrupts subsequently enabled, and it seems to work for me. Doesn't even break anything too obvious... Signed-off-by: David S. Miller --- drivers/net/sungem.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index b17efa9cc530..491876341068 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -2221,6 +2221,8 @@ static int gem_do_start(struct net_device *dev) gp->running = 1; + napi_enable(&gp->napi); + if (gp->lstate == link_up) { netif_carrier_on(gp->dev); gem_set_link_modes(gp); @@ -2238,6 +2240,8 @@ static int gem_do_start(struct net_device *dev) spin_lock_irqsave(&gp->lock, flags); spin_lock(&gp->tx_lock); + napi_disable(&gp->napi); + gp->running = 0; gem_reset(gp); gem_clean_rings(gp); @@ -2338,8 +2342,6 @@ static int gem_open(struct net_device *dev) if (!gp->asleep) rc = gem_do_start(dev); gp->opened = (rc == 0); - if (gp->opened) - napi_enable(&gp->napi); mutex_unlock(&gp->pm_mutex); @@ -2476,8 +2478,6 @@ static int gem_resume(struct pci_dev *pdev) /* Re-attach net device */ netif_device_attach(dev); - - napi_enable(&gp->napi); } spin_lock_irqsave(&gp->lock, flags); -- cgit v1.2.1 From 152abd139cca049c9b559a7cca762fa7fd9fd264 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Fri, 6 Feb 2009 22:04:08 -0800 Subject: 3c509: Fix resume from hibernation for PnP mode. From: Ondrej Zary last year, I posted a patch which fixed hibernation on 3c509 cards. That was back in 2.6.24. It worked fine in 2.6.25. But then I stopped using hibernation (as it did not work with my new IT8212 RAID controller). Now I fixed it and noticed that 3c509 does not wake up properly anymore (in 2.6.28) - neither in PnP nor in ISA modes. ifconfig down/up makes the card work again in PnP mode. However, in ISA mode, ifconfig up ends with "No such device" error. Comparing the 3c509 driver between 2.6.25 and 2.6.28, there's only some statistics-related change. So the cause of the problem must be somewhere else. This patch makes the resume work in PnP mode, but it's still not enough for ISA mode. Signed-off-by: David S. Miller --- drivers/net/3c509.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index 535c234286ea..8c694213035b 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -1475,6 +1475,7 @@ el3_resume(struct device *pdev) spin_lock_irqsave(&lp->lock, flags); outw(PowerUp, ioaddr + EL3_CMD); + EL3WINDOW(0); el3_up(dev); if (netif_running(dev)) -- cgit v1.2.1 From 0b492fce3d72d982a7981905f85484a1e1ba7fde Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 7 Feb 2009 02:20:25 -0800 Subject: sunhme: Don't match PCI devices in SBUS probe. Unfortunately, the OF device tree nodes for SBUS and PCI hme devices have the same device node name on some systems. So if the name of the parent node isn't 'sbus', skip it. Based upon an excellent report and detective work by Meelis Roos and Eric Brower. Signed-off-by: David S. Miller Tested-by: Meelis Roos --- drivers/net/sunhme.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index 7a72a3112f0a..cc4013be5e18 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -2629,6 +2629,14 @@ static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe) int i, qfe_slot = -1; int err = -ENODEV; + sbus_dp = to_of_device(op->dev.parent)->node; + if (is_qfe) + sbus_dp = to_of_device(op->dev.parent->parent)->node; + + /* We can match PCI devices too, do not accept those here. */ + if (strcmp(sbus_dp->name, "sbus")) + return err; + if (is_qfe) { qp = quattro_sbus_find(op); if (qp == NULL) @@ -2734,10 +2742,6 @@ static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe) if (qp != NULL) hp->happy_flags |= HFLAG_QUATTRO; - sbus_dp = to_of_device(op->dev.parent)->node; - if (is_qfe) - sbus_dp = to_of_device(op->dev.parent->parent)->node; - /* Get the supported DVMA burst sizes from our Happy SBUS. */ hp->happy_bursts = of_getintprop_default(sbus_dp, "burst-sizes", 0x00); -- cgit v1.2.1 From 9b8d5a124f133fe9a75397d20b874844a2e3d7e9 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Sat, 7 Feb 2009 11:15:41 +1000 Subject: drm/radeon: fix ioremap conflict with AGP mappings this solves a regression from http://bugzilla.kernel.org/show_bug.cgi?id=12441 Reported-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_memory.c | 7 ++++++- drivers/gpu/drm/radeon/radeon_cp.c | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c index 803bc9e7ce3c..bcc869bc4092 100644 --- a/drivers/gpu/drm/drm_memory.c +++ b/drivers/gpu/drm/drm_memory.c @@ -171,9 +171,14 @@ EXPORT_SYMBOL(drm_core_ioremap); void drm_core_ioremap_wc(struct drm_map *map, struct drm_device *dev) { - map->handle = ioremap_wc(map->offset, map->size); + if (drm_core_has_AGP(dev) && + dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP) + map->handle = agp_remap(map->offset, map->size, dev); + else + map->handle = ioremap_wc(map->offset, map->size); } EXPORT_SYMBOL(drm_core_ioremap_wc); + void drm_core_ioremapfree(struct drm_map *map, struct drm_device *dev) { if (!map->handle || !map->size) diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index 63212d7bbc28..df4cf97e5d97 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -1039,9 +1039,9 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init, #if __OS_HAS_AGP if (dev_priv->flags & RADEON_IS_AGP) { - drm_core_ioremap(dev_priv->cp_ring, dev); - drm_core_ioremap(dev_priv->ring_rptr, dev); - drm_core_ioremap(dev->agp_buffer_map, dev); + drm_core_ioremap_wc(dev_priv->cp_ring, dev); + drm_core_ioremap_wc(dev_priv->ring_rptr, dev); + drm_core_ioremap_wc(dev->agp_buffer_map, dev); if (!dev_priv->cp_ring->handle || !dev_priv->ring_rptr->handle || !dev->agp_buffer_map->handle) { -- cgit v1.2.1 From e806b4957412bf472d826bd8cc571da041248799 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 22 Jan 2009 09:56:58 -0800 Subject: drm/i915: Suppress GEM teardown on X Server exit in KMS mode. Fixes hangs when starting X for the second time. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index debad5c04cc0..a590d61ff692 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3273,6 +3273,9 @@ i915_gem_lastclose(struct drm_device *dev) { int ret; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + ret = i915_gem_idle(dev); if (ret) DRM_ERROR("failed to idle hardware: %d\n", ret); -- cgit v1.2.1 From 725e30ad6601d7fe443d9215d6331758a9d7e0c8 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 22 Jan 2009 13:01:02 -0800 Subject: drm/i915: Skip SDVO/HDMI init when the chipset tells us it's not present. This saves startup time from probing SDVO, and saves setting up HDMI outputs on G4X devices that don't have them. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/intel_display.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 31c3732b7a69..dce1abfa6496 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1452,6 +1452,7 @@ static int intel_connector_clones(struct drm_device *dev, int type_mask) static void intel_setup_outputs(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_connector *connector; intel_crt_init(dev); @@ -1463,13 +1464,16 @@ static void intel_setup_outputs(struct drm_device *dev) if (IS_I9XX(dev)) { int found; - found = intel_sdvo_init(dev, SDVOB); - if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) - intel_hdmi_init(dev, SDVOB); - - found = intel_sdvo_init(dev, SDVOC); - if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) - intel_hdmi_init(dev, SDVOC); + if (I915_READ(SDVOB) & SDVO_DETECTED) { + found = intel_sdvo_init(dev, SDVOB); + if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) + intel_hdmi_init(dev, SDVOB); + } + if (!IS_G4X(dev) || (I915_READ(SDVOB) & SDVO_DETECTED)) { + found = intel_sdvo_init(dev, SDVOC); + if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) + intel_hdmi_init(dev, SDVOC); + } } else intel_dvo_init(dev); -- cgit v1.2.1 From ab657db12d7020629f26f30d287558a8d0e32b41 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 23 Jan 2009 12:57:47 -0800 Subject: drm/i915: Set up an MTRR covering the GTT at driver load. We'd love to just be using PAT, but even on chips with PAT it gets disabled sometimes due to an errata. It would probably be better to have pat_enabled exported and only bother with this when !pat_enabled. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 29 ++++++++++++++++++++++++----- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 6 ------ 3 files changed, 25 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ee64b7301f67..1e01e7847155 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -966,10 +966,6 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto kfree_devname; - dev_priv->mm.gtt_mapping = - io_mapping_create_wc(dev->agp->base, - dev->agp->agp_info.aper_size * 1024*1024); - /* Allow hardware batchbuffers unless told otherwise. */ dev_priv->allow_batchbuffer = 1; @@ -1081,6 +1077,23 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto free_priv; } + dev_priv->mm.gtt_mapping = + io_mapping_create_wc(dev->agp->base, + dev->agp->agp_info.aper_size * 1024*1024); + /* Set up a WC MTRR for non-PAT systems. This is more common than + * one would think, because the kernel disables PAT on first + * generation Core chips because WC PAT gets overridden by a UC + * MTRR if present. Even if a UC MTRR isn't present. + */ + dev_priv->mm.gtt_mtrr = mtrr_add(dev->agp->base, + dev->agp->agp_info.aper_size * + 1024 * 1024, + MTRR_TYPE_WRCOMB, 1); + if (dev_priv->mm.gtt_mtrr < 0) { + DRM_INFO("MTRR allocation failed\n. Graphics " + "performance may suffer.\n"); + } + #ifdef CONFIG_HIGHMEM64G /* don't enable GEM on PAE - needs agp + set_memory_* interface fixes */ dev_priv->has_gem = 0; @@ -1145,8 +1158,14 @@ int i915_driver_unload(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + io_mapping_free(dev_priv->mm.gtt_mapping); + if (dev_priv->mm.gtt_mtrr >= 0) { + mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base, + dev->agp->agp_info.aper_size * 1024 * 1024); + dev_priv->mm.gtt_mtrr = -1; + } + if (drm_core_check_feature(dev, DRIVER_MODESET)) { - io_mapping_free(dev_priv->mm.gtt_mapping); drm_irq_uninstall(dev); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e13518252007..f471d218b89a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -284,6 +284,7 @@ typedef struct drm_i915_private { struct drm_mm gtt_space; struct io_mapping *gtt_mapping; + int gtt_mtrr; /** * List of objects currently involved in rendering from the diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a590d61ff692..af8034d52511 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3229,10 +3229,6 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, dev_priv->mm.wedged = 0; } - dev_priv->mm.gtt_mapping = io_mapping_create_wc(dev->agp->base, - dev->agp->agp_info.aper_size - * 1024 * 1024); - mutex_lock(&dev->struct_mutex); dev_priv->mm.suspended = 0; @@ -3255,7 +3251,6 @@ int i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - drm_i915_private_t *dev_priv = dev->dev_private; int ret; if (drm_core_check_feature(dev, DRIVER_MODESET)) @@ -3264,7 +3259,6 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data, ret = i915_gem_idle(dev); drm_irq_uninstall(dev); - io_mapping_free(dev_priv->mm.gtt_mapping); return ret; } -- cgit v1.2.1 From d9ddcb96e05cfbadf3dbf66859bcaf5eae25af0b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 27 Jan 2009 10:33:49 -0800 Subject: drm/i915: Return error from i915_gem_object_get_fence_reg() when failing. Previously, the caller would continue along without knowing that the function failed, resulting in potential mis-rendering. Right now vm_fault just returns SIGBUS in that case, and we may need to disable signal handling to avoid that happening. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index af8034d52511..e1f831f166ca 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -52,7 +52,7 @@ static void i915_gem_object_free_page_list(struct drm_gem_object *obj); static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment); -static void i915_gem_object_get_fence_reg(struct drm_gem_object *obj); +static int i915_gem_object_get_fence_reg(struct drm_gem_object *obj); static void i915_gem_clear_fence_reg(struct drm_gem_object *obj); static int i915_gem_evict_something(struct drm_device *dev); static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, @@ -585,8 +585,11 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /* Need a new fence register? */ if (obj_priv->fence_reg == I915_FENCE_REG_NONE && - obj_priv->tiling_mode != I915_TILING_NONE) - i915_gem_object_get_fence_reg(obj); + obj_priv->tiling_mode != I915_TILING_NONE) { + ret = i915_gem_object_get_fence_reg(obj); + if (ret != 0) + return VM_FAULT_SIGBUS; + } pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) + page_offset; @@ -1513,7 +1516,7 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) * It then sets up the reg based on the object's properties: address, pitch * and tiling format. */ -static void +static int i915_gem_object_get_fence_reg(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; @@ -1563,10 +1566,11 @@ try_again: * objects to finish before trying again. */ if (i == dev_priv->num_fence_regs) { - ret = i915_gem_object_wait_rendering(reg->obj); + ret = i915_gem_object_set_to_gtt_domain(reg->obj, 0); if (ret) { - WARN(ret, "wait_rendering failed: %d\n", ret); - return; + WARN(ret != -ERESTARTSYS, + "switch to GTT domain failed: %d\n", ret); + return ret; } goto try_again; } @@ -1591,6 +1595,8 @@ try_again: i915_write_fence_reg(reg); else i830_write_fence_reg(reg); + + return 0; } /** -- cgit v1.2.1 From 0f973f27888e4664b253ab2cf69c67c2eb80ab1b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 26 Jan 2009 17:10:45 -0800 Subject: drm/i915: add fence register management to execbuf Adds code to set up fence registers at execbuf time on pre-965 chips as necessary. Also fixes up a few bugs in the pre-965 tile register support (get_order != ffs). The number of fences available to the kernel defaults to the hw limit minus 3 (for legacy X front/back/depth), but a new parameter allows userspace to override that as needed. Signed-off-by: Jesse Barnes Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 10 ++++ drivers/gpu/drm/i915/i915_drv.h | 6 +++ drivers/gpu/drm/i915/i915_gem.c | 56 +++++++++++++++------- drivers/gpu/drm/i915/i915_gem_tiling.c | 88 +++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/i915_reg.h | 4 +- 5 files changed, 144 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 1e01e7847155..cc0adb428cee 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -731,6 +731,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_GEM: value = dev_priv->has_gem; break; + case I915_PARAM_NUM_FENCES_AVAIL: + value = dev_priv->num_fence_regs - dev_priv->fence_reg_start; + break; default: DRM_ERROR("Unknown parameter %d\n", param->param); return -EINVAL; @@ -764,6 +767,13 @@ static int i915_setparam(struct drm_device *dev, void *data, case I915_SETPARAM_ALLOW_BATCHBUFFER: dev_priv->allow_batchbuffer = param->value; break; + case I915_SETPARAM_NUM_USED_FENCES: + if (param->value > dev_priv->num_fence_regs || + param->value < 0) + return -EINVAL; + /* Userspace can use first N regs */ + dev_priv->fence_reg_start = param->value; + break; default: DRM_ERROR("unknown parameter %d\n", param->param); return -EINVAL; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f471d218b89a..a70bf77290fc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -602,6 +602,7 @@ int i915_gem_init_object(struct drm_gem_object *obj); void i915_gem_free_object(struct drm_gem_object *obj); int i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment); void i915_gem_object_unpin(struct drm_gem_object *obj); +int i915_gem_object_unbind(struct drm_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); uint32_t i915_get_gem_seqno(struct drm_device *dev); void i915_gem_retire_requests(struct drm_device *dev); @@ -785,6 +786,11 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev)) #define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev)) +/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte + * rows, which changed the alignment requirements and fence programming. + */ +#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \ + IS_I915GM(dev))) #define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev)) #define PRIMARY_RINGBUFFER_SIZE (128*1024) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e1f831f166ca..6a9e3a875083 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -52,7 +52,7 @@ static void i915_gem_object_free_page_list(struct drm_gem_object *obj); static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment); -static int i915_gem_object_get_fence_reg(struct drm_gem_object *obj); +static int i915_gem_object_get_fence_reg(struct drm_gem_object *obj, bool write); static void i915_gem_clear_fence_reg(struct drm_gem_object *obj); static int i915_gem_evict_something(struct drm_device *dev); static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj, @@ -567,6 +567,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) pgoff_t page_offset; unsigned long pfn; int ret = 0; + bool write = !!(vmf->flags & FAULT_FLAG_WRITE); /* We don't use vmf->pgoff since that has the fake offset */ page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> @@ -586,7 +587,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /* Need a new fence register? */ if (obj_priv->fence_reg == I915_FENCE_REG_NONE && obj_priv->tiling_mode != I915_TILING_NONE) { - ret = i915_gem_object_get_fence_reg(obj); + ret = i915_gem_object_get_fence_reg(obj, write); if (ret != 0) return VM_FAULT_SIGBUS; } @@ -1214,7 +1215,7 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) /** * Unbinds an object from the GTT aperture. */ -static int +int i915_gem_object_unbind(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; @@ -1448,21 +1449,26 @@ static void i915_write_fence_reg(struct drm_i915_fence_reg *reg) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj_priv = obj->driver_private; int regnum = obj_priv->fence_reg; + int tile_width; uint32_t val; uint32_t pitch_val; if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) || (obj_priv->gtt_offset & (obj->size - 1))) { - WARN(1, "%s: object not 1M or size aligned\n", __func__); + WARN(1, "%s: object 0x%08x not 1M or size (0x%x) aligned\n", + __func__, obj_priv->gtt_offset, obj->size); return; } - if (obj_priv->tiling_mode == I915_TILING_Y && (IS_I945G(dev) || - IS_I945GM(dev) || - IS_G33(dev))) - pitch_val = (obj_priv->stride / 128) - 1; + if (obj_priv->tiling_mode == I915_TILING_Y && + HAS_128_BYTE_Y_TILING(dev)) + tile_width = 128; else - pitch_val = (obj_priv->stride / 512) - 1; + tile_width = 512; + + /* Note: pitch better be a power of two tile widths */ + pitch_val = obj_priv->stride / tile_width; + pitch_val = ffs(pitch_val) - 1; val = obj_priv->gtt_offset; if (obj_priv->tiling_mode == I915_TILING_Y) @@ -1486,7 +1492,8 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) || (obj_priv->gtt_offset & (obj->size - 1))) { - WARN(1, "%s: object not 1M or size aligned\n", __func__); + WARN(1, "%s: object 0x%08x not 1M or size aligned\n", + __func__, obj_priv->gtt_offset); return; } @@ -1506,6 +1513,7 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) /** * i915_gem_object_get_fence_reg - set up a fence reg for an object * @obj: object to map through a fence reg + * @write: object is about to be written * * When mapping objects through the GTT, userspace wants to be able to write * to them without having to worry about swizzling if the object is tiled. @@ -1517,7 +1525,7 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) * and tiling format. */ static int -i915_gem_object_get_fence_reg(struct drm_gem_object *obj) +i915_gem_object_get_fence_reg(struct drm_gem_object *obj, bool write) { struct drm_device *dev = obj->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1530,12 +1538,18 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj) WARN(1, "allocating a fence for non-tiled object?\n"); break; case I915_TILING_X: - WARN(obj_priv->stride & (512 - 1), - "object is X tiled but has non-512B pitch\n"); + if (!obj_priv->stride) + return -EINVAL; + WARN((obj_priv->stride & (512 - 1)), + "object 0x%08x is X tiled but has non-512B pitch\n", + obj_priv->gtt_offset); break; case I915_TILING_Y: - WARN(obj_priv->stride & (128 - 1), - "object is Y tiled but has non-128B pitch\n"); + if (!obj_priv->stride) + return -EINVAL; + WARN((obj_priv->stride & (128 - 1)), + "object 0x%08x is Y tiled but has non-128B pitch\n", + obj_priv->gtt_offset); break; } @@ -1637,7 +1651,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) if (dev_priv->mm.suspended) return -EBUSY; if (alignment == 0) - alignment = PAGE_SIZE; + alignment = i915_gem_get_gtt_alignment(obj); if (alignment & (PAGE_SIZE - 1)) { DRM_ERROR("Invalid object alignment requested %u\n", alignment); return -EINVAL; @@ -2658,6 +2672,14 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) DRM_ERROR("Failure to bind: %d", ret); return ret; } + /* + * Pre-965 chips need a fence register set up in order to + * properly handle tiled surfaces. + */ + if (!IS_I965G(dev) && + obj_priv->fence_reg == I915_FENCE_REG_NONE && + obj_priv->tiling_mode != I915_TILING_NONE) + i915_gem_object_get_fence_reg(obj, true); } obj_priv->pin_count++; @@ -3297,7 +3319,7 @@ i915_gem_load(struct drm_device *dev) /* Old X drivers will take 0-2 for front, back, depth buffers */ dev_priv->fence_reg_start = 3; - if (IS_I965G(dev)) + if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) dev_priv->num_fence_regs = 16; else dev_priv->num_fence_regs = 8; diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 241f39b7f460..2534c792808e 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -173,6 +173,73 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) dev_priv->mm.bit_6_swizzle_y = swizzle_y; } + +/** + * Returns the size of the fence for a tiled object of the given size. + */ +static int +i915_get_fence_size(struct drm_device *dev, int size) +{ + int i; + int start; + + if (IS_I965G(dev)) { + /* The 965 can have fences at any page boundary. */ + return ALIGN(size, 4096); + } else { + /* Align the size to a power of two greater than the smallest + * fence size. + */ + if (IS_I9XX(dev)) + start = 1024 * 1024; + else + start = 512 * 1024; + + for (i = start; i < size; i <<= 1) + ; + + return i; + } +} + +/* Check pitch constriants for all chips & tiling formats */ +static bool +i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) +{ + int tile_width; + + /* Linear is always fine */ + if (tiling_mode == I915_TILING_NONE) + return true; + + if (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)) + tile_width = 128; + else + tile_width = 512; + + /* 965+ just needs multiples of tile width */ + if (IS_I965G(dev)) { + if (stride & (tile_width - 1)) + return false; + return true; + } + + /* Pre-965 needs power of two tile widths */ + if (stride < tile_width) + return false; + + if (stride & (stride - 1)) + return false; + + /* We don't handle the aperture area covered by the fence being bigger + * than the object size. + */ + if (i915_get_fence_size(dev, size) != size) + return false; + + return true; +} + /** * Sets the tiling mode of an object, returning the required swizzling of * bit 6 of addresses in the object. @@ -191,6 +258,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, return -EINVAL; obj_priv = obj->driver_private; + if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) + return -EINVAL; + mutex_lock(&dev->struct_mutex); if (args->tiling_mode == I915_TILING_NONE) { @@ -207,7 +277,23 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, args->swizzle_mode = I915_BIT_6_SWIZZLE_NONE; } } - obj_priv->tiling_mode = args->tiling_mode; + if (args->tiling_mode != obj_priv->tiling_mode) { + int ret; + + /* Unbind the object, as switching tiling means we're + * switching the cache organization due to fencing, probably. + */ + ret = i915_gem_object_unbind(obj); + if (ret != 0) { + WARN(ret != -ERESTARTSYS, + "failed to unbind object for tiling switch"); + args->tiling_mode = obj_priv->tiling_mode; + mutex_unlock(&dev->struct_mutex); + + return ret; + } + obj_priv->tiling_mode = args->tiling_mode; + } obj_priv->stride = args->stride; mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 273162579e1b..928e00462570 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -186,12 +186,12 @@ #define FENCE_REG_830_0 0x2000 #define I830_FENCE_START_MASK 0x07f80000 #define I830_FENCE_TILING_Y_SHIFT 12 -#define I830_FENCE_SIZE_BITS(size) ((get_order(size >> 19) - 1) << 8) +#define I830_FENCE_SIZE_BITS(size) ((ffs((size) >> 19) - 1) << 8) #define I830_FENCE_PITCH_SHIFT 4 #define I830_FENCE_REG_VALID (1<<0) #define I915_FENCE_START_MASK 0x0ff00000 -#define I915_FENCE_SIZE_BITS(size) ((get_order(size >> 20) - 1) << 8) +#define I915_FENCE_SIZE_BITS(size) ((ffs((size) >> 20) - 1) << 8) #define FENCE_REG_965_0 0x03000 #define I965_FENCE_PITCH_SHIFT 2 -- cgit v1.2.1 From 72daad40dc0be179e0dc85c17d5dc1e850b5e8e4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 30 Jan 2009 21:10:22 +0000 Subject: drm/i915: Unref the object after failing to set tiling mode. Cleanup the object reference on the error paths. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem_tiling.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 2534c792808e..fa1685cba840 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -258,8 +258,10 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, return -EINVAL; obj_priv = obj->driver_private; - if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) + if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) { + drm_gem_object_unreference(obj); return -EINVAL; + } mutex_lock(&dev->struct_mutex); @@ -289,6 +291,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, "failed to unbind object for tiling switch"); args->tiling_mode = obj_priv->tiling_mode; mutex_unlock(&dev->struct_mutex); + drm_gem_object_unreference(obj); return ret; } -- cgit v1.2.1 From e2f0ba97d60e59fe5c6237851933a9c38a8f9a24 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 2 Feb 2009 15:11:52 -0800 Subject: drm/i915: sync SDVO code with stable userland modesetting driver Pull in an update from the 2D driver (hopefully the last one, future work should be done here and pulled back into xf86-video-intel as needed). Signed-off-by: Jesse Barnes Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/intel_display.c | 2 + drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_sdvo.c | 870 +++++++++++++++++++++++++++++---- drivers/gpu/drm/i915/intel_sdvo_regs.h | 404 ++++++++++++++- 4 files changed, 1173 insertions(+), 104 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dce1abfa6496..bbdd72909a11 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -755,6 +755,8 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_SDVO: case INTEL_OUTPUT_HDMI: is_sdvo = true; + if (intel_output->needs_tv_clock) + is_tv = true; break; case INTEL_OUTPUT_DVO: is_dvo = true; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8a4cc50c5b4e..957daef8edff 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -82,6 +82,7 @@ struct intel_output { struct intel_i2c_chan *i2c_bus; /* for control functions */ struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */ bool load_detect_temp; + bool needs_tv_clock; void *dev_priv; }; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 407215469102..a30508b639ba 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -40,13 +40,59 @@ struct intel_sdvo_priv { struct intel_i2c_chan *i2c_bus; int slaveaddr; + + /* Register for the SDVO device: SDVOB or SDVOC */ int output_device; - u16 active_outputs; + /* Active outputs controlled by this SDVO output */ + uint16_t controlled_output; + /* + * Capabilities of the SDVO device returned by + * i830_sdvo_get_capabilities() + */ struct intel_sdvo_caps caps; + + /* Pixel clock limitations reported by the SDVO device, in kHz */ int pixel_clock_min, pixel_clock_max; + /** + * This is set if we're going to treat the device as TV-out. + * + * While we have these nice friendly flags for output types that ought + * to decide this for us, the S-Video output on our HDMI+S-Video card + * shows up as RGB1 (VGA). + */ + bool is_tv; + + /** + * This is set if we treat the device as HDMI, instead of DVI. + */ + bool is_hdmi; + + /** + * Returned SDTV resolutions allowed for the current format, if the + * device reported it. + */ + struct intel_sdvo_sdtv_resolution_reply sdtv_resolutions; + + /** + * Current selected TV format. + * + * This is stored in the same structure that's passed to the device, for + * convenience. + */ + struct intel_sdvo_tv_format tv_format; + + /* + * supported encoding mode, used to determine whether HDMI is + * supported + */ + struct intel_sdvo_encode encode; + + /* DDC bus used by this SDVO output */ + uint8_t ddc_bus; + int save_sdvo_mult; u16 save_active_outputs; struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; @@ -148,8 +194,8 @@ static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr, #define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} /** Mapping of command numbers to names, for debug output */ const static struct _sdvo_cmd_name { - u8 cmd; - char *name; + u8 cmd; + char *name; } sdvo_cmd_names[] = { SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), @@ -186,8 +232,35 @@ const static struct _sdvo_cmd_name { SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), - SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE), SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS), + /* HDMI op code */ + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA), }; #define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC") @@ -506,6 +579,50 @@ static bool intel_sdvo_set_output_timing(struct intel_output *intel_output, SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd); } +static bool +intel_sdvo_create_preferred_input_timing(struct intel_output *output, + uint16_t clock, + uint16_t width, + uint16_t height) +{ + struct intel_sdvo_preferred_input_timing_args args; + uint8_t status; + + args.clock = clock; + args.width = width; + args.height = height; + intel_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING, + &args, sizeof(args)); + status = intel_sdvo_read_response(output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return true; +} + +static bool intel_sdvo_get_preferred_input_timing(struct intel_output *output, + struct intel_sdvo_dtd *dtd) +{ + bool status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1, + NULL, 0); + + status = intel_sdvo_read_response(output, &dtd->part1, + sizeof(dtd->part1)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2, + NULL, 0); + + status = intel_sdvo_read_response(output, &dtd->part2, + sizeof(dtd->part2)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + return false; +} static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output) { @@ -536,36 +653,12 @@ static bool intel_sdvo_set_clock_rate_mult(struct intel_output *intel_output, u8 return true; } -static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO - * device will be told of the multiplier during mode_set. - */ - adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); - return true; -} - -static void intel_sdvo_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd, + struct drm_display_mode *mode) { - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = encoder->crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_output *intel_output = enc_to_intel_output(encoder); - struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; - u16 width, height; - u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len; - u16 h_sync_offset, v_sync_offset; - u32 sdvox; - struct intel_sdvo_dtd output_dtd; - int sdvo_pixel_multiply; - - if (!mode) - return; + uint16_t width, height; + uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len; + uint16_t h_sync_offset, v_sync_offset; width = mode->crtc_hdisplay; height = mode->crtc_vdisplay; @@ -580,93 +673,423 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; - output_dtd.part1.clock = mode->clock / 10; - output_dtd.part1.h_active = width & 0xff; - output_dtd.part1.h_blank = h_blank_len & 0xff; - output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) | + dtd->part1.clock = mode->clock / 10; + dtd->part1.h_active = width & 0xff; + dtd->part1.h_blank = h_blank_len & 0xff; + dtd->part1.h_high = (((width >> 8) & 0xf) << 4) | ((h_blank_len >> 8) & 0xf); - output_dtd.part1.v_active = height & 0xff; - output_dtd.part1.v_blank = v_blank_len & 0xff; - output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) | + dtd->part1.v_active = height & 0xff; + dtd->part1.v_blank = v_blank_len & 0xff; + dtd->part1.v_high = (((height >> 8) & 0xf) << 4) | ((v_blank_len >> 8) & 0xf); - output_dtd.part2.h_sync_off = h_sync_offset; - output_dtd.part2.h_sync_width = h_sync_len & 0xff; - output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | + dtd->part2.h_sync_off = h_sync_offset; + dtd->part2.h_sync_width = h_sync_len & 0xff; + dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | (v_sync_len & 0xf); - output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) | + dtd->part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) | ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) | ((v_sync_len & 0x30) >> 4); - output_dtd.part2.dtd_flags = 0x18; + dtd->part2.dtd_flags = 0x18; if (mode->flags & DRM_MODE_FLAG_PHSYNC) - output_dtd.part2.dtd_flags |= 0x2; + dtd->part2.dtd_flags |= 0x2; if (mode->flags & DRM_MODE_FLAG_PVSYNC) - output_dtd.part2.dtd_flags |= 0x4; + dtd->part2.dtd_flags |= 0x4; + + dtd->part2.sdvo_flags = 0; + dtd->part2.v_sync_off_high = v_sync_offset & 0xc0; + dtd->part2.reserved = 0; +} + +static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode, + struct intel_sdvo_dtd *dtd) +{ + uint16_t width, height; + uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len; + uint16_t h_sync_offset, v_sync_offset; + + width = mode->crtc_hdisplay; + height = mode->crtc_vdisplay; + + /* do some mode translations */ + h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; + h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + + v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; + v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + + h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; + v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; + + mode->hdisplay = dtd->part1.h_active; + mode->hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8; + mode->hsync_start = mode->hdisplay + dtd->part2.h_sync_off; + mode->hsync_start += (dtd->part2.sync_off_width_high & 0xa0) << 2; + mode->hsync_end = mode->hsync_start + dtd->part2.h_sync_width; + mode->hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4; + mode->htotal = mode->hdisplay + dtd->part1.h_blank; + mode->htotal += (dtd->part1.h_high & 0xf) << 8; + + mode->vdisplay = dtd->part1.v_active; + mode->vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8; + mode->vsync_start = mode->vdisplay; + mode->vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf; + mode->vsync_start += (dtd->part2.sync_off_width_high & 0x0a) << 2; + mode->vsync_start += dtd->part2.v_sync_off_high & 0xc0; + mode->vsync_end = mode->vsync_start + + (dtd->part2.v_sync_off_width & 0xf); + mode->vsync_end += (dtd->part2.sync_off_width_high & 0x3) << 4; + mode->vtotal = mode->vdisplay + dtd->part1.v_blank; + mode->vtotal += (dtd->part1.v_high & 0xf) << 8; + + mode->clock = dtd->part1.clock * 10; + + mode->flags &= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC); + if (dtd->part2.dtd_flags & 0x2) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + if (dtd->part2.dtd_flags & 0x4) + mode->flags |= DRM_MODE_FLAG_PVSYNC; +} + +static bool intel_sdvo_get_supp_encode(struct intel_output *output, + struct intel_sdvo_encode *encode) +{ + uint8_t status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_SUPP_ENCODE, NULL, 0); + status = intel_sdvo_read_response(output, encode, sizeof(*encode)); + if (status != SDVO_CMD_STATUS_SUCCESS) { /* non-support means DVI */ + memset(encode, 0, sizeof(*encode)); + return false; + } + + return true; +} + +static bool intel_sdvo_set_encode(struct intel_output *output, uint8_t mode) +{ + uint8_t status; + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_ENCODE, &mode, 1); + status = intel_sdvo_read_response(output, NULL, 0); + + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_colorimetry(struct intel_output *output, + uint8_t mode) +{ + uint8_t status; + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_COLORIMETRY, &mode, 1); + status = intel_sdvo_read_response(output, NULL, 0); + + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +#if 0 +static void intel_sdvo_dump_hdmi_buf(struct intel_output *output) +{ + int i, j; + uint8_t set_buf_index[2]; + uint8_t av_split; + uint8_t buf_size; + uint8_t buf[48]; + uint8_t *pos; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_AV_SPLIT, NULL, 0); + intel_sdvo_read_response(output, &av_split, 1); + + for (i = 0; i <= av_split; i++) { + set_buf_index[0] = i; set_buf_index[1] = 0; + intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_INDEX, + set_buf_index, 2); + intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_INFO, NULL, 0); + intel_sdvo_read_response(output, &buf_size, 1); + + pos = buf; + for (j = 0; j <= buf_size; j += 8) { + intel_sdvo_write_cmd(output, SDVO_CMD_GET_HBUF_DATA, + NULL, 0); + intel_sdvo_read_response(output, pos, 8); + pos += 8; + } + } +} +#endif + +static void intel_sdvo_set_hdmi_buf(struct intel_output *output, int index, + uint8_t *data, int8_t size, uint8_t tx_rate) +{ + uint8_t set_buf_index[2]; + + set_buf_index[0] = index; + set_buf_index[1] = 0; + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_INDEX, set_buf_index, 2); + + for (; size > 0; size -= 8) { + intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_DATA, data, 8); + data += 8; + } + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_HBUF_TXRATE, &tx_rate, 1); +} + +static uint8_t intel_sdvo_calc_hbuf_csum(uint8_t *data, uint8_t size) +{ + uint8_t csum = 0; + int i; + + for (i = 0; i < size; i++) + csum += data[i]; + + return 0x100 - csum; +} + +#define DIP_TYPE_AVI 0x82 +#define DIP_VERSION_AVI 0x2 +#define DIP_LEN_AVI 13 + +struct dip_infoframe { + uint8_t type; + uint8_t version; + uint8_t len; + uint8_t checksum; + union { + struct { + /* Packet Byte #1 */ + uint8_t S:2; + uint8_t B:2; + uint8_t A:1; + uint8_t Y:2; + uint8_t rsvd1:1; + /* Packet Byte #2 */ + uint8_t R:4; + uint8_t M:2; + uint8_t C:2; + /* Packet Byte #3 */ + uint8_t SC:2; + uint8_t Q:2; + uint8_t EC:3; + uint8_t ITC:1; + /* Packet Byte #4 */ + uint8_t VIC:7; + uint8_t rsvd2:1; + /* Packet Byte #5 */ + uint8_t PR:4; + uint8_t rsvd3:4; + /* Packet Byte #6~13 */ + uint16_t top_bar_end; + uint16_t bottom_bar_start; + uint16_t left_bar_end; + uint16_t right_bar_start; + } avi; + struct { + /* Packet Byte #1 */ + uint8_t channel_count:3; + uint8_t rsvd1:1; + uint8_t coding_type:4; + /* Packet Byte #2 */ + uint8_t sample_size:2; /* SS0, SS1 */ + uint8_t sample_frequency:3; + uint8_t rsvd2:3; + /* Packet Byte #3 */ + uint8_t coding_type_private:5; + uint8_t rsvd3:3; + /* Packet Byte #4 */ + uint8_t channel_allocation; + /* Packet Byte #5 */ + uint8_t rsvd4:3; + uint8_t level_shift:4; + uint8_t downmix_inhibit:1; + } audio; + uint8_t payload[28]; + } __attribute__ ((packed)) u; +} __attribute__((packed)); + +static void intel_sdvo_set_avi_infoframe(struct intel_output *output, + struct drm_display_mode * mode) +{ + struct dip_infoframe avi_if = { + .type = DIP_TYPE_AVI, + .version = DIP_VERSION_AVI, + .len = DIP_LEN_AVI, + }; + + avi_if.checksum = intel_sdvo_calc_hbuf_csum((uint8_t *)&avi_if, + 4 + avi_if.len); + intel_sdvo_set_hdmi_buf(output, 1, (uint8_t *)&avi_if, 4 + avi_if.len, + SDVO_HBUF_TX_VSYNC); +} + +static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct intel_output *output = enc_to_intel_output(encoder); + struct intel_sdvo_priv *dev_priv = output->dev_priv; - output_dtd.part2.sdvo_flags = 0; - output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0; - output_dtd.part2.reserved = 0; + if (!dev_priv->is_tv) { + /* Make the CRTC code factor in the SDVO pixel multiplier. The + * SDVO device will be told of the multiplier during mode_set. + */ + adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); + } else { + struct intel_sdvo_dtd output_dtd; + bool success; + + /* We need to construct preferred input timings based on our + * output timings. To do that, we have to set the output + * timings, even though this isn't really the right place in + * the sequence to do it. Oh well. + */ + + + /* Set output timings */ + intel_sdvo_get_dtd_from_mode(&output_dtd, mode); + intel_sdvo_set_target_output(output, + dev_priv->controlled_output); + intel_sdvo_set_output_timing(output, &output_dtd); + + /* Set the input timing to the screen. Assume always input 0. */ + intel_sdvo_set_target_input(output, true, false); + + + success = intel_sdvo_create_preferred_input_timing(output, + mode->clock / 10, + mode->hdisplay, + mode->vdisplay); + if (success) { + struct intel_sdvo_dtd input_dtd; - /* Set the output timing to the screen */ - intel_sdvo_set_target_output(intel_output, sdvo_priv->active_outputs); - intel_sdvo_set_output_timing(intel_output, &output_dtd); + intel_sdvo_get_preferred_input_timing(output, + &input_dtd); + intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd); + + } else { + return false; + } + } + return true; +} + +static void intel_sdvo_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_output *output = enc_to_intel_output(encoder); + struct intel_sdvo_priv *sdvo_priv = output->dev_priv; + u32 sdvox = 0; + int sdvo_pixel_multiply; + struct intel_sdvo_in_out_map in_out; + struct intel_sdvo_dtd input_dtd; + u8 status; + + if (!mode) + return; + + /* First, set the input mapping for the first input to our controlled + * output. This is only correct if we're a single-input device, in + * which case the first input is the output from the appropriate SDVO + * channel on the motherboard. In a two-input device, the first input + * will be SDVOB and the second SDVOC. + */ + in_out.in0 = sdvo_priv->controlled_output; + in_out.in1 = 0; + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_IN_OUT_MAP, + &in_out, sizeof(in_out)); + status = intel_sdvo_read_response(output, NULL, 0); + + if (sdvo_priv->is_hdmi) { + intel_sdvo_set_avi_infoframe(output, mode); + sdvox |= SDVO_AUDIO_ENABLE; + } + + intel_sdvo_get_dtd_from_mode(&input_dtd, mode); + + /* If it's a TV, we already set the output timing in mode_fixup. + * Otherwise, the output timing is equal to the input timing. + */ + if (!sdvo_priv->is_tv) { + /* Set the output timing to the screen */ + intel_sdvo_set_target_output(output, + sdvo_priv->controlled_output); + intel_sdvo_set_output_timing(output, &input_dtd); + } /* Set the input timing to the screen. Assume always input 0. */ - intel_sdvo_set_target_input(intel_output, true, false); + intel_sdvo_set_target_input(output, true, false); - /* We would like to use i830_sdvo_create_preferred_input_timing() to + /* We would like to use intel_sdvo_create_preferred_input_timing() to * provide the device with a timing it can support, if it supports that * feature. However, presumably we would need to adjust the CRTC to * output the preferred timing, and we don't support that currently. */ - intel_sdvo_set_input_timing(intel_output, &output_dtd); +#if 0 + success = intel_sdvo_create_preferred_input_timing(output, clock, + width, height); + if (success) { + struct intel_sdvo_dtd *input_dtd; + + intel_sdvo_get_preferred_input_timing(output, &input_dtd); + intel_sdvo_set_input_timing(output, &input_dtd); + } +#else + intel_sdvo_set_input_timing(output, &input_dtd); +#endif switch (intel_sdvo_get_pixel_multiplier(mode)) { case 1: - intel_sdvo_set_clock_rate_mult(intel_output, + intel_sdvo_set_clock_rate_mult(output, SDVO_CLOCK_RATE_MULT_1X); break; case 2: - intel_sdvo_set_clock_rate_mult(intel_output, + intel_sdvo_set_clock_rate_mult(output, SDVO_CLOCK_RATE_MULT_2X); break; case 4: - intel_sdvo_set_clock_rate_mult(intel_output, + intel_sdvo_set_clock_rate_mult(output, SDVO_CLOCK_RATE_MULT_4X); break; } /* Set the SDVO control regs. */ - if (0/*IS_I965GM(dev)*/) { - sdvox = SDVO_BORDER_ENABLE; - } else { - sdvox = I915_READ(sdvo_priv->output_device); - switch (sdvo_priv->output_device) { - case SDVOB: - sdvox &= SDVOB_PRESERVE_MASK; - break; - case SDVOC: - sdvox &= SDVOC_PRESERVE_MASK; - break; - } - sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; - } + if (IS_I965G(dev)) { + sdvox |= SDVO_BORDER_ENABLE | + SDVO_VSYNC_ACTIVE_HIGH | + SDVO_HSYNC_ACTIVE_HIGH; + } else { + sdvox |= I915_READ(sdvo_priv->output_device); + switch (sdvo_priv->output_device) { + case SDVOB: + sdvox &= SDVOB_PRESERVE_MASK; + break; + case SDVOC: + sdvox &= SDVOC_PRESERVE_MASK; + break; + } + sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; + } if (intel_crtc->pipe == 1) sdvox |= SDVO_PIPE_B_SELECT; sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode); if (IS_I965G(dev)) { - /* done in crtc_mode_set as the dpll_md reg must be written - early */ - } else if (IS_I945G(dev) || IS_I945GM(dev)) { - /* done in crtc_mode_set as it lives inside the - dpll register */ + /* done in crtc_mode_set as the dpll_md reg must be written early */ + } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { + /* done in crtc_mode_set as it lives inside the dpll register */ } else { sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; } - intel_sdvo_write_sdvox(intel_output, sdvox); + intel_sdvo_write_sdvox(output, sdvox); } static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) @@ -714,7 +1137,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) if (0) intel_sdvo_set_encoder_power_state(intel_output, mode); - intel_sdvo_set_active_outputs(intel_output, sdvo_priv->active_outputs); + intel_sdvo_set_active_outputs(intel_output, sdvo_priv->controlled_output); } return; } @@ -752,6 +1175,9 @@ static void intel_sdvo_save(struct drm_connector *connector) &sdvo_priv->save_output_dtd[o]); } } + if (sdvo_priv->is_tv) { + /* XXX: Save TV format/enhancements. */ + } sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device); } @@ -759,7 +1185,6 @@ static void intel_sdvo_save(struct drm_connector *connector) static void intel_sdvo_restore(struct drm_connector *connector) { struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_output *intel_output = to_intel_output(connector); struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; int o; @@ -790,7 +1215,11 @@ static void intel_sdvo_restore(struct drm_connector *connector) intel_sdvo_set_clock_rate_mult(intel_output, sdvo_priv->save_sdvo_mult); - I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX); + if (sdvo_priv->is_tv) { + /* XXX: Restore TV format/enhancements. */ + } + + intel_sdvo_write_sdvox(intel_output, sdvo_priv->save_SDVOX); if (sdvo_priv->save_SDVOX & SDVO_ENABLE) { @@ -916,20 +1345,173 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect status = intel_sdvo_read_response(intel_output, &response, 2); DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]); + + if (status != SDVO_CMD_STATUS_SUCCESS) + return connector_status_unknown; + if ((response[0] != 0) || (response[1] != 0)) return connector_status_connected; else return connector_status_disconnected; } -static int intel_sdvo_get_modes(struct drm_connector *connector) +static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) { struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; /* set the bus switch and get the modes */ - intel_sdvo_set_control_bus_switch(intel_output, SDVO_CONTROL_BUS_DDC2); + intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus); intel_ddc_get_modes(intel_output); +#if 0 + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + /* Mac mini hack. On this device, I get DDC through the analog, which + * load-detects as disconnected. I fail to DDC through the SDVO DDC, + * but it does load-detect as connected. So, just steal the DDC bits + * from analog when we fail at finding it the right way. + */ + crt = xf86_config->output[0]; + intel_output = crt->driver_private; + if (intel_output->type == I830_OUTPUT_ANALOG && + crt->funcs->detect(crt) == XF86OutputStatusDisconnected) { + I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOA, "CRTDDC_A"); + edid_mon = xf86OutputGetEDID(crt, intel_output->pDDCBus); + xf86DestroyI2CBusRec(intel_output->pDDCBus, true, true); + } + if (edid_mon) { + xf86OutputSetEDID(output, edid_mon); + modes = xf86OutputGetEDIDModes(output); + } +#endif +} + +/** + * This function checks the current TV format, and chooses a default if + * it hasn't been set. + */ +static void +intel_sdvo_check_tv_format(struct intel_output *output) +{ + struct intel_sdvo_priv *dev_priv = output->dev_priv; + struct intel_sdvo_tv_format format, unset; + uint8_t status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_TV_FORMAT, NULL, 0); + status = intel_sdvo_read_response(output, &format, sizeof(format)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return; + + memset(&unset, 0, sizeof(unset)); + if (memcmp(&format, &unset, sizeof(format))) { + DRM_DEBUG("%s: Choosing default TV format of NTSC-M\n", + SDVO_NAME(dev_priv)); + + format.ntsc_m = true; + intel_sdvo_write_cmd(output, SDVO_CMD_SET_TV_FORMAT, NULL, 0); + status = intel_sdvo_read_response(output, NULL, 0); + } +} + +/* + * Set of SDVO TV modes. + * Note! This is in reply order (see loop in get_tv_modes). + * XXX: all 60Hz refresh? + */ +struct drm_display_mode sdvo_tv_modes[] = { + { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815680, 321, 384, 416, + 200, 0, 232, 201, 233, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 6814080, 321, 384, 416, + 240, 0, 272, 241, 273, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 9910080, 401, 464, 496, + 300, 0, 332, 301, 333, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 16913280, 641, 704, 736, + 350, 0, 382, 351, 383, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121280, 641, 704, 736, + 400, 0, 432, 401, 433, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121280, 641, 704, 736, + 400, 0, 432, 401, 433, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("704x480", DRM_MODE_TYPE_DRIVER, 24624000, 705, 768, 800, + 480, 0, 512, 481, 513, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("704x576", DRM_MODE_TYPE_DRIVER, 29232000, 705, 768, 800, + 576, 0, 608, 577, 609, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("720x350", DRM_MODE_TYPE_DRIVER, 18751680, 721, 784, 816, + 350, 0, 382, 351, 383, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 21199680, 721, 784, 816, + 400, 0, 432, 401, 433, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25116480, 721, 784, 816, + 480, 0, 512, 481, 513, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("720x540", DRM_MODE_TYPE_DRIVER, 28054080, 721, 784, 816, + 540, 0, 572, 541, 573, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 29816640, 721, 784, 816, + 576, 0, 608, 577, 609, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("768x576", DRM_MODE_TYPE_DRIVER, 31570560, 769, 832, 864, + 576, 0, 608, 577, 609, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 34030080, 801, 864, 896, + 600, 0, 632, 601, 633, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 36581760, 833, 896, 928, + 624, 0, 656, 625, 657, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("920x766", DRM_MODE_TYPE_DRIVER, 48707040, 921, 984, 1016, + 766, 0, 798, 767, 799, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 53827200, 1025, 1088, 1120, + 768, 0, 800, 769, 801, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 87265920, 1281, 1344, 1376, + 1024, 0, 1056, 1025, 1057, 4196112, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, +}; + +static void intel_sdvo_get_tv_modes(struct drm_connector *connector) +{ + struct intel_output *output = to_intel_output(connector); + uint32_t reply = 0; + uint8_t status; + int i = 0; + + intel_sdvo_check_tv_format(output); + + /* Read the list of supported input resolutions for the selected TV + * format. + */ + intel_sdvo_write_cmd(output, SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT, + NULL, 0); + status = intel_sdvo_read_response(output, &reply, 3); + if (status != SDVO_CMD_STATUS_SUCCESS) + return; + + for (i = 0; i < ARRAY_SIZE(sdvo_tv_modes); i++) + if (reply & (1 << i)) + drm_mode_probed_add(connector, &sdvo_tv_modes[i]); +} + +static int intel_sdvo_get_modes(struct drm_connector *connector) +{ + struct intel_output *output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = output->dev_priv; + + if (sdvo_priv->is_tv) + intel_sdvo_get_tv_modes(connector); + else + intel_sdvo_get_ddc_modes(connector); + if (list_empty(&connector->probed_modes)) return 0; return 1; @@ -978,6 +1560,65 @@ static const struct drm_encoder_funcs intel_sdvo_enc_funcs = { }; +/** + * Choose the appropriate DDC bus for control bus switch command for this + * SDVO output based on the controlled output. + * + * DDC bus number assignment is in a priority order of RGB outputs, then TMDS + * outputs, then LVDS outputs. + */ +static void +intel_sdvo_select_ddc_bus(struct intel_sdvo_priv *dev_priv) +{ + uint16_t mask = 0; + unsigned int num_bits; + + /* Make a mask of outputs less than or equal to our own priority in the + * list. + */ + switch (dev_priv->controlled_output) { + case SDVO_OUTPUT_LVDS1: + mask |= SDVO_OUTPUT_LVDS1; + case SDVO_OUTPUT_LVDS0: + mask |= SDVO_OUTPUT_LVDS0; + case SDVO_OUTPUT_TMDS1: + mask |= SDVO_OUTPUT_TMDS1; + case SDVO_OUTPUT_TMDS0: + mask |= SDVO_OUTPUT_TMDS0; + case SDVO_OUTPUT_RGB1: + mask |= SDVO_OUTPUT_RGB1; + case SDVO_OUTPUT_RGB0: + mask |= SDVO_OUTPUT_RGB0; + break; + } + + /* Count bits to find what number we are in the priority list. */ + mask &= dev_priv->caps.output_flags; + num_bits = hweight16(mask); + if (num_bits > 3) { + /* if more than 3 outputs, default to DDC bus 3 for now */ + num_bits = 3; + } + + /* Corresponds to SDVO_CONTROL_BUS_DDCx */ + dev_priv->ddc_bus = 1 << num_bits; +} + +static bool +intel_sdvo_get_digital_encoding_mode(struct intel_output *output) +{ + struct intel_sdvo_priv *sdvo_priv = output->dev_priv; + uint8_t status; + + intel_sdvo_set_target_output(output, sdvo_priv->controlled_output); + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_ENCODE, NULL, 0); + status = intel_sdvo_read_response(output, &sdvo_priv->is_hdmi, 1); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + return true; +} + bool intel_sdvo_init(struct drm_device *dev, int output_device) { struct drm_connector *connector; @@ -1040,45 +1681,76 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps); - memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs)); + if (sdvo_priv->caps.output_flags & + (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)) { + if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) + sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS0; + else + sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1; + + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_TMDS; + connector_type = DRM_MODE_CONNECTOR_DVID; - /* TODO, CVBS, SVID, YPRPB & SCART outputs. */ - if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) + if (intel_sdvo_get_supp_encode(intel_output, + &sdvo_priv->encode) && + intel_sdvo_get_digital_encoding_mode(intel_output) && + sdvo_priv->is_hdmi) { + /* enable hdmi encoding mode if supported */ + intel_sdvo_set_encode(intel_output, SDVO_ENCODE_HDMI); + intel_sdvo_set_colorimetry(intel_output, + SDVO_COLORIMETRY_RGB256); + connector_type = DRM_MODE_CONNECTOR_HDMIA; + } + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_SVID0) { - sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0; + sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0; + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + encoder_type = DRM_MODE_ENCODER_TVDAC; + connector_type = DRM_MODE_CONNECTOR_SVIDEO; + sdvo_priv->is_tv = true; + intel_output->needs_tv_clock = true; + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) + { + sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0; connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_DAC; connector_type = DRM_MODE_CONNECTOR_VGA; } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) { - sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1; + sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1; connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_DAC; connector_type = DRM_MODE_CONNECTOR_VGA; } - else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS0) { - sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0; + sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0; connector->display_info.subpixel_order = SubPixelHorizontalRGB; - encoder_type = DRM_MODE_ENCODER_TMDS; - connector_type = DRM_MODE_CONNECTOR_DVID; + encoder_type = DRM_MODE_ENCODER_LVDS; + connector_type = DRM_MODE_CONNECTOR_LVDS; } - else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1) { - sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1; + sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1; connector->display_info.subpixel_order = SubPixelHorizontalRGB; - encoder_type = DRM_MODE_ENCODER_TMDS; - connector_type = DRM_MODE_CONNECTOR_DVID; + encoder_type = DRM_MODE_ENCODER_LVDS; + connector_type = DRM_MODE_CONNECTOR_LVDS; } else { unsigned char bytes[2]; + sdvo_priv->controlled_output = 0; memcpy (bytes, &sdvo_priv->caps.output_flags, 2); - DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n", + DRM_DEBUG("%s: Unknown SDVO output type (0x%02x%02x)\n", SDVO_NAME(sdvo_priv), bytes[0], bytes[1]); + encoder_type = DRM_MODE_ENCODER_NONE; + connector_type = DRM_MODE_CONNECTOR_Unknown; goto err_i2c; } @@ -1089,6 +1761,8 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); drm_sysfs_connector_add(connector); + intel_sdvo_select_ddc_bus(sdvo_priv); + /* Set the input timing to the screen. Assume always input 0. */ intel_sdvo_set_target_input(intel_output, true, false); diff --git a/drivers/gpu/drm/i915/intel_sdvo_regs.h b/drivers/gpu/drm/i915/intel_sdvo_regs.h index 861a43f8693c..1117b9c151a6 100644 --- a/drivers/gpu/drm/i915/intel_sdvo_regs.h +++ b/drivers/gpu/drm/i915/intel_sdvo_regs.h @@ -173,6 +173,9 @@ struct intel_sdvo_get_trained_inputs_response { * Returns two struct intel_sdvo_output_flags structures. */ #define SDVO_CMD_GET_IN_OUT_MAP 0x06 +struct intel_sdvo_in_out_map { + u16 in0, in1; +}; /** * Sets the current mapping of SDVO inputs to outputs on the device. @@ -206,7 +209,8 @@ struct intel_sdvo_get_trained_inputs_response { struct intel_sdvo_get_interrupt_event_source_response { u16 interrupt_status; unsigned int ambient_light_interrupt:1; - unsigned int pad:7; + unsigned int hdmi_audio_encrypt_change:1; + unsigned int pad:6; } __attribute__((packed)); /** @@ -305,23 +309,411 @@ struct intel_sdvo_set_target_input_args { # define SDVO_CLOCK_RATE_MULT_4X (1 << 3) #define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27 +/** 5 bytes of bit flags for TV formats shared by all TV format functions */ +struct intel_sdvo_tv_format { + unsigned int ntsc_m:1; + unsigned int ntsc_j:1; + unsigned int ntsc_443:1; + unsigned int pal_b:1; + unsigned int pal_d:1; + unsigned int pal_g:1; + unsigned int pal_h:1; + unsigned int pal_i:1; + + unsigned int pal_m:1; + unsigned int pal_n:1; + unsigned int pal_nc:1; + unsigned int pal_60:1; + unsigned int secam_b:1; + unsigned int secam_d:1; + unsigned int secam_g:1; + unsigned int secam_k:1; + + unsigned int secam_k1:1; + unsigned int secam_l:1; + unsigned int secam_60:1; + unsigned int hdtv_std_smpte_240m_1080i_59:1; + unsigned int hdtv_std_smpte_240m_1080i_60:1; + unsigned int hdtv_std_smpte_260m_1080i_59:1; + unsigned int hdtv_std_smpte_260m_1080i_60:1; + unsigned int hdtv_std_smpte_274m_1080i_50:1; + + unsigned int hdtv_std_smpte_274m_1080i_59:1; + unsigned int hdtv_std_smpte_274m_1080i_60:1; + unsigned int hdtv_std_smpte_274m_1080p_23:1; + unsigned int hdtv_std_smpte_274m_1080p_24:1; + unsigned int hdtv_std_smpte_274m_1080p_25:1; + unsigned int hdtv_std_smpte_274m_1080p_29:1; + unsigned int hdtv_std_smpte_274m_1080p_30:1; + unsigned int hdtv_std_smpte_274m_1080p_50:1; + + unsigned int hdtv_std_smpte_274m_1080p_59:1; + unsigned int hdtv_std_smpte_274m_1080p_60:1; + unsigned int hdtv_std_smpte_295m_1080i_50:1; + unsigned int hdtv_std_smpte_295m_1080p_50:1; + unsigned int hdtv_std_smpte_296m_720p_59:1; + unsigned int hdtv_std_smpte_296m_720p_60:1; + unsigned int hdtv_std_smpte_296m_720p_50:1; + unsigned int hdtv_std_smpte_293m_480p_59:1; + + unsigned int hdtv_std_smpte_170m_480i_59:1; + unsigned int hdtv_std_iturbt601_576i_50:1; + unsigned int hdtv_std_iturbt601_576p_50:1; + unsigned int hdtv_std_eia_7702a_480i_60:1; + unsigned int hdtv_std_eia_7702a_480p_60:1; + unsigned int pad:3; +} __attribute__((packed)); #define SDVO_CMD_GET_TV_FORMAT 0x28 #define SDVO_CMD_SET_TV_FORMAT 0x29 +/** Returns the resolutiosn that can be used with the given TV format */ +#define SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT 0x83 +struct intel_sdvo_sdtv_resolution_request { + unsigned int ntsc_m:1; + unsigned int ntsc_j:1; + unsigned int ntsc_443:1; + unsigned int pal_b:1; + unsigned int pal_d:1; + unsigned int pal_g:1; + unsigned int pal_h:1; + unsigned int pal_i:1; + + unsigned int pal_m:1; + unsigned int pal_n:1; + unsigned int pal_nc:1; + unsigned int pal_60:1; + unsigned int secam_b:1; + unsigned int secam_d:1; + unsigned int secam_g:1; + unsigned int secam_k:1; + + unsigned int secam_k1:1; + unsigned int secam_l:1; + unsigned int secam_60:1; + unsigned int pad:5; +} __attribute__((packed)); + +struct intel_sdvo_sdtv_resolution_reply { + unsigned int res_320x200:1; + unsigned int res_320x240:1; + unsigned int res_400x300:1; + unsigned int res_640x350:1; + unsigned int res_640x400:1; + unsigned int res_640x480:1; + unsigned int res_704x480:1; + unsigned int res_704x576:1; + + unsigned int res_720x350:1; + unsigned int res_720x400:1; + unsigned int res_720x480:1; + unsigned int res_720x540:1; + unsigned int res_720x576:1; + unsigned int res_768x576:1; + unsigned int res_800x600:1; + unsigned int res_832x624:1; + + unsigned int res_920x766:1; + unsigned int res_1024x768:1; + unsigned int res_1280x1024:1; + unsigned int pad:5; +} __attribute__((packed)); + +/* Get supported resolution with squire pixel aspect ratio that can be + scaled for the requested HDTV format */ +#define SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT 0x85 + +struct intel_sdvo_hdtv_resolution_request { + unsigned int hdtv_std_smpte_240m_1080i_59:1; + unsigned int hdtv_std_smpte_240m_1080i_60:1; + unsigned int hdtv_std_smpte_260m_1080i_59:1; + unsigned int hdtv_std_smpte_260m_1080i_60:1; + unsigned int hdtv_std_smpte_274m_1080i_50:1; + unsigned int hdtv_std_smpte_274m_1080i_59:1; + unsigned int hdtv_std_smpte_274m_1080i_60:1; + unsigned int hdtv_std_smpte_274m_1080p_23:1; + + unsigned int hdtv_std_smpte_274m_1080p_24:1; + unsigned int hdtv_std_smpte_274m_1080p_25:1; + unsigned int hdtv_std_smpte_274m_1080p_29:1; + unsigned int hdtv_std_smpte_274m_1080p_30:1; + unsigned int hdtv_std_smpte_274m_1080p_50:1; + unsigned int hdtv_std_smpte_274m_1080p_59:1; + unsigned int hdtv_std_smpte_274m_1080p_60:1; + unsigned int hdtv_std_smpte_295m_1080i_50:1; + + unsigned int hdtv_std_smpte_295m_1080p_50:1; + unsigned int hdtv_std_smpte_296m_720p_59:1; + unsigned int hdtv_std_smpte_296m_720p_60:1; + unsigned int hdtv_std_smpte_296m_720p_50:1; + unsigned int hdtv_std_smpte_293m_480p_59:1; + unsigned int hdtv_std_smpte_170m_480i_59:1; + unsigned int hdtv_std_iturbt601_576i_50:1; + unsigned int hdtv_std_iturbt601_576p_50:1; + + unsigned int hdtv_std_eia_7702a_480i_60:1; + unsigned int hdtv_std_eia_7702a_480p_60:1; + unsigned int pad:6; +} __attribute__((packed)); + +struct intel_sdvo_hdtv_resolution_reply { + unsigned int res_640x480:1; + unsigned int res_800x600:1; + unsigned int res_1024x768:1; + unsigned int res_1280x960:1; + unsigned int res_1400x1050:1; + unsigned int res_1600x1200:1; + unsigned int res_1920x1440:1; + unsigned int res_2048x1536:1; + + unsigned int res_2560x1920:1; + unsigned int res_3200x2400:1; + unsigned int res_3840x2880:1; + unsigned int pad1:5; + + unsigned int res_848x480:1; + unsigned int res_1064x600:1; + unsigned int res_1280x720:1; + unsigned int res_1360x768:1; + unsigned int res_1704x960:1; + unsigned int res_1864x1050:1; + unsigned int res_1920x1080:1; + unsigned int res_2128x1200:1; + + unsigned int res_2560x1400:1; + unsigned int res_2728x1536:1; + unsigned int res_3408x1920:1; + unsigned int res_4264x2400:1; + unsigned int res_5120x2880:1; + unsigned int pad2:3; + + unsigned int res_768x480:1; + unsigned int res_960x600:1; + unsigned int res_1152x720:1; + unsigned int res_1124x768:1; + unsigned int res_1536x960:1; + unsigned int res_1680x1050:1; + unsigned int res_1728x1080:1; + unsigned int res_1920x1200:1; + + unsigned int res_2304x1440:1; + unsigned int res_2456x1536:1; + unsigned int res_3072x1920:1; + unsigned int res_3840x2400:1; + unsigned int res_4608x2880:1; + unsigned int pad3:3; + + unsigned int res_1280x1024:1; + unsigned int pad4:7; + + unsigned int res_1280x768:1; + unsigned int pad5:7; +} __attribute__((packed)); + +/* Get supported power state returns info for encoder and monitor, rely on + last SetTargetInput and SetTargetOutput calls */ #define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a +/* Get power state returns info for encoder and monitor, rely on last + SetTargetInput and SetTargetOutput calls */ +#define SDVO_CMD_GET_POWER_STATE 0x2b #define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b #define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c # define SDVO_ENCODER_STATE_ON (1 << 0) # define SDVO_ENCODER_STATE_STANDBY (1 << 1) # define SDVO_ENCODER_STATE_SUSPEND (1 << 2) # define SDVO_ENCODER_STATE_OFF (1 << 3) +# define SDVO_MONITOR_STATE_ON (1 << 4) +# define SDVO_MONITOR_STATE_STANDBY (1 << 5) +# define SDVO_MONITOR_STATE_SUSPEND (1 << 6) +# define SDVO_MONITOR_STATE_OFF (1 << 7) + +#define SDVO_CMD_GET_MAX_PANEL_POWER_SEQUENCING 0x2d +#define SDVO_CMD_GET_PANEL_POWER_SEQUENCING 0x2e +#define SDVO_CMD_SET_PANEL_POWER_SEQUENCING 0x2f +/** + * The panel power sequencing parameters are in units of milliseconds. + * The high fields are bits 8:9 of the 10-bit values. + */ +struct sdvo_panel_power_sequencing { + u8 t0; + u8 t1; + u8 t2; + u8 t3; + u8 t4; + + unsigned int t0_high:2; + unsigned int t1_high:2; + unsigned int t2_high:2; + unsigned int t3_high:2; + + unsigned int t4_high:2; + unsigned int pad:6; +} __attribute__((packed)); + +#define SDVO_CMD_GET_MAX_BACKLIGHT_LEVEL 0x30 +struct sdvo_max_backlight_reply { + u8 max_value; + u8 default_value; +} __attribute__((packed)); + +#define SDVO_CMD_GET_BACKLIGHT_LEVEL 0x31 +#define SDVO_CMD_SET_BACKLIGHT_LEVEL 0x32 + +#define SDVO_CMD_GET_AMBIENT_LIGHT 0x33 +struct sdvo_get_ambient_light_reply { + u16 trip_low; + u16 trip_high; + u16 value; +} __attribute__((packed)); +#define SDVO_CMD_SET_AMBIENT_LIGHT 0x34 +struct sdvo_set_ambient_light_reply { + u16 trip_low; + u16 trip_high; + unsigned int enable:1; + unsigned int pad:7; +} __attribute__((packed)); + +/* Set display power state */ +#define SDVO_CMD_SET_DISPLAY_POWER_STATE 0x7d +# define SDVO_DISPLAY_STATE_ON (1 << 0) +# define SDVO_DISPLAY_STATE_STANDBY (1 << 1) +# define SDVO_DISPLAY_STATE_SUSPEND (1 << 2) +# define SDVO_DISPLAY_STATE_OFF (1 << 3) + +#define SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS 0x84 +struct intel_sdvo_enhancements_reply { + unsigned int flicker_filter:1; + unsigned int flicker_filter_adaptive:1; + unsigned int flicker_filter_2d:1; + unsigned int saturation:1; + unsigned int hue:1; + unsigned int brightness:1; + unsigned int contrast:1; + unsigned int overscan_h:1; + + unsigned int overscan_v:1; + unsigned int position_h:1; + unsigned int position_v:1; + unsigned int sharpness:1; + unsigned int dot_crawl:1; + unsigned int dither:1; + unsigned int max_tv_chroma_filter:1; + unsigned int max_tv_luma_filter:1; +} __attribute__((packed)); + +/* Picture enhancement limits below are dependent on the current TV format, + * and thus need to be queried and set after it. + */ +#define SDVO_CMD_GET_MAX_FLICKER_FITER 0x4d +#define SDVO_CMD_GET_MAX_ADAPTIVE_FLICKER_FITER 0x7b +#define SDVO_CMD_GET_MAX_2D_FLICKER_FITER 0x52 +#define SDVO_CMD_GET_MAX_SATURATION 0x55 +#define SDVO_CMD_GET_MAX_HUE 0x58 +#define SDVO_CMD_GET_MAX_BRIGHTNESS 0x5b +#define SDVO_CMD_GET_MAX_CONTRAST 0x5e +#define SDVO_CMD_GET_MAX_OVERSCAN_H 0x61 +#define SDVO_CMD_GET_MAX_OVERSCAN_V 0x64 +#define SDVO_CMD_GET_MAX_POSITION_H 0x67 +#define SDVO_CMD_GET_MAX_POSITION_V 0x6a +#define SDVO_CMD_GET_MAX_SHARPNESS_V 0x6d +#define SDVO_CMD_GET_MAX_TV_CHROMA 0x74 +#define SDVO_CMD_GET_MAX_TV_LUMA 0x77 +struct intel_sdvo_enhancement_limits_reply { + u16 max_value; + u16 default_value; +} __attribute__((packed)); -#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93 +#define SDVO_CMD_GET_LVDS_PANEL_INFORMATION 0x7f +#define SDVO_CMD_SET_LVDS_PANEL_INFORMATION 0x80 +# define SDVO_LVDS_COLOR_DEPTH_18 (0 << 0) +# define SDVO_LVDS_COLOR_DEPTH_24 (1 << 0) +# define SDVO_LVDS_CONNECTOR_SPWG (0 << 2) +# define SDVO_LVDS_CONNECTOR_OPENLDI (1 << 2) +# define SDVO_LVDS_SINGLE_CHANNEL (0 << 4) +# define SDVO_LVDS_DUAL_CHANNEL (1 << 4) + +#define SDVO_CMD_GET_FLICKER_FILTER 0x4e +#define SDVO_CMD_SET_FLICKER_FILTER 0x4f +#define SDVO_CMD_GET_ADAPTIVE_FLICKER_FITER 0x50 +#define SDVO_CMD_SET_ADAPTIVE_FLICKER_FITER 0x51 +#define SDVO_CMD_GET_2D_FLICKER_FITER 0x53 +#define SDVO_CMD_SET_2D_FLICKER_FITER 0x54 +#define SDVO_CMD_GET_SATURATION 0x56 +#define SDVO_CMD_SET_SATURATION 0x57 +#define SDVO_CMD_GET_HUE 0x59 +#define SDVO_CMD_SET_HUE 0x5a +#define SDVO_CMD_GET_BRIGHTNESS 0x5c +#define SDVO_CMD_SET_BRIGHTNESS 0x5d +#define SDVO_CMD_GET_CONTRAST 0x5f +#define SDVO_CMD_SET_CONTRAST 0x60 +#define SDVO_CMD_GET_OVERSCAN_H 0x62 +#define SDVO_CMD_SET_OVERSCAN_H 0x63 +#define SDVO_CMD_GET_OVERSCAN_V 0x65 +#define SDVO_CMD_SET_OVERSCAN_V 0x66 +#define SDVO_CMD_GET_POSITION_H 0x68 +#define SDVO_CMD_SET_POSITION_H 0x69 +#define SDVO_CMD_GET_POSITION_V 0x6b +#define SDVO_CMD_SET_POSITION_V 0x6c +#define SDVO_CMD_GET_SHARPNESS 0x6e +#define SDVO_CMD_SET_SHARPNESS 0x6f +#define SDVO_CMD_GET_TV_CHROMA 0x75 +#define SDVO_CMD_SET_TV_CHROMA 0x76 +#define SDVO_CMD_GET_TV_LUMA 0x78 +#define SDVO_CMD_SET_TV_LUMA 0x79 +struct intel_sdvo_enhancements_arg { + u16 value; +}__attribute__((packed)); + +#define SDVO_CMD_GET_DOT_CRAWL 0x70 +#define SDVO_CMD_SET_DOT_CRAWL 0x71 +# define SDVO_DOT_CRAWL_ON (1 << 0) +# define SDVO_DOT_CRAWL_DEFAULT_ON (1 << 1) + +#define SDVO_CMD_GET_DITHER 0x72 +#define SDVO_CMD_SET_DITHER 0x73 +# define SDVO_DITHER_ON (1 << 0) +# define SDVO_DITHER_DEFAULT_ON (1 << 1) #define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a -# define SDVO_CONTROL_BUS_PROM 0x0 -# define SDVO_CONTROL_BUS_DDC1 0x1 -# define SDVO_CONTROL_BUS_DDC2 0x2 -# define SDVO_CONTROL_BUS_DDC3 0x3 +# define SDVO_CONTROL_BUS_PROM (1 << 0) +# define SDVO_CONTROL_BUS_DDC1 (1 << 1) +# define SDVO_CONTROL_BUS_DDC2 (1 << 2) +# define SDVO_CONTROL_BUS_DDC3 (1 << 3) + +/* HDMI op codes */ +#define SDVO_CMD_GET_SUPP_ENCODE 0x9d +#define SDVO_CMD_GET_ENCODE 0x9e +#define SDVO_CMD_SET_ENCODE 0x9f + #define SDVO_ENCODE_DVI 0x0 + #define SDVO_ENCODE_HDMI 0x1 +#define SDVO_CMD_SET_PIXEL_REPLI 0x8b +#define SDVO_CMD_GET_PIXEL_REPLI 0x8c +#define SDVO_CMD_GET_COLORIMETRY_CAP 0x8d +#define SDVO_CMD_SET_COLORIMETRY 0x8e + #define SDVO_COLORIMETRY_RGB256 0x0 + #define SDVO_COLORIMETRY_RGB220 0x1 + #define SDVO_COLORIMETRY_YCrCb422 0x3 + #define SDVO_COLORIMETRY_YCrCb444 0x4 +#define SDVO_CMD_GET_COLORIMETRY 0x8f +#define SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER 0x90 +#define SDVO_CMD_SET_AUDIO_STAT 0x91 +#define SDVO_CMD_GET_AUDIO_STAT 0x92 +#define SDVO_CMD_SET_HBUF_INDEX 0x93 +#define SDVO_CMD_GET_HBUF_INDEX 0x94 +#define SDVO_CMD_GET_HBUF_INFO 0x95 +#define SDVO_CMD_SET_HBUF_AV_SPLIT 0x96 +#define SDVO_CMD_GET_HBUF_AV_SPLIT 0x97 +#define SDVO_CMD_SET_HBUF_DATA 0x98 +#define SDVO_CMD_GET_HBUF_DATA 0x99 +#define SDVO_CMD_SET_HBUF_TXRATE 0x9a +#define SDVO_CMD_GET_HBUF_TXRATE 0x9b + #define SDVO_HBUF_TX_DISABLED (0 << 6) + #define SDVO_HBUF_TX_ONCE (2 << 6) + #define SDVO_HBUF_TX_VSYNC (3 << 6) +#define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c + +struct intel_sdvo_encode{ + u8 dvi_rev; + u8 hdmi_rev; +} __attribute__ ((packed)); -- cgit v1.2.1 From 565dcd4635f4f8c0ac4dee38a5625bc325799b1e Mon Sep 17 00:00:00 2001 From: Paul Collins Date: Wed, 4 Feb 2009 23:05:41 +1300 Subject: drm/i915: skip LVDS initialization on Apple Mac Mini The Apple Mac Mini falsely reports LVDS. Use DMI to check whether we are running on a Mac Mini, and skip LVDS initialization if that proves to be the case. Signed-off-by: Paul Collins Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/intel_lvds.c | 47 +++++++++++---------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index cf8da644faf2..6d4f91265354 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -27,6 +27,7 @@ * Jesse Barnes */ +#include #include #include "drmP.h" #include "drm.h" @@ -403,6 +404,16 @@ void intel_lvds_init(struct drm_device *dev) u32 lvds; int pipe; + /* Blacklist machines that we know falsely report LVDS. */ + /* FIXME: add a check for the Aopen Mini PC */ + + /* Apple Mac Mini Core Duo and Mac Mini Core 2 Duo */ + if(dmi_match(DMI_PRODUCT_NAME, "Macmini1,1") || + dmi_match(DMI_PRODUCT_NAME, "Macmini2,1")) { + DRM_DEBUG("Skipping LVDS initialization for Apple Mac Mini\n"); + return; + } + intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL); if (!intel_output) { return; @@ -456,7 +467,7 @@ void intel_lvds_init(struct drm_device *dev) dev_priv->panel_fixed_mode = drm_mode_duplicate(dev, scan); mutex_unlock(&dev->mode_config.mutex); - goto out; /* FIXME: check for quirks */ + goto out; } mutex_unlock(&dev->mode_config.mutex); } @@ -490,7 +501,7 @@ void intel_lvds_init(struct drm_device *dev) if (dev_priv->panel_fixed_mode) { dev_priv->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; - goto out; /* FIXME: check for quirks */ + goto out; } } @@ -498,38 +509,6 @@ void intel_lvds_init(struct drm_device *dev) if (!dev_priv->panel_fixed_mode) goto failed; - /* FIXME: detect aopen & mac mini type stuff automatically? */ - /* - * Blacklist machines with BIOSes that list an LVDS panel without - * actually having one. - */ - if (IS_I945GM(dev)) { - /* aopen mini pc */ - if (dev->pdev->subsystem_vendor == 0xa0a0) - goto failed; - - if ((dev->pdev->subsystem_vendor == 0x8086) && - (dev->pdev->subsystem_device == 0x7270)) { - /* It's a Mac Mini or Macbook Pro. - * - * Apple hardware is out to get us. The macbook pro - * has a real LVDS panel, but the mac mini does not, - * and they have the same device IDs. We'll - * distinguish by panel size, on the assumption - * that Apple isn't about to make any machines with an - * 800x600 display. - */ - - if (dev_priv->panel_fixed_mode != NULL && - dev_priv->panel_fixed_mode->hdisplay == 800 && - dev_priv->panel_fixed_mode->vdisplay == 600) { - DRM_DEBUG("Suspected Mac Mini, ignoring the LVDS\n"); - goto failed; - } - } - } - - out: drm_sysfs_connector_add(connector); return; -- cgit v1.2.1 From 122ee2a63bc49d21f402f6b6d2208306cdcc98c1 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 3 Feb 2009 12:10:21 -0800 Subject: drm/i915: Quiet the message on get/setparam ioctl with an unknown value. Getting an unknown get/setparam used to be more significant back when they didn't change much. However, now that we're in the git world we're using them instead of a monotonic version number to signal feature availability, so clients ask about unknown params on older kernels more often. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index cc0adb428cee..0ded483d9ac7 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -735,7 +735,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = dev_priv->num_fence_regs - dev_priv->fence_reg_start; break; default: - DRM_ERROR("Unknown parameter %d\n", param->param); + DRM_DEBUG("Unknown parameter %d\n", param->param); return -EINVAL; } @@ -775,7 +775,7 @@ static int i915_setparam(struct drm_device *dev, void *data, dev_priv->fence_reg_start = param->value; break; default: - DRM_ERROR("unknown parameter %d\n", param->param); + DRM_DEBUG("unknown parameter %d\n", param->param); return -EINVAL; } -- cgit v1.2.1 From 7d8d58b23fd01e60ed44d8d8c10b2df86e638faa Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 4 Feb 2009 14:15:10 +0000 Subject: drm/i915: Unlock mutex on i915_gem_fault() error path If we failed to allocate a new fence register we would return VM_FAULT_SIGBUS without relinquishing the lock. Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6a9e3a875083..1441831fa06e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -588,8 +588,10 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (obj_priv->fence_reg == I915_FENCE_REG_NONE && obj_priv->tiling_mode != I915_TILING_NONE) { ret = i915_gem_object_get_fence_reg(obj, write); - if (ret != 0) + if (ret) { + mutex_unlock(&dev->struct_mutex); return VM_FAULT_SIGBUS; + } } pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) + -- cgit v1.2.1 From 14d200c5e5bd19219d930bbb9a5a22758c8f5bec Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 6 Feb 2009 13:04:49 -0800 Subject: drm/i915: capture last_vblank count at IRQ uninstall time too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In dc1336ff4fe08ae7cfe8301bfd7f0b2cfd31d20a (set vblank enable flag correctly across IRQ uninstall), we made sure drivers that uninstall their interrupt handler set the vblank enabled flag correctly, so that when interrupts are re-enabled, vblank interrupts & counts work as expected. However I missed the last_vblank field: it needs to be updated as well, otherwise, at the next drm_update_vblank_count we'll end up comparing a current count to a stale one (the last one captured by the disable function), which may trigger the wraparound handling, leading to a jumpy counter and hangs in drm_wait_vblank. The jumpy counter can prevent the DRM_WAIT_ON from returning success if the difference between the current count and the requested count is greater than 2^23, leading to timeouts or hangs, if the ioctl is restarted in a loop (as is the case in libdrm < 2.4.4). Signed-off-by: Jesse Barnes Acked-by: Michel Dänzer Tested-by: Timo Aaltonen Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_irq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 69aa0ab28403..3795dbc0f50c 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -276,6 +276,7 @@ int drm_irq_uninstall(struct drm_device * dev) for (i = 0; i < dev->num_crtcs; i++) { DRM_WAKEUP(&dev->vbl_queue[i]); dev->vblank_enabled[i] = 0; + dev->last_vblank[i] = dev->driver->get_vblank_counter(dev, i); } spin_unlock_irqrestore(&dev->vbl_lock, irqflags); -- cgit v1.2.1 From 9880b7a527ffbb52f65c2de0a8d4eea86e24775e Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 6 Feb 2009 10:22:41 -0800 Subject: drm/i915: add get_vblank_counter function for GM45 As discussed in the long thread about vblank related timeouts, it turns out GM45 has different frame count registers than previous chips. This patch adds support for them, which prevents us from waiting on really stale sequence values in drm_wait_vblank (which rather than returning immediately ends up timing out or getting interrupted). Signed-off-by: Jesse Barnes Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 4 ++++ drivers/gpu/drm/i915/i915_drv.c | 1 - drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_irq.c | 13 +++++++++++++ drivers/gpu/drm/i915/i915_reg.h | 6 ++++++ 5 files changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0ded483d9ac7..81f1cff56fd5 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1112,6 +1112,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev_priv->has_gem = 1; #endif + dev->driver->get_vblank_counter = i915_get_vblank_counter; + if (IS_GM45(dev)) + dev->driver->get_vblank_counter = gm45_get_vblank_counter; + i915_gem_load(dev); /* Init HWS */ diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f8b3df0926c0..aac12ee31a46 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -112,7 +112,6 @@ static struct drm_driver driver = { .suspend = i915_suspend, .resume = i915_resume, .device_is_agp = i915_driver_device_is_agp, - .get_vblank_counter = i915_get_vblank_counter, .enable_vblank = i915_enable_vblank, .disable_vblank = i915_disable_vblank, .irq_preinstall = i915_driver_irq_preinstall, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a70bf77290fc..7325363164f8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -535,6 +535,7 @@ extern int i915_vblank_pipe_get(struct drm_device *dev, void *data, extern int i915_enable_vblank(struct drm_device *dev, int crtc); extern void i915_disable_vblank(struct drm_device *dev, int crtc); extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); +extern u32 gm45_get_vblank_counter(struct drm_device *dev, int crtc); extern int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv); extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6290219de6c8..548ff2c66431 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -174,6 +174,19 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe) return count; } +u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45; + + if (!i915_pipe_enabled(dev, pipe)) { + DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); + return 0; + } + + return I915_READ(reg); +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 928e00462570..9d6539a868b3 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1371,6 +1371,9 @@ #define PIPE_FRAME_LOW_SHIFT 24 #define PIPE_PIXEL_MASK 0x00ffffff #define PIPE_PIXEL_SHIFT 0 +/* GM45+ just has to be different */ +#define PIPEA_FRMCOUNT_GM45 0x70040 +#define PIPEA_FLIPCOUNT_GM45 0x70044 /* Cursor A & B regs */ #define CURACNTR 0x70080 @@ -1439,6 +1442,9 @@ #define PIPEBSTAT 0x71024 #define PIPEBFRAMEHIGH 0x71040 #define PIPEBFRAMEPIXEL 0x71044 +#define PIPEB_FRMCOUNT_GM45 0x71040 +#define PIPEB_FLIPCOUNT_GM45 0x71044 + /* Display B control */ #define DSPBCNTR 0x71180 -- cgit v1.2.1 From d2f59357700487a8b944f4f7777d1e97cf5ea2ed Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 5 Feb 2009 16:03:34 +0100 Subject: drm/i915: select framebuffer support automatically Migration helper. The i915 driver recently added a 'depends on FB' rule to its Kconfig entry - which silently turns off DRM_I915 if someone has a working config but no CONFIG_FB selected, and upgrades to the latest upstream kernel. Norbert Preining reported this problem: Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=12599 Subject : dri /dev node disappeared with 2.6.29-rc1 So change it to "select FB", which auto-selects framebuffer support. This way the driver keeps working, regardless of whether FB was enabled before or not. Kconfig select's of interactive options can be problematic to dependencies and can cause build breakages - but in this case it's safe because it's a leaf entry with no dependencies of its own. ( There is some minor circular dependency fallout as FB_I810 and FB_INTEL also used 'depends on FB' constructs - update those to "select FB" too. ) Reported-by: Norbert Preining Signed-off-by: Ingo Molnar Signed-off-by: Dave Airlie --- drivers/gpu/drm/Kconfig | 2 +- drivers/video/Kconfig | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 5130b72d593c..4be3acbaaf9a 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -70,7 +70,7 @@ config DRM_I915 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - depends on FB + select FB tristate "i915 driver" help Choose this option if you have a system that has Intel 830M, 845G, diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index f0267706cb45..bf0af660df8a 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1054,9 +1054,10 @@ config FB_RIVA_BACKLIGHT config FB_I810 tristate "Intel 810/815 support (EXPERIMENTAL)" - depends on FB && EXPERIMENTAL && PCI && X86_32 + depends on EXPERIMENTAL && PCI && X86_32 select AGP select AGP_INTEL + select FB select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1119,7 +1120,8 @@ config FB_CARILLO_RANCH config FB_INTEL tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G/945GM/965G/965GM support (EXPERIMENTAL)" - depends on FB && EXPERIMENTAL && PCI && X86 + depends on EXPERIMENTAL && PCI && X86 + select FB select AGP select AGP_INTEL select FB_MODE_HELPERS -- cgit v1.2.1 From bc111d570ba87cff48ec8dfa15a2a598e59c0f4b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 8 Feb 2009 17:00:02 -0800 Subject: drivers/atm: introduce missing kfree Error handling code following a kmalloc should free the allocated data. The semantic match that finds the problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; statement S; expression E; identifier f,l; position p1,p2; expression *ptr != NULL; @@ ( if ((x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...)) == NULL) S | x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S ) <... when != x when != if (...) { <+...x...+> } x->f = E ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/atm/solos-pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 72fc0f799a64..89d7a6e94c9c 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -685,6 +685,7 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) out_release_regions: pci_release_regions(dev); out: + kfree(card); return err; } -- cgit v1.2.1 From 23b904f35128f3c596831cc3320bab1f2db81f60 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 8 Feb 2009 17:00:49 -0800 Subject: drivers/isdn: introduce missing kfree Error handling code following a kmalloc should free the allocated data. The semantic match that finds the problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; statement S; expression E; identifier f,l; position p1,p2; expression *ptr != NULL; @@ ( if ((x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...)) == NULL) S | x@p1 = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S ) <... when != x when != if (...) { <+...x...+> } x->f = E ...> ( return \(0\|<+...x...+>\|ptr\); | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmalloc %s return %s" % (p1[0].file,p1[0].line,p2[0].line) // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/isdn/hardware/mISDN/hfcmulti.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index 595ba8eb4a07..0b28141e43bf 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -4599,6 +4599,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) printk(KERN_ERR "%s: no memory for coeffs\n", __func__); ret = -ENOMEM; + kfree(bch); goto free_chan; } bch->nr = ch; @@ -4767,6 +4768,7 @@ init_multi_port(struct hfc_multi *hc, int pt) printk(KERN_ERR "%s: no memory for coeffs\n", __func__); ret = -ENOMEM; + kfree(bch); goto free_chan; } bch->nr = ch + 1; -- cgit v1.2.1 From cfbf84fcbcda98bb91ada683a8dc8e6901a83ebd Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Sun, 8 Feb 2009 17:49:17 -0800 Subject: tun: Fix unicast filter overflow Tap devices can make use of a small MAC filter set via the TUNSETTXFILTER ioctl. The filter has a set of exact matches plus a hash for imperfect filtering of additional multicast addresses. The current code is unbalanced, adding unicast addresses to the multicast hash, but only checking the hash against multicast addresses. This results in the filter dropping unicast addresses that overflow the exact filter. The fix is simply to disable the filter by leaving count set to zero if we find non-multicast addresses after the exact match table is filled. Signed-off-by: Alex Williamson Signed-off-by: David S. Miller --- drivers/net/tun.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index d7b81e4fdd56..09fea31d3e36 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -157,10 +157,16 @@ static int update_filter(struct tap_filter *filter, void __user *arg) nexact = n; - /* The rest is hashed */ + /* Remaining multicast addresses are hashed, + * unicast will leave the filter disabled. */ memset(filter->mask, 0, sizeof(filter->mask)); - for (; n < uf.count; n++) + for (; n < uf.count; n++) { + if (!is_multicast_ether_addr(addr[n].u)) { + err = 0; /* no filter */ + goto done; + } addr_hash_set(filter->mask, addr[n].u); + } /* For ALLMULTI just set the mask to all ones. * This overrides the mask populated above. */ -- cgit v1.2.1 From b991d2bc4a6e1821555bdc2a682f9aed24650c98 Mon Sep 17 00:00:00 2001 From: Risto Suominen Date: Sun, 8 Feb 2009 17:50:34 -0800 Subject: de2104x: force correct order when writing to rx ring DescOwn should not be set, thus allowing the chip to use the descriptor, before everything else is set up correctly. Signed-off-by: Risto Suominen Signed-off-by: David S. Miller --- drivers/net/tulip/de2104x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index 0bf2114738be..d4c5ecc51f77 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -464,13 +464,14 @@ static void de_rx (struct de_private *de) drop = 1; rx_next: - de->rx_ring[rx_tail].opts1 = cpu_to_le32(DescOwn); if (rx_tail == (DE_RX_RING_SIZE - 1)) de->rx_ring[rx_tail].opts2 = cpu_to_le32(RingEnd | de->rx_buf_sz); else de->rx_ring[rx_tail].opts2 = cpu_to_le32(de->rx_buf_sz); de->rx_ring[rx_tail].addr1 = cpu_to_le32(mapping); + wmb(); + de->rx_ring[rx_tail].opts1 = cpu_to_le32(DescOwn); rx_tail = NEXT_RX(rx_tail); } -- cgit v1.2.1 From b3df68f8f5a29888ae693fdb84ebabbc28ed9400 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Sun, 8 Feb 2009 19:20:19 -0800 Subject: netxen: fix msi-x interrupt handling o Cut down msi-x vectors from 8 to 1 since only one is used for now. o Use separate handler for msi-x, that doesn't unnecessarily scrub msi status register. Signed-off-by: Dhananjay Phadke Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic.h | 2 +- drivers/net/netxen/netxen_nic_main.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index 9c78c963b721..f4dd9acb6877 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -1203,7 +1203,7 @@ typedef struct { #define NETXEN_IS_MSI_FAMILY(adapter) \ ((adapter)->flags & (NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED)) -#define MSIX_ENTRIES_PER_ADAPTER 8 +#define MSIX_ENTRIES_PER_ADAPTER 1 #define NETXEN_MSIX_TBL_SPACE 8192 #define NETXEN_PCI_REG_MSIX_TBL 0x44 diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 645d384fe87e..3b17a7936147 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -76,6 +76,7 @@ static void netxen_nic_poll_controller(struct net_device *netdev); #endif static irqreturn_t netxen_intr(int irq, void *data); static irqreturn_t netxen_msi_intr(int irq, void *data); +static irqreturn_t netxen_msix_intr(int irq, void *data); /* PCI Device ID Table */ #define ENTRY(device) \ @@ -1084,7 +1085,9 @@ static int netxen_nic_open(struct net_device *netdev) for (ring = 0; ring < adapter->max_rds_rings; ring++) netxen_post_rx_buffers(adapter, ctx, ring); } - if (NETXEN_IS_MSI_FAMILY(adapter)) + if (adapter->flags & NETXEN_NIC_MSIX_ENABLED) + handler = netxen_msix_intr; + else if (adapter->flags & NETXEN_NIC_MSI_ENABLED) handler = netxen_msi_intr; else { flags |= IRQF_SHARED; @@ -1612,6 +1615,14 @@ static irqreturn_t netxen_msi_intr(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t netxen_msix_intr(int irq, void *data) +{ + struct netxen_adapter *adapter = data; + + napi_schedule(&adapter->napi); + return IRQ_HANDLED; +} + static int netxen_nic_poll(struct napi_struct *napi, int budget) { struct netxen_adapter *adapter = container_of(napi, struct netxen_adapter, napi); -- cgit v1.2.1 From f06da264cfb0f9444d41ca247213e419f90aa72a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 9 Feb 2009 08:57:29 -0800 Subject: i915: Fix more size_t format string warnings The DRI people seem to have a hard time getting these right (see also commit aeb565dfc3ac4c8b47c5049085b4c7bfb2c7d5d7). Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1441831fa06e..818576654092 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1457,7 +1457,7 @@ static void i915_write_fence_reg(struct drm_i915_fence_reg *reg) if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) || (obj_priv->gtt_offset & (obj->size - 1))) { - WARN(1, "%s: object 0x%08x not 1M or size (0x%x) aligned\n", + WARN(1, "%s: object 0x%08x not 1M or size (0x%zx) aligned\n", __func__, obj_priv->gtt_offset, obj->size); return; } -- cgit v1.2.1 From 94f341db3dd080851f918da37e84659ef760da26 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:02 +0300 Subject: USB: fsl_qe_udc: Fix oops on QE UDC probe failure In case of probing errors the driver kfrees the udc_controller, but it doesn't set the pointer to NULL. When usb_gadget_register_driver is called, it checks for udc_controller != NULL, the check passes and the driver accesses nonexistent memory. Fix this by setting udc_controller to NULL in case of errors. While at it, also implement irq_of_parse_and_map()'s failure and cleanup cases. Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index d6c5bcd40064..e8f7862acb3d 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2604,6 +2604,10 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, (unsigned long)udc_controller); /* request irq and disable DR */ udc_controller->usb_irq = irq_of_parse_and_map(np, 0); + if (!udc_controller->usb_irq) { + ret = -EINVAL; + goto err_noirq; + } ret = request_irq(udc_controller->usb_irq, qe_udc_irq, 0, driver_name, udc_controller); @@ -2625,6 +2629,8 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, err6: free_irq(udc_controller->usb_irq, udc_controller); err5: + irq_dispose_mapping(udc_controller->usb_irq); +err_noirq: if (udc_controller->nullmap) { dma_unmap_single(udc_controller->gadget.dev.parent, udc_controller->nullp, 256, @@ -2648,7 +2654,7 @@ err2: iounmap(udc_controller->usb_regs); err1: kfree(udc_controller); - + udc_controller = NULL; return ret; } @@ -2710,6 +2716,7 @@ static int __devexit qe_udc_remove(struct of_device *ofdev) kfree(ep->txframe); free_irq(udc_controller->usb_irq, udc_controller); + irq_dispose_mapping(udc_controller->usb_irq); tasklet_kill(&udc_controller->rx_tasklet); -- cgit v1.2.1 From a30551db66afa1b53a4fa7ceadddb7122bdcf491 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:05 +0300 Subject: USB: fsl_qe_udc: Fix recursive locking bug in ch9getstatus() The call chain is this: qe_udc_irq() <- grabs the udc->lock spinlock rx_irq() qe_ep0_rx() ep0_setup_handle() setup_received_handle() ch9getstatus() qe_ep_queue() <- tries to grab the udc->lock again It seems unsafe to temporarily drop the lock in the ch9getstatus(), so to fix that bug the lock-less __qe_ep_queue() function implemented and used by the ch9getstatus(). Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index e8f7862acb3d..064582fb6a87 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -1681,14 +1681,11 @@ static void qe_free_request(struct usb_ep *_ep, struct usb_request *_req) kfree(req); } -/* queues (submits) an I/O request to an endpoint */ -static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, - gfp_t gfp_flags) +static int __qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req) { struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); struct qe_req *req = container_of(_req, struct qe_req, req); struct qe_udc *udc; - unsigned long flags; int reval; udc = ep->udc; @@ -1732,7 +1729,7 @@ static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, list_add_tail(&req->queue, &ep->queue); dev_vdbg(udc->dev, "gadget have request in %s! %d\n", ep->name, req->req.length); - spin_lock_irqsave(&udc->lock, flags); + /* push the request to device */ if (ep_is_in(ep)) reval = ep_req_send(ep, req); @@ -1748,11 +1745,24 @@ static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, if (ep->dir == USB_DIR_OUT) reval = ep_req_receive(ep, req); - spin_unlock_irqrestore(&udc->lock, flags); - return 0; } +/* queues (submits) an I/O request to an endpoint */ +static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); + struct qe_udc *udc = ep->udc; + unsigned long flags; + int ret; + + spin_lock_irqsave(&udc->lock, flags); + ret = __qe_ep_queue(_ep, _req); + spin_unlock_irqrestore(&udc->lock, flags); + return ret; +} + /* dequeues (cancels, unlinks) an I/O request from an endpoint */ static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { @@ -2008,7 +2018,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, udc->ep0_dir = USB_DIR_IN; /* data phase */ - status = qe_ep_queue(&ep->ep, &req->req, GFP_ATOMIC); + status = __qe_ep_queue(&ep->ep, &req->req); if (status == 0) return; -- cgit v1.2.1 From 2247818a329687f30d1e5c3a62efc33d07c47522 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:07 +0300 Subject: USB: fsl_qe_udc: Fix QE USB controller initialization qe_udc_reg_init() leaves the USB controller enabled before muram memory initialized. Sometimes the uninitialized muram memory confuses the controller, and it start sending the busy interrupts. Fix this by disabling the controller, it will be enabled later by the gadget driver, at bind time. Signed-off-by: Anton Vorontsov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 064582fb6a87..1319f8f7acba 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2452,8 +2452,12 @@ static int __devinit qe_udc_reg_init(struct qe_udc *udc) struct usb_ctlr __iomem *qe_usbregs; qe_usbregs = udc->usb_regs; - /* Init the usb register */ + /* Spec says that we must enable the USB controller to change mode. */ out_8(&qe_usbregs->usb_usmod, 0x01); + /* Mode changed, now disable it, since muram isn't initialized yet. */ + out_8(&qe_usbregs->usb_usmod, 0x00); + + /* Initialize the rest. */ out_be16(&qe_usbregs->usb_usbmr, 0); out_8(&qe_usbregs->usb_uscom, 0); out_be16(&qe_usbregs->usb_usber, USBER_ALL_CLEAR); -- cgit v1.2.1 From ef84e4055f3561495c4c0e0dfb0b9f4a6e20479d Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:09 +0300 Subject: USB: fsl_qe_udc: Fix disconnects reporting during bus reset Freescale QE UDC controllers can't report the "port change" states, so the only way to handle disconnects is to process bus reset interrupts. The bus reset can take some time, that is, few irqs. Gadgets may print the disconnection events, and this causes few repetitive messages in the kernel log. This patch fixes the issue by using the usb_state machine, if the usb controller has been already reset, just quit the reset irq early. Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 1319f8f7acba..7a820a3b4acd 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2161,6 +2161,9 @@ static int reset_irq(struct qe_udc *udc) { unsigned char i; + if (udc->usb_state == USB_STATE_DEFAULT) + return 0; + qe_usb_disable(); out_8(&udc->usb_regs->usb_usadr, 0); -- cgit v1.2.1 From 82341b3690fce8f70998e3cfb79fbffff0eb7e6b Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:11 +0300 Subject: USB: fsl_qe_udc: Fix muram corruption by disabled endpoints Before freeing an endpoint's muram memory, we should stop all activity of the endpoint, otherwise the QE UDC controller might do nasty things with the muram memory that isn't belong to that endpoint anymore. The qe_ep_reset() effectively flushes the hardware fifos, finishes all late transaction and thus prevents the corruption. Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 7a820a3b4acd..32f415ebd3df 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -1622,6 +1622,7 @@ static int qe_ep_disable(struct usb_ep *_ep) nuke(ep, -ESHUTDOWN); ep->desc = NULL; ep->stopped = 1; + qe_ep_reset(udc, ep->epnum); spin_unlock_irqrestore(&udc->lock, flags); cpm_muram_free(cpm_muram_offset(ep->rxbase)); -- cgit v1.2.1 From af3ddbd76304f9f602c970f9b09a0c9d8cf8336c Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 25 Dec 2008 17:15:14 +0300 Subject: USB: fsl_qe_udc: Fix stalled TX requests bug While disabling an endpoint the driver nuking any pending requests, thus completing them with -ESHUTDOWN status. But the driver doesn't clear the tx_req, which means that a next TX request (after ep_enable), might get stalled, since the driver won't queue the new reqests. This patch fixes a bug I'm observing with ethernet gadget while playing with ifconfig usb0 up/down (the up/down sequence disables and enables `in' and `out' endpoints). Signed-off-by: Anton Vorontsov Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_qe_udc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 32f415ebd3df..d701bf4698d2 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -1622,6 +1622,7 @@ static int qe_ep_disable(struct usb_ep *_ep) nuke(ep, -ESHUTDOWN); ep->desc = NULL; ep->stopped = 1; + ep->tx_req = NULL; qe_ep_reset(udc, ep->epnum); spin_unlock_irqrestore(&udc->lock, flags); -- cgit v1.2.1 From 2057ac86da09955c9f8671e36d4f6bd1e7a5d7d2 Mon Sep 17 00:00:00 2001 From: James Treacy Date: Thu, 29 Jan 2009 20:17:17 -0500 Subject: USB: cdc-acm.c: remove duplicate lines for MTK gps support The same patch to add support for MTK gps loggers was submitted by two different people and applied twice. Remove the redundant lines. Signed-off-by: James Treacy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 97ba4a985edc..326dd7f65ee9 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1349,9 +1349,6 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, - { USB_DEVICE(0x0e8d, 0x3329), /* i-blue 747, Qstarz BT-Q1000, Holux M-241 */ - .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ - }, { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, -- cgit v1.2.1 From 6b40c0057a7935bcf63a38a924094c7e61d4731f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 3 Feb 2009 16:02:21 -0800 Subject: Revert USB: option: add Pantech cards Revert 8b6346ec899713a90890c9e832f7eff91ea73504 as these devices really work just fine with the cdc-acm driver, as they follow the spec properly. Thanks to Chuck Ebbert for pointing out the problem here. Cc: Chuck Ebbert Cc: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 6c89da9c6fea..9d2e77d9af20 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -274,12 +274,6 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define ERICSSON_VENDOR_ID 0x0bdb #define ERICSSON_PRODUCT_F3507G 0x1900 -/* Pantech products */ -#define PANTECH_VENDOR_ID 0x106c -#define PANTECH_PRODUCT_PC5740 0x3701 -#define PANTECH_PRODUCT_PC5750 0x3702 /* PX-500 */ -#define PANTECH_PRODUCT_UM150 0x3711 - static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -488,9 +482,6 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, { USB_DEVICE(ERICSSON_VENDOR_ID, ERICSSON_PRODUCT_F3507G) }, - { USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_PC5740) }, - { USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_PC5750) }, - { USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_UM150) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); -- cgit v1.2.1 From 0d020aae0a154cffce680a7775c74788fa0bea92 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 2 Feb 2009 09:51:01 -0500 Subject: USB: usb-storage: remove WARN from last-sector hacks This patch (as1201) removes the WARN() from the last-sector hacks in usb-storage, thereby making the code match the version now in .27-stable and .28-stable. The WARN() isn't needed, since there is no longer any intention of assuming that all storage devices have an even number of sectors, and it annoys users for no good reason. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/transport.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 1d5438e6363b..fb65d221cedf 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -558,32 +558,10 @@ static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb) if (srb->result == SAM_STAT_GOOD && scsi_get_resid(srb) == 0) { - /* The command succeeded. If the capacity is odd - * (i.e., if the sector number is even) then the - * "always-even" heuristic would be wrong for this - * device. Issue a WARN() so that the kerneloops.org - * project will be notified and we will then know to - * mark the device with a CAPACITY_OK flag. Hopefully - * this will occur for only a few devices. - * - * Use the sign of us->last_sector_hacks to tell whether - * the warning has already been issued; we don't need - * more than one warning per device. + /* The command succeeded. We know this device doesn't + * have the last-sector bug, so stop checking it. */ - if (!(sector & 1) && us->use_last_sector_hacks > 0) { - unsigned vid = le16_to_cpu( - us->pusb_dev->descriptor.idVendor); - unsigned pid = le16_to_cpu( - us->pusb_dev->descriptor.idProduct); - unsigned rev = le16_to_cpu( - us->pusb_dev->descriptor.bcdDevice); - - WARN(1, "%s: Successful last sector success at %u, " - "device %04x:%04x:%04x\n", - sdkp->disk->disk_name, sector, - vid, pid, rev); - us->use_last_sector_hacks = -1; - } + us->use_last_sector_hacks = 0; } else { /* The command failed. Allow up to 3 retries in case this @@ -599,14 +577,6 @@ static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb) srb->result = SAM_STAT_CHECK_CONDITION; memcpy(srb->sense_buffer, record_not_found, sizeof(record_not_found)); - - /* In theory we might want to issue a WARN() here if the - * capacity is even, since it could indicate the device - * has the READ CAPACITY bug _and_ the real capacity is - * odd. But it could also indicate that the device - * simply can't access its last sector, a failure mode - * which is surprisingly common. So no warning. - */ } done: -- cgit v1.2.1 From 78c8fb3717cdff35ad118e17ac7495d0cf21a61f Mon Sep 17 00:00:00 2001 From: Dave Young Date: Sun, 1 Feb 2009 18:54:54 +0800 Subject: USB: usb-serial: fix the aircable_init failure path The failure path of aircable_init is wrong, fix the order of (goto) labels. Signed-off-by: Dave Young Acked-by: Naranjo Manuel Francisco Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/aircable.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 537f953bd7f8..6d106e74265e 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -621,9 +621,9 @@ static int __init aircable_init(void) goto failed_usb_register; return 0; -failed_serial_register: - usb_serial_deregister(&aircable_device); failed_usb_register: + usb_serial_deregister(&aircable_device); +failed_serial_register: return retval; } -- cgit v1.2.1 From e38c287447e5a3ff905a59dd81269c14cd12ffa1 Mon Sep 17 00:00:00 2001 From: Stephane Clerambault Date: Mon, 2 Feb 2009 13:39:12 -0800 Subject: USB: ftdi_sio: add support for the NDI Polaris system Add support for the NDI Polaris system *http://www.ndigital.com/). Cc: Ian Abbott Cc: Oliver Neukum Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 75597337583e..64b84291c07e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -662,6 +662,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 1b62eff475d2..e300c840f8ca 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -844,6 +844,9 @@ #define TML_VID 0x1B91 /* Vendor ID */ #define TML_USB_SERIAL_PID 0x0064 /* USB - Serial Converter */ +/* NDI Polaris System */ +#define FTDI_NDI_HUC_PID 0xDA70 + /* Propox devices */ #define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 -- cgit v1.2.1 From 506e9469833c66ed6bb9acd902e208f7301b6adb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 4 Feb 2009 15:48:03 -0500 Subject: USB: usb-storage: add Pentax to the bad-vendor list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch (as1202) adds Pentax to usb-storage's list of bad vendors whose devices always need the CAPACITY_HEURISTICS flag. This is in addition to the existing entries: Nokia, Nikon, and Motorola. Signed-off-by: Alan Stern Tested-by: Virgo Pärna Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 2 ++ drivers/usb/storage/unusual_devs.h | 15 --------------- 2 files changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 2a42b862aa9f..727c506417cc 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -64,6 +64,7 @@ */ #define VENDOR_ID_NOKIA 0x0421 #define VENDOR_ID_NIKON 0x04b0 +#define VENDOR_ID_PENTAX 0x0a17 #define VENDOR_ID_MOTOROLA 0x22b8 /*********************************************************************** @@ -158,6 +159,7 @@ static int slave_configure(struct scsi_device *sdev) switch (le16_to_cpu(us->pusb_dev->descriptor.idVendor)) { case VENDOR_ID_NOKIA: case VENDOR_ID_NIKON: + case VENDOR_ID_PENTAX: case VENDOR_ID_MOTOROLA: if (!(us->fflags & (US_FL_FIX_CAPACITY | US_FL_CAPACITY_OK))) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 69269f739563..8e878cab8dc7 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1354,21 +1354,6 @@ UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), - -/* Submitted by Per Winkvist */ -UNUSUAL_DEV( 0x0a17, 0x006, 0x0000, 0xffff, - "Pentax", - "Optio S/S4", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), - -/* Reported by Jaak Ristioja */ -UNUSUAL_DEV( 0x0a17, 0x006e, 0x0100, 0x0100, - "Pentax", - "K10D", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - /* These are virtual windows driver CDs, which the zd1211rw driver * automatically converts into WLAN devices. */ UNUSUAL_DEV( 0x0ace, 0x2011, 0x0101, 0x0101, -- cgit v1.2.1 From 64905b48098761e779bb848e69365c018894ea81 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 3 Feb 2009 11:11:38 +0300 Subject: USB: ftdi_sio: unlock_kernel() on error in set_serial_info() There was one error path where unlock_kernel() wasn't called. This was found with a code checker (http://repo.or.cz/w/smatch.git/) Compile tested only, sorry. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 64b84291c07e..f92f4d773374 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1065,8 +1065,10 @@ static int set_serial_info(struct tty_struct *tty, if (!capable(CAP_SYS_ADMIN)) { if (((new_serial.flags & ~ASYNC_USR_MASK) != - (priv->flags & ~ASYNC_USR_MASK))) + (priv->flags & ~ASYNC_USR_MASK))) { + unlock_kernel(); return -EPERM; + } priv->flags = ((priv->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); priv->custom_divisor = new_serial.custom_divisor; -- cgit v1.2.1 From 97dcf0416e390fc5c997d4ea60e6f975c7b7a1c3 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 4 Feb 2009 16:38:33 +0100 Subject: USB: two more usb ids for ti_usb_3410_5052 This patch adds device IDs and balances the counts to make the hot ID additioning mechanism work. Signed-off-by: Oliver Neukum Cc: stable Cc: Chris Adams Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ti_usb_3410_5052.c | 10 +++++++--- drivers/usb/serial/ti_usb_3410_5052.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index baf591137b80..2620bf6fe5e1 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -176,7 +176,7 @@ static unsigned int product_5052_count; /* the array dimension is the number of default entries plus */ /* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */ /* null entry */ -static struct usb_device_id ti_id_table_3410[7+TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_3410[10+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -185,9 +185,11 @@ static struct usb_device_id ti_id_table_3410[7+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, }; -static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, @@ -195,7 +197,7 @@ static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, }; -static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = { +static struct usb_device_id ti_id_table_combined[14+2*TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) }, @@ -208,6 +210,8 @@ static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) }, { } }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.h b/drivers/usb/serial/ti_usb_3410_5052.h index b7ea5dbadee5..f323c6025858 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.h +++ b/drivers/usb/serial/ti_usb_3410_5052.h @@ -30,6 +30,8 @@ #define IBM_VENDOR_ID 0x04b3 #define TI_3410_PRODUCT_ID 0x3410 #define IBM_4543_PRODUCT_ID 0x4543 +#define IBM_454B_PRODUCT_ID 0x454b +#define IBM_454C_PRODUCT_ID 0x454c #define TI_3410_EZ430_ID 0xF430 /* TI ez430 development tool */ #define TI_5052_BOOT_PRODUCT_ID 0x5052 /* no EEPROM, no firmware */ #define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */ -- cgit v1.2.1 From c200b9c9e8ec93cdd262cfa1699ad92e883d4876 Mon Sep 17 00:00:00 2001 From: Dirk De Schepper Date: Fri, 6 Feb 2009 20:48:34 +0000 Subject: USB: option: New mobile broadband modems to be supported - New Novatel and Dell mobile broadband modem products added - Dell pid variables used in stead of numerical PIDs for known products Signed-off-by: Dirk De Schepper Cc: stable Signed-off-by: Matthias Urlichs Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 84 ++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 9d2e77d9af20..bfd0b68ceccd 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -199,14 +199,15 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define NOVATELWIRELESS_PRODUCT_MC950D 0x4400 /* FUTURE NOVATEL PRODUCTS */ -#define NOVATELWIRELESS_PRODUCT_EVDO_1 0x6000 -#define NOVATELWIRELESS_PRODUCT_HSPA_1 0x7000 -#define NOVATELWIRELESS_PRODUCT_EMBEDDED_1 0x8000 -#define NOVATELWIRELESS_PRODUCT_GLOBAL_1 0x9000 -#define NOVATELWIRELESS_PRODUCT_EVDO_2 0x6001 -#define NOVATELWIRELESS_PRODUCT_HSPA_2 0x7001 -#define NOVATELWIRELESS_PRODUCT_EMBEDDED_2 0x8001 -#define NOVATELWIRELESS_PRODUCT_GLOBAL_2 0x9001 +#define NOVATELWIRELESS_PRODUCT_EVDO_FULLSPEED 0X6000 +#define NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED 0X6001 +#define NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED 0X7000 +#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED 0X7001 +#define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED 0X8000 +#define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED 0X8001 +#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED 0X9000 +#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED 0X9001 +#define NOVATELWIRELESS_PRODUCT_GLOBAL 0XA001 /* AMOI PRODUCTS */ #define AMOI_VENDOR_ID 0x1614 @@ -216,6 +217,27 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define DELL_VENDOR_ID 0x413C +/* Dell modems */ +#define DELL_PRODUCT_5700_MINICARD 0x8114 +#define DELL_PRODUCT_5500_MINICARD 0x8115 +#define DELL_PRODUCT_5505_MINICARD 0x8116 +#define DELL_PRODUCT_5700_EXPRESSCARD 0x8117 +#define DELL_PRODUCT_5510_EXPRESSCARD 0x8118 + +#define DELL_PRODUCT_5700_MINICARD_SPRINT 0x8128 +#define DELL_PRODUCT_5700_MINICARD_TELUS 0x8129 + +#define DELL_PRODUCT_5720_MINICARD_VZW 0x8133 +#define DELL_PRODUCT_5720_MINICARD_SPRINT 0x8134 +#define DELL_PRODUCT_5720_MINICARD_TELUS 0x8135 +#define DELL_PRODUCT_5520_MINICARD_CINGULAR 0x8136 +#define DELL_PRODUCT_5520_MINICARD_GENERIC_L 0x8137 +#define DELL_PRODUCT_5520_MINICARD_GENERIC_I 0x8138 + +#define DELL_PRODUCT_5730_MINICARD_SPRINT 0x8180 +#define DELL_PRODUCT_5730_MINICARD_TELUS 0x8181 +#define DELL_PRODUCT_5730_MINICARD_VZW 0x8182 + #define KYOCERA_VENDOR_ID 0x0c88 #define KYOCERA_PRODUCT_KPC650 0x17da #define KYOCERA_PRODUCT_KPC680 0x180a @@ -389,31 +411,37 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU870D) }, /* Novatel EU850D/EU860D/EU870D */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC950D) }, /* Novatel MC930D/MC950D */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) }, /* Novatel MC727/U727/USB727 */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_1) }, /* Novatel EVDO product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_1) }, /* Novatel HSPA product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EMBEDDED_1) }, /* Novatel Embedded product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_GLOBAL_1) }, /* Novatel Global product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_2) }, /* Novatel EVDO product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_2) }, /* Novatel HSPA product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EMBEDDED_2) }, /* Novatel Embedded product */ - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_GLOBAL_2) }, /* Novatel Global product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_FULLSPEED) }, /* Novatel EVDO product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED) }, /* Novatel HSPA product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, /* Novatel EVDO Embedded product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, /* Novatel HSPA Embedded product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED) }, /* Novatel EVDO product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED) }, /* Novatel HSPA product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED) }, /* Novatel EVDO Embedded product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED) }, /* Novatel HSPA Embedded product */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_GLOBAL) }, /* Novatel Global product */ { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H02) }, - { USB_DEVICE(DELL_VENDOR_ID, 0x8114) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite EV620 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8115) }, /* Dell Wireless 5500 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8116) }, /* Dell Wireless 5505 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8117) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO ExpressCard == Novatel Merlin XV620 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard == Novatel Merlin XU870 HSDPA/3G */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8128) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite E720 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8129) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite ET620 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8133) }, /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8136) }, /* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8137) }, /* Dell Wireless HSDPA 5520 */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8138) }, /* Dell Wireless 5520 Voda I Mobile Broadband (3G HSDPA) Minicard */ - { USB_DEVICE(DELL_VENDOR_ID, 0x8147) }, /* Dell Wireless 5530 Mobile Broadband (3G HSPA) Mini-Card */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite EV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5500_MINICARD) }, /* Dell Wireless 5500 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5505_MINICARD) }, /* Dell Wireless 5505 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_EXPRESSCARD) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO ExpressCard == Novatel Merlin XV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5510_EXPRESSCARD) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard == Novatel Merlin XU870 HSDPA/3G */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD_SPRINT) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite E720 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD_TELUS) }, /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite ET620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_VZW) }, /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_SPRINT) }, /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_TELUS) }, /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_CINGULAR) }, /* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_GENERIC_L) }, /* Dell Wireless HSDPA 5520 */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_GENERIC_I) }, /* Dell Wireless 5520 Voda I Mobile Broadband (3G HSDPA) Minicard */ + { USB_DEVICE(DELL_VENDOR_ID, 0x8147) }, /* Dell Wireless 5530 Mobile Broadband (3G HSPA) Mini-Card */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_SPRINT) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_TELUS) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ + { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_VZW) }, /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) }, { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) }, -- cgit v1.2.1 From 26e1287594864169577327fef233befc9739be3b Mon Sep 17 00:00:00 2001 From: Ivan Kuten Date: Fri, 6 Feb 2009 17:42:34 +0800 Subject: USB: Correct Makefile to make isp1760 buildable Signed-off-by: Ivan Kuten Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 8bcde8cde554..b2ceb4aff233 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_PCI) += host/ obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_ISP116X_HCD) += host/ +obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_FHCI_HCD) += host/ -- cgit v1.2.1 From 049a6acb503ca7acc11d563db79a2d54f054e0d0 Mon Sep 17 00:00:00 2001 From: Nick Holloway Date: Sun, 25 Jan 2009 16:58:43 +0000 Subject: USB: Storage: Update unusual_devs entry for Datafab KECF-USB This device suffers from the off-by-one error when reporting the capacity, so add US_FL_FIX_CAPACITY to the existing entry. Signed-off-by: Nick Holloway Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 8e878cab8dc7..50dc33a6065b 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1214,7 +1214,7 @@ UNUSUAL_DEV( 0x07c4, 0xa400, 0x0000, 0xffff, "Datafab", "KECF-USB", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), + US_FL_FIX_INQUIRY | US_FL_FIX_CAPACITY ), /* Reported by Rauch Wolke */ UNUSUAL_DEV( 0x07c4, 0xa4a5, 0x0000, 0xffff, -- cgit v1.2.1 From 89cb7e7fd6c0917bb9236ea48bf538d4668ed009 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 3 Feb 2009 16:28:48 -0800 Subject: Revert Staging: at76_usb: update drivers/staging/at76_usb w/ mac80211 port Reverts 02227c28391b5059a7710d6039c52912b0ee2c1d (Had to be done by hand due to other patches that had come after this.) Turns out that we don't want the mac80211 port of this driver just yet, as there is a different driver working on adding this support. So keep things old and different for now. This is being reverted at the request of the linux-wireless developers. Cc: Kalle Valo Cc: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/staging/at76_usb/Kconfig | 2 +- drivers/staging/at76_usb/at76_usb.c | 4742 ++++++++++++++++++++++++++++------- drivers/staging/at76_usb/at76_usb.h | 227 +- 3 files changed, 3942 insertions(+), 1029 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/at76_usb/Kconfig b/drivers/staging/at76_usb/Kconfig index 4c0e55e15448..8606f9621624 100644 --- a/drivers/staging/at76_usb/Kconfig +++ b/drivers/staging/at76_usb/Kconfig @@ -1,6 +1,6 @@ config USB_ATMEL tristate "Atmel at76c503/at76c505/at76c505a USB cards" - depends on MAC80211 && WLAN_80211 && USB + depends on WLAN_80211 && USB default N select FW_LOADER ---help--- diff --git a/drivers/staging/at76_usb/at76_usb.c b/drivers/staging/at76_usb/at76_usb.c index 185533e54769..9195ee9319ff 100644 --- a/drivers/staging/at76_usb/at76_usb.c +++ b/drivers/staging/at76_usb/at76_usb.c @@ -6,7 +6,6 @@ * Copyright (c) 2004 Nick Jones * Copyright (c) 2004 Balint Seeber * Copyright (c) 2007 Guido Guenther - * Copyright (c) 2007 Kalle Valo * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -17,13 +16,6 @@ * Atmel AT76C503A/505/505A. * * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed - * - * TODO for the mac80211 port: - * o adhoc support - * o RTS/CTS support - * o Power Save Mode support - * o support for short/long preambles - * o export variables through debugfs/sysfs */ #include @@ -44,7 +36,7 @@ #include #include #include -#include +#include #include "at76_usb.h" @@ -84,43 +76,31 @@ #define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ #define DBG_FW 0x10000000 /* firmware download */ #define DBG_DFU 0x20000000 /* device firmware upgrade */ -#define DBG_CMD 0x40000000 -#define DBG_MAC80211 0x80000000 #define DBG_DEFAULTS 0 /* Use our own dbg macro */ #define at76_dbg(bits, format, arg...) \ -do { \ - if (at76_debug & (bits)) \ - printk(KERN_DEBUG DRIVER_NAME ": " format "\n" , ## arg); \ -} while (0) - -#define at76_dbg_dump(bits, buf, len, format, arg...) \ -do { \ - if (at76_debug & (bits)) { \ + do { \ + if (at76_debug & (bits)) \ printk(KERN_DEBUG DRIVER_NAME ": " format "\n" , ## arg); \ - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); \ - } \ -} while (0) + } while (0) static int at76_debug = DBG_DEFAULTS; -#define FIRMWARE_IS_WPA(ver) ((ver.major == 1) && (ver.minor == 103)) - /* Protect against concurrent firmware loading and parsing */ static struct mutex fw_mutex; static struct fwentry firmwares[] = { - [0] = { "" }, - [BOARD_503_ISL3861] = { "atmel_at76c503-i3861.bin" }, - [BOARD_503_ISL3863] = { "atmel_at76c503-i3863.bin" }, - [BOARD_503] = { "atmel_at76c503-rfmd.bin" }, - [BOARD_503_ACC] = { "atmel_at76c503-rfmd-acc.bin" }, - [BOARD_505] = { "atmel_at76c505-rfmd.bin" }, - [BOARD_505_2958] = { "atmel_at76c505-rfmd2958.bin" }, - [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" }, - [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" }, + [0] = {""}, + [BOARD_503_ISL3861] = {"atmel_at76c503-i3861.bin"}, + [BOARD_503_ISL3863] = {"atmel_at76c503-i3863.bin"}, + [BOARD_503] = {"atmel_at76c503-rfmd.bin"}, + [BOARD_503_ACC] = {"atmel_at76c503-rfmd-acc.bin"}, + [BOARD_505] = {"atmel_at76c505-rfmd.bin"}, + [BOARD_505_2958] = {"atmel_at76c505-rfmd2958.bin"}, + [BOARD_505A] = {"atmel_at76c505a-rfmd2958.bin"}, + [BOARD_505AMX] = {"atmel_at76c505amx-rfmd.bin"}, }; #define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) @@ -130,133 +110,133 @@ static struct usb_device_id dev_table[] = { * at76c503-i3861 */ /* Generic AT76C503/3861 device */ - { USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Linksys WUSB11 v2.1/v2.6 */ - { USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Netgear MA101 rev. A */ - { USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Tekram U300C / Allnet ALL0193 */ - { USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* HP HN210W J7801A */ - { USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Sitecom/Z-Com/Zyxel M4Y-750 */ - { USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Dynalink/Askey WLL013 (intersil) */ - { USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */ - { USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* BenQ AWL300 */ - { USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Addtron AWU-120, Compex WLU11 */ - { USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Intel AP310 AnyPoint II USB */ - { USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Dynalink L11U */ - { USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* Arescom WL-210, FCC id 07J-GL2411USB */ - { USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* I-O DATA WN-B11/USB */ - { USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* BT Voyager 1010 */ - { USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + {USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861)}, /* * at76c503-i3863 */ /* Generic AT76C503/3863 device */ - { USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863) }, + {USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863)}, /* Samsung SWL-2100U */ - { USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863) }, + {USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863)}, /* * at76c503-rfmd */ /* Generic AT76C503/RFMD device */ - { USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503)}, /* Dynalink/Askey WLL013 (rfmd) */ - { USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503)}, /* Linksys WUSB11 v2.6 */ - { USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503)}, /* Network Everywhere NWU11B */ - { USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503)}, /* Netgear MA101 rev. B */ - { USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503)}, /* D-Link DWL-120 rev. E */ - { USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503)}, /* Actiontec 802UAT1, HWU01150-01UK */ - { USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503)}, /* AirVast W-Buddie WN210 */ - { USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503)}, /* Dick Smith Electronics XH1153 802.11b USB adapter */ - { USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503)}, /* CNet CNUSB611 */ - { USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503)}, /* FiberLine FL-WL200U */ - { USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503)}, /* BenQ AWL400 USB stick */ - { USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503)}, /* 3Com 3CRSHEW696 */ - { USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503)}, /* Siemens Santis ADSL WLAN USB adapter WLL 013 */ - { USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503)}, /* Belkin F5D6050, version 2 */ - { USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503)}, /* iBlitzz, BWU613 (not *B or *SB) */ - { USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503)}, /* Gigabyte GN-WLBM101 */ - { USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503)}, /* Planex GW-US11S */ - { USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503)}, /* Internal WLAN adapter in h5[4,5]xx series iPAQs */ - { USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503)}, /* Corega Wireless LAN USB-11 mini */ - { USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503)}, /* Corega Wireless LAN USB-11 mini2 */ - { USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503)}, /* Uniden PCW100 */ - { USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503) }, + {USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503)}, /* * at76c503-rfmd-acc */ /* SMC2664W */ - { USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC) }, + {USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC)}, /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */ - { USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC) }, + {USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC)}, /* * at76c505-rfmd */ /* Generic AT76C505/RFMD */ - { USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505) }, + {USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505)}, /* * at76c505-rfmd2958 */ /* Generic AT76C505/RFMD, OvisLink WL-1130USB */ - { USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, + {USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958)}, /* Fiberline FL-WL240U */ - { USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958) }, + {USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958)}, /* CNet CNUSB-611G */ - { USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958) }, + {USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958)}, /* Linksys WUSB11 v2.8 */ - { USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958) }, + {USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958)}, /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */ - { USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958) }, + {USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958)}, /* Corega WLAN USB Stick 11 */ - { USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, + {USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958)}, /* Microstar MSI Box MS6978 */ - { USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958) }, + {USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958)}, /* * at76c505a-rfmd2958 */ /* Generic AT76C505A device */ - { USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A) }, + {USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A)}, /* Generic AT76C505AS device */ - { USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A) }, + {USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A)}, /* Siemens Gigaset USB WLAN Adapter 11 */ - { USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A) }, + {USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A)}, /* * at76c505amx-rfmd */ /* Generic AT76C505AMX device */ - { USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX) }, - { } + {USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX)}, + {} }; MODULE_DEVICE_TABLE(usb, dev_table); @@ -264,8 +244,26 @@ MODULE_DEVICE_TABLE(usb, dev_table); /* Supported rates of this hardware, bit 7 marks basic rates */ static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; +/* Frequency of each channel in MHz */ +static const long channel_frequency[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 +}; + +#define NUM_CHANNELS ARRAY_SIZE(channel_frequency) + static const char *const preambles[] = { "long", "short", "auto" }; +static const char *const mac_states[] = { + [MAC_INIT] = "INIT", + [MAC_SCANNING] = "SCANNING", + [MAC_AUTH] = "AUTH", + [MAC_ASSOC] = "ASSOC", + [MAC_JOINING] = "JOINING", + [MAC_CONNECTED] = "CONNECTED", + [MAC_OWN_IBSS] = "OWN_IBSS" +}; + /* Firmware download */ /* DFU states */ #define STATE_IDLE 0x00 @@ -300,30 +298,17 @@ struct dfu_status { static inline int at76_is_intersil(enum board_type board) { - if (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863) - return 1; - return 0; + return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863); } static inline int at76_is_503rfmd(enum board_type board) { - if (board == BOARD_503 || board == BOARD_503_ACC) - return 1; - return 0; -} - -static inline int at76_is_505(enum board_type board) -{ - if (board == BOARD_505 || board == BOARD_505_2958) - return 1; - return 0; + return (board == BOARD_503 || board == BOARD_503_ACC); } static inline int at76_is_505a(enum board_type board) { - if (board == BOARD_505A || board == BOARD_505AMX) - return 1; - return 0; + return (board == BOARD_505A || board == BOARD_505AMX); } /* Load a block of the first (internal) part of the firmware */ @@ -504,6 +489,41 @@ exit: return ret; } +/* Report that the scan results are ready */ +static inline void at76_iwevent_scan_complete(struct net_device *netdev) +{ + union iwreq_data wrqu; + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(netdev, SIOCGIWSCAN, &wrqu, NULL); + at76_dbg(DBG_WE_EVENTS, "%s: SIOCGIWSCAN sent", netdev->name); +} + +static inline void at76_iwevent_bss_connect(struct net_device *netdev, + u8 *bssid) +{ + union iwreq_data wrqu; + wrqu.data.length = 0; + wrqu.data.flags = 0; + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL); + at76_dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", netdev->name, + __func__); +} + +static inline void at76_iwevent_bss_disconnect(struct net_device *netdev) +{ + union iwreq_data wrqu; + wrqu.data.length = 0; + wrqu.data.flags = 0; + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL); + at76_dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", netdev->name, + __func__); +} + #define HEX2STR_BUFFERS 4 #define HEX2STR_MAX_LEN 64 #define BIN2HEX(x) ((x) < 10 ? '0' + (x) : (x) + 'A' - 10) @@ -575,6 +595,37 @@ static void at76_ledtrig_tx_activity(void) mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); } +/* Check if the given ssid is hidden */ +static inline int at76_is_hidden_ssid(u8 *ssid, int length) +{ + static const u8 zeros[32]; + + if (length == 0) + return 1; + + if (length == 1 && ssid[0] == ' ') + return 1; + + return (memcmp(ssid, zeros, length) == 0); +} + +static inline void at76_free_bss_list(struct at76_priv *priv) +{ + struct list_head *next, *ptr; + unsigned long flags; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + + priv->curr_bss = NULL; + + list_for_each_safe(ptr, next, &priv->bss_list) { + list_del(ptr); + kfree(list_entry(ptr, struct bss_info, list)); + } + + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); +} + static int at76_remap(struct usb_device *udev) { int ret; @@ -676,7 +727,7 @@ exit: kfree(hwcfg); if (ret < 0) printk(KERN_ERR "%s: cannot get HW Config (error %d)\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); return ret; } @@ -685,15 +736,15 @@ static struct reg_domain const *at76_get_reg_domain(u16 code) { int i; static struct reg_domain const fd_tab[] = { - { 0x10, "FCC (USA)", 0x7ff }, /* ch 1-11 */ - { 0x20, "IC (Canada)", 0x7ff }, /* ch 1-11 */ - { 0x30, "ETSI (most of Europe)", 0x1fff }, /* ch 1-13 */ - { 0x31, "Spain", 0x600 }, /* ch 10-11 */ - { 0x32, "France", 0x1e00 }, /* ch 10-13 */ - { 0x40, "MKK (Japan)", 0x2000 }, /* ch 14 */ - { 0x41, "MKK1 (Japan)", 0x3fff }, /* ch 1-14 */ - { 0x50, "Israel", 0x3fc }, /* ch 3-9 */ - { 0x00, "", 0xffffffff } /* ch 1-32 */ + {0x10, "FCC (USA)", 0x7ff}, /* ch 1-11 */ + {0x20, "IC (Canada)", 0x7ff}, /* ch 1-11 */ + {0x30, "ETSI (most of Europe)", 0x1fff}, /* ch 1-13 */ + {0x31, "Spain", 0x600}, /* ch 10-11 */ + {0x32, "France", 0x1e00}, /* ch 10-13 */ + {0x40, "MKK (Japan)", 0x2000}, /* ch 14 */ + {0x41, "MKK1 (Japan)", 0x3fff}, /* ch 1-14 */ + {0x50, "Israel", 0x3fc}, /* ch 3-9 */ + {0x00, "", 0xffffffff} /* ch 1-32 */ }; /* Last entry is fallback for unknown domain code */ @@ -739,24 +790,6 @@ static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) return ret; } -#define MAKE_CMD_CASE(c) case (c): return #c - -static const char *at76_get_cmd_string(u8 cmd_status) -{ - switch (cmd_status) { - MAKE_CMD_CASE(CMD_SET_MIB); - MAKE_CMD_CASE(CMD_GET_MIB); - MAKE_CMD_CASE(CMD_SCAN); - MAKE_CMD_CASE(CMD_JOIN); - MAKE_CMD_CASE(CMD_START_IBSS); - MAKE_CMD_CASE(CMD_RADIO_ON); - MAKE_CMD_CASE(CMD_RADIO_OFF); - MAKE_CMD_CASE(CMD_STARTUP); - } - - return "UNKNOWN"; -} - static int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf, int buf_size) { @@ -772,10 +805,6 @@ static int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf, cmd_buf->size = cpu_to_le16(buf_size); memcpy(cmd_buf->data, buf, buf_size); - at76_dbg_dump(DBG_CMD, cmd_buf, sizeof(struct at76_command) + buf_size, - "issuing command %s (0x%02x)", - at76_get_cmd_string(cmd), cmd); - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, 0, 0, cmd_buf, @@ -813,13 +842,13 @@ static int at76_wait_completion(struct at76_priv *priv, int cmd) status = at76_get_cmd_status(priv->udev, cmd); if (status < 0) { printk(KERN_ERR "%s: at76_get_cmd_status failed: %d\n", - wiphy_name(priv->hw->wiphy), status); + priv->netdev->name, status); break; } at76_dbg(DBG_WAIT_COMPLETE, "%s: Waiting on cmd %d, status = %d (%s)", - wiphy_name(priv->hw->wiphy), cmd, status, + priv->netdev->name, cmd, status, at76_get_cmd_status_string(status)); if (status != CMD_STATUS_IN_PROGRESS @@ -830,7 +859,7 @@ static int at76_wait_completion(struct at76_priv *priv, int cmd) if (time_after(jiffies, timeout)) { printk(KERN_ERR "%s: completion timeout for command %d\n", - wiphy_name(priv->hw->wiphy), cmd); + priv->netdev->name, cmd); status = -ETIMEDOUT; break; } @@ -853,7 +882,7 @@ static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf) if (ret != CMD_STATUS_COMPLETE) { printk(KERN_INFO "%s: set_mib: at76_wait_completion failed " - "with %d\n", wiphy_name(priv->hw->wiphy), ret); + "with %d\n", priv->netdev->name, ret); ret = -EIO; } @@ -874,7 +903,7 @@ static int at76_set_radio(struct at76_priv *priv, int enable) ret = at76_set_card_command(priv->udev, cmd, NULL, 0); if (ret < 0) printk(KERN_ERR "%s: at76_set_card_command(%d) failed: %d\n", - wiphy_name(priv->hw->wiphy), cmd, ret); + priv->netdev->name, cmd, ret); else ret = 1; @@ -895,7 +924,44 @@ static int at76_set_pm_mode(struct at76_priv *priv) ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) printk(KERN_ERR "%s: set_mib (pm_mode) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); + + return ret; +} + +/* Set the association id for power save mode */ +static int at76_set_associd(struct at76_priv *priv, u16 id) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, station_id); + priv->mib_buf.data.word = cpu_to_le16(id); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (associd) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +/* Set the listen interval for power save mode */ +static int at76_set_listen_interval(struct at76_priv *priv, u16 interval) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac, listen_interval); + priv->mib_buf.data.word = cpu_to_le16(interval); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR + "%s: set_mib (listen_interval) failed: %d\n", + priv->netdev->name, ret); return ret; } @@ -912,7 +978,7 @@ static int at76_set_preamble(struct at76_priv *priv, u8 type) ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) printk(KERN_ERR "%s: set_mib (preamble) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); return ret; } @@ -929,7 +995,7 @@ static int at76_set_frag(struct at76_priv *priv, u16 size) ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) printk(KERN_ERR "%s: set_mib (frag threshold) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); return ret; } @@ -946,7 +1012,7 @@ static int at76_set_rts(struct at76_priv *priv, u16 size) ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) printk(KERN_ERR "%s: set_mib (rts) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); return ret; } @@ -963,41 +1029,24 @@ static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff) ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) printk(KERN_ERR "%s: set_mib (autorate fallback) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); return ret; } -static int at76_set_tkip_bssid(struct at76_priv *priv, const void *addr) +static int at76_add_mac_address(struct at76_priv *priv, void *addr) { int ret = 0; - priv->mib_buf.type = MIB_MAC_ENCRYPTION; + priv->mib_buf.type = MIB_MAC_ADDR; priv->mib_buf.size = ETH_ALEN; - priv->mib_buf.index = offsetof(struct mib_mac_encryption, tkip_bssid); + priv->mib_buf.index = offsetof(struct mib_mac_addr, mac_addr); memcpy(priv->mib_buf.data.addr, addr, ETH_ALEN); ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) - printk(KERN_ERR "%s: set_mib (MAC_ENCRYPTION, tkip_bssid) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); - - return ret; -} - -static int at76_reset_rsc(struct at76_priv *priv) -{ - int ret = 0; - - priv->mib_buf.type = MIB_MAC_ENCRYPTION; - priv->mib_buf.size = 4 * 8; - priv->mib_buf.index = offsetof(struct mib_mac_encryption, key_rsc); - memset(priv->mib_buf.data.data, 0 , priv->mib_buf.size); - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - printk(KERN_ERR "%s: set_mib (MAC_ENCRYPTION, key_rsc) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + printk(KERN_ERR "%s: set_mib (MAC_ADDR, mac_addr) failed: %d\n", + priv->netdev->name, ret); return ret; } @@ -1016,16 +1065,16 @@ static void at76_dump_mib_mac_addr(struct at76_priv *priv) sizeof(struct mib_mac_addr)); if (ret < 0) { printk(KERN_ERR "%s: at76_get_mib (MAC_ADDR) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %s res 0x%x 0x%x", - wiphy_name(priv->hw->wiphy), + priv->netdev->name, mac2str(m->mac_addr), m->res[0], m->res[1]); for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %s, " - "status %d", wiphy_name(priv->hw->wiphy), i, + "status %d", priv->netdev->name, i, mac2str(m->group_addr[i]), m->group_addr_status[i]); exit: kfree(m); @@ -1045,13 +1094,13 @@ static void at76_dump_mib_mac_wep(struct at76_priv *priv) sizeof(struct mib_mac_wep)); if (ret < 0) { printk(KERN_ERR "%s: at76_get_mib (MAC_WEP) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u " "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u " - "encr_level %u key %d", wiphy_name(priv->hw->wiphy), + "encr_level %u key %d", priv->netdev->name, m->privacy_invoked, m->wep_default_key_id, m->wep_key_mapping_len, m->exclude_unencrypted, le32_to_cpu(m->wep_icv_error_count), @@ -1063,55 +1112,12 @@ static void at76_dump_mib_mac_wep(struct at76_priv *priv) for (i = 0; i < WEP_KEYS; i++) at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %s", - wiphy_name(priv->hw->wiphy), i, + priv->netdev->name, i, hex2str(m->wep_default_keyvalue[i], key_len)); exit: kfree(m); } -static void at76_dump_mib_mac_encryption(struct at76_priv *priv) -{ - int i; - int ret; - /*int key_len;*/ - struct mib_mac_encryption *m; - - m = kmalloc(sizeof(struct mib_mac_encryption), GFP_KERNEL); - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC_ENCRYPTION, m, - sizeof(struct mib_mac_encryption)); - if (ret < 0) { - dev_err(&priv->udev->dev, - "%s: at76_get_mib (MAC_ENCRYPTION) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); - goto exit; - } - - at76_dbg(DBG_MIB, - "%s: MIB MAC_ENCRYPTION: tkip_bssid %s priv_invoked %u " - "ciph_key_id %u grp_key_id %u excl_unencr %u " - "ckip_key_perm %u wep_icv_err %u wep_excluded %u", - wiphy_name(priv->hw->wiphy), mac2str(m->tkip_bssid), - m->privacy_invoked, m->cipher_default_key_id, - m->cipher_default_group_key_id, m->exclude_unencrypted, - m->ckip_key_permutation, - le32_to_cpu(m->wep_icv_error_count), - le32_to_cpu(m->wep_excluded_count)); - - /*key_len = (m->encryption_level == 1) ? - WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN;*/ - - for (i = 0; i < CIPHER_KEYS; i++) - at76_dbg(DBG_MIB, "%s: MIB MAC_ENCRYPTION: key %d: %s", - wiphy_name(priv->hw->wiphy), i, - hex2str(m->cipher_default_keyvalue[i], - CIPHER_KEY_LEN)); -exit: - kfree(m); -} - static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) { int ret; @@ -1125,7 +1131,7 @@ static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) sizeof(struct mib_mac_mgmt)); if (ret < 0) { printk(KERN_ERR "%s: at76_get_mib (MAC_MGMT) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); goto exit; } @@ -1136,7 +1142,7 @@ static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) "pm_mode %d ibss_change %d res %d " "multi_domain_capability_implemented %d " "international_roaming %d country_string %.3s", - wiphy_name(priv->hw->wiphy), le16_to_cpu(m->beacon_period), + priv->netdev->name, le16_to_cpu(m->beacon_period), le16_to_cpu(m->CFP_max_duration), le16_to_cpu(m->medium_occupancy_limit), le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), @@ -1161,7 +1167,7 @@ static void at76_dump_mib_mac(struct at76_priv *priv) ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac)); if (ret < 0) { printk(KERN_ERR "%s: at76_get_mib (MAC) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); goto exit; } @@ -1171,8 +1177,7 @@ static void at76_dump_mib_mac(struct at76_priv *priv) "scan_type %d scan_channel %d probe_delay %u " "min_channel_time %d max_channel_time %d listen_int %d " "desired_ssid %s desired_bssid %s desired_bsstype %d", - wiphy_name(priv->hw->wiphy), - le32_to_cpu(m->max_tx_msdu_lifetime), + priv->netdev->name, le32_to_cpu(m->max_tx_msdu_lifetime), le32_to_cpu(m->max_rx_lifetime), le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold), le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax), @@ -1198,7 +1203,7 @@ static void at76_dump_mib_phy(struct at76_priv *priv) ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy)); if (ret < 0) { printk(KERN_ERR "%s: at76_get_mib (PHY) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); goto exit; } @@ -1207,7 +1212,7 @@ static void at76_dump_mib_phy(struct at76_priv *priv) "mpdu_max_length %d cca_mode_supported %d operation_rate_set " "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " "phy_type %d current_reg_domain %d", - wiphy_name(priv->hw->wiphy), le32_to_cpu(m->ed_threshold), + priv->netdev->name, le32_to_cpu(m->ed_threshold), le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time), le16_to_cpu(m->preamble_length), le16_to_cpu(m->plcp_header_length), @@ -1231,14 +1236,13 @@ static void at76_dump_mib_local(struct at76_priv *priv) ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(struct mib_local)); if (ret < 0) { printk(KERN_ERR "%s: at76_get_mib (LOCAL) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d " "txautorate_fallback %d ssid_size %d promiscuous_mode %d " - "preamble_type %d", wiphy_name(priv->hw->wiphy), - m->beacon_enable, + "preamble_type %d", priv->netdev->name, m->beacon_enable, m->txautorate_fallback, m->ssid_size, m->promiscuous_mode, m->preamble_type); exit: @@ -1257,21 +1261,118 @@ static void at76_dump_mib_mdomain(struct at76_priv *priv) sizeof(struct mib_mdomain)); if (ret < 0) { printk(KERN_ERR "%s: at76_get_mib (MDOMAIN) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); goto exit; } at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %s", - wiphy_name(priv->hw->wiphy), + priv->netdev->name, hex2str(m->channel_list, sizeof(m->channel_list))); at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %s", - wiphy_name(priv->hw->wiphy), + priv->netdev->name, hex2str(m->tx_powerlevel, sizeof(m->tx_powerlevel))); exit: kfree(m); } +static int at76_get_current_bssid(struct at76_priv *priv) +{ + int ret = 0; + struct mib_mac_mgmt *mac_mgmt = + kmalloc(sizeof(struct mib_mac_mgmt), GFP_KERNEL); + + if (!mac_mgmt) { + ret = -ENOMEM; + goto exit; + } + + ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, mac_mgmt, + sizeof(struct mib_mac_mgmt)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib failed: %d\n", + priv->netdev->name, ret); + goto error; + } + memcpy(priv->bssid, mac_mgmt->current_bssid, ETH_ALEN); + printk(KERN_INFO "%s: using BSSID %s\n", priv->netdev->name, + mac2str(priv->bssid)); +error: + kfree(mac_mgmt); +exit: + return ret; +} + +static int at76_get_current_channel(struct at76_priv *priv) +{ + int ret = 0; + struct mib_phy *phy = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); + + if (!phy) { + ret = -ENOMEM; + goto exit; + } + ret = at76_get_mib(priv->udev, MIB_PHY, phy, sizeof(struct mib_phy)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib(MIB_PHY) failed: %d\n", + priv->netdev->name, ret); + goto error; + } + priv->channel = phy->channel_id; +error: + kfree(phy); +exit: + return ret; +} + +/** + * at76_start_scan - start a scan + * + * @use_essid - use the configured ESSID in non passive mode + */ +static int at76_start_scan(struct at76_priv *priv, int use_essid) +{ + struct at76_req_scan scan; + + memset(&scan, 0, sizeof(struct at76_req_scan)); + memset(scan.bssid, 0xff, ETH_ALEN); + + if (use_essid) { + memcpy(scan.essid, priv->essid, IW_ESSID_MAX_SIZE); + scan.essid_size = priv->essid_size; + } else + scan.essid_size = 0; + + /* jal: why should we start at a certain channel? we do scan the whole + range allowed by reg domain. */ + scan.channel = priv->channel; + + /* atmelwlandriver differs between scan type 0 and 1 (active/passive) + For ad-hoc mode, it uses type 0 only. */ + scan.scan_type = priv->scan_mode; + + /* INFO: For probe_delay, not multiplying by 1024 as this will be + slightly less than min_channel_time + (per spec: probe delay < min. channel time) */ + scan.min_channel_time = cpu_to_le16(priv->scan_min_time); + scan.max_channel_time = cpu_to_le16(priv->scan_max_time); + scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); + scan.international_scan = 0; + + /* other values are set to 0 for type 0 */ + + at76_dbg(DBG_PROGRESS, "%s: start_scan (use_essid = %d, intl = %d, " + "channel = %d, probe_delay = %d, scan_min_time = %d, " + "scan_max_time = %d)", + priv->netdev->name, use_essid, + scan.international_scan, scan.channel, + le16_to_cpu(scan.probe_delay), + le16_to_cpu(scan.min_channel_time), + le16_to_cpu(scan.max_channel_time)); + + return at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); +} + /* Enable monitor mode */ static int at76_start_monitor(struct at76_priv *priv) { @@ -1292,6 +1393,86 @@ static int at76_start_monitor(struct at76_priv *priv) return ret; } +static int at76_start_ibss(struct at76_priv *priv) +{ + struct at76_req_ibss bss; + int ret; + + WARN_ON(priv->mac_state != MAC_OWN_IBSS); + if (priv->mac_state != MAC_OWN_IBSS) + return -EBUSY; + + memset(&bss, 0, sizeof(struct at76_req_ibss)); + memset(bss.bssid, 0xff, ETH_ALEN); + memcpy(bss.essid, priv->essid, IW_ESSID_MAX_SIZE); + bss.essid_size = priv->essid_size; + bss.bss_type = ADHOC_MODE; + bss.channel = priv->channel; + + ret = at76_set_card_command(priv->udev, CMD_START_IBSS, &bss, + sizeof(struct at76_req_ibss)); + if (ret < 0) { + printk(KERN_ERR "%s: start_ibss failed: %d\n", + priv->netdev->name, ret); + return ret; + } + + ret = at76_wait_completion(priv, CMD_START_IBSS); + if (ret != CMD_STATUS_COMPLETE) { + printk(KERN_ERR "%s: start_ibss failed to complete, %d\n", + priv->netdev->name, ret); + return ret; + } + + ret = at76_get_current_bssid(priv); + if (ret < 0) + return ret; + + ret = at76_get_current_channel(priv); + if (ret < 0) + return ret; + + /* not sure what this is good for ??? */ + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, ibss_change); + priv->mib_buf.data.byte = 0; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) { + printk(KERN_ERR "%s: set_mib (ibss change ok) failed: %d\n", + priv->netdev->name, ret); + return ret; + } + + netif_carrier_on(priv->netdev); + netif_start_queue(priv->netdev); + return 0; +} + +/* Request card to join BSS in managed or ad-hoc mode */ +static int at76_join_bss(struct at76_priv *priv, struct bss_info *ptr) +{ + struct at76_req_join join; + + BUG_ON(!ptr); + + memset(&join, 0, sizeof(struct at76_req_join)); + memcpy(join.bssid, ptr->bssid, ETH_ALEN); + memcpy(join.essid, ptr->ssid, ptr->ssid_len); + join.essid_size = ptr->ssid_len; + join.bss_type = (priv->iw_mode == IW_MODE_ADHOC ? 1 : 2); + join.channel = ptr->channel; + join.timeout = cpu_to_le16(2000); + + at76_dbg(DBG_PROGRESS, + "%s join addr %s ssid %s type %d ch %d timeout %d", + priv->netdev->name, mac2str(join.bssid), join.essid, + join.bss_type, join.channel, le16_to_cpu(join.timeout)); + return at76_set_card_command(priv->udev, CMD_JOIN, &join, + sizeof(struct at76_req_join)); +} + /* Calculate padding from txbuf->wlength (which excludes the USB TX header), likely to compensate a flaw in the AT76C503A USB part ... */ static inline int at76_calc_padding(int wlen) @@ -1310,6 +1491,14 @@ static inline int at76_calc_padding(int wlen) return 0; } +/* We are doing a lot of things here in an interrupt. Need + a bh handler (Watching TV with a TV card is probably + a good test: if you see flickers, we are doing too much. + Currently I do see flickers... even with our tasklet :-( ) + Maybe because the bttv driver and usb-uhci use the same interrupt +*/ +/* Or maybe because our BH handler is preempting bttv's BH handler.. BHs don't + * solve everything.. (alex) */ static void at76_rx_callback(struct urb *urb) { struct at76_priv *priv = urb->context; @@ -1319,67 +1508,1911 @@ static void at76_rx_callback(struct urb *urb) return; } -static int at76_submit_rx_urb(struct at76_priv *priv) +static void at76_tx_callback(struct urb *urb) { + struct at76_priv *priv = urb->context; + struct net_device_stats *stats = &priv->stats; + unsigned long flags; + struct at76_tx_buffer *mgmt_buf; int ret; - int size; - struct sk_buff *skb = priv->rx_skb; - if (!priv->rx_urb) { - printk(KERN_ERR "%s: %s: priv->rx_urb is NULL\n", - wiphy_name(priv->hw->wiphy), __func__); - return -EFAULT; + switch (urb->status) { + case 0: + stats->tx_packets++; + break; + case -ENOENT: + case -ECONNRESET: + /* urb has been unlinked */ + return; + default: + at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", + __func__, urb->status); + stats->tx_errors++; + break; } - if (!skb) { - skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); - if (!skb) { - printk(KERN_ERR "%s: cannot allocate rx skbuff\n", - wiphy_name(priv->hw->wiphy)); - ret = -ENOMEM; - goto exit; - } - priv->rx_skb = skb; - } else { - skb_push(skb, skb_headroom(skb)); - skb_trim(skb, 0); - } + spin_lock_irqsave(&priv->mgmt_spinlock, flags); + mgmt_buf = priv->next_mgmt_bulk; + priv->next_mgmt_bulk = NULL; + spin_unlock_irqrestore(&priv->mgmt_spinlock, flags); - size = skb_tailroom(skb); - usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, - skb_put(skb, size), size, at76_rx_callback, priv); - ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); - if (ret < 0) { - if (ret == -ENODEV) - at76_dbg(DBG_DEVSTART, - "usb_submit_urb returned -ENODEV"); - else - printk(KERN_ERR "%s: rx, usb_submit_urb failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + if (!mgmt_buf) { + netif_wake_queue(priv->netdev); + return; } -exit: - if (ret < 0 && ret != -ENODEV) - printk(KERN_ERR "%s: cannot submit rx urb - please unload the " - "driver and/or power cycle the device\n", - wiphy_name(priv->hw->wiphy)); + /* we don't copy the padding bytes, but add them + to the length */ + memcpy(priv->bulk_out_buffer, mgmt_buf, + le16_to_cpu(mgmt_buf->wlength) + AT76_TX_HDRLEN); + usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, + priv->bulk_out_buffer, + le16_to_cpu(mgmt_buf->wlength) + mgmt_buf->padding + + AT76_TX_HDRLEN, at76_tx_callback, priv); + ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); + if (ret) + printk(KERN_ERR "%s: error in tx submit urb: %d\n", + priv->netdev->name, ret); - return ret; + kfree(mgmt_buf); } -/* Download external firmware */ -static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) +/* Send a management frame on bulk-out. txbuf->wlength must be set */ +static int at76_tx_mgmt(struct at76_priv *priv, struct at76_tx_buffer *txbuf) { + unsigned long flags; int ret; - int op_mode; - int blockno = 0; - int bsize; - u8 *block; - u8 *buf = fwe->extfw; - int size = fwe->extfw_size; + int urb_status; + void *oldbuf = NULL; - if (!buf || !size) - return -ENOENT; + netif_carrier_off(priv->netdev); /* stop netdev watchdog */ + netif_stop_queue(priv->netdev); /* stop tx data packets */ + + spin_lock_irqsave(&priv->mgmt_spinlock, flags); + + urb_status = priv->tx_urb->status; + if (urb_status == -EINPROGRESS) { + /* cannot transmit now, put in the queue */ + oldbuf = priv->next_mgmt_bulk; + priv->next_mgmt_bulk = txbuf; + } + spin_unlock_irqrestore(&priv->mgmt_spinlock, flags); + + if (oldbuf) { + /* a data/mgmt tx is already pending in the URB - + if this is no error in some situations we must + implement a queue or silently modify the old msg */ + printk(KERN_ERR "%s: removed pending mgmt buffer %s\n", + priv->netdev->name, hex2str(oldbuf, 64)); + kfree(oldbuf); + return 0; + } + + txbuf->tx_rate = TX_RATE_1MBIT; + txbuf->padding = at76_calc_padding(le16_to_cpu(txbuf->wlength)); + memset(txbuf->reserved, 0, sizeof(txbuf->reserved)); + + if (priv->next_mgmt_bulk) + printk(KERN_ERR "%s: URB status %d, but mgmt is pending\n", + priv->netdev->name, urb_status); + + at76_dbg(DBG_TX_MGMT, + "%s: tx mgmt: wlen %d tx_rate %d pad %d %s", + priv->netdev->name, le16_to_cpu(txbuf->wlength), + txbuf->tx_rate, txbuf->padding, + hex2str(txbuf->packet, le16_to_cpu(txbuf->wlength))); + + /* txbuf was not consumed above -> send mgmt msg immediately */ + memcpy(priv->bulk_out_buffer, txbuf, + le16_to_cpu(txbuf->wlength) + AT76_TX_HDRLEN); + usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, + priv->bulk_out_buffer, + le16_to_cpu(txbuf->wlength) + txbuf->padding + + AT76_TX_HDRLEN, at76_tx_callback, priv); + ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); + if (ret) + printk(KERN_ERR "%s: error in tx submit urb: %d\n", + priv->netdev->name, ret); + + kfree(txbuf); + + return ret; +} + +/* Go to the next information element */ +static inline void next_ie(struct ieee80211_info_element **ie) +{ + *ie = (struct ieee80211_info_element *)(&(*ie)->data[(*ie)->len]); +} + +/* Challenge is the challenge string (in TLV format) + we got with seq_nr 2 for shared secret authentication only and + send in seq_nr 3 WEP encrypted to prove we have the correct WEP key; + otherwise it is NULL */ +static int at76_auth_req(struct at76_priv *priv, struct bss_info *bss, + int seq_nr, struct ieee80211_info_element *challenge) +{ + struct at76_tx_buffer *tx_buffer; + struct ieee80211_hdr_3addr *mgmt; + struct ieee80211_auth *req; + int buf_len = (seq_nr != 3 ? AUTH_FRAME_SIZE : + AUTH_FRAME_SIZE + 1 + 1 + challenge->len); + + BUG_ON(!bss); + BUG_ON(seq_nr == 3 && !challenge); + tx_buffer = kmalloc(buf_len + MAX_PADDING_SIZE, GFP_ATOMIC); + if (!tx_buffer) + return -ENOMEM; + + req = (struct ieee80211_auth *)tx_buffer->packet; + mgmt = &req->header; + + /* make wireless header */ + /* first auth msg is not encrypted, only the second (seq_nr == 3) */ + mgmt->frame_ctl = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH | + (seq_nr == 3 ? IEEE80211_FCTL_PROTECTED : 0)); + + mgmt->duration_id = cpu_to_le16(0x8000); + memcpy(mgmt->addr1, bss->bssid, ETH_ALEN); + memcpy(mgmt->addr2, priv->netdev->dev_addr, ETH_ALEN); + memcpy(mgmt->addr3, bss->bssid, ETH_ALEN); + mgmt->seq_ctl = cpu_to_le16(0); + + req->algorithm = cpu_to_le16(priv->auth_mode); + req->transaction = cpu_to_le16(seq_nr); + req->status = cpu_to_le16(0); + + if (seq_nr == 3) + memcpy(req->info_element, challenge, 1 + 1 + challenge->len); + + /* init. at76_priv tx header */ + tx_buffer->wlength = cpu_to_le16(buf_len - AT76_TX_HDRLEN); + at76_dbg(DBG_TX_MGMT, "%s: AuthReq bssid %s alg %d seq_nr %d", + priv->netdev->name, mac2str(mgmt->addr3), + le16_to_cpu(req->algorithm), le16_to_cpu(req->transaction)); + if (seq_nr == 3) + at76_dbg(DBG_TX_MGMT, "%s: AuthReq challenge: %s ...", + priv->netdev->name, hex2str(req->info_element, 18)); + + /* either send immediately (if no data tx is pending + or put it in pending list */ + return at76_tx_mgmt(priv, tx_buffer); +} + +static int at76_assoc_req(struct at76_priv *priv, struct bss_info *bss) +{ + struct at76_tx_buffer *tx_buffer; + struct ieee80211_hdr_3addr *mgmt; + struct ieee80211_assoc_request *req; + struct ieee80211_info_element *ie; + char *essid; + int essid_len; + u16 capa; + + BUG_ON(!bss); + + tx_buffer = kmalloc(ASSOCREQ_MAX_SIZE + MAX_PADDING_SIZE, GFP_ATOMIC); + if (!tx_buffer) + return -ENOMEM; + + req = (struct ieee80211_assoc_request *)tx_buffer->packet; + mgmt = &req->header; + ie = req->info_element; + + /* make wireless header */ + mgmt->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ASSOC_REQ); + + mgmt->duration_id = cpu_to_le16(0x8000); + memcpy(mgmt->addr1, bss->bssid, ETH_ALEN); + memcpy(mgmt->addr2, priv->netdev->dev_addr, ETH_ALEN); + memcpy(mgmt->addr3, bss->bssid, ETH_ALEN); + mgmt->seq_ctl = cpu_to_le16(0); + + /* we must set the Privacy bit in the capabilities to assure an + Agere-based AP with optional WEP transmits encrypted frames + to us. AP only set the Privacy bit in their capabilities + if WEP is mandatory in the BSS! */ + capa = bss->capa; + if (priv->wep_enabled) + capa |= WLAN_CAPABILITY_PRIVACY; + if (priv->preamble_type != PREAMBLE_TYPE_LONG) + capa |= WLAN_CAPABILITY_SHORT_PREAMBLE; + req->capability = cpu_to_le16(capa); + + req->listen_interval = cpu_to_le16(2 * bss->beacon_interval); + + /* write TLV data elements */ + + ie->id = MFIE_TYPE_SSID; + ie->len = bss->ssid_len; + memcpy(ie->data, bss->ssid, bss->ssid_len); + next_ie(&ie); + + ie->id = MFIE_TYPE_RATES; + ie->len = sizeof(hw_rates); + memcpy(ie->data, hw_rates, sizeof(hw_rates)); + next_ie(&ie); /* ie points behind the supp_rates field */ + + /* init. at76_priv tx header */ + tx_buffer->wlength = cpu_to_le16((u8 *)ie - (u8 *)mgmt); + + ie = req->info_element; + essid = ie->data; + essid_len = min_t(int, IW_ESSID_MAX_SIZE, ie->len); + + next_ie(&ie); /* points to IE of rates now */ + at76_dbg(DBG_TX_MGMT, + "%s: AssocReq bssid %s capa 0x%04x ssid %.*s rates %s", + priv->netdev->name, mac2str(mgmt->addr3), + le16_to_cpu(req->capability), essid_len, essid, + hex2str(ie->data, ie->len)); + + /* either send immediately (if no data tx is pending + or put it in pending list */ + return at76_tx_mgmt(priv, tx_buffer); +} + +/* We got to check the bss_list for old entries */ +static void at76_bss_list_timeout(unsigned long par) +{ + struct at76_priv *priv = (struct at76_priv *)par; + unsigned long flags; + struct list_head *lptr, *nptr; + struct bss_info *ptr; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + + list_for_each_safe(lptr, nptr, &priv->bss_list) { + + ptr = list_entry(lptr, struct bss_info, list); + + if (ptr != priv->curr_bss + && time_after(jiffies, ptr->last_rx + BSS_LIST_TIMEOUT)) { + at76_dbg(DBG_BSS_TABLE_RM, + "%s: bss_list: removing old BSS %s ch %d", + priv->netdev->name, mac2str(ptr->bssid), + ptr->channel); + list_del(&ptr->list); + kfree(ptr); + } + } + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); + /* restart the timer */ + mod_timer(&priv->bss_list_timer, jiffies + BSS_LIST_TIMEOUT); +} + +static inline void at76_set_mac_state(struct at76_priv *priv, + enum mac_state mac_state) +{ + at76_dbg(DBG_MAC_STATE, "%s state: %s", priv->netdev->name, + mac_states[mac_state]); + priv->mac_state = mac_state; +} + +static void at76_dump_bss_table(struct at76_priv *priv) +{ + struct bss_info *ptr; + unsigned long flags; + struct list_head *lptr; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + + at76_dbg(DBG_BSS_TABLE, "%s BSS table (curr=%p):", priv->netdev->name, + priv->curr_bss); + + list_for_each(lptr, &priv->bss_list) { + ptr = list_entry(lptr, struct bss_info, list); + at76_dbg(DBG_BSS_TABLE, "0x%p: bssid %s channel %d ssid %.*s " + "(%s) capa 0x%04x rates %s rssi %d link %d noise %d", + ptr, mac2str(ptr->bssid), ptr->channel, ptr->ssid_len, + ptr->ssid, hex2str(ptr->ssid, ptr->ssid_len), + ptr->capa, hex2str(ptr->rates, ptr->rates_len), + ptr->rssi, ptr->link_qual, ptr->noise_level); + } + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); +} + +/* Called upon successful association to mark interface as connected */ +static void at76_work_assoc_done(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_assoc_done); + + mutex_lock(&priv->mtx); + + WARN_ON(priv->mac_state != MAC_ASSOC); + WARN_ON(!priv->curr_bss); + if (priv->mac_state != MAC_ASSOC || !priv->curr_bss) + goto exit; + + if (priv->iw_mode == IW_MODE_INFRA) { + if (priv->pm_mode != AT76_PM_OFF) { + /* calculate the listen interval in units of + beacon intervals of the curr_bss */ + u32 pm_period_beacon = (priv->pm_period >> 10) / + priv->curr_bss->beacon_interval; + + pm_period_beacon = max(pm_period_beacon, 2u); + pm_period_beacon = min(pm_period_beacon, 0xffffu); + + at76_dbg(DBG_PM, + "%s: pm_mode %d assoc id 0x%x listen int %d", + priv->netdev->name, priv->pm_mode, + priv->assoc_id, pm_period_beacon); + + at76_set_associd(priv, priv->assoc_id); + at76_set_listen_interval(priv, (u16)pm_period_beacon); + } + schedule_delayed_work(&priv->dwork_beacon, BEACON_TIMEOUT); + } + at76_set_pm_mode(priv); + + netif_carrier_on(priv->netdev); + netif_wake_queue(priv->netdev); + at76_set_mac_state(priv, MAC_CONNECTED); + at76_iwevent_bss_connect(priv->netdev, priv->curr_bss->bssid); + at76_dbg(DBG_PROGRESS, "%s: connected to BSSID %s", + priv->netdev->name, mac2str(priv->curr_bss->bssid)); + +exit: + mutex_unlock(&priv->mtx); +} + +/* We only store the new mac address in netdev struct, + it gets set when the netdev is opened. */ +static int at76_set_mac_address(struct net_device *netdev, void *addr) +{ + struct sockaddr *mac = addr; + memcpy(netdev->dev_addr, mac->sa_data, ETH_ALEN); + return 1; +} + +static struct net_device_stats *at76_get_stats(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + return &priv->stats; +} + +static struct iw_statistics *at76_get_wireless_stats(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "RETURN qual %d level %d noise %d updated %d", + priv->wstats.qual.qual, priv->wstats.qual.level, + priv->wstats.qual.noise, priv->wstats.qual.updated); + + return &priv->wstats; +} + +static void at76_set_multicast(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + int promisc; + + promisc = ((netdev->flags & IFF_PROMISC) != 0); + if (promisc != priv->promisc) { + /* This gets called in interrupt, must reschedule */ + priv->promisc = promisc; + schedule_work(&priv->work_set_promisc); + } +} + +/* Stop all network activity, flush all pending tasks */ +static void at76_quiesce(struct at76_priv *priv) +{ + unsigned long flags; + + netif_stop_queue(priv->netdev); + netif_carrier_off(priv->netdev); + + at76_set_mac_state(priv, MAC_INIT); + + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_beacon); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); + cancel_delayed_work(&priv->dwork_restart); + + spin_lock_irqsave(&priv->mgmt_spinlock, flags); + kfree(priv->next_mgmt_bulk); + priv->next_mgmt_bulk = NULL; + spin_unlock_irqrestore(&priv->mgmt_spinlock, flags); +} + +/******************************************************************************* + * at76_priv implementations of iw_handler functions: + */ +static int at76_iw_handler_commit(struct net_device *netdev, + struct iw_request_info *info, + void *null, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s %s: restarting the device", netdev->name, + __func__); + + if (priv->mac_state != MAC_INIT) + at76_quiesce(priv); + + /* Wait half second before the restart to process subsequent + * requests from the same iwconfig in a single restart */ + schedule_delayed_work(&priv->dwork_restart, HZ / 2); + + return 0; +} + +static int at76_iw_handler_get_name(struct net_device *netdev, + struct iw_request_info *info, + char *name, char *extra) +{ + strcpy(name, "IEEE 802.11b"); + at76_dbg(DBG_IOCTL, "%s: SIOCGIWNAME - name %s", netdev->name, name); + return 0; +} + +static int at76_iw_handler_set_freq(struct net_device *netdev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int chan = -1; + int ret = -EIWCOMMIT; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - freq.m %d freq.e %d", + netdev->name, freq->m, freq->e); + + if ((freq->e == 0) && (freq->m <= 1000)) + /* Setting by channel number */ + chan = freq->m; + else { + /* Setting by frequency - search the table */ + int mult = 1; + int i; + + for (i = 0; i < (6 - freq->e); i++) + mult *= 10; + + for (i = 0; i < NUM_CHANNELS; i++) { + if (freq->m == (channel_frequency[i] * mult)) + chan = i + 1; + } + } + + if (chan < 1 || !priv->domain) + /* non-positive channels are invalid + * we need a domain info to set the channel + * either that or an invalid frequency was + * provided by the user */ + ret = -EINVAL; + else if (!(priv->domain->channel_map & (1 << (chan - 1)))) { + printk(KERN_INFO "%s: channel %d not allowed for domain %s\n", + priv->netdev->name, chan, priv->domain->name); + ret = -EINVAL; + } + + if (ret == -EIWCOMMIT) { + priv->channel = chan; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - ch %d", netdev->name, + chan); + } + + return ret; +} + +static int at76_iw_handler_get_freq(struct net_device *netdev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + freq->m = priv->channel; + freq->e = 0; + + if (priv->channel) + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - freq %ld x 10e%d", + netdev->name, channel_frequency[priv->channel - 1], 6); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - ch %d", netdev->name, + priv->channel); + + return 0; +} + +static int at76_iw_handler_set_mode(struct net_device *netdev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWMODE - %d", netdev->name, *mode); + + if ((*mode != IW_MODE_ADHOC) && (*mode != IW_MODE_INFRA) && + (*mode != IW_MODE_MONITOR)) + return -EINVAL; + + priv->iw_mode = *mode; + if (priv->iw_mode != IW_MODE_INFRA) + priv->pm_mode = AT76_PM_OFF; + + return -EIWCOMMIT; +} + +static int at76_iw_handler_get_mode(struct net_device *netdev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + *mode = priv->iw_mode; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWMODE - %d", netdev->name, *mode); + + return 0; +} + +static int at76_iw_handler_get_range(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + /* inspired by atmel.c */ + struct at76_priv *priv = netdev_priv(netdev); + struct iw_range *range = (struct iw_range *)extra; + int i; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + /* TODO: range->throughput = xxxxxx; */ + + range->min_nwid = 0x0000; + range->max_nwid = 0x0000; + + /* this driver doesn't maintain sensitivity information */ + range->sensitivity = 0; + + range->max_qual.qual = 100; + range->max_qual.level = 100; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_NOISE_INVALID; + + range->avg_qual.qual = 50; + range->avg_qual.level = 50; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_NOISE_INVALID; + + range->bitrate[0] = 1000000; + range->bitrate[1] = 2000000; + range->bitrate[2] = 5500000; + range->bitrate[3] = 11000000; + range->num_bitrates = 4; + + range->min_rts = 0; + range->max_rts = MAX_RTS_THRESHOLD; + + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_ON; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_ALL_R; + + range->encoding_size[0] = WEP_SMALL_KEY_LEN; + range->encoding_size[1] = WEP_LARGE_KEY_LEN; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = WEP_KEYS; + + /* both WL-240U and Linksys WUSB11 v2.6 specify 15 dBm as output power + - take this for all (ignore antenna gains) */ + range->txpower[0] = 15; + range->num_txpower = 1; + range->txpower_capa = IW_TXPOW_DBM; + + range->we_version_source = WIRELESS_EXT; + range->we_version_compiled = WIRELESS_EXT; + + /* same as the values used in atmel.c */ + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = 0; + range->min_retry = 1; + range->max_retry = 255; + + range->num_channels = NUM_CHANNELS; + range->num_frequency = 0; + + for (i = 0; i < NUM_CHANNELS; i++) { + /* test if channel map bit is raised */ + if (priv->domain->channel_map & (0x1 << i)) { + range->num_frequency += 1; + + range->freq[i].i = i + 1; + range->freq[i].m = channel_frequency[i] * 100000; + range->freq[i].e = 1; /* freq * 10^1 */ + } + } + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRANGE", netdev->name); + + return 0; +} + +static int at76_iw_handler_set_spy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWSPY - number of addresses %d", + netdev->name, data->length); + + spin_lock_bh(&priv->spy_spinlock); + ret = iw_handler_set_spy(priv->netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&priv->spy_spinlock); + + return ret; +} + +static int at76_iw_handler_get_spy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + spin_lock_bh(&priv->spy_spinlock); + ret = iw_handler_get_spy(priv->netdev, info, + (union iwreq_data *)data, extra); + spin_unlock_bh(&priv->spy_spinlock); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWSPY - number of addresses %d", + netdev->name, data->length); + + return ret; +} + +static int at76_iw_handler_set_thrspy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWTHRSPY - number of addresses %d)", + netdev->name, data->length); + + spin_lock_bh(&priv->spy_spinlock); + ret = iw_handler_set_thrspy(netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&priv->spy_spinlock); + + return ret; +} + +static int at76_iw_handler_get_thrspy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret; + + spin_lock_bh(&priv->spy_spinlock); + ret = iw_handler_get_thrspy(netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&priv->spy_spinlock); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWTHRSPY - number of addresses %d)", + netdev->name, data->length); + + return ret; +} + +static int at76_iw_handler_set_wap(struct net_device *netdev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWAP - wap/bssid %s", netdev->name, + mac2str(ap_addr->sa_data)); + + /* if the incoming address == ff:ff:ff:ff:ff:ff, the user has + chosen any or auto AP preference */ + if (is_broadcast_ether_addr(ap_addr->sa_data) + || is_zero_ether_addr(ap_addr->sa_data)) + priv->wanted_bssid_valid = 0; + else { + /* user wants to set a preferred AP address */ + priv->wanted_bssid_valid = 1; + memcpy(priv->wanted_bssid, ap_addr->sa_data, ETH_ALEN); + } + + return -EIWCOMMIT; +} + +static int at76_iw_handler_get_wap(struct net_device *netdev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + ap_addr->sa_family = ARPHRD_ETHER; + memcpy(ap_addr->sa_data, priv->bssid, ETH_ALEN); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWAP - wap/bssid %s", netdev->name, + mac2str(ap_addr->sa_data)); + + return 0; +} + +static int at76_iw_handler_set_scan(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWSCAN", netdev->name); + + if (mutex_lock_interruptible(&priv->mtx)) + return -EINTR; + + if (!netif_running(netdev)) { + ret = -ENETDOWN; + goto exit; + } + + /* jal: we don't allow "iwlist ethX scan" while we are + in monitor mode */ + if (priv->iw_mode == IW_MODE_MONITOR) { + ret = -EBUSY; + goto exit; + } + + /* Discard old scan results */ + if ((jiffies - priv->last_scan) > (20 * HZ)) + priv->scan_state = SCAN_IDLE; + priv->last_scan = jiffies; + + /* Initiate a scan command */ + if (priv->scan_state == SCAN_IN_PROGRESS) { + ret = -EBUSY; + goto exit; + } + + priv->scan_state = SCAN_IN_PROGRESS; + + at76_quiesce(priv); + + /* Try to do passive or active scan if WE asks as. */ + if (wrqu->data.length + && wrqu->data.length == sizeof(struct iw_scan_req)) { + struct iw_scan_req *req = (struct iw_scan_req *)extra; + + if (req->scan_type == IW_SCAN_TYPE_PASSIVE) + priv->scan_mode = SCAN_TYPE_PASSIVE; + else if (req->scan_type == IW_SCAN_TYPE_ACTIVE) + priv->scan_mode = SCAN_TYPE_ACTIVE; + + /* Sanity check values? */ + if (req->min_channel_time > 0) + priv->scan_min_time = req->min_channel_time; + + if (req->max_channel_time > 0) + priv->scan_max_time = req->max_channel_time; + } + + /* change to scanning state */ + at76_set_mac_state(priv, MAC_SCANNING); + schedule_work(&priv->work_start_scan); + +exit: + mutex_unlock(&priv->mtx); + return ret; +} + +static int at76_iw_handler_get_scan(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + unsigned long flags; + struct list_head *lptr, *nptr; + struct bss_info *curr_bss; + struct iw_event *iwe = kmalloc(sizeof(struct iw_event), GFP_KERNEL); + char *curr_val, *curr_pos = extra; + int i; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWSCAN", netdev->name); + + if (!iwe) + return -ENOMEM; + + if (priv->scan_state != SCAN_COMPLETED) { + /* scan not yet finished */ + kfree(iwe); + return -EAGAIN; + } + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + + list_for_each_safe(lptr, nptr, &priv->bss_list) { + curr_bss = list_entry(lptr, struct bss_info, list); + + iwe->cmd = SIOCGIWAP; + iwe->u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe->u.ap_addr.sa_data, curr_bss->bssid, 6); + curr_pos = iwe_stream_add_event(info, curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_ADDR_LEN); + + iwe->u.data.length = curr_bss->ssid_len; + iwe->cmd = SIOCGIWESSID; + iwe->u.data.flags = 1; + + curr_pos = iwe_stream_add_point(info, curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + curr_bss->ssid); + + iwe->cmd = SIOCGIWMODE; + iwe->u.mode = (curr_bss->capa & WLAN_CAPABILITY_IBSS) ? + IW_MODE_ADHOC : + (curr_bss->capa & WLAN_CAPABILITY_ESS) ? + IW_MODE_MASTER : IW_MODE_AUTO; + /* IW_MODE_AUTO = 0 which I thought is + * the most logical value to return in this case */ + curr_pos = iwe_stream_add_event(info, curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_UINT_LEN); + + iwe->cmd = SIOCGIWFREQ; + iwe->u.freq.m = curr_bss->channel; + iwe->u.freq.e = 0; + curr_pos = iwe_stream_add_event(info, curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_FREQ_LEN); + + iwe->cmd = SIOCGIWENCODE; + if (curr_bss->capa & WLAN_CAPABILITY_PRIVACY) + iwe->u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe->u.data.flags = IW_ENCODE_DISABLED; + + iwe->u.data.length = 0; + curr_pos = iwe_stream_add_point(info, curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + NULL); + + /* Add quality statistics */ + iwe->cmd = IWEVQUAL; + iwe->u.qual.noise = 0; + iwe->u.qual.updated = + IW_QUAL_NOISE_INVALID | IW_QUAL_LEVEL_UPDATED; + iwe->u.qual.level = (curr_bss->rssi * 100 / 42); + if (iwe->u.qual.level > 100) + iwe->u.qual.level = 100; + if (at76_is_intersil(priv->board_type)) + iwe->u.qual.qual = curr_bss->link_qual; + else { + iwe->u.qual.qual = 0; + iwe->u.qual.updated |= IW_QUAL_QUAL_INVALID; + } + /* Add new value to event */ + curr_pos = iwe_stream_add_event(info, curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_QUAL_LEN); + + /* Rate: stuffing multiple values in a single event requires + * a bit more of magic - Jean II */ + curr_val = curr_pos + IW_EV_LCP_LEN; + + iwe->cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe->u.bitrate.fixed = 0; + iwe->u.bitrate.disabled = 0; + /* Max 8 values */ + for (i = 0; i < curr_bss->rates_len; i++) { + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe->u.bitrate.value = + ((curr_bss->rates[i] & 0x7f) * 500000); + /* Add new value to event */ + curr_val = iwe_stream_add_value(info, curr_pos, + curr_val, + extra + + IW_SCAN_MAX_DATA, iwe, + IW_EV_PARAM_LEN); + } + + /* Check if we added any event */ + if ((curr_val - curr_pos) > IW_EV_LCP_LEN) + curr_pos = curr_val; + + /* more information may be sent back using IWECUSTOM */ + + } + + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); + + data->length = (curr_pos - extra); + data->flags = 0; + + kfree(iwe); + return 0; +} + +static int at76_iw_handler_set_essid(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWESSID - %s", netdev->name, extra); + + if (data->flags) { + memcpy(priv->essid, extra, data->length); + priv->essid_size = data->length; + } else + priv->essid_size = 0; /* Use any SSID */ + + return -EIWCOMMIT; +} + +static int at76_iw_handler_get_essid(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + if (priv->essid_size) { + /* not the ANY ssid in priv->essid */ + data->flags = 1; + data->length = priv->essid_size; + memcpy(extra, priv->essid, data->length); + } else { + /* the ANY ssid was specified */ + if (priv->mac_state == MAC_CONNECTED && priv->curr_bss) { + /* report the SSID we have found */ + data->flags = 1; + data->length = priv->curr_bss->ssid_len; + memcpy(extra, priv->curr_bss->ssid, data->length); + } else { + /* report ANY back */ + data->flags = 0; + data->length = 0; + } + } + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWESSID - %.*s", netdev->name, + data->length, extra); + + return 0; +} + +static int at76_iw_handler_set_rate(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *bitrate, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRATE - %d", netdev->name, + bitrate->value); + + switch (bitrate->value) { + case -1: + priv->txrate = TX_RATE_AUTO; + break; /* auto rate */ + case 1000000: + priv->txrate = TX_RATE_1MBIT; + break; + case 2000000: + priv->txrate = TX_RATE_2MBIT; + break; + case 5500000: + priv->txrate = TX_RATE_5_5MBIT; + break; + case 11000000: + priv->txrate = TX_RATE_11MBIT; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int at76_iw_handler_get_rate(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *bitrate, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + switch (priv->txrate) { + /* return max rate if RATE_AUTO */ + case TX_RATE_AUTO: + bitrate->value = 11000000; + break; + case TX_RATE_1MBIT: + bitrate->value = 1000000; + break; + case TX_RATE_2MBIT: + bitrate->value = 2000000; + break; + case TX_RATE_5_5MBIT: + bitrate->value = 5500000; + break; + case TX_RATE_11MBIT: + bitrate->value = 11000000; + break; + default: + ret = -EINVAL; + } + + bitrate->fixed = (priv->txrate != TX_RATE_AUTO); + bitrate->disabled = 0; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRATE - %d", netdev->name, + bitrate->value); + + return ret; +} + +static int at76_iw_handler_set_rts(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = -EIWCOMMIT; + int rthr = rts->value; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRTS - value %d disabled %s", + netdev->name, rts->value, (rts->disabled) ? "true" : "false"); + + if (rts->disabled) + rthr = MAX_RTS_THRESHOLD; + + if ((rthr < 0) || (rthr > MAX_RTS_THRESHOLD)) + ret = -EINVAL; + else + priv->rts_threshold = rthr; + + return ret; +} + +static int at76_iw_handler_get_rts(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + rts->value = priv->rts_threshold; + rts->disabled = (rts->value >= MAX_RTS_THRESHOLD); + rts->fixed = 1; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRTS - value %d disabled %s", + netdev->name, rts->value, (rts->disabled) ? "true" : "false"); + + return 0; +} + +static int at76_iw_handler_set_frag(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = -EIWCOMMIT; + int fthr = frag->value; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWFRAG - value %d, disabled %s", + netdev->name, frag->value, + (frag->disabled) ? "true" : "false"); + + if (frag->disabled) + fthr = MAX_FRAG_THRESHOLD; + + if ((fthr < MIN_FRAG_THRESHOLD) || (fthr > MAX_FRAG_THRESHOLD)) + ret = -EINVAL; + else + priv->frag_threshold = fthr & ~0x1; /* get an even value */ + + return ret; +} + +static int at76_iw_handler_get_frag(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + frag->value = priv->frag_threshold; + frag->disabled = (frag->value >= MAX_FRAG_THRESHOLD); + frag->fixed = 1; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFRAG - value %d, disabled %s", + netdev->name, frag->value, + (frag->disabled) ? "true" : "false"); + + return 0; +} + +static int at76_iw_handler_get_txpow(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *power, char *extra) +{ + power->value = 15; + power->fixed = 1; /* No power control */ + power->disabled = 0; + power->flags = IW_TXPOW_DBM; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWTXPOW - txpow %d dBm", netdev->name, + power->value); + + return 0; +} + +/* jal: short retry is handled by the firmware (at least 0.90.x), + while long retry is not (?) */ +static int at76_iw_handler_set_retry(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRETRY disabled %d flags 0x%x val %d", + netdev->name, retry->disabled, retry->flags, retry->value); + + if (!retry->disabled && (retry->flags & IW_RETRY_LIMIT)) { + if ((retry->flags & IW_RETRY_MIN) || + !(retry->flags & IW_RETRY_MAX)) + priv->short_retry_limit = retry->value; + else + ret = -EINVAL; + } else + ret = -EINVAL; + + return ret; +} + +/* Adapted (ripped) from atmel.c */ +static int at76_iw_handler_get_retry(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRETRY", netdev->name); + + retry->disabled = 0; /* Can't be disabled */ + retry->flags = IW_RETRY_LIMIT; + retry->value = priv->short_retry_limit; + + return 0; +} + +static int at76_iw_handler_set_encode(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *encoding, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int index = (encoding->flags & IW_ENCODE_INDEX) - 1; + int len = encoding->length; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWENCODE - enc.flags %08x " + "pointer %p len %d", netdev->name, encoding->flags, + encoding->pointer, encoding->length); + at76_dbg(DBG_IOCTL, + "%s: SIOCSIWENCODE - old wepstate: enabled %s key_id %d " + "auth_mode %s", netdev->name, + (priv->wep_enabled) ? "true" : "false", priv->wep_key_id, + (priv->auth_mode == + WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); + + /* take the old default key if index is invalid */ + if ((index < 0) || (index >= WEP_KEYS)) + index = priv->wep_key_id; + + if (len > 0) { + if (len > WEP_LARGE_KEY_LEN) + len = WEP_LARGE_KEY_LEN; + + memset(priv->wep_keys[index], 0, WEP_KEY_LEN); + memcpy(priv->wep_keys[index], extra, len); + priv->wep_keys_len[index] = (len <= WEP_SMALL_KEY_LEN) ? + WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; + priv->wep_enabled = 1; + } + + priv->wep_key_id = index; + priv->wep_enabled = ((encoding->flags & IW_ENCODE_DISABLED) == 0); + + if (encoding->flags & IW_ENCODE_RESTRICTED) + priv->auth_mode = WLAN_AUTH_SHARED_KEY; + if (encoding->flags & IW_ENCODE_OPEN) + priv->auth_mode = WLAN_AUTH_OPEN; + + at76_dbg(DBG_IOCTL, + "%s: SIOCSIWENCODE - new wepstate: enabled %s key_id %d " + "key_len %d auth_mode %s", netdev->name, + (priv->wep_enabled) ? "true" : "false", priv->wep_key_id + 1, + priv->wep_keys_len[priv->wep_key_id], + (priv->auth_mode == + WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); + + return -EIWCOMMIT; +} + +static int at76_iw_handler_get_encode(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *encoding, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int index = (encoding->flags & IW_ENCODE_INDEX) - 1; + + if ((index < 0) || (index >= WEP_KEYS)) + index = priv->wep_key_id; + + encoding->flags = + (priv->auth_mode == WLAN_AUTH_SHARED_KEY) ? + IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; + + if (!priv->wep_enabled) + encoding->flags |= IW_ENCODE_DISABLED; + + if (encoding->pointer) { + encoding->length = priv->wep_keys_len[index]; + + memcpy(extra, priv->wep_keys[index], priv->wep_keys_len[index]); + + encoding->flags |= (index + 1); + } + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - enc.flags %08x " + "pointer %p len %d", netdev->name, encoding->flags, + encoding->pointer, encoding->length); + at76_dbg(DBG_IOCTL, + "%s: SIOCGIWENCODE - wepstate: enabled %s key_id %d " + "key_len %d auth_mode %s", netdev->name, + (priv->wep_enabled) ? "true" : "false", priv->wep_key_id + 1, + priv->wep_keys_len[priv->wep_key_id], + (priv->auth_mode == + WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); + + return 0; +} + +static int at76_iw_handler_set_power(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *prq, char *extra) +{ + int err = -EIWCOMMIT; + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, + "%s: SIOCSIWPOWER - disabled %s flags 0x%x value 0x%x", + netdev->name, (prq->disabled) ? "true" : "false", prq->flags, + prq->value); + + if (prq->disabled) + priv->pm_mode = AT76_PM_OFF; + else { + switch (prq->flags & IW_POWER_MODE) { + case IW_POWER_ALL_R: + case IW_POWER_ON: + break; + default: + err = -EINVAL; + goto exit; + } + if (prq->flags & IW_POWER_PERIOD) + priv->pm_period = prq->value; + + if (prq->flags & IW_POWER_TIMEOUT) { + err = -EINVAL; + goto exit; + } + priv->pm_mode = AT76_PM_ON; + } +exit: + return err; +} + +static int at76_iw_handler_get_power(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *power, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + power->disabled = (priv->pm_mode == AT76_PM_OFF); + if (!power->disabled) { + power->flags = IW_POWER_PERIOD | IW_POWER_ALL_R; + power->value = priv->pm_period; + } + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWPOWER - %s flags 0x%x value 0x%x", + netdev->name, power->disabled ? "disabled" : "enabled", + power->flags, power->value); + + return 0; +} + +/******************************************************************************* + * Private IOCTLS + */ +static int at76_iw_set_short_preamble(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int val = *((int *)name); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_SHORT_PREAMBLE, %d", + netdev->name, val); + + if (val < PREAMBLE_TYPE_LONG || val > PREAMBLE_TYPE_AUTO) + ret = -EINVAL; + else + priv->preamble_type = val; + + return ret; +} + +static int at76_iw_get_short_preamble(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + snprintf(wrqu->name, sizeof(wrqu->name), "%s (%d)", + preambles[priv->preamble_type], priv->preamble_type); + return 0; +} + +static int at76_iw_set_debug(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + char *ptr; + u32 val; + + if (data->length > 0) { + val = simple_strtol(extra, &ptr, 0); + + if (ptr == extra) + val = DBG_DEFAULTS; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG input %d: %s -> 0x%x", + netdev->name, data->length, extra, val); + } else + val = DBG_DEFAULTS; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG, old 0x%x, new 0x%x", + netdev->name, at76_debug, val); + + /* jal: some more output to pin down lockups */ + at76_dbg(DBG_IOCTL, "%s: netif running %d queue_stopped %d " + "carrier_ok %d", netdev->name, netif_running(netdev), + netif_queue_stopped(netdev), netif_carrier_ok(netdev)); + + at76_debug = val; + + return 0; +} + +static int at76_iw_get_debug(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + snprintf(wrqu->name, sizeof(wrqu->name), "0x%08x", at76_debug); + return 0; +} + +static int at76_iw_set_powersave_mode(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int val = *((int *)name); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_POWERSAVE_MODE, %d (%s)", + netdev->name, val, + val == AT76_PM_OFF ? "active" : val == AT76_PM_ON ? "save" : + val == AT76_PM_SMART ? "smart save" : ""); + if (val < AT76_PM_OFF || val > AT76_PM_SMART) + ret = -EINVAL; + else + priv->pm_mode = val; + + return ret; +} + +static int at76_iw_get_powersave_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int *param = (int *)extra; + + param[0] = priv->pm_mode; + return 0; +} + +static int at76_iw_set_scan_times(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int mint = *((int *)name); + int maxt = *((int *)name + 1); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_TIMES - min %d max %d", + netdev->name, mint, maxt); + if (mint <= 0 || maxt <= 0 || mint > maxt) + ret = -EINVAL; + else { + priv->scan_min_time = mint; + priv->scan_max_time = maxt; + } + + return ret; +} + +static int at76_iw_get_scan_times(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int *param = (int *)extra; + + param[0] = priv->scan_min_time; + param[1] = priv->scan_max_time; + return 0; +} + +static int at76_iw_set_scan_mode(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int val = *((int *)name); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_MODE - mode %s", + netdev->name, (val = SCAN_TYPE_ACTIVE) ? "active" : + (val = SCAN_TYPE_PASSIVE) ? "passive" : ""); + + if (val != SCAN_TYPE_ACTIVE && val != SCAN_TYPE_PASSIVE) + ret = -EINVAL; + else + priv->scan_mode = val; + + return ret; +} + +static int at76_iw_get_scan_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int *param = (int *)extra; + + param[0] = priv->scan_mode; + return 0; +} + +#define AT76_SET_HANDLER(h, f) [h - SIOCIWFIRST] = (iw_handler) f + +/* Standard wireless handlers */ +static const iw_handler at76_handlers[] = { + AT76_SET_HANDLER(SIOCSIWCOMMIT, at76_iw_handler_commit), + AT76_SET_HANDLER(SIOCGIWNAME, at76_iw_handler_get_name), + AT76_SET_HANDLER(SIOCSIWFREQ, at76_iw_handler_set_freq), + AT76_SET_HANDLER(SIOCGIWFREQ, at76_iw_handler_get_freq), + AT76_SET_HANDLER(SIOCSIWMODE, at76_iw_handler_set_mode), + AT76_SET_HANDLER(SIOCGIWMODE, at76_iw_handler_get_mode), + AT76_SET_HANDLER(SIOCGIWRANGE, at76_iw_handler_get_range), + AT76_SET_HANDLER(SIOCSIWSPY, at76_iw_handler_set_spy), + AT76_SET_HANDLER(SIOCGIWSPY, at76_iw_handler_get_spy), + AT76_SET_HANDLER(SIOCSIWTHRSPY, at76_iw_handler_set_thrspy), + AT76_SET_HANDLER(SIOCGIWTHRSPY, at76_iw_handler_get_thrspy), + AT76_SET_HANDLER(SIOCSIWAP, at76_iw_handler_set_wap), + AT76_SET_HANDLER(SIOCGIWAP, at76_iw_handler_get_wap), + AT76_SET_HANDLER(SIOCSIWSCAN, at76_iw_handler_set_scan), + AT76_SET_HANDLER(SIOCGIWSCAN, at76_iw_handler_get_scan), + AT76_SET_HANDLER(SIOCSIWESSID, at76_iw_handler_set_essid), + AT76_SET_HANDLER(SIOCGIWESSID, at76_iw_handler_get_essid), + AT76_SET_HANDLER(SIOCSIWRATE, at76_iw_handler_set_rate), + AT76_SET_HANDLER(SIOCGIWRATE, at76_iw_handler_get_rate), + AT76_SET_HANDLER(SIOCSIWRTS, at76_iw_handler_set_rts), + AT76_SET_HANDLER(SIOCGIWRTS, at76_iw_handler_get_rts), + AT76_SET_HANDLER(SIOCSIWFRAG, at76_iw_handler_set_frag), + AT76_SET_HANDLER(SIOCGIWFRAG, at76_iw_handler_get_frag), + AT76_SET_HANDLER(SIOCGIWTXPOW, at76_iw_handler_get_txpow), + AT76_SET_HANDLER(SIOCSIWRETRY, at76_iw_handler_set_retry), + AT76_SET_HANDLER(SIOCGIWRETRY, at76_iw_handler_get_retry), + AT76_SET_HANDLER(SIOCSIWENCODE, at76_iw_handler_set_encode), + AT76_SET_HANDLER(SIOCGIWENCODE, at76_iw_handler_get_encode), + AT76_SET_HANDLER(SIOCSIWPOWER, at76_iw_handler_set_power), + AT76_SET_HANDLER(SIOCGIWPOWER, at76_iw_handler_get_power) +}; + +#define AT76_SET_PRIV(h, f) [h - SIOCIWFIRSTPRIV] = (iw_handler) f + +/* Private wireless handlers */ +static const iw_handler at76_priv_handlers[] = { + AT76_SET_PRIV(AT76_SET_SHORT_PREAMBLE, at76_iw_set_short_preamble), + AT76_SET_PRIV(AT76_GET_SHORT_PREAMBLE, at76_iw_get_short_preamble), + AT76_SET_PRIV(AT76_SET_DEBUG, at76_iw_set_debug), + AT76_SET_PRIV(AT76_GET_DEBUG, at76_iw_get_debug), + AT76_SET_PRIV(AT76_SET_POWERSAVE_MODE, at76_iw_set_powersave_mode), + AT76_SET_PRIV(AT76_GET_POWERSAVE_MODE, at76_iw_get_powersave_mode), + AT76_SET_PRIV(AT76_SET_SCAN_TIMES, at76_iw_set_scan_times), + AT76_SET_PRIV(AT76_GET_SCAN_TIMES, at76_iw_get_scan_times), + AT76_SET_PRIV(AT76_SET_SCAN_MODE, at76_iw_set_scan_mode), + AT76_SET_PRIV(AT76_GET_SCAN_MODE, at76_iw_get_scan_mode), +}; + +/* Names and arguments of private wireless handlers */ +static const struct iw_priv_args at76_priv_args[] = { + /* 0 - long, 1 - short */ + {AT76_SET_SHORT_PREAMBLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"}, + + {AT76_GET_SHORT_PREAMBLE, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_preamble"}, + + /* we must pass the new debug mask as a string, because iwpriv cannot + * parse hex numbers starting with 0x :-( */ + {AT76_SET_DEBUG, + IW_PRIV_TYPE_CHAR | 10, 0, "set_debug"}, + + {AT76_GET_DEBUG, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_debug"}, + + /* 1 - active, 2 - power save, 3 - smart power save */ + {AT76_SET_POWERSAVE_MODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_powersave"}, + + {AT76_GET_POWERSAVE_MODE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_powersave"}, + + /* min_channel_time, max_channel_time */ + {AT76_SET_SCAN_TIMES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_scan_times"}, + + {AT76_GET_SCAN_TIMES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, "get_scan_times"}, + + /* 0 - active, 1 - passive scan */ + {AT76_SET_SCAN_MODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_scan_mode"}, + + {AT76_GET_SCAN_MODE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scan_mode"}, +}; + +static const struct iw_handler_def at76_handler_def = { + .num_standard = ARRAY_SIZE(at76_handlers), + .num_private = ARRAY_SIZE(at76_priv_handlers), + .num_private_args = ARRAY_SIZE(at76_priv_args), + .standard = at76_handlers, + .private = at76_priv_handlers, + .private_args = at76_priv_args, + .get_wireless_stats = at76_get_wireless_stats, +}; + +static const u8 snapsig[] = { 0xaa, 0xaa, 0x03 }; + +/* RFC 1042 encapsulates Ethernet frames in 802.2 SNAP (0xaa, 0xaa, 0x03) with + * a SNAP OID of 0 (0x00, 0x00, 0x00) */ +static const u8 rfc1042sig[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +static int at76_tx(struct sk_buff *skb, struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + struct net_device_stats *stats = &priv->stats; + int ret = 0; + int wlen; + int submit_len; + struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; + struct ieee80211_hdr_3addr *i802_11_hdr = + (struct ieee80211_hdr_3addr *)tx_buffer->packet; + u8 *payload = i802_11_hdr->payload; + struct ethhdr *eh = (struct ethhdr *)skb->data; + + if (netif_queue_stopped(netdev)) { + printk(KERN_ERR "%s: %s called while netdev is stopped\n", + netdev->name, __func__); + /* skip this packet */ + dev_kfree_skb(skb); + return 0; + } + + if (priv->tx_urb->status == -EINPROGRESS) { + printk(KERN_ERR "%s: %s called while tx urb is pending\n", + netdev->name, __func__); + /* skip this packet */ + dev_kfree_skb(skb); + return 0; + } + + if (skb->len < ETH_HLEN) { + printk(KERN_ERR "%s: %s: skb too short (%d)\n", + netdev->name, __func__, skb->len); + dev_kfree_skb(skb); + return 0; + } + + at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ + + /* we can get rid of memcpy if we set netdev->hard_header_len to + reserve enough space, but we would need to keep the skb around */ + + if (ntohs(eh->h_proto) <= ETH_DATA_LEN) { + /* this is a 802.3 packet */ + if (skb->len >= ETH_HLEN + sizeof(rfc1042sig) + && skb->data[ETH_HLEN] == rfc1042sig[0] + && skb->data[ETH_HLEN + 1] == rfc1042sig[1]) { + /* higher layer delivered SNAP header - keep it */ + memcpy(payload, skb->data + ETH_HLEN, + skb->len - ETH_HLEN); + wlen = IEEE80211_3ADDR_LEN + skb->len - ETH_HLEN; + } else { + printk(KERN_ERR "%s: dropping non-SNAP 802.2 packet " + "(DSAP 0x%02x SSAP 0x%02x cntrl 0x%02x)\n", + priv->netdev->name, skb->data[ETH_HLEN], + skb->data[ETH_HLEN + 1], + skb->data[ETH_HLEN + 2]); + dev_kfree_skb(skb); + return 0; + } + } else { + /* add RFC 1042 header in front */ + memcpy(payload, rfc1042sig, sizeof(rfc1042sig)); + memcpy(payload + sizeof(rfc1042sig), &eh->h_proto, + skb->len - offsetof(struct ethhdr, h_proto)); + wlen = IEEE80211_3ADDR_LEN + sizeof(rfc1042sig) + skb->len - + offsetof(struct ethhdr, h_proto); + } + + /* make wireless header */ + i802_11_hdr->frame_ctl = + cpu_to_le16(IEEE80211_FTYPE_DATA | + (priv->wep_enabled ? IEEE80211_FCTL_PROTECTED : 0) | + (priv->iw_mode == + IW_MODE_INFRA ? IEEE80211_FCTL_TODS : 0)); + + if (priv->iw_mode == IW_MODE_ADHOC) { + memcpy(i802_11_hdr->addr1, eh->h_dest, ETH_ALEN); + memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN); + memcpy(i802_11_hdr->addr3, priv->bssid, ETH_ALEN); + } else if (priv->iw_mode == IW_MODE_INFRA) { + memcpy(i802_11_hdr->addr1, priv->bssid, ETH_ALEN); + memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN); + memcpy(i802_11_hdr->addr3, eh->h_dest, ETH_ALEN); + } + + i802_11_hdr->duration_id = cpu_to_le16(0); + i802_11_hdr->seq_ctl = cpu_to_le16(0); + + /* setup 'Atmel' header */ + tx_buffer->wlength = cpu_to_le16(wlen); + tx_buffer->tx_rate = priv->txrate; + /* for broadcast destination addresses, the firmware 0.100.x + seems to choose the highest rate set with CMD_STARTUP in + basic_rate_set replacing this value */ + + memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); + + tx_buffer->padding = at76_calc_padding(wlen); + submit_len = wlen + AT76_TX_HDRLEN + tx_buffer->padding; + + at76_dbg(DBG_TX_DATA_CONTENT, "%s skb->data %s", priv->netdev->name, + hex2str(skb->data, 32)); + at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr %s", + priv->netdev->name, + le16_to_cpu(tx_buffer->wlength), + tx_buffer->padding, tx_buffer->tx_rate, + hex2str(i802_11_hdr, sizeof(*i802_11_hdr))); + at76_dbg(DBG_TX_DATA_CONTENT, "%s payload %s", priv->netdev->name, + hex2str(payload, 48)); + + /* send stuff */ + netif_stop_queue(netdev); + netdev->trans_start = jiffies; + + usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, + submit_len, at76_tx_callback, priv); + ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); + if (ret) { + stats->tx_errors++; + printk(KERN_ERR "%s: error in tx submit urb: %d\n", + netdev->name, ret); + if (ret == -EINVAL) + printk(KERN_ERR + "%s: -EINVAL: tx urb %p hcpriv %p complete %p\n", + priv->netdev->name, priv->tx_urb, + priv->tx_urb->hcpriv, priv->tx_urb->complete); + } else { + stats->tx_bytes += skb->len; + dev_kfree_skb(skb); + } + + return ret; +} + +static void at76_tx_timeout(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + + if (!priv) + return; + dev_warn(&netdev->dev, "tx timeout."); + + usb_unlink_urb(priv->tx_urb); + priv->stats.tx_errors++; +} + +static int at76_submit_rx_urb(struct at76_priv *priv) +{ + int ret; + int size; + struct sk_buff *skb = priv->rx_skb; + + if (!priv->rx_urb) { + printk(KERN_ERR "%s: %s: priv->rx_urb is NULL\n", + priv->netdev->name, __func__); + return -EFAULT; + } + + if (!skb) { + skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); + if (!skb) { + printk(KERN_ERR "%s: cannot allocate rx skbuff\n", + priv->netdev->name); + ret = -ENOMEM; + goto exit; + } + priv->rx_skb = skb; + } else { + skb_push(skb, skb_headroom(skb)); + skb_trim(skb, 0); + } + + size = skb_tailroom(skb); + usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, + skb_put(skb, size), size, at76_rx_callback, priv); + ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); + if (ret < 0) { + if (ret == -ENODEV) + at76_dbg(DBG_DEVSTART, + "usb_submit_urb returned -ENODEV"); + else + printk(KERN_ERR "%s: rx, usb_submit_urb failed: %d\n", + priv->netdev->name, ret); + } + +exit: + if (ret < 0 && ret != -ENODEV) + printk(KERN_ERR "%s: cannot submit rx urb - please unload the " + "driver and/or power cycle the device\n", + priv->netdev->name); + + return ret; +} + +static int at76_open(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + at76_dbg(DBG_PROC_ENTRY, "%s(): entry", __func__); + + if (mutex_lock_interruptible(&priv->mtx)) + return -EINTR; + + /* if netdev->dev_addr != priv->mac_addr we must + set the mac address in the device ! */ + if (compare_ether_addr(netdev->dev_addr, priv->mac_addr)) { + if (at76_add_mac_address(priv, netdev->dev_addr) >= 0) + at76_dbg(DBG_PROGRESS, "%s: set new MAC addr %s", + netdev->name, mac2str(netdev->dev_addr)); + } + + priv->scan_state = SCAN_IDLE; + priv->last_scan = jiffies; + + ret = at76_submit_rx_urb(priv); + if (ret < 0) { + printk(KERN_ERR "%s: open: submit_rx_urb failed: %d\n", + netdev->name, ret); + goto error; + } + + schedule_delayed_work(&priv->dwork_restart, 0); + + at76_dbg(DBG_PROC_ENTRY, "%s(): end", __func__); +error: + mutex_unlock(&priv->mtx); + return ret < 0 ? ret : 0; +} + +static int at76_stop(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_DEVSTART, "%s: ENTER", __func__); + + if (mutex_lock_interruptible(&priv->mtx)) + return -EINTR; + + at76_quiesce(priv); + + if (!priv->device_unplugged) { + /* We are called by "ifconfig ethX down", not because the + * device is not available anymore. */ + at76_set_radio(priv, 0); + + /* We unlink rx_urb because at76_open() re-submits it. + * If unplugged, at76_delete_device() takes care of it. */ + usb_kill_urb(priv->rx_urb); + } + + /* free the bss_list */ + at76_free_bss_list(priv); + + mutex_unlock(&priv->mtx); + at76_dbg(DBG_DEVSTART, "%s: EXIT", __func__); + + return 0; +} + +static void at76_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct at76_priv *priv = netdev_priv(netdev); + + strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); + + usb_make_path(priv->udev, info->bus_info, sizeof(info->bus_info)); + + snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d-%d", + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); +} + +static u32 at76_ethtool_get_link(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + return priv->mac_state == MAC_CONNECTED; +} + +static struct ethtool_ops at76_ethtool_ops = { + .get_drvinfo = at76_ethtool_get_drvinfo, + .get_link = at76_ethtool_get_link, +}; + +/* Download external firmware */ +static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) +{ + int ret; + int op_mode; + int blockno = 0; + int bsize; + u8 *block; + u8 *buf = fwe->extfw; + int size = fwe->extfw_size; + + if (!buf || !size) + return -ENOENT; op_mode = at76_get_op_mode(udev); at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); @@ -1428,39 +3461,439 @@ exit: return ret; } -/* Download internal firmware */ -static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) +/* Download internal firmware */ +static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) +{ + int ret; + int need_remap = !at76_is_505a(fwe->board_type); + + ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, + need_remap ? 0 : 2 * HZ); + + if (ret < 0) { + dev_printk(KERN_ERR, &udev->dev, + "downloading internal fw failed with %d\n", ret); + goto exit; + } + + at76_dbg(DBG_DEVSTART, "sending REMAP"); + + /* no REMAP for 505A (see SF driver) */ + if (need_remap) { + ret = at76_remap(udev); + if (ret < 0) { + dev_printk(KERN_ERR, &udev->dev, + "sending REMAP failed with %d\n", ret); + goto exit; + } + } + + at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); + schedule_timeout_interruptible(2 * HZ + 1); + usb_reset_device(udev); + +exit: + return ret; +} + +static int at76_match_essid(struct at76_priv *priv, struct bss_info *ptr) +{ + /* common criteria for both modi */ + + int ret = (priv->essid_size == 0 /* ANY ssid */ || + (priv->essid_size == ptr->ssid_len && + !memcmp(priv->essid, ptr->ssid, ptr->ssid_len))); + if (!ret) + at76_dbg(DBG_BSS_MATCH, + "%s bss table entry %p: essid didn't match", + priv->netdev->name, ptr); + return ret; +} + +static inline int at76_match_mode(struct at76_priv *priv, struct bss_info *ptr) +{ + int ret; + + if (priv->iw_mode == IW_MODE_ADHOC) + ret = ptr->capa & WLAN_CAPABILITY_IBSS; + else + ret = ptr->capa & WLAN_CAPABILITY_ESS; + if (!ret) + at76_dbg(DBG_BSS_MATCH, + "%s bss table entry %p: mode didn't match", + priv->netdev->name, ptr); + return ret; +} + +static int at76_match_rates(struct at76_priv *priv, struct bss_info *ptr) +{ + int i; + + for (i = 0; i < ptr->rates_len; i++) { + u8 rate = ptr->rates[i]; + + if (!(rate & 0x80)) + continue; + + /* this is a basic rate we have to support + (see IEEE802.11, ch. 7.3.2.2) */ + if (rate != (0x80 | hw_rates[0]) + && rate != (0x80 | hw_rates[1]) + && rate != (0x80 | hw_rates[2]) + && rate != (0x80 | hw_rates[3])) { + at76_dbg(DBG_BSS_MATCH, + "%s: bss table entry %p: basic rate %02x not " + "supported", priv->netdev->name, ptr, rate); + return 0; + } + } + + /* if we use short preamble, the bss must support it */ + if (priv->preamble_type == PREAMBLE_TYPE_SHORT && + !(ptr->capa & WLAN_CAPABILITY_SHORT_PREAMBLE)) { + at76_dbg(DBG_BSS_MATCH, + "%s: %p does not support short preamble", + priv->netdev->name, ptr); + return 0; + } else + return 1; +} + +static inline int at76_match_wep(struct at76_priv *priv, struct bss_info *ptr) +{ + if (!priv->wep_enabled && ptr->capa & WLAN_CAPABILITY_PRIVACY) { + /* we have disabled WEP, but the BSS signals privacy */ + at76_dbg(DBG_BSS_MATCH, + "%s: bss table entry %p: requires encryption", + priv->netdev->name, ptr); + return 0; + } + /* otherwise if the BSS does not signal privacy it may well + accept encrypted packets from us ... */ + return 1; +} + +static inline int at76_match_bssid(struct at76_priv *priv, struct bss_info *ptr) +{ + if (!priv->wanted_bssid_valid || + !compare_ether_addr(ptr->bssid, priv->wanted_bssid)) + return 1; + + at76_dbg(DBG_BSS_MATCH, + "%s: requested bssid - %s does not match", + priv->netdev->name, mac2str(priv->wanted_bssid)); + at76_dbg(DBG_BSS_MATCH, + " AP bssid - %s of bss table entry %p", + mac2str(ptr->bssid), ptr); + return 0; +} + +/** + * at76_match_bss - try to find a matching bss in priv->bss + * + * last - last bss tried + * + * last == NULL signals a new round starting with priv->bss_list.next + * this function must be called inside an acquired priv->bss_list_spinlock + * otherwise the timeout on bss may remove the newly chosen entry + */ +static struct bss_info *at76_match_bss(struct at76_priv *priv, + struct bss_info *last) +{ + struct bss_info *ptr = NULL; + struct list_head *curr; + + curr = last ? last->list.next : priv->bss_list.next; + while (curr != &priv->bss_list) { + ptr = list_entry(curr, struct bss_info, list); + if (at76_match_essid(priv, ptr) && at76_match_mode(priv, ptr) + && at76_match_wep(priv, ptr) && at76_match_rates(priv, ptr) + && at76_match_bssid(priv, ptr)) + break; + curr = curr->next; + } + + if (curr == &priv->bss_list) + ptr = NULL; + /* otherwise ptr points to the struct bss_info we have chosen */ + + at76_dbg(DBG_BSS_TABLE, "%s %s: returned %p", priv->netdev->name, + __func__, ptr); + return ptr; +} + +/* Start joining a matching BSS, or create own IBSS */ +static void at76_work_join(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_join); + int ret; + unsigned long flags; + + mutex_lock(&priv->mtx); + + WARN_ON(priv->mac_state != MAC_JOINING); + if (priv->mac_state != MAC_JOINING) + goto exit; + + /* secure the access to priv->curr_bss ! */ + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + priv->curr_bss = at76_match_bss(priv, priv->curr_bss); + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); + + if (!priv->curr_bss) { + /* here we haven't found a matching (i)bss ... */ + if (priv->iw_mode == IW_MODE_ADHOC) { + at76_set_mac_state(priv, MAC_OWN_IBSS); + at76_start_ibss(priv); + goto exit; + } + /* haven't found a matching BSS in infra mode - try again */ + at76_set_mac_state(priv, MAC_SCANNING); + schedule_work(&priv->work_start_scan); + goto exit; + } + + ret = at76_join_bss(priv, priv->curr_bss); + if (ret < 0) { + printk(KERN_ERR "%s: join_bss failed with %d\n", + priv->netdev->name, ret); + goto exit; + } + + ret = at76_wait_completion(priv, CMD_JOIN); + if (ret != CMD_STATUS_COMPLETE) { + if (ret != CMD_STATUS_TIME_OUT) + printk(KERN_ERR "%s: join_bss completed with %d\n", + priv->netdev->name, ret); + else + printk(KERN_INFO "%s: join_bss ssid %s timed out\n", + priv->netdev->name, + mac2str(priv->curr_bss->bssid)); + + /* retry next BSS immediately */ + schedule_work(&priv->work_join); + goto exit; + } + + /* here we have joined the (I)BSS */ + if (priv->iw_mode == IW_MODE_ADHOC) { + struct bss_info *bptr = priv->curr_bss; + at76_set_mac_state(priv, MAC_CONNECTED); + /* get ESSID, BSSID and channel for priv->curr_bss */ + priv->essid_size = bptr->ssid_len; + memcpy(priv->essid, bptr->ssid, bptr->ssid_len); + memcpy(priv->bssid, bptr->bssid, ETH_ALEN); + priv->channel = bptr->channel; + at76_iwevent_bss_connect(priv->netdev, bptr->bssid); + netif_carrier_on(priv->netdev); + netif_start_queue(priv->netdev); + /* just to be sure */ + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); + } else { + /* send auth req */ + priv->retries = AUTH_RETRIES; + at76_set_mac_state(priv, MAC_AUTH); + at76_auth_req(priv, priv->curr_bss, 1, NULL); + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__); + schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Reap scan results */ +static void at76_dwork_get_scan(struct work_struct *work) +{ + int status; + int ret; + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_get_scan.work); + + mutex_lock(&priv->mtx); + WARN_ON(priv->mac_state != MAC_SCANNING); + if (priv->mac_state != MAC_SCANNING) + goto exit; + + status = at76_get_cmd_status(priv->udev, CMD_SCAN); + if (status < 0) { + printk(KERN_ERR "%s: %s: at76_get_cmd_status failed with %d\n", + priv->netdev->name, __func__, status); + status = CMD_STATUS_IN_PROGRESS; + /* INFO: Hope it was a one off error - if not, scanning + further down the line and stop this cycle */ + } + at76_dbg(DBG_PROGRESS, + "%s %s: got cmd_status %d (state %s, need_any %d)", + priv->netdev->name, __func__, status, + mac_states[priv->mac_state], priv->scan_need_any); + + if (status != CMD_STATUS_COMPLETE) { + if ((status != CMD_STATUS_IN_PROGRESS) && + (status != CMD_STATUS_IDLE)) + printk(KERN_ERR "%s: %s: Bad scan status: %s\n", + priv->netdev->name, __func__, + at76_get_cmd_status_string(status)); + + /* the first cmd status after scan start is always a IDLE -> + start the timer to poll again until COMPLETED */ + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer for %d ticks", + __func__, __LINE__, SCAN_POLL_INTERVAL); + schedule_delayed_work(&priv->dwork_get_scan, + SCAN_POLL_INTERVAL); + goto exit; + } + + if (at76_debug & DBG_BSS_TABLE) + at76_dump_bss_table(priv); + + if (priv->scan_need_any) { + ret = at76_start_scan(priv, 0); + if (ret < 0) + printk(KERN_ERR + "%s: %s: start_scan (ANY) failed with %d\n", + priv->netdev->name, __func__, ret); + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer for %d ticks", __func__, + __LINE__, SCAN_POLL_INTERVAL); + schedule_delayed_work(&priv->dwork_get_scan, + SCAN_POLL_INTERVAL); + priv->scan_need_any = 0; + } else { + priv->scan_state = SCAN_COMPLETED; + /* report the end of scan to user space */ + at76_iwevent_scan_complete(priv->netdev); + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Handle loss of beacons from the AP */ +static void at76_dwork_beacon(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_beacon.work); + + mutex_lock(&priv->mtx); + if (priv->mac_state != MAC_CONNECTED || priv->iw_mode != IW_MODE_INFRA) + goto exit; + + /* We haven't received any beacons from out AP for BEACON_TIMEOUT */ + printk(KERN_INFO "%s: lost beacon bssid %s\n", + priv->netdev->name, mac2str(priv->curr_bss->bssid)); + + netif_carrier_off(priv->netdev); + netif_stop_queue(priv->netdev); + at76_iwevent_bss_disconnect(priv->netdev); + at76_set_mac_state(priv, MAC_SCANNING); + schedule_work(&priv->work_start_scan); + +exit: + mutex_unlock(&priv->mtx); +} + +/* Handle authentication response timeout */ +static void at76_dwork_auth(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_auth.work); + + mutex_lock(&priv->mtx); + WARN_ON(priv->mac_state != MAC_AUTH); + if (priv->mac_state != MAC_AUTH) + goto exit; + + at76_dbg(DBG_PROGRESS, "%s: authentication response timeout", + priv->netdev->name); + + if (priv->retries-- >= 0) { + at76_auth_req(priv, priv->curr_bss, 1, NULL); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __func__, __LINE__); + schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT); + } else { + /* try to get next matching BSS */ + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Handle association response timeout */ +static void at76_dwork_assoc(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_assoc.work); + + mutex_lock(&priv->mtx); + WARN_ON(priv->mac_state != MAC_ASSOC); + if (priv->mac_state != MAC_ASSOC) + goto exit; + + at76_dbg(DBG_PROGRESS, "%s: association response timeout", + priv->netdev->name); + + if (priv->retries-- >= 0) { + at76_assoc_req(priv, priv->curr_bss); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __func__, __LINE__); + schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT); + } else { + /* try to get next matching BSS */ + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Read new bssid in ad-hoc mode */ +static void at76_work_new_bss(struct work_struct *work) { + struct at76_priv *priv = container_of(work, struct at76_priv, + work_new_bss); int ret; - int need_remap = !at76_is_505a(fwe->board_type); + struct mib_mac_mgmt mac_mgmt; - ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, - need_remap ? 0 : 2 * HZ); + mutex_lock(&priv->mtx); + ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, &mac_mgmt, + sizeof(struct mib_mac_mgmt)); if (ret < 0) { - dev_printk(KERN_ERR, &udev->dev, - "downloading internal fw failed with %d\n", ret); + printk(KERN_ERR "%s: at76_get_mib failed: %d\n", + priv->netdev->name, ret); goto exit; } - at76_dbg(DBG_DEVSTART, "sending REMAP"); + at76_dbg(DBG_PROGRESS, "ibss_change = 0x%2x", mac_mgmt.ibss_change); + memcpy(priv->bssid, mac_mgmt.current_bssid, ETH_ALEN); + at76_dbg(DBG_PROGRESS, "using BSSID %s", mac2str(priv->bssid)); - /* no REMAP for 505A (see SF driver) */ - if (need_remap) { - ret = at76_remap(udev); - if (ret < 0) { - dev_printk(KERN_ERR, &udev->dev, - "sending REMAP failed with %d\n", ret); - goto exit; - } - } + at76_iwevent_bss_connect(priv->netdev, priv->bssid); - at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); - schedule_timeout_interruptible(2 * HZ + 1); - usb_reset_device(udev); + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, ibss_change); + priv->mib_buf.data.byte = 0; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (ibss change ok) failed: %d\n", + priv->netdev->name, ret); exit: - return ret; + mutex_unlock(&priv->mtx); } static int at76_startup_device(struct at76_priv *priv) @@ -1470,14 +3903,14 @@ static int at76_startup_device(struct at76_priv *priv) at76_dbg(DBG_PARAMS, "%s param: ssid %.*s (%s) mode %s ch %d wep %s key %d " - "keylen %d", wiphy_name(priv->hw->wiphy), priv->essid_size, - priv->essid, hex2str(priv->essid, IW_ESSID_MAX_SIZE), + "keylen %d", priv->netdev->name, priv->essid_size, priv->essid, + hex2str(priv->essid, IW_ESSID_MAX_SIZE), priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", priv->channel, priv->wep_enabled ? "enabled" : "disabled", priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]); at76_dbg(DBG_PARAMS, "%s param: preamble %s rts %d retry %d frag %d " - "txrate %s auth_mode %d", wiphy_name(priv->hw->wiphy), + "txrate %s auth_mode %d", priv->netdev->name, preambles[priv->preamble_type], priv->rts_threshold, priv->short_retry_limit, priv->frag_threshold, priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate == @@ -1488,7 +3921,7 @@ static int at76_startup_device(struct at76_priv *priv) at76_dbg(DBG_PARAMS, "%s param: pm_mode %d pm_period %d auth_mode %s " "scan_times %d %d scan_mode %s", - wiphy_name(priv->hw->wiphy), priv->pm_mode, priv->pm_period, + priv->netdev->name, priv->pm_mode, priv->pm_period, priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret", priv->scan_min_time, priv->scan_max_time, priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive"); @@ -1522,8 +3955,7 @@ static int at76_startup_device(struct at76_priv *priv) ccfg->ssid_len = priv->essid_size; ccfg->wep_default_key_id = priv->wep_key_id; - memcpy(ccfg->wep_default_key_value, priv->wep_keys, - sizeof(priv->wep_keys)); + memcpy(ccfg->wep_default_key_value, priv->wep_keys, 4 * WEP_KEY_LEN); ccfg->short_preamble = priv->preamble_type; ccfg->beacon_period = cpu_to_le16(priv->beacon_period); @@ -1532,7 +3964,7 @@ static int at76_startup_device(struct at76_priv *priv) sizeof(struct at76_card_config)); if (ret < 0) { printk(KERN_ERR "%s: at76_set_card_command failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); return ret; } @@ -1578,6 +4010,69 @@ static int at76_startup_device(struct at76_priv *priv) return 0; } +/* Restart the interface */ +static void at76_dwork_restart(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_restart.work); + + mutex_lock(&priv->mtx); + + netif_carrier_off(priv->netdev); /* stop netdev watchdog */ + netif_stop_queue(priv->netdev); /* stop tx data packets */ + + at76_startup_device(priv); + + if (priv->iw_mode != IW_MODE_MONITOR) { + priv->netdev->type = ARPHRD_ETHER; + at76_set_mac_state(priv, MAC_SCANNING); + schedule_work(&priv->work_start_scan); + } else { + priv->netdev->type = ARPHRD_IEEE80211_RADIOTAP; + at76_start_monitor(priv); + } + + mutex_unlock(&priv->mtx); +} + +/* Initiate scanning */ +static void at76_work_start_scan(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_start_scan); + int ret; + + mutex_lock(&priv->mtx); + + WARN_ON(priv->mac_state != MAC_SCANNING); + if (priv->mac_state != MAC_SCANNING) + goto exit; + + /* only clear the bss list when a scan is actively initiated, + * otherwise simply rely on at76_bss_list_timeout */ + if (priv->scan_state == SCAN_IN_PROGRESS) { + at76_free_bss_list(priv); + priv->scan_need_any = 1; + } else + priv->scan_need_any = 0; + + ret = at76_start_scan(priv, 1); + + if (ret < 0) + printk(KERN_ERR "%s: %s: start_scan failed with %d\n", + priv->netdev->name, __func__, ret); + else { + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer for %d ticks", + __func__, __LINE__, SCAN_POLL_INTERVAL); + schedule_delayed_work(&priv->dwork_get_scan, + SCAN_POLL_INTERVAL); + } + +exit: + mutex_unlock(&priv->mtx); +} + /* Enable or disable promiscuous mode */ static void at76_work_set_promisc(struct work_struct *work) { @@ -1595,7 +4090,7 @@ static void at76_work_set_promisc(struct work_struct *work) ret = at76_set_mib(priv, &priv->mib_buf); if (ret < 0) printk(KERN_ERR "%s: set_mib (promiscuous_mode) failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); + priv->netdev->name, ret); mutex_unlock(&priv->mtx); } @@ -1611,759 +4106,1088 @@ static void at76_work_submit_rx(struct work_struct *work) mutex_unlock(&priv->mtx); } -static void at76_rx_tasklet(unsigned long param) +/* We got an association response */ +static void at76_rx_mgmt_assoc(struct at76_priv *priv, + struct at76_rx_buffer *buf) { - struct urb *urb = (struct urb *)param; - struct at76_priv *priv = urb->context; - struct at76_rx_buffer *buf; - struct ieee80211_rx_status rx_status = { 0 }; - - if (priv->device_unplugged) { - at76_dbg(DBG_DEVSTART, "device unplugged"); - if (urb) - at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); + struct ieee80211_assoc_response *resp = + (struct ieee80211_assoc_response *)buf->packet; + u16 assoc_id = le16_to_cpu(resp->aid); + u16 status = le16_to_cpu(resp->status); + + at76_dbg(DBG_RX_MGMT, "%s: rx AssocResp bssid %s capa 0x%04x status " + "0x%04x assoc_id 0x%04x rates %s", priv->netdev->name, + mac2str(resp->header.addr3), le16_to_cpu(resp->capability), + status, assoc_id, hex2str(resp->info_element->data, + resp->info_element->len)); + + if (priv->mac_state != MAC_ASSOC) { + printk(KERN_INFO "%s: AssocResp in state %s ignored\n", + priv->netdev->name, mac_states[priv->mac_state]); return; } - if (!priv->rx_skb || !priv->rx_skb->data) - return; - - buf = (struct at76_rx_buffer *)priv->rx_skb->data; - - if (urb->status != 0) { - if (urb->status != -ENOENT && urb->status != -ECONNRESET) - at76_dbg(DBG_URB, - "%s %s: - nonzero Rx bulk status received: %d", - __func__, wiphy_name(priv->hw->wiphy), - urb->status); - return; + BUG_ON(!priv->curr_bss); + + cancel_delayed_work(&priv->dwork_assoc); + if (status == WLAN_STATUS_SUCCESS) { + struct bss_info *ptr = priv->curr_bss; + priv->assoc_id = assoc_id & 0x3fff; + /* update iwconfig params */ + memcpy(priv->bssid, ptr->bssid, ETH_ALEN); + memcpy(priv->essid, ptr->ssid, ptr->ssid_len); + priv->essid_size = ptr->ssid_len; + priv->channel = ptr->channel; + schedule_work(&priv->work_assoc_done); + } else { + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); } +} - at76_dbg(DBG_RX_ATMEL_HDR, - "%s: rx frame: rate %d rssi %d noise %d link %d", - wiphy_name(priv->hw->wiphy), buf->rx_rate, buf->rssi, - buf->noise_level, buf->link_quality); - - skb_trim(priv->rx_skb, le16_to_cpu(buf->wlength) + AT76_RX_HDRLEN); - at76_dbg_dump(DBG_RX_DATA, &priv->rx_skb->data[AT76_RX_HDRLEN], - priv->rx_skb->len, "RX: len=%d", - (int)(priv->rx_skb->len - AT76_RX_HDRLEN)); +/* Process disassociation request from the AP */ +static void at76_rx_mgmt_disassoc(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct ieee80211_disassoc *resp = + (struct ieee80211_disassoc *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + + at76_dbg(DBG_RX_MGMT, + "%s: rx DisAssoc bssid %s reason 0x%04x destination %s", + priv->netdev->name, mac2str(mgmt->addr3), + le16_to_cpu(resp->reason), mac2str(mgmt->addr1)); + + /* We are not connected, ignore */ + if (priv->mac_state == MAC_SCANNING || priv->mac_state == MAC_INIT + || !priv->curr_bss) + return; - rx_status.signal = buf->rssi; - /* FIXME: is rate_idx still present in structure? */ - rx_status.rate_idx = buf->rx_rate; - rx_status.flag |= RX_FLAG_DECRYPTED; - rx_status.flag |= RX_FLAG_IV_STRIPPED; + /* Not our BSSID, ignore */ + if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid)) + return; - skb_pull(priv->rx_skb, AT76_RX_HDRLEN); - at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", - priv->rx_skb->len, priv->rx_skb->data_len); - ieee80211_rx_irqsafe(priv->hw, priv->rx_skb, &rx_status); + /* Not for our STA and not broadcast, ignore */ + if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1) + && !is_broadcast_ether_addr(mgmt->addr1)) + return; - /* Use a new skb for the next receive */ - priv->rx_skb = NULL; + if (priv->mac_state != MAC_ASSOC && priv->mac_state != MAC_CONNECTED + && priv->mac_state != MAC_JOINING) { + printk(KERN_INFO "%s: DisAssoc in state %s ignored\n", + priv->netdev->name, mac_states[priv->mac_state]); + return; + } - at76_submit_rx_urb(priv); + if (priv->mac_state == MAC_CONNECTED) { + netif_carrier_off(priv->netdev); + netif_stop_queue(priv->netdev); + at76_iwevent_bss_disconnect(priv->netdev); + } + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_beacon); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); } -/* Load firmware into kernel memory and parse it */ -static struct fwentry *at76_load_firmware(struct usb_device *udev, - enum board_type board_type) +static void at76_rx_mgmt_auth(struct at76_priv *priv, + struct at76_rx_buffer *buf) { - int ret; - char *str; - struct at76_fw_header *fwh; - struct fwentry *fwe = &firmwares[board_type]; - - mutex_lock(&fw_mutex); - - if (fwe->loaded) { - at76_dbg(DBG_FW, "re-using previously loaded fw"); - goto exit; + struct ieee80211_auth *resp = (struct ieee80211_auth *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + int seq_nr = le16_to_cpu(resp->transaction); + int alg = le16_to_cpu(resp->algorithm); + int status = le16_to_cpu(resp->status); + + at76_dbg(DBG_RX_MGMT, + "%s: rx AuthFrame bssid %s alg %d seq_nr %d status %d " + "destination %s", priv->netdev->name, mac2str(mgmt->addr3), + alg, seq_nr, status, mac2str(mgmt->addr1)); + + if (alg == WLAN_AUTH_SHARED_KEY && seq_nr == 2) + at76_dbg(DBG_RX_MGMT, "%s: AuthFrame challenge %s ...", + priv->netdev->name, hex2str(resp->info_element, 18)); + + if (priv->mac_state != MAC_AUTH) { + printk(KERN_INFO "%s: ignored AuthFrame in state %s\n", + priv->netdev->name, mac_states[priv->mac_state]); + return; } - - at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); - ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); - if (ret < 0) { - dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n", - fwe->fwname); - dev_printk(KERN_ERR, &udev->dev, - "you may need to download the firmware from " - "http://developer.berlios.de/projects/at76c503a/\n"); - goto exit; + if (priv->auth_mode != alg) { + printk(KERN_INFO "%s: ignored AuthFrame for alg %d\n", + priv->netdev->name, alg); + return; } - at76_dbg(DBG_FW, "got it."); - fwh = (struct at76_fw_header *)(fwe->fw->data); + BUG_ON(!priv->curr_bss); - if (fwe->fw->size <= sizeof(*fwh)) { - dev_printk(KERN_ERR, &udev->dev, - "firmware is too short (0x%zx)\n", fwe->fw->size); - goto exit; + /* Not our BSSID or not for our STA, ignore */ + if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid) + || compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1)) + return; + + cancel_delayed_work(&priv->dwork_auth); + if (status != WLAN_STATUS_SUCCESS) { + /* try to join next bss */ + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + return; } - /* CRC currently not checked */ - fwe->board_type = le32_to_cpu(fwh->board_type); - if (fwe->board_type != board_type) { - dev_printk(KERN_ERR, &udev->dev, - "board type mismatch, requested %u, got %u\n", - board_type, fwe->board_type); - goto exit; + if (priv->auth_mode == WLAN_AUTH_OPEN || seq_nr == 4) { + priv->retries = ASSOC_RETRIES; + at76_set_mac_state(priv, MAC_ASSOC); + at76_assoc_req(priv, priv->curr_bss); + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__); + schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT); + return; } - fwe->fw_version.major = fwh->major; - fwe->fw_version.minor = fwh->minor; - fwe->fw_version.patch = fwh->patch; - fwe->fw_version.build = fwh->build; + WARN_ON(seq_nr != 2); + at76_auth_req(priv, priv->curr_bss, seq_nr + 1, resp->info_element); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __func__, + __LINE__); + schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT); +} - str = (char *)fwh + le32_to_cpu(fwh->str_offset); - fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); - fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); - fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); - fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); +static void at76_rx_mgmt_deauth(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct ieee80211_disassoc *resp = + (struct ieee80211_disassoc *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + + at76_dbg(DBG_RX_MGMT | DBG_PROGRESS, + "%s: rx DeAuth bssid %s reason 0x%04x destination %s", + priv->netdev->name, mac2str(mgmt->addr3), + le16_to_cpu(resp->reason), mac2str(mgmt->addr1)); + + if (priv->mac_state != MAC_AUTH && priv->mac_state != MAC_ASSOC + && priv->mac_state != MAC_CONNECTED) { + printk(KERN_INFO "%s: DeAuth in state %s ignored\n", + priv->netdev->name, mac_states[priv->mac_state]); + return; + } - fwe->loaded = 1; + BUG_ON(!priv->curr_bss); - dev_printk(KERN_DEBUG, &udev->dev, - "using firmware %s (version %d.%d.%d-%d)\n", - fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); + /* Not our BSSID, ignore */ + if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid)) + return; - at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, - le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), - le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); - at76_dbg(DBG_DEVSTART, "firmware id %s", str); + /* Not for our STA and not broadcast, ignore */ + if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1) + && !is_broadcast_ether_addr(mgmt->addr1)) + return; -exit: - mutex_unlock(&fw_mutex); + if (priv->mac_state == MAC_CONNECTED) + at76_iwevent_bss_disconnect(priv->netdev); - if (fwe->loaded) - return fwe; - else - return NULL; + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_beacon); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); } -static void at76_mac80211_tx_callback(struct urb *urb) +static void at76_rx_mgmt_beacon(struct at76_priv *priv, + struct at76_rx_buffer *buf) { - struct at76_priv *priv = urb->context; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); + int varpar_len; + /* beacon content */ + struct ieee80211_beacon *bdata = (struct ieee80211_beacon *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &bdata->header; + + struct list_head *lptr; + struct bss_info *match; /* entry matching addr3 with its bssid */ + int new_entry = 0; + int len; + struct ieee80211_info_element *ie; + int have_ssid = 0; + int have_rates = 0; + int have_channel = 0; + int keep_going = 1; + unsigned long flags; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + if (priv->mac_state == MAC_CONNECTED) { + /* in state MAC_CONNECTED we use the mgmt_timer to control + the beacon of the BSS */ + BUG_ON(!priv->curr_bss); + + if (!compare_ether_addr(priv->curr_bss->bssid, mgmt->addr3)) { + /* We got our AP's beacon, defer the timeout handler. + Kill pending work first, as schedule_delayed_work() + won't do it. */ + cancel_delayed_work(&priv->dwork_beacon); + schedule_delayed_work(&priv->dwork_beacon, + BEACON_TIMEOUT); + priv->curr_bss->rssi = buf->rssi; + priv->beacons_received++; + goto exit; + } + } - at76_dbg(DBG_MAC80211, "%s()", __func__); + /* look if we have this BSS already in the list */ + match = NULL; - switch (urb->status) { - case 0: - /* success */ - /* FIXME: - * is the frame really ACKed when tx_callback is called ? */ - info->flags |= IEEE80211_TX_STAT_ACK; - break; - case -ENOENT: - case -ECONNRESET: - /* fail, urb has been unlinked */ - /* FIXME: add error message */ - break; - default: - at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", - __func__, urb->status); - break; + if (!list_empty(&priv->bss_list)) { + list_for_each(lptr, &priv->bss_list) { + struct bss_info *bss_ptr = + list_entry(lptr, struct bss_info, list); + if (!compare_ether_addr(bss_ptr->bssid, mgmt->addr3)) { + match = bss_ptr; + break; + } + } } - memset(&info->status, 0, sizeof(info->status)); + if (!match) { + /* BSS not in the list - append it */ + match = kzalloc(sizeof(struct bss_info), GFP_ATOMIC); + if (!match) { + at76_dbg(DBG_BSS_TABLE, + "%s: cannot kmalloc new bss info (%zd byte)", + priv->netdev->name, sizeof(struct bss_info)); + goto exit; + } + new_entry = 1; + list_add_tail(&match->list, &priv->bss_list); + } - ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); + match->capa = le16_to_cpu(bdata->capability); + match->beacon_interval = le16_to_cpu(bdata->beacon_interval); + match->rssi = buf->rssi; + match->link_qual = buf->link_quality; + match->noise_level = buf->noise_level; + memcpy(match->bssid, mgmt->addr3, ETH_ALEN); + at76_dbg(DBG_RX_BEACON, "%s: bssid %s", priv->netdev->name, + mac2str(match->bssid)); + + ie = bdata->info_element; + + /* length of var length beacon parameters */ + varpar_len = min_t(int, le16_to_cpu(buf->wlength) - + sizeof(struct ieee80211_beacon), + BEACON_MAX_DATA_LENGTH); + + /* This routine steps through the bdata->data array to get + * some useful information about the access point. + * Currently, this implementation supports receipt of: SSID, + * supported transfer rates and channel, in any order, with some + * tolerance for intermittent unknown codes (although this + * functionality may not be necessary as the useful information will + * usually arrive in consecutively, but there have been some + * reports of some of the useful information fields arriving in a + * different order). + * It does not support any more IE types although MFIE_TYPE_TIM may + * be supported (on my AP at least). + * The bdata->data array is about 1500 bytes long but only ~36 of those + * bytes are useful, hence the have_ssid etc optimizations. */ + + while (keep_going && + ((&ie->data[ie->len] - (u8 *)bdata->info_element) <= + varpar_len)) { + + switch (ie->id) { + + case MFIE_TYPE_SSID: + if (have_ssid) + break; - priv->tx_skb = NULL; + len = min_t(int, IW_ESSID_MAX_SIZE, ie->len); - ieee80211_wake_queues(priv->hw); -} + /* we copy only if this is a new entry, + or the incoming SSID is not a hidden SSID. This + will protect us from overwriting a real SSID read + in a ProbeResponse with a hidden one from a + following beacon. */ + if (!new_entry && at76_is_hidden_ssid(ie->data, len)) { + have_ssid = 1; + break; + } -static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - struct at76_priv *priv = hw->priv; - struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int padding, submit_len, ret; + match->ssid_len = len; + memcpy(match->ssid, ie->data, len); + at76_dbg(DBG_RX_BEACON, "%s: SSID - %.*s", + priv->netdev->name, len, match->ssid); + have_ssid = 1; + break; - at76_dbg(DBG_MAC80211, "%s()", __func__); + case MFIE_TYPE_RATES: + if (have_rates) + break; - if (priv->tx_urb->status == -EINPROGRESS) { - printk(KERN_ERR "%s: %s called while tx urb is pending\n", - wiphy_name(priv->hw->wiphy), __func__); - return NETDEV_TX_BUSY; - } + match->rates_len = + min_t(int, sizeof(match->rates), ie->len); + memcpy(match->rates, ie->data, match->rates_len); + have_rates = 1; + at76_dbg(DBG_RX_BEACON, "%s: SUPPORTED RATES %s", + priv->netdev->name, + hex2str(ie->data, ie->len)); + break; - ieee80211_stop_queues(hw); + case MFIE_TYPE_DS_SET: + if (have_channel) + break; - at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ + match->channel = ie->data[0]; + have_channel = 1; + at76_dbg(DBG_RX_BEACON, "%s: CHANNEL - %d", + priv->netdev->name, match->channel); + break; - WARN_ON(priv->tx_skb != NULL); + case MFIE_TYPE_CF_SET: + case MFIE_TYPE_TIM: + case MFIE_TYPE_IBSS_SET: + default: + at76_dbg(DBG_RX_BEACON, "%s: beacon IE id %d len %d %s", + priv->netdev->name, ie->id, ie->len, + hex2str(ie->data, ie->len)); + break; + } - priv->tx_skb = skb; - padding = at76_calc_padding(skb->len); - submit_len = AT76_TX_HDRLEN + skb->len + padding; + /* advance to the next informational element */ + next_ie(&ie); - /* setup 'Atmel' header */ - memset(tx_buffer, 0, sizeof(*tx_buffer)); - tx_buffer->padding = padding; - tx_buffer->wlength = cpu_to_le16(skb->len); - tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, info)->hw_value; - if (FIRMWARE_IS_WPA(priv->fw_version) && info->control.hw_key) { - tx_buffer->key_id = (info->control.hw_key->keyidx); - tx_buffer->cipher_type = - priv->keys[info->control.hw_key->keyidx].cipher; - tx_buffer->cipher_length = - priv->keys[info->control.hw_key->keyidx].keylen; - tx_buffer->reserved = 0; - } else { - tx_buffer->key_id = 0; - tx_buffer->cipher_type = 0; - tx_buffer->cipher_length = 0; - tx_buffer->reserved = 0; - }; - /* memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); */ - memcpy(tx_buffer->packet, skb->data, skb->len); + /* Optimization: after all, the bdata->data array is + * varpar_len bytes long, whereas we get all of the useful + * information after only ~36 bytes, this saves us a lot of + * time (and trouble as the remaining portion of the array + * could be full of junk) + * Comment this out if you want to see what other information + * comes from the AP - although little of it may be useful */ + } - at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr", - wiphy_name(priv->hw->wiphy), le16_to_cpu(tx_buffer->wlength), - tx_buffer->padding, tx_buffer->tx_rate); + at76_dbg(DBG_RX_BEACON, "%s: Finished processing beacon data", + priv->netdev->name); - /* send stuff */ - at76_dbg_dump(DBG_TX_DATA_CONTENT, tx_buffer, submit_len, - "%s(): tx_buffer %d bytes:", __func__, submit_len); - usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, - submit_len, at76_mac80211_tx_callback, priv); - ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); - if (ret) { - printk(KERN_ERR "%s: error in tx submit urb: %d\n", - wiphy_name(priv->hw->wiphy), ret); - if (ret == -EINVAL) - printk(KERN_ERR - "%s: -EINVAL: tx urb %p hcpriv %p complete %p\n", - wiphy_name(priv->hw->wiphy), priv->tx_urb, - priv->tx_urb->hcpriv, priv->tx_urb->complete); - } + match->last_rx = jiffies; /* record last rx of beacon */ - return 0; +exit: + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); } -static int at76_mac80211_start(struct ieee80211_hw *hw) +/* Calculate the link level from a given rx_buffer */ +static void at76_calc_level(struct at76_priv *priv, struct at76_rx_buffer *buf, + struct iw_quality *qual) { - struct at76_priv *priv = hw->priv; - int ret; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - mutex_lock(&priv->mtx); + /* just a guess for now, might be different for other chips */ + int max_rssi = 42; - ret = at76_submit_rx_urb(priv); - if (ret < 0) { - printk(KERN_ERR "%s: open: submit_rx_urb failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); - goto error; - } + qual->level = (buf->rssi * 100 / max_rssi); + if (qual->level > 100) + qual->level = 100; + qual->updated |= IW_QUAL_LEVEL_UPDATED; +} - at76_startup_device(priv); +/* Calculate the link quality from a given rx_buffer */ +static void at76_calc_qual(struct at76_priv *priv, struct at76_rx_buffer *buf, + struct iw_quality *qual) +{ + if (at76_is_intersil(priv->board_type)) + qual->qual = buf->link_quality; + else { + unsigned long elapsed; - at76_start_monitor(priv); + /* Update qual at most once a second */ + elapsed = jiffies - priv->beacons_last_qual; + if (elapsed < 1 * HZ) + return; -error: - mutex_unlock(&priv->mtx); + qual->qual = qual->level * priv->beacons_received * + msecs_to_jiffies(priv->beacon_period) / elapsed; - return 0; + priv->beacons_last_qual = jiffies; + priv->beacons_received = 0; + } + qual->qual = (qual->qual > 100) ? 100 : qual->qual; + qual->updated |= IW_QUAL_QUAL_UPDATED; } -static void at76_mac80211_stop(struct ieee80211_hw *hw) +/* Calculate the noise quality from a given rx_buffer */ +static void at76_calc_noise(struct at76_priv *priv, struct at76_rx_buffer *buf, + struct iw_quality *qual) { - struct at76_priv *priv = hw->priv; - - at76_dbg(DBG_MAC80211, "%s()", __func__); + qual->noise = 0; + qual->updated |= IW_QUAL_NOISE_INVALID; +} - mutex_lock(&priv->mtx); +static void at76_update_wstats(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct iw_quality *qual = &priv->wstats.qual; - if (!priv->device_unplugged) { - /* We are called by "ifconfig ethX down", not because the - * device is not available anymore. */ - if (at76_set_radio(priv, 0) == 1) - at76_wait_completion(priv, CMD_RADIO_ON); + if (buf->rssi && priv->mac_state == MAC_CONNECTED) { + qual->updated = 0; + at76_calc_level(priv, buf, qual); + at76_calc_qual(priv, buf, qual); + at76_calc_noise(priv, buf, qual); + } else { + qual->qual = 0; + qual->level = 0; + qual->noise = 0; + qual->updated = IW_QUAL_ALL_INVALID; + } +} - /* We unlink rx_urb because at76_open() re-submits it. - * If unplugged, at76_delete_device() takes care of it. */ - usb_kill_urb(priv->rx_urb); +static void at76_rx_mgmt(struct at76_priv *priv, struct at76_rx_buffer *buf) +{ + struct ieee80211_hdr_3addr *mgmt = + (struct ieee80211_hdr_3addr *)buf->packet; + u16 framectl = le16_to_cpu(mgmt->frame_ctl); + + /* update wstats */ + if (priv->mac_state != MAC_INIT && priv->mac_state != MAC_SCANNING) { + /* jal: this is a dirty hack needed by Tim in ad-hoc mode */ + /* Data packets always seem to have a 0 link level, so we + only read link quality info from management packets. + Atmel driver actually averages the present, and previous + values, we just present the raw value at the moment - TJS */ + if (priv->iw_mode == IW_MODE_ADHOC + || (priv->curr_bss + && !compare_ether_addr(mgmt->addr3, + priv->curr_bss->bssid))) + at76_update_wstats(priv, buf); } - mutex_unlock(&priv->mtx); -} + at76_dbg(DBG_RX_MGMT_CONTENT, "%s rx mgmt framectl 0x%x %s", + priv->netdev->name, framectl, + hex2str(mgmt, le16_to_cpu(buf->wlength))); + + switch (framectl & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BEACON: + case IEEE80211_STYPE_PROBE_RESP: + at76_rx_mgmt_beacon(priv, buf); + break; -static int at76_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) -{ - struct at76_priv *priv = hw->priv; - int ret = 0; + case IEEE80211_STYPE_ASSOC_RESP: + at76_rx_mgmt_assoc(priv, buf); + break; - at76_dbg(DBG_MAC80211, "%s()", __func__); + case IEEE80211_STYPE_DISASSOC: + at76_rx_mgmt_disassoc(priv, buf); + break; - mutex_lock(&priv->mtx); + case IEEE80211_STYPE_AUTH: + at76_rx_mgmt_auth(priv, buf); + break; - switch (conf->type) { - case NL80211_IFTYPE_STATION: - priv->iw_mode = IW_MODE_INFRA; + case IEEE80211_STYPE_DEAUTH: + at76_rx_mgmt_deauth(priv, buf); break; + default: - ret = -EOPNOTSUPP; - goto exit; + printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n", + priv->netdev->name, framectl); } -exit: - mutex_unlock(&priv->mtx); - - return ret; + return; } -static void at76_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) +/* Convert the 802.11 header into an ethernet-style header, make skb + * ready for consumption by netif_rx() */ +static void at76_ieee80211_to_eth(struct sk_buff *skb, int iw_mode) { - at76_dbg(DBG_MAC80211, "%s()", __func__); -} + struct ieee80211_hdr_3addr *i802_11_hdr; + struct ethhdr *eth_hdr_p; + u8 *src_addr; + u8 *dest_addr; -static int at76_join(struct at76_priv *priv) -{ - struct at76_req_join join; - int ret; + i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; - memset(&join, 0, sizeof(struct at76_req_join)); - memcpy(join.essid, priv->essid, priv->essid_size); - join.essid_size = priv->essid_size; - memcpy(join.bssid, priv->bssid, ETH_ALEN); - join.bss_type = INFRASTRUCTURE_MODE; - join.channel = priv->channel; - join.timeout = cpu_to_le16(2000); + /* That would be the ethernet header if the hardware converted + * the frame for us. Make sure the source and the destination + * match the 802.11 header. Which hardware does it? */ + eth_hdr_p = (struct ethhdr *)skb_pull(skb, IEEE80211_3ADDR_LEN); - at76_dbg(DBG_MAC80211, "%s: sending CMD_JOIN", __func__); - ret = at76_set_card_command(priv->udev, CMD_JOIN, &join, - sizeof(struct at76_req_join)); + dest_addr = i802_11_hdr->addr1; + if (iw_mode == IW_MODE_ADHOC) + src_addr = i802_11_hdr->addr2; + else + src_addr = i802_11_hdr->addr3; - if (ret < 0) { - printk(KERN_ERR "%s: at76_set_card_command failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); - return 0; - } + if (!compare_ether_addr(eth_hdr_p->h_source, src_addr) && + !compare_ether_addr(eth_hdr_p->h_dest, dest_addr)) + /* Yes, we already have an ethernet header */ + skb_reset_mac_header(skb); + else { + u16 len; + + /* Need to build an ethernet header */ + if (!memcmp(skb->data, snapsig, sizeof(snapsig))) { + /* SNAP frame - decapsulate, keep proto */ + skb_push(skb, offsetof(struct ethhdr, h_proto) - + sizeof(rfc1042sig)); + len = 0; + } else { + /* 802.3 frame, proto is length */ + len = skb->len; + skb_push(skb, ETH_HLEN); + } - ret = at76_wait_completion(priv, CMD_JOIN); - at76_dbg(DBG_MAC80211, "%s: CMD_JOIN returned: 0x%02x", __func__, ret); - if (ret != CMD_STATUS_COMPLETE) { - printk(KERN_ERR "%s: at76_wait_completion failed: %d\n", - wiphy_name(priv->hw->wiphy), ret); - return 0; + skb_reset_mac_header(skb); + eth_hdr_p = eth_hdr(skb); + /* This needs to be done in this order (eth_hdr_p->h_dest may + * overlap src_addr) */ + memcpy(eth_hdr_p->h_source, src_addr, ETH_ALEN); + memcpy(eth_hdr_p->h_dest, dest_addr, ETH_ALEN); + if (len) + eth_hdr_p->h_proto = htons(len); } - at76_set_tkip_bssid(priv, priv->bssid); - at76_set_pm_mode(priv); - - return 0; + skb->protocol = eth_type_trans(skb, skb->dev); } -static void at76_dwork_hw_scan(struct work_struct *work) +/* Check for fragmented data in priv->rx_skb. If the packet was no fragment + or it was the last of a fragment set a skb containing the whole packet + is returned for further processing. Otherwise we get NULL and are + done and the packet is either stored inside the fragment buffer + or thrown away. Every returned skb starts with the ieee802_11 header + and contains _no_ FCS at the end */ +static struct sk_buff *at76_check_for_rx_frags(struct at76_priv *priv) { - struct at76_priv *priv = container_of(work, struct at76_priv, - dwork_hw_scan.work); - int ret; + struct sk_buff *skb = priv->rx_skb; + struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data; + struct ieee80211_hdr_3addr *i802_11_hdr = + (struct ieee80211_hdr_3addr *)buf->packet; + /* seq_ctrl, fragment_number, sequence number of new packet */ + u16 sctl = le16_to_cpu(i802_11_hdr->seq_ctl); + u16 fragnr = sctl & 0xf; + u16 seqnr = sctl >> 4; + u16 frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl); - ret = at76_get_cmd_status(priv->udev, CMD_SCAN); - at76_dbg(DBG_MAC80211, "%s: CMD_SCAN status 0x%02x", __func__, ret); + /* Length including the IEEE802.11 header, but without the trailing + * FCS and without the Atmel Rx header */ + int length = le16_to_cpu(buf->wlength) - IEEE80211_FCS_LEN; - /* FIXME: add maximum time for scan to complete */ + /* where does the data payload start in skb->data ? */ + u8 *data = i802_11_hdr->payload; - if (ret != CMD_STATUS_COMPLETE) { - queue_delayed_work(priv->hw->workqueue, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); - goto exit; + /* length of payload, excl. the trailing FCS */ + int data_len = length - IEEE80211_3ADDR_LEN; + + int i; + struct rx_data_buf *bptr, *optr; + unsigned long oldest = ~0UL; + + at76_dbg(DBG_RX_FRAGS, + "%s: rx data frame_ctl %04x addr2 %s seq/frag %d/%d " + "length %d data %d: %s ...", priv->netdev->name, frame_ctl, + mac2str(i802_11_hdr->addr2), seqnr, fragnr, length, data_len, + hex2str(data, 32)); + + at76_dbg(DBG_RX_FRAGS_SKB, "%s: incoming skb: head %p data %p " + "tail %p end %p len %d", priv->netdev->name, skb->head, + skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), + skb->len); + + if (data_len < 0) { + /* make sure data starts in the buffer */ + printk(KERN_INFO "%s: data frame too short\n", + priv->netdev->name); + return NULL; } - ieee80211_scan_completed(priv->hw); + WARN_ON(length <= AT76_RX_HDRLEN); + if (length <= AT76_RX_HDRLEN) + return NULL; - if (is_valid_ether_addr(priv->bssid)) { - ieee80211_wake_queues(priv->hw); - at76_join(priv); + /* remove the at76_rx_buffer header - we don't need it anymore */ + /* we need the IEEE802.11 header (for the addresses) if this packet + is the first of a chain */ + skb_pull(skb, AT76_RX_HDRLEN); + + /* remove FCS at end */ + skb_trim(skb, length); + + at76_dbg(DBG_RX_FRAGS_SKB, "%s: trimmed skb: head %p data %p tail %p " + "end %p len %d data %p data_len %d", priv->netdev->name, + skb->head, skb->data, skb_tail_pointer(skb), + skb_end_pointer(skb), skb->len, data, data_len); + + if (fragnr == 0 && !(frame_ctl & IEEE80211_FCTL_MOREFRAGS)) { + /* unfragmented packet received */ + /* Use a new skb for the next receive */ + priv->rx_skb = NULL; + at76_dbg(DBG_RX_FRAGS, "%s: unfragmented", priv->netdev->name); + return skb; } - ieee80211_wake_queues(priv->hw); + /* look if we've got a chain for the sender address. + afterwards optr points to first free or the oldest entry, + or, if i < NR_RX_DATA_BUF, bptr points to the entry for the + sender address */ + /* determining the oldest entry doesn't cope with jiffies wrapping + but I don't care to delete a young entry at these rare moments ... */ + + bptr = priv->rx_data; + optr = NULL; + for (i = 0; i < NR_RX_DATA_BUF; i++, bptr++) { + if (!bptr->skb) { + optr = bptr; + oldest = 0UL; + continue; + } -exit: - return; -} + if (!compare_ether_addr(i802_11_hdr->addr2, bptr->sender)) + break; -static int at76_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) -{ - struct at76_priv *priv = hw->priv; - struct at76_req_scan scan; - int ret; + if (!optr) { + optr = bptr; + oldest = bptr->last_rx; + } else if (bptr->last_rx < oldest) + optr = bptr; + } - at76_dbg(DBG_MAC80211, "%s():", __func__); - at76_dbg_dump(DBG_MAC80211, ssid, len, "ssid %zd bytes:", len); + if (i < NR_RX_DATA_BUF) { + + at76_dbg(DBG_RX_FRAGS, "%s: %d. cacheentry (seq/frag = %d/%d) " + "matched sender addr", + priv->netdev->name, i, bptr->seqnr, bptr->fragnr); + + /* bptr points to an entry for the sender address */ + if (bptr->seqnr == seqnr) { + int left; + /* the fragment has the current sequence number */ + if (((bptr->fragnr + 1) & 0xf) != fragnr) { + /* wrong fragment number -> ignore it */ + /* is & 0xf necessary above ??? */ + at76_dbg(DBG_RX_FRAGS, + "%s: frag nr mismatch: %d + 1 != %d", + priv->netdev->name, bptr->fragnr, + fragnr); + return NULL; + } + bptr->last_rx = jiffies; + /* the next following fragment number -> + add the data at the end */ + + /* for test only ??? */ + left = skb_tailroom(bptr->skb); + if (left < data_len) + printk(KERN_INFO + "%s: only %d byte free (need %d)\n", + priv->netdev->name, left, data_len); + else + memcpy(skb_put(bptr->skb, data_len), data, + data_len); + + bptr->fragnr = fragnr; + if (frame_ctl & IEEE80211_FCTL_MOREFRAGS) + return NULL; + + /* this was the last fragment - send it */ + skb = bptr->skb; + bptr->skb = NULL; /* free the entry */ + at76_dbg(DBG_RX_FRAGS, "%s: last frag of seq %d", + priv->netdev->name, seqnr); + return skb; + } - mutex_lock(&priv->mtx); + /* got another sequence number */ + if (fragnr == 0) { + /* it's the start of a new chain - replace the + old one by this */ + /* bptr->sender has the correct value already */ + at76_dbg(DBG_RX_FRAGS, + "%s: start of new seq %d, removing old seq %d", + priv->netdev->name, seqnr, bptr->seqnr); + bptr->seqnr = seqnr; + bptr->fragnr = 0; + bptr->last_rx = jiffies; + /* swap bptr->skb and priv->rx_skb */ + skb = bptr->skb; + bptr->skb = priv->rx_skb; + priv->rx_skb = skb; + } else { + /* it from the middle of a new chain -> + delete the old entry and skip the new one */ + at76_dbg(DBG_RX_FRAGS, + "%s: middle of new seq %d (%d) " + "removing old seq %d", + priv->netdev->name, seqnr, fragnr, + bptr->seqnr); + dev_kfree_skb(bptr->skb); + bptr->skb = NULL; + } + return NULL; + } - ieee80211_stop_queues(hw); + /* if we didn't find a chain for the sender address, optr + points either to the first free or the oldest entry */ - memset(&scan, 0, sizeof(struct at76_req_scan)); - memset(scan.bssid, 0xFF, ETH_ALEN); - scan.scan_type = SCAN_TYPE_ACTIVE; - if (priv->essid_size > 0) { - memcpy(scan.essid, ssid, len); - scan.essid_size = len; + if (fragnr != 0) { + /* this is not the begin of a fragment chain ... */ + at76_dbg(DBG_RX_FRAGS, + "%s: no chain for non-first fragment (%d)", + priv->netdev->name, fragnr); + return NULL; } - scan.min_channel_time = cpu_to_le16(priv->scan_min_time); - scan.max_channel_time = cpu_to_le16(priv->scan_max_time); - scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); - scan.international_scan = 0; - at76_dbg(DBG_MAC80211, "%s: sending CMD_SCAN", __func__); - ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); + BUG_ON(!optr); + if (optr->skb) { + /* swap the skb's */ + skb = optr->skb; + optr->skb = priv->rx_skb; + priv->rx_skb = skb; - if (ret < 0) { - err("CMD_SCAN failed: %d", ret); - goto exit; - } + at76_dbg(DBG_RX_FRAGS, + "%s: free old contents: sender %s seq/frag %d/%d", + priv->netdev->name, mac2str(optr->sender), + optr->seqnr, optr->fragnr); - queue_delayed_work(priv->hw->workqueue, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); + } else { + /* take the skb from priv->rx_skb */ + optr->skb = priv->rx_skb; + /* let at76_submit_rx_urb() allocate a new skb */ + priv->rx_skb = NULL; -exit: - mutex_unlock(&priv->mtx); + at76_dbg(DBG_RX_FRAGS, "%s: use a free entry", + priv->netdev->name); + } + memcpy(optr->sender, i802_11_hdr->addr2, ETH_ALEN); + optr->seqnr = seqnr; + optr->fragnr = 0; + optr->last_rx = jiffies; - return 0; + return NULL; } -static int at76_config(struct ieee80211_hw *hw, u32 changed) +/* Rx interrupt: we expect the complete data buffer in priv->rx_skb */ +static void at76_rx_data(struct at76_priv *priv) { - struct at76_priv *priv = hw->priv; - struct ieee80211_conf *conf = &hw->conf; + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &priv->stats; + struct sk_buff *skb = priv->rx_skb; + struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data; + struct ieee80211_hdr_3addr *i802_11_hdr; + int length = le16_to_cpu(buf->wlength); - at76_dbg(DBG_MAC80211, "%s(): channel %d radio %d", - __func__, conf->channel->hw_value, conf->radio_enabled); - at76_dbg_dump(DBG_MAC80211, priv->essid, priv->essid_size, "ssid:"); - at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); + at76_dbg(DBG_RX_DATA, "%s received data packet: %s", netdev->name, + hex2str(skb->data, AT76_RX_HDRLEN)); - mutex_lock(&priv->mtx); + at76_dbg(DBG_RX_DATA_CONTENT, "rx packet: %s", + hex2str(skb->data + AT76_RX_HDRLEN, length)); - priv->channel = conf->channel->hw_value; + skb = at76_check_for_rx_frags(priv); + if (!skb) + return; - if (is_valid_ether_addr(priv->bssid)) { - at76_join(priv); - ieee80211_wake_queues(priv->hw); - } else { - ieee80211_stop_queues(priv->hw); - at76_start_monitor(priv); - }; + /* Atmel header and the FCS are already removed */ + i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; - mutex_unlock(&priv->mtx); + skb->dev = netdev; + skb->ip_summed = CHECKSUM_NONE; /* TODO: should check CRC */ - return 0; -} + if (is_broadcast_ether_addr(i802_11_hdr->addr1)) { + if (!compare_ether_addr(i802_11_hdr->addr1, netdev->broadcast)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } else if (compare_ether_addr(i802_11_hdr->addr1, netdev->dev_addr)) + skb->pkt_type = PACKET_OTHERHOST; -static int at76_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct at76_priv *priv = hw->priv; + at76_ieee80211_to_eth(skb, priv->iw_mode); - at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); + netdev->last_rx = jiffies; + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += length; - mutex_lock(&priv->mtx); + return; +} - memcpy(priv->bssid, conf->bssid, ETH_ALEN); -// memcpy(priv->essid, conf->ssid, conf->ssid_len); -// priv->essid_size = conf->ssid_len; +static void at76_rx_monitor_mode(struct at76_priv *priv) +{ + struct at76_rx_radiotap *rt; + u8 *payload; + int skblen; + struct net_device *netdev = priv->netdev; + struct at76_rx_buffer *buf = + (struct at76_rx_buffer *)priv->rx_skb->data; + /* length including the IEEE802.11 header and the trailing FCS, + but not at76_rx_buffer */ + int length = le16_to_cpu(buf->wlength); + struct sk_buff *skb = priv->rx_skb; + struct net_device_stats *stats = &priv->stats; - if (is_valid_ether_addr(priv->bssid)) { - /* mac80211 is joining a bss */ - ieee80211_wake_queues(priv->hw); - at76_join(priv); - } else - ieee80211_stop_queues(priv->hw); + if (length < IEEE80211_FCS_LEN) { + /* buffer contains no data */ + at76_dbg(DBG_MONITOR_MODE, + "%s: MONITOR MODE: rx skb without data", + priv->netdev->name); + return; + } - mutex_unlock(&priv->mtx); + skblen = sizeof(struct at76_rx_radiotap) + length; - return 0; + skb = dev_alloc_skb(skblen); + if (!skb) { + printk(KERN_ERR "%s: MONITOR MODE: dev_alloc_skb for radiotap " + "header returned NULL\n", priv->netdev->name); + return; + } + + skb_put(skb, skblen); + + rt = (struct at76_rx_radiotap *)skb->data; + payload = skb->data + sizeof(struct at76_rx_radiotap); + + rt->rt_hdr.it_version = 0; + rt->rt_hdr.it_pad = 0; + rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct at76_rx_radiotap)); + rt->rt_hdr.it_present = cpu_to_le32(AT76_RX_RADIOTAP_PRESENT); + + rt->rt_tsft = cpu_to_le64(le32_to_cpu(buf->rx_time)); + rt->rt_rate = hw_rates[buf->rx_rate] & (~0x80); + rt->rt_signal = buf->rssi; + rt->rt_noise = buf->noise_level; + rt->rt_flags = IEEE80211_RADIOTAP_F_FCS; + if (buf->fragmentation) + rt->rt_flags |= IEEE80211_RADIOTAP_F_FRAG; + + memcpy(payload, buf->packet, length); + skb->dev = netdev; + skb->ip_summed = CHECKSUM_NONE; + skb_reset_mac_header(skb); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + + netdev->last_rx = jiffies; + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += length; } -/* must be atomic */ -static void at76_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, int mc_count, - struct dev_addr_list *mc_list) +/* Check if we spy on the sender address in buf and update stats */ +static void at76_iwspy_update(struct at76_priv *priv, + struct at76_rx_buffer *buf) { - struct at76_priv *priv = hw->priv; - int flags; - - at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " - "total_flags=0x%08x mc_count=%d", - __func__, changed_flags, *total_flags, mc_count); + struct ieee80211_hdr_3addr *hdr = + (struct ieee80211_hdr_3addr *)buf->packet; + struct iw_quality qual; - flags = changed_flags & AT76_SUPPORTED_FILTERS; - *total_flags = AT76_SUPPORTED_FILTERS; + /* We can only set the level here */ + qual.updated = IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; + qual.level = 0; + qual.noise = 0; + at76_calc_level(priv, buf, &qual); - /* FIXME: access to priv->promisc should be protected with - * priv->mtx, but it's impossible because this function needs to be - * atomic */ + spin_lock_bh(&priv->spy_spinlock); - if (flags && !priv->promisc) { - /* mac80211 wants us to enable promiscuous mode */ - priv->promisc = 1; - } else if (!flags && priv->promisc) { - /* we need to disable promiscuous mode */ - priv->promisc = 0; - } else - return; + if (priv->spy_data.spy_number > 0) + wireless_spy_update(priv->netdev, hdr->addr2, &qual); - queue_work(hw->workqueue, &priv->work_set_promisc); + spin_unlock_bh(&priv->spy_spinlock); } -static int at76_set_key_oldfw(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_address, const u8 *address, - struct ieee80211_key_conf *key) +static void at76_rx_tasklet(unsigned long param) { - struct at76_priv *priv = hw->priv; - - int i; - - at76_dbg(DBG_MAC80211, "%s(): cmd %d key->alg %d key->keyidx %d " - "key->keylen %d", - __func__, cmd, key->alg, key->keyidx, key->keylen); + struct urb *urb = (struct urb *)param; + struct at76_priv *priv = urb->context; + struct net_device *netdev = priv->netdev; + struct at76_rx_buffer *buf; + struct ieee80211_hdr_3addr *i802_11_hdr; + u16 frame_ctl; - if (key->alg != ALG_WEP) - return -EOPNOTSUPP; + if (priv->device_unplugged) { + at76_dbg(DBG_DEVSTART, "device unplugged"); + if (urb) + at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); + return; + } - key->hw_key_idx = key->keyidx; + if (!priv->rx_skb || !netdev || !priv->rx_skb->data) + return; - mutex_lock(&priv->mtx); + buf = (struct at76_rx_buffer *)priv->rx_skb->data; - switch (cmd) { - case SET_KEY: - memcpy(priv->wep_keys[key->keyidx], key->key, key->keylen); - priv->wep_keys_len[key->keyidx] = key->keylen; + i802_11_hdr = (struct ieee80211_hdr_3addr *)buf->packet; - /* FIXME: find out how to do this properly */ - priv->wep_key_id = key->keyidx; + frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl); - break; - case DISABLE_KEY: - default: - priv->wep_keys_len[key->keyidx] = 0; - break; + if (urb->status != 0) { + if (urb->status != -ENOENT && urb->status != -ECONNRESET) + at76_dbg(DBG_URB, + "%s %s: - nonzero Rx bulk status received: %d", + __func__, netdev->name, urb->status); + return; } - priv->wep_enabled = 0; - - for (i = 0; i < WEP_KEYS; i++) { - if (priv->wep_keys_len[i] != 0) - priv->wep_enabled = 1; + at76_dbg(DBG_RX_ATMEL_HDR, + "%s: rx frame: rate %d rssi %d noise %d link %d %s", + priv->netdev->name, buf->rx_rate, buf->rssi, buf->noise_level, + buf->link_quality, hex2str(i802_11_hdr, 48)); + if (priv->iw_mode == IW_MODE_MONITOR) { + at76_rx_monitor_mode(priv); + goto exit; } - at76_startup_device(priv); + /* there is a new bssid around, accept it: */ + if (buf->newbss && priv->iw_mode == IW_MODE_ADHOC) { + at76_dbg(DBG_PROGRESS, "%s: rx newbss", netdev->name); + schedule_work(&priv->work_new_bss); + } - mutex_unlock(&priv->mtx); + switch (frame_ctl & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + at76_rx_data(priv); + break; - return 0; -} + case IEEE80211_FTYPE_MGMT: + /* jal: TODO: find out if we can update iwspy also on + other frames than management (might depend on the + radio chip / firmware version !) */ -static int at76_set_key_newfw(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_address, const u8 *address, - struct ieee80211_key_conf *key) -{ - struct at76_priv *priv = hw->priv; - int ret = -EOPNOTSUPP; + at76_iwspy_update(priv, buf); - at76_dbg(DBG_MAC80211, "%s(): cmd %d key->alg %d key->keyidx %d " - "key->keylen %d", - __func__, cmd, key->alg, key->keyidx, key->keylen); + at76_rx_mgmt(priv, buf); + break; - mutex_lock(&priv->mtx); + case IEEE80211_FTYPE_CTL: + at76_dbg(DBG_RX_CTRL, "%s: ignored ctrl frame: %04x", + priv->netdev->name, frame_ctl); + break; - priv->mib_buf.type = MIB_MAC_ENCRYPTION; + default: + printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n", + priv->netdev->name, frame_ctl); + } +exit: + at76_submit_rx_urb(priv); +} - if (cmd == DISABLE_KEY) { - priv->mib_buf.size = CIPHER_KEY_LEN; - priv->mib_buf.index = offsetof(struct mib_mac_encryption, - cipher_default_keyvalue[key->keyidx]); - memset(priv->mib_buf.data.data, 0, CIPHER_KEY_LEN); - if (at76_set_mib(priv, &priv->mib_buf) != CMD_STATUS_COMPLETE) - ret = -EOPNOTSUPP; /* -EIO would be probably better */ - else { +/* Load firmware into kernel memory and parse it */ +static struct fwentry *at76_load_firmware(struct usb_device *udev, + enum board_type board_type) +{ + int ret; + char *str; + struct at76_fw_header *fwh; + struct fwentry *fwe = &firmwares[board_type]; - priv->keys[key->keyidx].cipher = CIPHER_NONE; - priv->keys[key->keyidx].keylen = 0; - }; - if (priv->default_group_key == key->keyidx) - priv->default_group_key = 0xff; + mutex_lock(&fw_mutex); - if (priv->default_pairwise_key == key->keyidx) - priv->default_pairwise_key = 0xff; - /* If default pairwise key is removed, fall back to - * group key? */ - ret = 0; + if (fwe->loaded) { + at76_dbg(DBG_FW, "re-using previously loaded fw"); goto exit; - }; - - if (cmd == SET_KEY) { - /* store key into MIB */ - priv->mib_buf.size = CIPHER_KEY_LEN; - priv->mib_buf.index = offsetof(struct mib_mac_encryption, - cipher_default_keyvalue[key->keyidx]); - memset(priv->mib_buf.data.data, 0, CIPHER_KEY_LEN); - memcpy(priv->mib_buf.data.data, key->key, key->keylen); - - switch (key->alg) { - case ALG_WEP: - if (key->keylen == 5) { - priv->keys[key->keyidx].cipher = - CIPHER_WEP64; - priv->keys[key->keyidx].keylen = 8; - } else if (key->keylen == 13) { - priv->keys[key->keyidx].cipher = - CIPHER_WEP128; - /* Firmware needs this */ - priv->keys[key->keyidx].keylen = 8; - } else { - ret = -EOPNOTSUPP; - goto exit; - }; - break; - case ALG_TKIP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - priv->keys[key->keyidx].cipher = CIPHER_TKIP; - priv->keys[key->keyidx].keylen = 12; - break; + } - case ALG_CCMP: - if (!at76_is_505a(priv->board_type)) { - ret = -EOPNOTSUPP; - goto exit; - }; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - priv->keys[key->keyidx].cipher = CIPHER_CCMP; - priv->keys[key->keyidx].keylen = 16; - break; + at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); + ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); + if (ret < 0) { + dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n", + fwe->fwname); + dev_printk(KERN_ERR, &udev->dev, + "you may need to download the firmware from " + "http://developer.berlios.de/projects/at76c503a/"); + goto exit; + } - default: - ret = -EOPNOTSUPP; - goto exit; - }; - - priv->mib_buf.data.data[38] = priv->keys[key->keyidx].cipher; - priv->mib_buf.data.data[39] = 1; /* Taken from atmelwlandriver, - not documented */ - - if (is_valid_ether_addr(address)) - /* Pairwise key */ - priv->mib_buf.data.data[39] |= (KEY_PAIRWISE | KEY_TX); - else if (is_broadcast_ether_addr(address)) - /* Group key */ - priv->mib_buf.data.data[39] |= (KEY_TX); - else /* Key used only for transmission ??? */ - priv->mib_buf.data.data[39] |= (KEY_TX); - - if (at76_set_mib(priv, &priv->mib_buf) != - CMD_STATUS_COMPLETE) { - ret = -EOPNOTSUPP; /* -EIO would be probably better */ - goto exit; - }; + at76_dbg(DBG_FW, "got it."); + fwh = (struct at76_fw_header *)(fwe->fw->data); - if ((key->alg == ALG_TKIP) || (key->alg == ALG_CCMP)) - at76_reset_rsc(priv); + if (fwe->fw->size <= sizeof(*fwh)) { + dev_printk(KERN_ERR, &udev->dev, + "firmware is too short (0x%zx)\n", fwe->fw->size); + goto exit; + } - key->hw_key_idx = key->keyidx; + /* CRC currently not checked */ + fwe->board_type = le32_to_cpu(fwh->board_type); + if (fwe->board_type != board_type) { + dev_printk(KERN_ERR, &udev->dev, + "board type mismatch, requested %u, got %u\n", + board_type, fwe->board_type); + goto exit; + } - /* Set up default keys */ - if (is_broadcast_ether_addr(address)) - priv->default_group_key = key->keyidx; - if (is_valid_ether_addr(address)) - priv->default_pairwise_key = key->keyidx; + fwe->fw_version.major = fwh->major; + fwe->fw_version.minor = fwh->minor; + fwe->fw_version.patch = fwh->patch; + fwe->fw_version.build = fwh->build; - /* Set up encryption MIBs */ + str = (char *)fwh + le32_to_cpu(fwh->str_offset); + fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); + fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); + fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); + fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); - /* first block of settings */ - priv->mib_buf.size = 3; - priv->mib_buf.index = offsetof(struct mib_mac_encryption, - privacy_invoked); - priv->mib_buf.data.data[0] = 1; /* privacy_invoked */ - priv->mib_buf.data.data[1] = priv->default_pairwise_key; - priv->mib_buf.data.data[2] = priv->default_group_key; + fwe->loaded = 1; - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret != CMD_STATUS_COMPLETE) - goto exit; + dev_printk(KERN_DEBUG, &udev->dev, + "using firmware %s (version %d.%d.%d-%d)\n", + fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); - /* second block of settings */ - priv->mib_buf.size = 3; - priv->mib_buf.index = offsetof(struct mib_mac_encryption, - exclude_unencrypted); - priv->mib_buf.data.data[0] = 1; /* exclude_unencrypted */ - priv->mib_buf.data.data[1] = 0; /* wep_encryption_type */ - priv->mib_buf.data.data[2] = 0; /* ckip_key_permutation */ + at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, + le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), + le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); + at76_dbg(DBG_DEVSTART, "firmware id %s", str); - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret != CMD_STATUS_COMPLETE) - goto exit; - ret = 0; - }; exit: - at76_dump_mib_mac_encryption(priv); - mutex_unlock(&priv->mtx); - return ret; -} - -static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_address, const u8 *address, - struct ieee80211_key_conf *key) -{ - struct at76_priv *priv = hw->priv; - - at76_dbg(DBG_MAC80211, "%s(): cmd %d key->alg %d key->keyidx %d " - "key->keylen %d", - __func__, cmd, key->alg, key->keyidx, key->keylen); + mutex_unlock(&fw_mutex); - if (FIRMWARE_IS_WPA(priv->fw_version)) - return at76_set_key_newfw(hw, cmd, local_address, address, key); + if (fwe->loaded) + return fwe; else - return at76_set_key_oldfw(hw, cmd, local_address, address, key); - + return NULL; } -static const struct ieee80211_ops at76_ops = { - .tx = at76_mac80211_tx, - .add_interface = at76_add_interface, - .remove_interface = at76_remove_interface, - .config = at76_config, - .config_interface = at76_config_interface, - .configure_filter = at76_configure_filter, - .start = at76_mac80211_start, - .stop = at76_mac80211_stop, - .hw_scan = at76_hw_scan, - .set_key = at76_set_key, -}; - /* Allocate network device and initialize private data */ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) { - struct ieee80211_hw *hw; + struct net_device *netdev; struct at76_priv *priv; + int i; - hw = ieee80211_alloc_hw(sizeof(struct at76_priv), &at76_ops); - if (!hw) { - printk(KERN_ERR DRIVER_NAME ": could not register" - " ieee80211_hw\n"); + /* allocate memory for our device state and initialize it */ + netdev = alloc_etherdev(sizeof(struct at76_priv)); + if (!netdev) { + dev_printk(KERN_ERR, &udev->dev, "out of memory\n"); return NULL; } - priv = hw->priv; - priv->hw = hw; + priv = netdev_priv(netdev); priv->udev = udev; + priv->netdev = netdev; mutex_init(&priv->mtx); + INIT_WORK(&priv->work_assoc_done, at76_work_assoc_done); + INIT_WORK(&priv->work_join, at76_work_join); + INIT_WORK(&priv->work_new_bss, at76_work_new_bss); + INIT_WORK(&priv->work_start_scan, at76_work_start_scan); INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); - INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan); + INIT_DELAYED_WORK(&priv->dwork_restart, at76_dwork_restart); + INIT_DELAYED_WORK(&priv->dwork_get_scan, at76_dwork_get_scan); + INIT_DELAYED_WORK(&priv->dwork_beacon, at76_dwork_beacon); + INIT_DELAYED_WORK(&priv->dwork_auth, at76_dwork_auth); + INIT_DELAYED_WORK(&priv->dwork_assoc, at76_dwork_assoc); + + spin_lock_init(&priv->mgmt_spinlock); + priv->next_mgmt_bulk = NULL; + priv->mac_state = MAC_INIT; + + /* initialize empty BSS list */ + priv->curr_bss = NULL; + INIT_LIST_HEAD(&priv->bss_list); + spin_lock_init(&priv->bss_list_spinlock); + + init_timer(&priv->bss_list_timer); + priv->bss_list_timer.data = (unsigned long)priv; + priv->bss_list_timer.function = at76_bss_list_timeout; + + spin_lock_init(&priv->spy_spinlock); + + /* mark all rx data entries as unused */ + for (i = 0; i < NR_RX_DATA_BUF; i++) + priv->rx_data[i].skb = NULL; priv->rx_tasklet.func = at76_rx_tasklet; priv->rx_tasklet.data = 0; @@ -2371,9 +5195,6 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) priv->pm_mode = AT76_PM_OFF; priv->pm_period = 0; - /* unit us */ - priv->hw->channel_change_time = 100000; - return priv; } @@ -2436,42 +5257,11 @@ static int at76_alloc_urbs(struct at76_priv *priv, return 0; } -static struct ieee80211_rate at76_rates[] = { - { .bitrate = 10, .hw_value = TX_RATE_1MBIT, }, - { .bitrate = 20, .hw_value = TX_RATE_2MBIT, }, - { .bitrate = 55, .hw_value = TX_RATE_5_5MBIT, }, - { .bitrate = 110, .hw_value = TX_RATE_11MBIT, }, -}; - -static struct ieee80211_channel at76_channels[] = { - { .center_freq = 2412, .hw_value = 1 }, - { .center_freq = 2417, .hw_value = 2 }, - { .center_freq = 2422, .hw_value = 3 }, - { .center_freq = 2427, .hw_value = 4 }, - { .center_freq = 2432, .hw_value = 5 }, - { .center_freq = 2437, .hw_value = 6 }, - { .center_freq = 2442, .hw_value = 7 }, - { .center_freq = 2447, .hw_value = 8 }, - { .center_freq = 2452, .hw_value = 9 }, - { .center_freq = 2457, .hw_value = 10 }, - { .center_freq = 2462, .hw_value = 11 }, - { .center_freq = 2467, .hw_value = 12 }, - { .center_freq = 2472, .hw_value = 13 }, - { .center_freq = 2484, .hw_value = 14 } -}; - -static struct ieee80211_supported_band at76_supported_band = { - .channels = at76_channels, - .n_channels = ARRAY_SIZE(at76_channels), - .bitrates = at76_rates, - .n_bitrates = ARRAY_SIZE(at76_rates), -}; - /* Register network device and initialize the hardware */ static int at76_init_new_device(struct at76_priv *priv, struct usb_interface *interface) { - struct device *dev = &interface->dev; + struct net_device *netdev = priv->netdev; int ret; /* set up the endpoint information */ @@ -2487,11 +5277,14 @@ static int at76_init_new_device(struct at76_priv *priv, /* MAC address */ ret = at76_get_hw_config(priv); if (ret < 0) { - dev_err(dev, "cannot get MAC address\n"); + dev_printk(KERN_ERR, &interface->dev, + "cannot get MAC address\n"); goto exit; } priv->domain = at76_get_reg_domain(priv->regulatory_domain); + /* init. netdev->dev_addr */ + memcpy(netdev->dev_addr, priv->mac_addr, ETH_ALEN); priv->channel = DEF_CHANNEL; priv->iw_mode = IW_MODE_INFRA; @@ -2501,54 +5294,47 @@ static int at76_init_new_device(struct at76_priv *priv, priv->txrate = TX_RATE_AUTO; priv->preamble_type = PREAMBLE_TYPE_LONG; priv->beacon_period = 100; + priv->beacons_last_qual = jiffies; priv->auth_mode = WLAN_AUTH_OPEN; priv->scan_min_time = DEF_SCAN_MIN_TIME; priv->scan_max_time = DEF_SCAN_MAX_TIME; priv->scan_mode = SCAN_TYPE_ACTIVE; - priv->default_pairwise_key = 0xff; - priv->default_group_key = 0xff; - - /* mac80211 initialisation */ - priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &at76_supported_band; - - if (FIRMWARE_IS_WPA(priv->fw_version) && - (at76_is_503rfmd(priv->board_type) || - at76_is_505(priv->board_type))) - priv->hw->flags = IEEE80211_HW_SIGNAL_UNSPEC; - else - priv->hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SIGNAL_UNSPEC; - - priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - SET_IEEE80211_DEV(priv->hw, &interface->dev); - SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); - - ret = ieee80211_register_hw(priv->hw); + netdev->flags &= ~IFF_MULTICAST; /* not yet or never */ + netdev->open = at76_open; + netdev->stop = at76_stop; + netdev->get_stats = at76_get_stats; + netdev->ethtool_ops = &at76_ethtool_ops; + + /* Add pointers to enable iwspy support. */ + priv->wireless_data.spy_data = &priv->spy_data; + netdev->wireless_data = &priv->wireless_data; + + netdev->hard_start_xmit = at76_tx; + netdev->tx_timeout = at76_tx_timeout; + netdev->watchdog_timeo = 2 * HZ; + netdev->wireless_handlers = &at76_handler_def; + netdev->set_multicast_list = at76_set_multicast; + netdev->set_mac_address = at76_set_mac_address; + dev_alloc_name(netdev, "wlan%d"); + + ret = register_netdev(priv->netdev); if (ret) { - dev_err(dev, "cannot register mac80211 hw (status %d)!\n", ret); + dev_printk(KERN_ERR, &interface->dev, + "cannot register netdevice (status %d)!\n", ret); goto exit; } + priv->netdev_registered = 1; - priv->mac80211_registered = 1; + printk(KERN_INFO "%s: USB %s, MAC %s, firmware %d.%d.%d-%d\n", + netdev->name, dev_name(&interface->dev), mac2str(priv->mac_addr), + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); + printk(KERN_INFO "%s: regulatory domain 0x%02x: %s\n", netdev->name, + priv->regulatory_domain, priv->domain->name); - dev_info(dev, "%s: USB %s, MAC %s, firmware %d.%d.%d-%d\n", - wiphy_name(priv->hw->wiphy), - dev_name(&interface->dev), mac2str(priv->mac_addr), - priv->fw_version.major, priv->fw_version.minor, - priv->fw_version.patch, priv->fw_version.build); - dev_info(dev, "%s: regulatory domain 0x%02x: %s\n", - wiphy_name(priv->hw->wiphy), - priv->regulatory_domain, priv->domain->name); - dev_info(dev, "%s: WPA support: ", wiphy_name(priv->hw->wiphy)); - if (!FIRMWARE_IS_WPA(priv->fw_version)) - printk("none\n"); - else { - if (!at76_is_505a(priv->board_type)) - printk("TKIP\n"); - else - printk("TKIP, AES/CCMP\n"); - }; + /* we let this timer run the whole time this driver instance lives */ + mod_timer(&priv->bss_list_timer, jiffies + BSS_LIST_TIMEOUT); exit: return ret; @@ -2556,13 +5342,15 @@ exit: static void at76_delete_device(struct at76_priv *priv) { + int i; + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); /* The device is gone, don't bother turning it off */ priv->device_unplugged = 1; - if (priv->mac80211_registered) - ieee80211_unregister_hw(priv->hw); + if (priv->netdev_registered) + unregister_netdev(priv->netdev); /* assuming we used keventd, it must quiesce too */ flush_scheduled_work(); @@ -2583,11 +5371,25 @@ static void at76_delete_device(struct at76_priv *priv) if (priv->rx_skb) kfree_skb(priv->rx_skb); + at76_free_bss_list(priv); + del_timer_sync(&priv->bss_list_timer); + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_beacon); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); + + if (priv->mac_state == MAC_CONNECTED) + at76_iwevent_bss_disconnect(priv->netdev); + + for (i = 0; i < NR_RX_DATA_BUF; i++) + if (priv->rx_data[i].skb) { + dev_kfree_skb(priv->rx_data[i].skb); + priv->rx_data[i].skb = NULL; + } usb_put_dev(priv->udev); - at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/ieee80211_hw", - __func__); - ieee80211_free_hw(priv->hw); + at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/netdev", __func__); + free_netdev(priv->netdev); /* priv is in netdev */ at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); } @@ -2621,8 +5423,8 @@ static int at76_probe(struct usb_interface *interface, we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ if (op_mode == OPMODE_HW_CONFIG_MODE) { - dev_err(&interface->dev, - "cannot handle a device in HW_CONFIG_MODE\n"); + dev_printk(KERN_ERR, &interface->dev, + "cannot handle a device in HW_CONFIG_MODE\n"); ret = -EBUSY; goto error; } @@ -2630,12 +5432,13 @@ static int at76_probe(struct usb_interface *interface, if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { /* download internal firmware part */ - dev_dbg(&interface->dev, "downloading internal firmware\n"); + dev_printk(KERN_DEBUG, &interface->dev, + "downloading internal firmware\n"); ret = at76_load_internal_fw(udev, fwe); if (ret < 0) { - dev_err(&interface->dev, - "error %d downloading internal firmware\n", - ret); + dev_printk(KERN_ERR, &interface->dev, + "error %d downloading internal firmware\n", + ret); goto error; } usb_put_dev(udev); @@ -2660,7 +5463,8 @@ static int at76_probe(struct usb_interface *interface, need_ext_fw = 1; if (need_ext_fw) { - dev_dbg(&interface->dev, "downloading external firmware\n"); + dev_printk(KERN_DEBUG, &interface->dev, + "downloading external firmware\n"); ret = at76_load_external_fw(udev, fwe); if (ret) @@ -2669,8 +5473,8 @@ static int at76_probe(struct usb_interface *interface, /* Re-check firmware version */ ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); if (ret < 0) { - dev_err(&interface->dev, - "error %d getting firmware version\n", ret); + dev_printk(KERN_ERR, &interface->dev, + "error %d getting firmware version\n", ret); goto error; } } @@ -2681,6 +5485,7 @@ static int at76_probe(struct usb_interface *interface, goto error; } + SET_NETDEV_DEV(priv->netdev, &interface->dev); usb_set_intfdata(interface, priv); memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version)); @@ -2708,7 +5513,7 @@ static void at76_disconnect(struct usb_interface *interface) if (!priv) return; - printk(KERN_INFO "%s: disconnecting\n", wiphy_name(priv->hw->wiphy)); + printk(KERN_INFO "%s: disconnecting\n", priv->netdev->name); at76_delete_device(priv); dev_printk(KERN_INFO, &interface->dev, "disconnected\n"); } @@ -2764,8 +5569,5 @@ MODULE_AUTHOR("Alex "); MODULE_AUTHOR("Nick Jones"); MODULE_AUTHOR("Balint Seeber "); MODULE_AUTHOR("Pavel Roskin "); -MODULE_AUTHOR("Guido Guenther "); -MODULE_AUTHOR("Kalle Valo "); -MODULE_AUTHOR("Milan Plzik "); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/staging/at76_usb/at76_usb.h b/drivers/staging/at76_usb/at76_usb.h index 8bb352f16d45..b20be9da1fa1 100644 --- a/drivers/staging/at76_usb/at76_usb.h +++ b/drivers/staging/at76_usb/at76_usb.h @@ -34,6 +34,23 @@ enum board_type { BOARD_505AMX = 8 }; +/* our private ioctl's */ +/* preamble length (0 - long, 1 - short, 2 - auto) */ +#define AT76_SET_SHORT_PREAMBLE (SIOCIWFIRSTPRIV + 0) +#define AT76_GET_SHORT_PREAMBLE (SIOCIWFIRSTPRIV + 1) +/* which debug channels are enabled */ +#define AT76_SET_DEBUG (SIOCIWFIRSTPRIV + 2) +#define AT76_GET_DEBUG (SIOCIWFIRSTPRIV + 3) +/* power save mode (incl. the Atmel proprietary smart save mode) */ +#define AT76_SET_POWERSAVE_MODE (SIOCIWFIRSTPRIV + 4) +#define AT76_GET_POWERSAVE_MODE (SIOCIWFIRSTPRIV + 5) +/* min and max channel times for scan */ +#define AT76_SET_SCAN_TIMES (SIOCIWFIRSTPRIV + 6) +#define AT76_GET_SCAN_TIMES (SIOCIWFIRSTPRIV + 7) +/* scan mode (0 - active, 1 - passive) */ +#define AT76_SET_SCAN_MODE (SIOCIWFIRSTPRIV + 8) +#define AT76_GET_SCAN_MODE (SIOCIWFIRSTPRIV + 9) + #define CMD_STATUS_IDLE 0x00 #define CMD_STATUS_COMPLETE 0x01 #define CMD_STATUS_UNKNOWN 0x02 @@ -65,7 +82,6 @@ enum board_type { #define MIB_MAC 0x03 #define MIB_MAC_MGMT 0x05 #define MIB_MAC_WEP 0x06 -#define MIB_MAC_ENCRYPTION 0x06 #define MIB_PHY 0x07 #define MIB_FW_VERSION 0x08 #define MIB_MDOMAIN 0x09 @@ -90,26 +106,6 @@ enum board_type { #define AT76_PM_ON 2 #define AT76_PM_SMART 3 -/* cipher values for encryption keys */ -#define CIPHER_NONE 0 /* this value is only guessed */ -#define CIPHER_WEP64 1 -#define CIPHER_TKIP 2 -#define CIPHER_CCMP 3 -#define CIPHER_CCX 4 /* for consistency sake only */ -#define CIPHER_WEP128 5 - -/* bit flags key types for encryption keys */ -#define KEY_PAIRWISE 2 -#define KEY_TX 4 - -#define CIPHER_KEYS (4) -#define CIPHER_KEY_LEN (40) - -struct key_config { - u8 cipher; - u8 keylen; -}; - struct hwcfg_r505 { u8 cr39_values[14]; u8 reserved1[14]; @@ -151,9 +147,6 @@ union at76_hwcfg { #define WEP_SMALL_KEY_LEN (40 / 8) #define WEP_LARGE_KEY_LEN (104 / 8) -#define WEP_KEYS (4) - - struct at76_card_config { u8 exclude_unencrypted; @@ -168,7 +161,7 @@ struct at76_card_config { u8 privacy_invoked; u8 wep_default_key_id; /* 0..3 */ u8 current_ssid[32]; - u8 wep_default_key_value[4][WEP_LARGE_KEY_LEN]; + u8 wep_default_key_value[4][WEP_KEY_LEN]; u8 ssid_len; u8 short_preamble; __le16 beacon_period; @@ -193,7 +186,7 @@ struct at76_rx_buffer { u8 link_quality; u8 noise_level; __le32 rx_time; - u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; + u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN]; } __attribute__((packed)); /* Length of Atmel-specific Tx header before 802.11 frame */ @@ -203,11 +196,8 @@ struct at76_tx_buffer { __le16 wlength; u8 tx_rate; u8 padding; - u8 key_id; - u8 cipher_type; - u8 cipher_length; - u8 reserved; - u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; + u8 reserved[4]; + u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN]; } __attribute__((packed)); /* defines for scan_type below */ @@ -254,7 +244,6 @@ struct set_mib_buffer { u8 byte; __le16 word; u8 addr[ETH_ALEN]; - u8 data[256]; /* we need more space for mib_mac_encryption */ } data; } __attribute__((packed)); @@ -328,24 +317,10 @@ struct mib_mac_wep { u8 exclude_unencrypted; __le32 wep_icv_error_count; __le32 wep_excluded_count; - u8 wep_default_keyvalue[WEP_KEYS][WEP_LARGE_KEY_LEN]; + u8 wep_default_keyvalue[WEP_KEYS][WEP_KEY_LEN]; u8 encryption_level; /* 1 for 40bit, 2 for 104bit encryption */ } __attribute__((packed)); -struct mib_mac_encryption { - u8 cipher_default_keyvalue[CIPHER_KEYS][CIPHER_KEY_LEN]; - u8 tkip_bssid[6]; - u8 privacy_invoked; - u8 cipher_default_key_id; - u8 cipher_default_group_key_id; - u8 exclude_unencrypted; - u8 wep_encryption_type; - u8 ckip_key_permutation; /* bool */ - __le32 wep_icv_error_count; - __le32 wep_excluded_count; - u8 key_rsc[CIPHER_KEYS][8]; -} __attribute__((packed)); - struct mib_phy { __le32 ed_threshold; @@ -389,6 +364,16 @@ struct at76_fw_header { __le32 ext_fw_len; /* external firmware image length */ } __attribute__((packed)); +enum mac_state { + MAC_INIT, + MAC_SCANNING, + MAC_AUTH, + MAC_ASSOC, + MAC_JOINING, + MAC_CONNECTED, + MAC_OWN_IBSS +}; + /* a description of a regulatory domain and the allowed channels */ struct reg_domain { u16 code; @@ -396,6 +381,47 @@ struct reg_domain { u32 channel_map; /* if bit N is set, channel (N+1) is allowed */ }; +/* how long do we keep a (I)BSS in the bss_list in jiffies + this should be long enough for the user to retrieve the table + (by iwlist ?) after the device started, because all entries from + other channels than the one the device locks on get removed, too */ +#define BSS_LIST_TIMEOUT (120 * HZ) +/* struct to store BSS info found during scan */ +#define BSS_LIST_MAX_RATE_LEN 32 /* 32 rates should be enough ... */ + +struct bss_info { + struct list_head list; + + u8 bssid[ETH_ALEN]; /* bssid */ + u8 ssid[IW_ESSID_MAX_SIZE]; /* essid */ + u8 ssid_len; /* length of ssid above */ + u8 channel; + u16 capa; /* BSS capabilities */ + u16 beacon_interval; /* beacon interval, Kus (1024 microseconds) */ + u8 rates[BSS_LIST_MAX_RATE_LEN]; /* supported rates in units of + 500 kbps, ORed with 0x80 for + basic rates */ + u8 rates_len; + + /* quality of received beacon */ + u8 rssi; + u8 link_qual; + u8 noise_level; + + unsigned long last_rx; /* time (jiffies) of last beacon received */ +}; + +/* a rx data buffer to collect rx fragments */ +struct rx_data_buf { + u8 sender[ETH_ALEN]; /* sender address */ + u16 seqnr; /* sequence number */ + u16 fragnr; /* last fragment received */ + unsigned long last_rx; /* jiffies of last rx */ + struct sk_buff *skb; /* == NULL if entry is free */ +}; + +#define NR_RX_DATA_BUF 8 + /* Data for one loaded firmware file */ struct fwentry { const char *const fwname; @@ -412,9 +438,11 @@ struct fwentry { struct at76_priv { struct usb_device *udev; /* USB device pointer */ + struct net_device *netdev; /* net device pointer */ + struct net_device_stats stats; /* net device stats */ + struct iw_statistics wstats; /* wireless stats */ struct sk_buff *rx_skb; /* skbuff for receiving data */ - struct sk_buff *tx_skb; /* skbuff for transmitting data */ void *bulk_out_buffer; /* buffer for sending data */ struct urb *tx_urb; /* URB for sending data */ @@ -426,17 +454,26 @@ struct at76_priv { struct mutex mtx; /* locks this structure */ /* work queues */ + struct work_struct work_assoc_done; + struct work_struct work_join; + struct work_struct work_new_bss; + struct work_struct work_start_scan; struct work_struct work_set_promisc; struct work_struct work_submit_rx; - struct delayed_work dwork_hw_scan; + struct delayed_work dwork_restart; + struct delayed_work dwork_get_scan; + struct delayed_work dwork_beacon; + struct delayed_work dwork_auth; + struct delayed_work dwork_assoc; struct tasklet_struct rx_tasklet; /* the WEP stuff */ int wep_enabled; /* 1 if WEP is enabled */ int wep_key_id; /* key id to be used */ - u8 wep_keys[WEP_KEYS][WEP_LARGE_KEY_LEN]; /* WEP keys */ - u8 wep_keys_len[WEP_KEYS]; /* length of WEP keys */ + u8 wep_keys[WEP_KEYS][WEP_KEY_LEN]; /* the four WEP keys, + 5 or 13 bytes are used */ + u8 wep_keys_len[WEP_KEYS]; /* the length of the above keys */ int channel; int iw_mode; @@ -458,13 +495,44 @@ struct at76_priv { int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */ int scan_need_any; /* if set, need to scan for any ESSID */ + /* the list we got from scanning */ + spinlock_t bss_list_spinlock; /* protects bss_list operations */ + struct list_head bss_list; /* list of BSS we got beacons from */ + struct timer_list bss_list_timer; /* timer to purge old entries + from bss_list */ + struct bss_info *curr_bss; /* current BSS */ u16 assoc_id; /* current association ID, if associated */ + u8 wanted_bssid[ETH_ALEN]; + int wanted_bssid_valid; /* != 0 if wanted_bssid is to be used */ + + /* some data for infrastructure mode only */ + spinlock_t mgmt_spinlock; /* this spinlock protects access to + next_mgmt_bulk */ + + struct at76_tx_buffer *next_mgmt_bulk; /* pending management msg to + send via bulk out */ + enum mac_state mac_state; + enum { + SCAN_IDLE, + SCAN_IN_PROGRESS, + SCAN_COMPLETED + } scan_state; + time_t last_scan; + + int retries; /* remaining retries in case of timeout when + * sending AuthReq or AssocReq */ u8 pm_mode; /* power management mode */ u32 pm_period; /* power management period in microseconds */ struct reg_domain const *domain; /* reg domain description */ + /* iwspy support */ + spinlock_t spy_spinlock; + struct iw_spy_data spy_data; + + struct iw_public_data wireless_data; + /* These fields contain HW config provided by the device (not all of * these fields are used by all board types) */ u8 mac_addr[ETH_ALEN]; @@ -472,6 +540,9 @@ struct at76_priv { struct at76_card_config card_config; + /* store rx fragments until complete */ + struct rx_data_buf rx_data[NR_RX_DATA_BUF]; + enum board_type board_type; struct mib_fw_version fw_version; @@ -479,20 +550,58 @@ struct at76_priv { unsigned int netdev_registered:1; struct set_mib_buffer mib_buf; /* global buffer for set_mib calls */ + /* beacon counting */ int beacon_period; /* period of mgmt beacons, Kus */ + int beacons_received; + unsigned long beacons_last_qual; /* time we restarted counting + beacons */ +}; - struct ieee80211_hw *hw; - int mac80211_registered; - - struct key_config keys[4]; /* installed key types */ - u8 default_pairwise_key; - u8 default_group_key; +struct at76_rx_radiotap { + struct ieee80211_radiotap_header rt_hdr; + __le64 rt_tsft; + u8 rt_flags; + u8 rt_rate; + s8 rt_signal; + s8 rt_noise; }; -#define AT76_SUPPORTED_FILTERS FIF_PROMISC_IN_BSS +#define AT76_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTNOISE)) + +#define BEACON_MAX_DATA_LENGTH 1500 + +/* the maximum size of an AssocReq packet */ +#define ASSOCREQ_MAX_SIZE \ + (AT76_TX_HDRLEN + sizeof(struct ieee80211_assoc_request) + \ + 1 + 1 + IW_ESSID_MAX_SIZE + 1 + 1 + 4) + +/* for shared secret auth, add the challenge text size */ +#define AUTH_FRAME_SIZE (AT76_TX_HDRLEN + sizeof(struct ieee80211_auth)) + +/* Maximal number of AuthReq retries */ +#define AUTH_RETRIES 3 +/* Maximal number of AssocReq retries */ +#define ASSOC_RETRIES 3 + +/* Beacon timeout in managed mode when we are connected */ +#define BEACON_TIMEOUT (10 * HZ) + +/* Timeout for authentication response */ +#define AUTH_TIMEOUT (1 * HZ) + +/* Timeout for association response */ +#define ASSOC_TIMEOUT (1 * HZ) + +/* Polling interval when scan is running */ #define SCAN_POLL_INTERVAL (HZ / 4) +/* Command completion timeout */ #define CMD_COMPLETION_TIMEOUT (5 * HZ) #define DEF_RTS_THRESHOLD 1536 @@ -502,6 +611,8 @@ struct at76_priv { #define DEF_SCAN_MIN_TIME 10 #define DEF_SCAN_MAX_TIME 120 +#define MAX_RTS_THRESHOLD (MAX_FRAG_THRESHOLD + 1) + /* the max padding size for tx in bytes (see calc_padding) */ #define MAX_PADDING_SIZE 53 -- cgit v1.2.1 From ea8f9fe634da9042c01ca2f4e459a7b187056021 Mon Sep 17 00:00:00 2001 From: Jason Andryuk Date: Fri, 30 Jan 2009 09:05:03 -0500 Subject: Staging: at76_usb: fix bugs introduced by "Staging: at76_usb: cleanup dma on stack issues" Tracking down the firmware loading problem led to this commit. $ git bisect bad 0d1d1424330cc1934f2b2742f0cfa2c31e6a250b is first bad commit commit 0d1d1424330cc1934f2b2742f0cfa2c31e6a250b Author: Oliver Neukum Date: Thu Dec 18 13:16:40 2008 +0100 Staging: at76_usb: cleanup dma on stack issues - no DMA on stack - cleanup unclear endianness issue Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman :040000 040000 c4fee9ea0fef25926229d810d19dc2f89cca9401 8b165a35d16280d2413b2700a6080ef290ca1009 M drivers The "no DMA on stack" conversion was incomplete with respect to updating the arguments passed to usb_control_msg. The value 40 is hardcoded as it was prior to conversion. The driver can now load firmware, but is not fully functional. Signed-off-by: Jason Andryuk Cc: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/staging/at76_usb/at76_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/at76_usb/at76_usb.c b/drivers/staging/at76_usb/at76_usb.c index 9195ee9319ff..06ae16341337 100644 --- a/drivers/staging/at76_usb/at76_usb.c +++ b/drivers/staging/at76_usb/at76_usb.c @@ -649,7 +649,7 @@ static int at76_get_op_mode(struct usb_device *udev) return -ENOMEM; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, 0x01, 0, &op_mode, 1, + USB_RECIP_INTERFACE, 0x01, 0, op_mode, 1, USB_CTRL_GET_TIMEOUT); saved = *op_mode; kfree(op_mode); @@ -782,7 +782,7 @@ static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE, cmd, 0, stat_buf, - sizeof(stat_buf), USB_CTRL_GET_TIMEOUT); + 40, USB_CTRL_GET_TIMEOUT); if (ret >= 0) ret = stat_buf[5]; kfree(stat_buf); -- cgit v1.2.1 From 07f269862a2981f1512de5393e2d0ce5b2ee8305 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Wed, 4 Feb 2009 13:38:44 +0000 Subject: Staging: at76_usb: Add support for OQO Model 01+ Add USB device ID for OQO 01+'s internal wireless LAN An OQO employee mentions the chip's true identity here:- ftp://ftp.oqo.com/unsupported/linux/OQOLinux.html Signed-off-by: Jamie Lentin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/at76_usb/at76_usb.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/at76_usb/at76_usb.c b/drivers/staging/at76_usb/at76_usb.c index 06ae16341337..c8e4d31c7df2 100644 --- a/drivers/staging/at76_usb/at76_usb.c +++ b/drivers/staging/at76_usb/at76_usb.c @@ -231,6 +231,8 @@ static struct usb_device_id dev_table[] = { {USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A)}, /* Siemens Gigaset USB WLAN Adapter 11 */ {USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A)}, + /* OQO Model 01+ Internal Wi-Fi */ + {USB_DEVICE(0x1557, 0x0002), USB_DEVICE_DATA(BOARD_505A)}, /* * at76c505amx-rfmd */ -- cgit v1.2.1 From 5701c0519b7a357a602fda5c96f26197ecfc4c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 30 Jan 2009 20:21:09 -0800 Subject: Staging: android: ram_console: Disable ECC when early init is enabled and validate buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/Kconfig | 1 + drivers/staging/android/ram_console.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 6b996db0dd6a..604bd1e0d546 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -27,6 +27,7 @@ menuconfig ANDROID_RAM_CONSOLE_ERROR_CORRECTION bool "Android RAM Console Enable error correction" default n depends on ANDROID_RAM_CONSOLE + depends on !ANDROID_RAM_CONSOLE_EARLY_INIT select REED_SOLOMON select REED_SOLOMON_ENC8 select REED_SOLOMON_DEC8 diff --git a/drivers/staging/android/ram_console.c b/drivers/staging/android/ram_console.c index bf006857a87a..643ac5ce381d 100644 --- a/drivers/staging/android/ram_console.c +++ b/drivers/staging/android/ram_console.c @@ -224,9 +224,23 @@ static int __init ram_console_init(struct ram_console_buffer *buffer, ram_console_buffer_size = buffer_size - sizeof(struct ram_console_buffer); + if (ram_console_buffer_size > buffer_size) { + pr_err("ram_console: buffer %p, invalid size %d, datasize %d\n", + buffer, buffer_size, ram_console_buffer_size); + return 0; + } + #ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION ram_console_buffer_size -= (DIV_ROUND_UP(ram_console_buffer_size, ECC_BLOCK_SIZE) + 1) * ECC_SIZE; + + if (ram_console_buffer_size > buffer_size) { + pr_err("ram_console: buffer %p, invalid size %d, " + "non-ecc datasize %d\n", + buffer, buffer_size, ram_console_buffer_size); + return 0; + } + ram_console_par_buffer = buffer->data + ram_console_buffer_size; -- cgit v1.2.1 From d88dfb8dc4bfb66066e7c68727c219faaa541206 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 4 Feb 2009 15:05:05 -0800 Subject: Staging: android: fix up units in timed_gpio The last build fix I did messed up the units of the sysfs file. This puts them back to be milliseconds, like they originally were. Thanks to Juha Motorsportcom for pointing this out. Reported-by: Juha Motorsportcom Cc: Mike Lockwood Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/timed_gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c index 903270cbbe02..33daff0481d2 100644 --- a/drivers/staging/android/timed_gpio.c +++ b/drivers/staging/android/timed_gpio.c @@ -50,7 +50,7 @@ static ssize_t gpio_enable_show(struct device *dev, struct device_attribute *att if (hrtimer_active(&gpio_data->timer)) { ktime_t r = hrtimer_get_remaining(&gpio_data->timer); struct timeval t = ktime_to_timeval(r); - remaining = t.tv_sec * 1000 + t.tv_usec; + remaining = t.tv_sec * 1000 + t.tv_usec / 1000; } else remaining = 0; -- cgit v1.2.1 From 6136ac86b719716694884a84cd9d403207cc52b9 Mon Sep 17 00:00:00 2001 From: "Sachin P. Sant" Date: Tue, 3 Feb 2009 21:10:58 +0530 Subject: Staging: panel: fix lcd panel driver build failure * Fix build break for lcd panel driver. Signed-off-by : Sachin Sant Cc: Willy Tarreau Signed-off-by: Greg Kroah-Hartman --- drivers/staging/panel/panel.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/panel/panel.c b/drivers/staging/panel/panel.c index 5ffe269c2382..ab69c1bf36a8 100644 --- a/drivers/staging/panel/panel.c +++ b/drivers/staging/panel/panel.c @@ -622,7 +622,7 @@ static int set_ctrl_bits(void) } /* sets ctrl & data port bits according to current signals values */ -static void set_bits(void) +static void panel_set_bits(void) { set_data_bits(); set_ctrl_bits(); @@ -707,12 +707,12 @@ static void lcd_send_serial(int byte) */ for (bit = 0; bit < 8; bit++) { bits.cl = BIT_CLR; /* CLK low */ - set_bits(); + panel_set_bits(); bits.da = byte & 1; - set_bits(); + panel_set_bits(); udelay(2); /* maintain the data during 2 us before CLK up */ bits.cl = BIT_SET; /* CLK high */ - set_bits(); + panel_set_bits(); udelay(1); /* maintain the strobe during 1 us */ byte >>= 1; } @@ -727,7 +727,7 @@ static void lcd_backlight(int on) /* The backlight is activated by seting the AUTOFEED line to +5V */ spin_lock(&pprt_lock); bits.bl = on; - set_bits(); + panel_set_bits(); spin_unlock(&pprt_lock); } -- cgit v1.2.1 From 8707bdd48ab705a459ac1b12014075a139d1d4f9 Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Mon, 9 Feb 2009 14:59:30 -0800 Subject: gianfar: Fix boot hangs while bringing up gianfar ethernet Ira Snyder found that commit 8c7396aebb68994c0519e438eecdf4d5fa9c7844 "gianfar: Merge Tx and Rx interrupt for scheduling clean up ring" can cause hangs. It's because there was removed clearing of interrupts in gfar_schedule_cleanup() (which is called by an interrupt handler) in case when netif scheduling has been disabled. This patch brings back this action and a comment. Reported-by: Ira Snyder Reported-by: Peter Korsgaard Bisected-by: Ira Snyder Tested-by: Peter Korsgaard Tested-by: Ira Snyder Signed-off-by: Jarek Poplawski Signed-off-by: David S. Miller --- drivers/net/gianfar.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index acae2d8cd688..9b12a13a640f 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -1629,6 +1629,12 @@ static void gfar_schedule_cleanup(struct net_device *dev) if (netif_rx_schedule_prep(&priv->napi)) { gfar_write(&priv->regs->imask, IMASK_RTX_DISABLED); __netif_rx_schedule(&priv->napi); + } else { + /* + * Clear IEVENT, so interrupts aren't called again + * because of the packets that have already arrived. + */ + gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK); } spin_unlock(&priv->rxlock); -- cgit v1.2.1 From ca0b4b7d2cb57a2e24d7e48ce9b411b9baa3bf63 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Wed, 11 Feb 2009 10:37:30 +0100 Subject: [S390] dasd: bus_id -> dev_name() conversion. bus_id usage crept in again; fix it. Signed-off-by: Cornelia Huck Signed-off-by: Heiko Carstens --- drivers/s390/block/dasd_devmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 300e28a531f8..34339902efb9 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -677,7 +677,7 @@ static ssize_t dasd_ff_show(struct device *dev, struct device_attribute *attr, struct dasd_devmap *devmap; int ff_flag; - devmap = dasd_find_busid(dev->bus_id); + devmap = dasd_find_busid(dev_name(dev)); if (!IS_ERR(devmap)) ff_flag = (devmap->features & DASD_FEATURE_FAILFAST) != 0; else -- cgit v1.2.1 From 48cae885d5a896030588978f503c73c5ed5e62b1 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Wed, 11 Feb 2009 10:37:31 +0100 Subject: [S390] dasd: fix race in dasd timer handling In dasd_device_set_timer and dasd_block_set_timer we interpret the return value of mod_timer in a wrong way. If the timer expires in the small window between our check of timer_pending and the call to mod_timer, then the timer will be set, mod_timer returns zero and we will call add_timer for a timer that is already pending. As del_timer and mod_timer do all the necessary checking themselves, we can simplify our code and remove the race a the same time. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index bd5914994142..08c23a921012 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -57,6 +57,8 @@ static void dasd_device_tasklet(struct dasd_device *); static void dasd_block_tasklet(struct dasd_block *); static void do_kick_device(struct work_struct *); static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); +static void dasd_device_timeout(unsigned long); +static void dasd_block_timeout(unsigned long); /* * SECTION: Operations on the device structure. @@ -99,6 +101,8 @@ struct dasd_device *dasd_alloc_device(void) (unsigned long) device); INIT_LIST_HEAD(&device->ccw_queue); init_timer(&device->timer); + device->timer.function = dasd_device_timeout; + device->timer.data = (unsigned long) device; INIT_WORK(&device->kick_work, do_kick_device); device->state = DASD_STATE_NEW; device->target = DASD_STATE_NEW; @@ -138,6 +142,8 @@ struct dasd_block *dasd_alloc_block(void) INIT_LIST_HEAD(&block->ccw_queue); spin_lock_init(&block->queue_lock); init_timer(&block->timer); + block->timer.function = dasd_block_timeout; + block->timer.data = (unsigned long) block; return block; } @@ -915,19 +921,10 @@ static void dasd_device_timeout(unsigned long ptr) */ void dasd_device_set_timer(struct dasd_device *device, int expires) { - if (expires == 0) { - if (timer_pending(&device->timer)) - del_timer(&device->timer); - return; - } - if (timer_pending(&device->timer)) { - if (mod_timer(&device->timer, jiffies + expires)) - return; - } - device->timer.function = dasd_device_timeout; - device->timer.data = (unsigned long) device; - device->timer.expires = jiffies + expires; - add_timer(&device->timer); + if (expires == 0) + del_timer(&device->timer); + else + mod_timer(&device->timer, jiffies + expires); } /* @@ -935,8 +932,7 @@ void dasd_device_set_timer(struct dasd_device *device, int expires) */ void dasd_device_clear_timer(struct dasd_device *device) { - if (timer_pending(&device->timer)) - del_timer(&device->timer); + del_timer(&device->timer); } static void dasd_handle_killed_request(struct ccw_device *cdev, @@ -1586,19 +1582,10 @@ static void dasd_block_timeout(unsigned long ptr) */ void dasd_block_set_timer(struct dasd_block *block, int expires) { - if (expires == 0) { - if (timer_pending(&block->timer)) - del_timer(&block->timer); - return; - } - if (timer_pending(&block->timer)) { - if (mod_timer(&block->timer, jiffies + expires)) - return; - } - block->timer.function = dasd_block_timeout; - block->timer.data = (unsigned long) block; - block->timer.expires = jiffies + expires; - add_timer(&block->timer); + if (expires == 0) + del_timer(&block->timer); + else + mod_timer(&block->timer, jiffies + expires); } /* @@ -1606,8 +1593,7 @@ void dasd_block_set_timer(struct dasd_block *block, int expires) */ void dasd_block_clear_timer(struct dasd_block *block) { - if (timer_pending(&block->timer)) - del_timer(&block->timer); + del_timer(&block->timer); } /* -- cgit v1.2.1 From 57f63bc8fe79e6598e7253f10f53f58c9fdc57be Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Wed, 11 Feb 2009 13:04:19 -0800 Subject: rtc: update maintainership of pxa rtc driver Signed-off-by: Robert Jarzmik Signed-off-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pxa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index bd56a033bfd0..bb8cc05605ac 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -485,7 +485,7 @@ static void __exit pxa_rtc_exit(void) module_init(pxa_rtc_init); module_exit(pxa_rtc_exit); -MODULE_AUTHOR("Robert Jarzmik"); +MODULE_AUTHOR("Robert Jarzmik "); MODULE_DESCRIPTION("PXA27x/PXA3xx Realtime Clock Driver (RTC)"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:pxa-rtc"); -- cgit v1.2.1 From 067f1293cc5916f8d88b602beeb8787d58515608 Mon Sep 17 00:00:00 2001 From: Marco La Porta Date: Wed, 11 Feb 2009 13:04:20 -0800 Subject: lxfb: properly alloc cmap in all cases and don't leak the memory We weren't properly allocating the cmap for depths greater than 8bpp, which caused pain for things like DirectFB. Also, we never freed the cmap memory upon module unload.. [dilinger@debian.org: dropped unnecessary code and clean up patch] [dilinger@debian.org: add error checking and handling] Signed-off-by: Andres Salomon Cc: Jordan Crouse Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/geode/lxfb_core.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/video/geode/lxfb_core.c b/drivers/video/geode/lxfb_core.c index b965ecdbc604..889cbe39e580 100644 --- a/drivers/video/geode/lxfb_core.c +++ b/drivers/video/geode/lxfb_core.c @@ -278,13 +278,10 @@ static int lxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) static int lxfb_set_par(struct fb_info *info) { - if (info->var.bits_per_pixel > 8) { + if (info->var.bits_per_pixel > 8) info->fix.visual = FB_VISUAL_TRUECOLOR; - fb_dealloc_cmap(&info->cmap); - } else { + else info->fix.visual = FB_VISUAL_PSEUDOCOLOR; - fb_alloc_cmap(&info->cmap, 1<var.bits_per_pixel, 0); - } info->fix.line_length = lx_get_pitch(info->var.xres, info->var.bits_per_pixel); @@ -451,6 +448,11 @@ static struct fb_info * __init lxfb_init_fbinfo(struct device *dev) info->pseudo_palette = (void *)par + sizeof(struct lxfb_par); + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + framebuffer_release(info); + return NULL; + } + info->var.grayscale = 0; return info; @@ -579,8 +581,10 @@ err: pci_release_region(pdev, 3); } - if (info) + if (info) { + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); + } return ret; } @@ -604,6 +608,7 @@ static void lxfb_remove(struct pci_dev *pdev) iounmap(par->vp_regs); pci_release_region(pdev, 3); + fb_dealloc_cmap(&info->cmap); pci_set_drvdata(pdev, NULL); framebuffer_release(info); } -- cgit v1.2.1 From b14caecdbe7730bf82c8510f1ba52e00273e15c4 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Wed, 11 Feb 2009 13:04:22 -0800 Subject: gxfb: properly alloc cmap and plug cmap leak We weren't properly allocating the cmap for depths greater than 8bpp, which caused pain for things like DirectFB. Also, we never freed the cmap memory upon module unload.. Signed-off-by: Andres Salomon Cc: Marco La Porta Cc: Jordan Crouse Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/geode/gxfb_core.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c index 484118926318..2552cac39e1c 100644 --- a/drivers/video/geode/gxfb_core.c +++ b/drivers/video/geode/gxfb_core.c @@ -171,13 +171,10 @@ static int gxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) static int gxfb_set_par(struct fb_info *info) { - if (info->var.bits_per_pixel > 8) { + if (info->var.bits_per_pixel > 8) info->fix.visual = FB_VISUAL_TRUECOLOR; - fb_dealloc_cmap(&info->cmap); - } else { + else info->fix.visual = FB_VISUAL_PSEUDOCOLOR; - fb_alloc_cmap(&info->cmap, 1<var.bits_per_pixel, 0); - } info->fix.line_length = gx_line_delta(info->var.xres, info->var.bits_per_pixel); @@ -331,6 +328,11 @@ static struct fb_info * __init gxfb_init_fbinfo(struct device *dev) info->var.grayscale = 0; + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + framebuffer_release(info); + return NULL; + } + return info; } @@ -443,8 +445,10 @@ static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *i pci_release_region(pdev, 1); } - if (info) + if (info) { + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); + } return ret; } @@ -467,6 +471,7 @@ static void gxfb_remove(struct pci_dev *pdev) iounmap(par->gp_regs); pci_release_region(pdev, 1); + fb_dealloc_cmap(&info->cmap); pci_set_drvdata(pdev, NULL); framebuffer_release(info); -- cgit v1.2.1 From 35887b1cf74dc751dd0574b26515142d3cea9376 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Wed, 11 Feb 2009 13:04:23 -0800 Subject: gx1fb: properly alloc cmap and plug cmap leak We weren't properly allocating the cmap for depths greater than 8bpp, which caused pain for things like DirectFB. Also, we never freed the cmap memory upon module unload.. Signed-off-by: Andres Salomon Cc: Marco La Porta Cc: Jordan Crouse Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/geode/gx1fb_core.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/video/geode/gx1fb_core.c b/drivers/video/geode/gx1fb_core.c index 751e491ca8c8..f20eff8c4a81 100644 --- a/drivers/video/geode/gx1fb_core.c +++ b/drivers/video/geode/gx1fb_core.c @@ -136,13 +136,10 @@ static int gx1fb_set_par(struct fb_info *info) { struct geodefb_par *par = info->par; - if (info->var.bits_per_pixel == 16) { + if (info->var.bits_per_pixel == 16) info->fix.visual = FB_VISUAL_TRUECOLOR; - fb_dealloc_cmap(&info->cmap); - } else { + else info->fix.visual = FB_VISUAL_PSEUDOCOLOR; - fb_alloc_cmap(&info->cmap, 1<var.bits_per_pixel, 0); - } info->fix.line_length = gx1_line_delta(info->var.xres, info->var.bits_per_pixel); @@ -315,6 +312,10 @@ static struct fb_info * __init gx1fb_init_fbinfo(struct device *dev) if (!par->panel_x) par->enable_crt = 1; /* fall back to CRT if no panel is specified */ + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + framebuffer_release(info); + return NULL; + } return info; } @@ -374,8 +375,11 @@ static int __init gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id * release_mem_region(gx1_gx_base() + 0x8300, 0x100); } - if (info) + if (info) { + fb_dealloc_cmap(&info->cmap); framebuffer_release(info); + } + return ret; } @@ -395,6 +399,7 @@ static void gx1fb_remove(struct pci_dev *pdev) iounmap(par->dc_regs); release_mem_region(gx1_gx_base() + 0x8300, 0x100); + fb_dealloc_cmap(&info->cmap); pci_set_drvdata(pdev, NULL); framebuffer_release(info); -- cgit v1.2.1 From 7dcce1334fa5879dc12bee001962e8f74bce60f1 Mon Sep 17 00:00:00 2001 From: Marcel Selhorst Date: Wed, 11 Feb 2009 13:04:27 -0800 Subject: tpm: correct email address for tpm_infineon-driver Update my email address. Signed-off-by: Marcel Selhorst Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_infineon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 726ee8a0277f..ecba4942fc8e 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -4,7 +4,7 @@ * SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module * Specifications at www.trustedcomputinggroup.org * - * Copyright (C) 2005, Marcel Selhorst + * Copyright (C) 2005, Marcel Selhorst * Sirrix AG - security technologies, http://www.sirrix.com and * Applied Data Security Group, Ruhr-University Bochum, Germany * Project-Homepage: http://www.prosec.rub.de/tpm @@ -636,7 +636,7 @@ static void __exit cleanup_inf(void) module_init(init_inf); module_exit(cleanup_inf); -MODULE_AUTHOR("Marcel Selhorst "); +MODULE_AUTHOR("Marcel Selhorst "); MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); MODULE_VERSION("1.9"); MODULE_LICENSE("GPL"); -- cgit v1.2.1 From d4097456cd1d9285e876fc5d08a789462804cc28 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-Koenig Date: Wed, 11 Feb 2009 13:04:28 -0800 Subject: video/framebuffer: move the probe func into .devinit.text in Blackfin LCD driver Signed-off-by: Uwe Kleine-Koenig Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/bfin-t350mcqb-fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c index 2a423d3a2a8e..90cfddabf1f7 100644 --- a/drivers/video/bfin-t350mcqb-fb.c +++ b/drivers/video/bfin-t350mcqb-fb.c @@ -447,7 +447,7 @@ static irqreturn_t bfin_t350mcqb_irq_error(int irq, void *dev_id) return IRQ_HANDLED; } -static int __init bfin_t350mcqb_probe(struct platform_device *pdev) +static int __devinit bfin_t350mcqb_probe(struct platform_device *pdev) { struct bfin_t350mcqbfb_info *info; struct fb_info *fbinfo; -- cgit v1.2.1 From c318c7ac49f9139f55da619bbace6137e1509390 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 11 Feb 2009 13:04:34 -0800 Subject: rtc: t reaches -1, tested 0 With a postfix decrement t will reach -1 rather than 0, so neither the warning nor the `goto error_out' will occur. Signed-off-by: Roel Kluin Acked-by: Manuel Lauss Acked-by: Alessandro Zummo Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-au1xxx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c index 8906a688e6a6..979ed0406ce9 100644 --- a/drivers/rtc/rtc-au1xxx.c +++ b/drivers/rtc/rtc-au1xxx.c @@ -81,7 +81,7 @@ static int __devinit au1xtoy_rtc_probe(struct platform_device *pdev) if (au_readl(SYS_TOYTRIM) != 32767) { /* wait until hardware gives access to TRIM register */ t = 0x00100000; - while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T0S) && t--) + while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T0S) && --t) msleep(1); if (!t) { -- cgit v1.2.1 From 3abdbf90a3ffb006108c831c56b092e35483b6ec Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 11 Feb 2009 13:04:40 -0800 Subject: parport: parport_serial, don't bind netmos ibm 0299 Since netmos 9835 with subids 0x1014(IBM):0x0299 is now bound with serial/8250_pci, because it has no parallel ports and subdevice id isn't in the expected form, return -ENODEV from probe function. This is performed in netmos preinit_hook. Signed-off-by: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/parport/parport_serial.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 101ed49a2d15..032db815b0f9 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -64,6 +64,11 @@ struct parport_pc_pci { static int __devinit netmos_parallel_init(struct pci_dev *dev, struct parport_pc_pci *card, int autoirq, int autodma) { + /* the rule described below doesn't hold for this device */ + if (dev->device == PCI_DEVICE_ID_NETMOS_9835 && + dev->subsystem_vendor == PCI_VENDOR_ID_IBM && + dev->subsystem_device == 0x0299) + return -ENODEV; /* * Netmos uses the subdevice ID to indicate the number of parallel * and serial ports. The form is 0x00PS, where

is the number of -- cgit v1.2.1 From 4d48a542b42747c36a5937447d9c3de7c897ea50 Mon Sep 17 00:00:00 2001 From: Paul Clements Date: Wed, 11 Feb 2009 13:04:45 -0800 Subject: nbd: fix I/O hang on disconnected nbds Fix a problem that causes I/O to a disconnected (or partially initialized) nbd device to hang indefinitely. To reproduce: # ioctl NBD_SET_SIZE_BLOCKS /dev/nbd23 514048 # dd if=/dev/nbd23 of=/dev/null bs=4096 count=1 ...hangs... This can also occur when an nbd device loses its nbd-client/server connection. Although we clear the queue of any outstanding I/Os after the client/server connection fails, any additional I/Os that get queued later will hang. This bug may also be the problem reported in this bug report: http://bugzilla.kernel.org/show_bug.cgi?id=12277 Testing would need to be performed to determine if the two issues are the same. This problem was introduced by the new request handling thread code ("NBD: allow nbd to be used locally", 3/2008), which entered into mainline around 2.6.25. The fix, which is fairly simple, is to restore the check for lo->sock being NULL in do_nbd_request. This causes I/O to an uninitialized nbd to immediately fail with an I/O error, as it did prior to the introduction of this bug. Signed-off-by: Paul Clements Reported-by: Jon Nelson Acked-by: Pavel Machek Cc: [2.6.26.x, 2.6.27.x, 2.6.28.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/nbd.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 34f80fa6fed1..8299e2d3b611 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -549,6 +549,15 @@ static void do_nbd_request(struct request_queue * q) BUG_ON(lo->magic != LO_MAGIC); + if (unlikely(!lo->sock)) { + printk(KERN_ERR "%s: Attempted send on closed socket\n", + lo->disk->disk_name); + req->errors++; + nbd_end_request(req); + spin_lock_irq(q->queue_lock); + continue; + } + spin_lock_irq(&lo->queue_lock); list_add_tail(&req->queuelist, &lo->waiting_queue); spin_unlock_irq(&lo->queue_lock); -- cgit v1.2.1 From 507e2fbaaacb6f164b4125b87c5002f95143174b Mon Sep 17 00:00:00 2001 From: Ian Dall Date: Wed, 11 Feb 2009 13:04:46 -0800 Subject: w1: w1 temp calculation overflow fix Addresses http://bugzilla.kernel.org/show_bug.cgi?id=12646 When the temperature exceeds 32767 milli-degrees the temperature overflows to -32768 millidegrees. These are bothe well within the -55 - +125 degree range for the sensor. Fix overflow in left-shift of a u8. Signed-off-by: Ian Dall Signed-off-by: Evgeniy Polyakov Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/w1/slaves/w1_therm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 2c8dff9f77da..1ed3d554e372 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -115,7 +115,7 @@ static struct w1_therm_family_converter w1_therm_families[] = { static inline int w1_DS18B20_convert_temp(u8 rom[9]) { - s16 t = (rom[1] << 8) | rom[0]; + int t = ((s16)rom[1] << 8) | rom[0]; t = t*1000/16; return t; } -- cgit v1.2.1